当前位置

Python ctypes 里使用 create_string_buffer, addressof, string_at

已经写了一篇 blog 里面介绍了如何传入结构体指针的方法,而前不久发现了另外一种传入一整块 buffer 的方案,不用定义 Structure class,直接类似 malloc 那样的方法去执行,就是 create_string_buffer addressof string_at 系列函数,感觉更接近底层调用.

首先修正上篇文档里面的一个问题,就是 load 这个方法已经取消了(当时我使用的好像是 0.9.9.3 版的 ctypes),LoadLibrary 这个接口随着 ctypes 1.0 的发布并被集成进 python 2.5,应该是正式确定了名称。

其次注意的是根据源代码里面对函数不同的声明,还得选择采用 cdll 还是 windll 来实例化一个动态库,用 WINAPI 声明的函数需要用 windll.LoadLibrary(dll_file_path)。因为不同的声明导致参数传递的方法不一样,我自己就是费了好长时间的尝试,才搞清楚为什么总是报错"rocedure called with not enough arguments (xx bytes missing) or wrong calling convention"。ctypes 的手册里面还提到另外有 oledll, pydll 两种类型.

如果一个 dll 里面即有普通方法定义的函数,也有 WINAPI 定义的函数,而你只希望只 Load 一次动态库,那么就需要 WINFUNCTYPE 或者 CFUNCTYPE 这样的方法来指定不同的函数类型了。

大家都应该机器上安装 XviD 解码器了吧,:)
现在我们分别用两个不同的方法取出 XviD 编码器的缺省配置(C 源代码参考 xvidcore-x.y.z\vfw\src\driverproc.c)

  1. from ctypes import *
  2. DriverProc = windll.LoadLibrary("c:\\windows\\system32\\xvidvfw.dll").DriverProc
  3.  
  4. configsize = DriverProc(c_int(0), c_int(0), c_int(0x5000), c_voidp(0), c_voidp(0))  
  5. # 获取结构体大小 configsize
  6.  
  7. did = DriverProc(c_int(0), c_int(0), c_int(0x0003), c_voidp(0), c_voidp(0))
  8. # 获取访问句柄 did
  9.  
  10. config = create_string_buffer(configsize, configsize)
  11. # 创建 buffer, 返回一个 Python 对象
  12.  
  13. pconfig = addressof(config)
  14. # buffer 的地址,让我想起了 C 里面的 &
  15.  
  16. DriverProc(c_int(did), c_int(0), c_int(0x5000), pconfig, c_voidp(0))
  17. # 这次调用就是把缺省配置复制到传入的内存区域内
  18.  
  19. s = string_at(pconfig, configsize)
  20. # 最后我们从 pconfig 地址里的内容生成一个字符串对象出来

上面的代码通过 cdll + WINFUNCTYPE 来写就是

  1. from ctypes import *
  2. from ctypes.wintypes import *
  3. xvidvfw = cdll.LoadLibrary("c:\\windows\\system32\\xvidvfw.dll")
  4. # 看好了,这里可用的是 cdll
  5.  
  6. prototype = WINFUNCTYPE(LONG, DWORD, DWORD, UINT, LPARAM, LPARAM)
  7. # LONG 是返回值,DWORD, DWORD, UINT, LPARAM, LPARAM 是参数列表
  8.  
  9. paramflags = (1, "driverid", 0), \
  10.              (1, "hdriver", 0), \
  11.              (1, "umsg", 0), \
  12.              (1, "para1", 0), \
  13.              (1, "para2", 0)
  14. # 设定一下参数表,以及缺省参数,就可以用 key=value 的方式来传递了
  15.  
  16. x = prototype(("DriverProc", xvidvfw), paramflags)
  17. # ...
  18. configsize = x(umsg=0x5000)
  19. # 这里只传入一个参数,其它的就自动用缺省的了;而且无需 c_int 这样来转换
  20. did = x(umsg=0x0003)
  21. # .... 以下就不需要注释了吧
  22. config = create_string_buffer(configsize, configsize)
  23. pconfig = addressof(config)
  24. y = x(driverid=did, umsg=0x5000, para1=pconfig)
  25. s = string_at(pconfig, configsize)

最后要说的是通过 dumpbin.exe /exports dll_file_path,就可以查看一个 dll 里面有哪些函数是被 export 出来,可以给我们来调用的(我自己猜测) . dumpbin.exe 可以从 masm32 里面免费获得

update: 在上述第一个例子里面,传入的参数用 addressof 去做一次转换并不是必须的. 可能 ctypes 会在内部自动处理

Topic: