Python之点到为止: 调用Windows API

2020年04月06日 367点热度 2人点赞 0条评论

上一期讲过如何调用dll: Python之点到为止: 调用动态链接库(DLL), 这期进一步深入Windows.

Windows API

Note that this was formerly called the Win32 API. The name Windows API more accurately reflects its roots in 16-bit Windows and its support on 64-bit Windows.

之前叫做Win32 API, 后来更名为Windows API. 随着64位操作系统以及软件的普及, 原来的名字已经不合适了, 已经囊括了对16位、64位系统得支持所以叫现在得名字更合理. 有了这些API才有得现在Windows上所有得软件.

然而这些API不需要去记住, 只是用到得时候去查文档就好了(详见文章末尾).

模块

  • ctypes —— Python 的外部函数库
  • pywin32 —— Python对Windows的扩展

前面也说了, 这些Windows API, 不需要去记太多了. 模块部分也不介绍太多, 直接演示代码.

实操

假如有个需求, 需要执行某些操作后给个提示弹窗. 当然有知道GUI库的童鞋会想到用他们, 很好可以联想到. 但是如果仅仅是一个弹窗就用上GUI库是不是有点浪费了. 当然本期主题是调用Windows API, 自然再Windows环境上操作.

首先我们去查一下, Windows API中哪个函数是信息框. https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-messageboxw , 直接发给大家有兴趣可以按照微软的文档查看其他API.

PS: MessageBox函数分两种 MessageBoxA, MessageBoxW. 在C系列直接调用MessageBox就可以了, 编译器可以自动帮你选择是用A或者W. A和W的区别就是一个用于窄字符一个用于宽字符. 窄字符多指ANSI(单字节占用8 bit), 宽字节多指UNICODE(单字节占用16 bit) . Python中默认编码UTF-8所以直接用MessageBoxW就行.

扩展: ANSI编码, 还是Windows上使用的默认编码, 例如中文以GBK编码标准. 各个国家文字不同编码标准也不同, 乱糟糟也不能互相转换. 所以有了UNICODE编码, 但是默认编码1个字符占用16 bit, 如果是简单的一串英文很显然是ANSI的两倍, 然后UTF-8编码标准就来了, 这个大哥来了就说"你们ANSI之前那些字符我要了还占用6 bit, 然后其他的字符按情况往上加.", 哎这就解决了占用大的问题, 还可以完美显示全字符.

int MessageBoxW(
  HWND    hWnd,
  LPCWSTR lpText,
  LPCWSTR lpCaption,
  UINT    uType
);

看到API定义之后就好办了, 文档下面也解释了对应参数的作用.

  • hWnd: 窗口句柄, 来判断这个信息框是从哪个窗口弹出来的
  • lpText: 你提示的信息
  • lpCaption: 信息框标题
  • uType: 信息框的类型

ctypes实现

from ctypes import *
messagebox = windll.user32.MessageBoxW
ret = messagebox(0, '提示信息', '提示标题', 0)
print(ret)

user32 = windll.LoadLibrary('user32.dll')
messageboxA = user32.MessageBoxA
ret = messageboxA(0, '提示信息'.encode('ansi'), '提示标题'.encode('ansi'), 1)
print(ret)

messageboxW = user32.MessageBoxW
ret = messageboxW(0, '提示信息', '提示标题', 2)
print(ret)
为了观看方便, 将三个截图拼接了一下. 实际是一个一个弹出的

A和W的区别不在多说, 大家可以动手试试. 对于这三个信息框不同的地方那就是按钮了, https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-messageboxw#parameters 可以看下 参数 uType 值得范围. 使用方法就是这些数直接加在一起.

新需求来了, 一段代码执行之后出错了提示一下是否重新执行.

from ctypes import *

user32 = windll.LoadLibrary('user32.dll')
messageboxW = user32.MessageBoxW


def todo():
    try:
        assert False, '执行出错' # 出错代码
    except Exception as e:
        # 5代表重试和取消按钮, int('20', 16) 是叹号提示(16进制的20转10进制)
        ret = messageboxW(0, '错误:{}, 是否要重试?'.format(e), '出错', 5 + int('20', 16))
        if ret == 4:
            print('重试执行')
            todo()


if __name__ == "__main__":
    todo()

其中要注意的就是文档中给的值都是16进制. 返回值文档中也有, 不想看的话代码跑一下print或者debug就知道了. 这样你就有一个稍微人性化点的提示了(只是某个方面上).

那么用pywin32来实现呢?

from win32api import MessageBox
from win32con import MB_RETRYCANCEL, MB_ICONQUESTION, IDRETRY

def todo():
    try:
        assert False, '执行出错' # 出错代码
    except Exception as e:
        ret = MessageBox(0, '错误:{}, 是否要重试?'.format(e), '出错', MB_RETRYCANCEL + MB_ICONQUESTION)
        if ret == IDRETRY:
            print('重试执行')
            todo()


if __name__ == "__main__":
    todo()

这就更简单了, API名字包括常量名字都和微软文档的一摸一样, 你直接从文档中复制过来直接 import 就OK了.

总结

本文讲的一个非常简单的例子, 只是可能扩展部分有点多, 选择性观看. 这只是Windows API中的冰山一角哦. 曾经的一个朋友(易语言患者)因为我走了Python来"嘲讽我", 说Python能写修改器么. ??? 这世上恐怕没什么事情是不能做的, 只是该不该或是有没有必要罢了.

那么我就点到为止了
Just give a hint.

相关资料:
Windows API文档: https://docs.microsoft.com/en-us/windows/win32/apiindex/windows-api-list
ctypes文档: https://docs.python.org/zh-cn/3.7/library/ctypes.html
pywin32: https://github.com/mhammond/pywin32
pywin32文档: 有兴趣的可以加交流群98354582, 文档也是在上面的库中提取的

文章评论