Python调用Win32 API实现截图

0x01 Win32 API简介

Win32 API即为Microsoft 32位平台(包括:Windows 9x, Windows NT3.1/4.0/5.0, WindowsCE等)的应用程序编程接口(Application Programming Interface),是构筑所有32位Windows平台的基石,所有在Win32平台上运行的应用程序都可以调用这些函数。

使用Win32 API,应用程序可以充分挖掘Windows的32位操作系统的潜力。 Mircrosoft的所有32位平台都支持统一的API,包括函数、结构、消息、宏及接口。使用 Win32 API不但可以开发出在各种平台上都能成功运行的应用程序,而且也可以充分利用每个平台特有的功能和属性。

在具体编程时,程序实现方式的差异依赖于相应平台的底层功能的不同。最显著的差异是某些函数只能在更强大的平台上实现其功能。例如,安全函数只能在Windows NT操作系统下使用。另外一些主要差别就是系统限制,比如值的范围约束,或函数可管理的项目个数等等。

标准Win32 API函数可以分为以下几类:

  • 窗口管理
  • 窗口通用控制
  • Shell特性
  • 图形设备接口
  • 系统服务
  • 国际特性
  • 网络服务

win32api可参考https://msdn.microsoft.com/en-us/library/windows/desktop/ff818516(v=vs.85).aspx

0x02 Pywin32

1、安装Pywin32

由于本次操作是使用python来操作Win32api,需要先安装Pywin32,可以从这里下载对应的Python和Windows版本文件,下载完后直接运行安装就可以了。

安装文件:https://sourceforge.net/projects/pywin32/files/

2、截图思路

截图操作的大致思路如下:

  • 模拟按下键盘上的“win + PrtSc”组合键
  • 从剪贴板读取缓存的截图
  • 将截图文件保存在本地

3、模拟按键

使用的是win32api中的keydb_event函数,相关详细信息可以参考https://msdn.microsoft.com/en-us/library/windows/desktop/ms646304(v=vs.85).aspx

键位码可以参考https://msdn.microsoft.com/en-us/library/windows/desktop/dd375731(v=vs.85).aspx

win32api.keybd_event(0x91, 0, 0, 0)     # 0x91 --> win key
win32api.keybd_event(0x2C, 0, 0, 0)     # 0x2C --> PRINT SCREEN key
win32api.keybd_event(0x91, 0, win32con.KEYEVENTF_KEYUP, 0)
win32api.keybd_event(0x2C, 0, win32con.KEYEVENTF_KEYUP, 0)

4、PrtSc相关知识

当按下“win + PrtSc”组合键后,截图被保存在剪贴板中,可以直接在word或者是附件中的画图程序里粘贴后使用,使用剪贴板查看器可以查看剪贴板的内容。

剪贴板在Windows 7/8/10 中是没有的,可以在网上搜索下载一个,一般为clipbrd.exe。

clipbrd.png

自己操作下就会发现,剪贴板只能存储最近复制的内容,新内容会替换旧内容(不区分图片或文字),QQ截图使用的也是系统剪贴板。

从剪贴板查看器的查看选项可以看到,这里有两种图片格式:位图和DIB位图。

clipbrd_screen.png

5、从剪贴板中读取截图

win32api中有一个模块win32clipboard是负责剪贴板相关的操作。相关详细信息可以参考https://msdn.microsoft.com/zh-cn/library/windows/desktop/ff468802(v=vs.85).aspx

win32clipboard.IsClipboardFormatAvailable(formats)确定剪贴板是否包含指定格式的数据。

win32clipboard.GetClipboardData(formats)可以从剪贴板里读取数据。

需要指定数据的格式formats,有关标准剪贴板格式的说明,请参阅标准剪贴板格式https://msdn.microsoft.com/zh-cn/library/windows/desktop/ms649013(v=vs.85).aspx#_win32_Standard_Clipboard_Formats

此处选用CF_DIBCF_DIB返回一个内存对象,包含BIT格式图片的信息。相关详细信息可以参考https://msdn.microsoft.com/zh-cn/library/windows/desktop/ff729168(v=vs.85).aspx

win32clipboard.OpenClipboard()
if win32clipboard.IsClipboardFormatAvailable(win32clipboard.CF_DIB):
    data = win32clipboard.GetClipboardData(win32clipboard.CF_DIB)

6、BMP简介

BMP(全称Bitmap)是Windows操作系统中的标准图像文件格式,它采用位映射存储格式,除了图像深度可选以外,不采用其他任何压缩,因此,BMP文件所占用的空间很大。BMP文件的图像深度可选lbit、4bit、8bit及24bit。BMP文件存储数据时,图像的扫描方式是按从左到右、从下到上的顺序。由于BMP文件格式是Windows环境中交换与图有关的数据的一种标准,因此在Windows环境中运行的图形图像软件都支持BMP图像格式。

典型的BMP图像文件由四部分组成:

  • 位图头文件数据结构,它包含BMP图像文件的类型、显示内容等信息;
  • 位图信息数据结构,它包含有BMP图像的宽、高、压缩方法,以及定义颜色等信息;
  • 调色板,这个部分是可选的,有些位图需要调色板,有些位图,比如真彩色图(24位的BMP)就不需要调色板;
  • 位图数据,这部分的内容根据BMP位图使用的位数不同而不同,在24位图中直接使用RGB,而其他的小于24位的使用调色板中颜色索引值。

详细内容可查看https://baike.baidu.com/item/BMP/35116?fr=aladdin

7、构建结构体

BMP文件头结构体

详细内容可参考https://msdn.microsoft.com/en-us/library/windows/desktop/dd183374(v=vs.85).aspx

class BITMAPFILEHEADER(Structure):
    _pack_   = 1
    _fields_ = [
        ('bfType', WORD),
        ('bfSize', DWORD),
        ('bfReserved1', WORD),
        ('bfReserved2', WORD),
        ('bfOffBits', DWORD),
        ]
位图信息头结构体

详细内容可参考https://msdn.microsoft.com/en-us/library/windows/desktop/dd183376(v=vs.85).aspx

class BITMAPINFOHEADER(Structure):
    _pack_   = 1
    _fields_ = [
        ('biSize', DWORD),
        ('biWidth', LONG),
        ('biHeight', LONG),
        ('biPLanes', WORD),
        ('biBitCount', WORD),
        ('biCompression', DWORD),
        ('biSizeImage', DWORD),
        ('biXpelsPerMeter', LONG),
        ('biYpelsPerMeter', LONG),
        ('biClrUsed', DWORD),
        ('biClrImportant', DWORD),
        ]

用sizeof获取二者的大小并赋值给SIZEOF_BITMAPFILEHEADERSIZEOF_BITMAPINFOHEADER

8、拷贝截图数据

从内存中拷贝出BMP数据用到的是ctypes中的memmove函数。详细内容可查看https://docs.python.org/2/library/ctypes.html?highlight=memmove#ctypes.memmove

同时,新建一块区域,全部用0来填充,用到的是ctypes中的memset函数。详细内容可查看https://docs.python.org/2/library/ctypes.html?highlight=memmove#ctypes.memset

BitMapInfoHeaderHandle = BITMAPINFOHEADER()
memmove(pointer(BitMapInfoHeaderHandle), data, SIZEOF_BITMAPINFOHEADER)
        
BitMapFileHeaderHandle = BITMAPFILEHEADER()
memset(pointer(BitMapFileHeaderHandle), 0, SIZEOF_BITMAPFILEHEADER)

9、写文件头

构建文件头的详细内容可参考https://baike.baidu.com/item/BMP/35116?fr=aladdin

BitMapFileHeaderHandle.bfType = ord('B') | (ord('M') << 8)
BitMapFileHeaderHandle.bfSize = SIZEOF_BITMAPFILEHEADER + len(data)
SIZEOF_COLORTABLE = 0
BitMapFileHeaderHandle.bfOffBits = SIZEOF_BITMAPFILEHEADER + SIZEOF_BITMAPINFOHEADER + SIZEOF_COLORTABLE

10、生成图片

以二进制方式,先写文件头,再写从剪贴板获取到的字符串到本地的.bmp文件中,完成图片生成。

with open(filename, 'wb') as bmp_file:
    bmp_file.write(BitMapFileHeaderHandle)
    bmp_file.write(data)
print 'file "{}" created from clipboard image'.format(filename)

0x03 完整代码

PrintScreen.py

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,117评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,328评论 1 293
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,839评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,007评论 0 206
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,384评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,629评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,880评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,593评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,313评论 1 243
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,575评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,066评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,392评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,052评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,082评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,844评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,662评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,575评论 2 270

推荐阅读更多精彩内容