美文网首页
C/C++:保存剪贴板中的截图为bmp文件(编译成dll供pyt

C/C++:保存剪贴板中的截图为bmp文件(编译成dll供pyt

作者: 雷霆同学 | 来源:发表于2018-02-11 17:13 被阅读0次

    获取更多文章和更新,请关注我的个人主页:https://leiting6.cn

    最近在研究windows API,仿佛发现新大陆一般 --- 想在windows系统下面玩的更溜一些,还是得了解系统本身的API函数才行。其实python下通过安装pywin32也可以调用win32API,但是总觉得是瞎折腾;而且,毕竟C语言是学生时期的启蒙语言,直接利用VC编写动态链接库(dll)也不是难事。刚好这些天想写一个自动保存截图的小工具,经过一番搜索和自己的修改完善,目前已经完成了核心部分。

    2018/10/21更新

    除了生成dll,也稍微修改一下代码生成了exe版本,方便更多语言调用,比如最近一直在研究的autohotkey,毕竟在ahk中调用exe更简单直接。
    dll和exe下载链接:点击跳转

    C/C++部分

    这部分只有一个函数,详情如下:

    • 函数名:SaveBmpFileFromClipboard()
    • 参数: char *file_name (BMP文件的文件名,可以带上完整路径)
    • 功能:从剪贴板保存图片为BMP格式到本地
    • 返回值: 成功保存则返回0, 遇到错误则返回1

    需要注意的是,想要保存一个BMP文件,还是要先了解BMP文件的构成的,点击查看

    简单的说:

    BMP文件=文件头(14字节)+信息头(40字节,全部默认,实测可不写入)+调色板(可选,不写入)+图片数据
    

    而我这里生成的:

    BMP文件=文件头(14字节)+图片数据
    

    因此BMP文件的数据构成为

    1. 写入事先手动设置好的文件头(占用14字节)
    2. 写入从剪贴板得到的图片数据

    return值说明

    0:保存bmp文成功
    1:剪贴板中内容不是DIB格式
    2:打开剪贴板失败
    3:路径无法访问/没有操作权限

    // SaveBmpFileFromClipboard.cpp : 定义 DLL 应用程序的导出函数。
    //
    
    
    //返回值:
    //0 - 存储bmp文件成功
    //1 - 剪贴板内容非DIB格式
    //2 - 打开剪贴板失败
    //3 - 存储bmp文件失败
    #include "stdafx.h"
    #include <windows.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <io.h>
    
    #define EXPORT extern "C" _declspec(dllexport)
    
    EXPORT int SaveBmpFileFromClipboard(const char *file_name = "capture.bmp")
    {
        if (!OpenClipboard(NULL))   //尝试打开剪贴板
        {
            printf("%s", "Open clipboard fail!\n");
            CloseClipboard(); //关闭剪贴板
            return 2;
        }
    
        HANDLE hDib;
        LPVOID lpDib;
        BITMAPFILEHEADER bfBuf;
    
        if (IsClipboardFormatAvailable(CF_DIB)) //检查剪贴板内容是否为DIB类型的数据
        {
            hDib = GetClipboardData(CF_DIB);    //获取剪贴板内容并存入hDib句柄
            lpDib = GlobalLock(hDib);           //利用GlobalLock锁住hDib占用的内存,并返回内存块的首字节指针
    
            printf("%s", "Getting data...\n");
            int iData = GlobalSize(lpDib);      //利用GlobalSize获取上述锁住的内存块的大小
                                                //bfBuf是BITMAPFILEHEADER结构体类型,下面手动指定BMP文件头参数
            bfBuf.bfType = 0x4d42;              //"BM"的ASCII码的hex值
            bfBuf.bfSize = iData + sizeof(BITMAPFILEHEADER);    //BMP文件大小=数据大小+文件头大小
            bfBuf.bfReserved1 = 0;  //固定值
            bfBuf.bfReserved2 = 0;  //固定值
            bfBuf.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);  //文件数据写入时的偏移量=文件头大小+信息头大小
    
            printf("%s", "Writing files...\n");
            //下面是创建文件并写入文件头和写入文件数据,即生成了BMP文件
            DWORD dwWriten;
            HANDLE hf = CreateFileA(file_name, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
            WriteFile(hf, &bfBuf, sizeof(BITMAPFILEHEADER), &dwWriten, NULL);                       //写入文件头
            WriteFile(hf, lpDib, iData, &dwWriten, NULL);   //写入图片数据
            CloseHandle(hf);
    
            if (!_access(file_name, 0))
            {
                printf("%s", "Bmp file saved!\n");
            }
            else
            {
                printf("%s", "Bmp file not saved!\n");
                CloseClipboard(); //关闭剪贴板
                return 3;
            }
    
            GlobalUnlock(hDib); //解锁被锁住的内存块
            GlobalFree(hDib);   //彻底释放hDib
        }
        else
        {
            printf("%s", "Not DIB format!\n");
            CloseClipboard(); //关闭剪贴板
            return 1;
        }
    
        CloseClipboard(); //关闭剪贴板
    
        return 0;
    }
    

    Python部分

    接下来python部分的代码就很简单了,导入ctypes模块,利用ctypes.cdll加载上述dll,传入代表文件路径和文件名的字符串:

    import ctypes
    
    file_name = r'D:\\clip.bmp'
    
    dll = ctypes.cdll.LoadLibrary('SaveBmpFileFromClipboard.dll')
    dll.SaveBmpFileFromClipboard(file_name.encode('gbk'))
    

    exe版本

    在dll上小修了一下,用main函数把代码包裹起来,这样就可以生成一个更方便调用和使用的exe格式可执行文件。需要注意的是,报错时的return值重新编排了,是为了适应autohotkey中RunWait函数调用exe文件的返回值,觉得不习惯的可以自行修改重新编译,问题不大。

    return值说明

    0:保存bmp文成功
    11:剪贴板中内容不是DIB格式
    12:打开剪贴板失败
    13:路径无法访问/没有操作权限
    14:没有传入参数(至少得有一个参数,即截图文件的完整路径)

    完整代码如下:

    // SaveBmpFile.cpp: 定义控制台应用程序的入口点。
    //
    
    //返回值:
    //0 - 存储bmp文件成功
    //11 - 剪贴板内容非DIB格式
    //12 - 打开剪贴板失败
    //13 - 存储bmp文件失败
    //14 - 没有传入参数
    #include "stdafx.h"
    #include <windows.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <io.h>
    
    int main(int argc, char* argv[])
    {
        char* file_name;
        if (argc < 2)
        {
            printf("No file name given!");
            return 14;
        }
        else file_name = argv[1];
            
        if (!OpenClipboard(NULL))   //尝试打开剪贴板
        {
            printf("%s", "Open clipboard fail!\n");
            CloseClipboard(); //关闭剪贴板
            return 12;
        }
    
        HANDLE hDib;
        LPVOID lpDib;
        BITMAPFILEHEADER bfBuf;
    
        if (IsClipboardFormatAvailable(CF_DIB)) //检查剪贴板内容是否为DIB类型的数据
        {
            hDib = GetClipboardData(CF_DIB);    //获取剪贴板内容并存入hDib句柄
            lpDib = GlobalLock(hDib);           //利用GlobalLock锁住hDib占用的内存,并返回内存块的首字节指针
    
            printf("%s", "Getting data...\n");
            int iData = GlobalSize(lpDib);      //利用GlobalSize获取上述锁住的内存块的大小
                                                //bfBuf是BITMAPFILEHEADER结构体类型,下面手动指定BMP文件头参数
            bfBuf.bfType = 0x4d42;              //"BM"的ASCII码的hex值
            bfBuf.bfSize = iData + sizeof(BITMAPFILEHEADER);    //BMP文件大小=数据大小+文件头大小
            bfBuf.bfReserved1 = 0;  //固定值
            bfBuf.bfReserved2 = 0;  //固定值
            bfBuf.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);  //文件数据写入时的偏移量=文件头大小+信息头大小
    
            printf("%s", "Writing files...\n");
            //下面是创建文件并写入文件头和写入文件数据,即生成了BMP文件
            DWORD dwWriten;
            HANDLE hf;
            hf = CreateFileA(file_name, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
            printf("%s", "Start writing...\n");
            WriteFile(hf, &bfBuf, sizeof(BITMAPFILEHEADER), &dwWriten, NULL);                       //写入文件头
            printf("%s", "File head writen!\n");
            WriteFile(hf, lpDib, iData, &dwWriten, NULL);   //写入图片数据
            printf("%s", "Pic data writen!\n");
            CloseHandle(hf);
            printf("%s", "Close handle!\n");
    
            if (!_access(file_name, 0))
            {
                printf("%s", "Bmp file saved!\n");
            }
            else
            {
                printf("%s", "Bmp file not saved!\n");
                CloseClipboard(); //关闭剪贴板
                return 13;
            }
    
            GlobalUnlock(hDib); //解锁被锁住的内存块
            GlobalFree(hDib);   //彻底释放hDib
        }
        else
        {
            printf("%s", "Not DIB format!\n");
            CloseClipboard(); //关闭剪贴板
            return 11;
        }
    
        CloseClipboard(); //关闭剪贴板
    
        return 0;
    }
    
    

    相关文章

      网友评论

          本文标题:C/C++:保存剪贴板中的截图为bmp文件(编译成dll供pyt

          本文链接:https://www.haomeiwen.com/subject/fdqltftx.html