首页 > 学院 > 开发设计 > 正文

ctypes调用dll

2019-11-14 17:42:20
字体:
来源:转载
供稿:网友

1. 加载 Windows API 和 C 运行库

先看例子

from ctypes import *u32 = windll.LoadLibrary('user32.dll') #加载user32.dllu32.MessageBoxW(0, u'内容', u'标题',0)crt = cdll.LoadLibrary('msvcrt.dll')   #加载C运行库crt.PRintf('hello world !/n')   

调用 C 库用 cdll,而调用 Windows API 用 windll。

之所以有 cdll 和 windll 的区别,是因为 Windows API 和 C 库函数的调用规范不一样,前者遵从的是 __stdcall,后者是 __cdecl。

__cdecl 是 C Declaration 的缩写,表示 C 编译器默认的函数调用规范,这种方式下由调用者负责清理参数栈。比如在VC中,如果函数前面不加任何修饰,默认的就是 __cdecl。

__stdcall 是 Standard Call 的缩写,这种方式下被调的函数自己负责清理参数栈。在 VC 中,如果函数前有 __stdcall 修饰的话,编译出来的就是这种类型。

Windows API 全部采用的是 __stdcall 。因为 Windows API 要被各种编译器下的各种语言调用,如果采用 __cdecl 的方式,由于各种编译器中使用的栈结构也许并不相同,所以就不敢保证其每种编译器都能正确清理 Windows API 产生的参数栈。而采用 __stdcall 的方式,由被调用者自己清理参数栈,就能避免了以上麻烦。

在 Windows 环境,LoadLibrary() 的参数可以不包括扩展名。还有一种更简短的写法,比如下面三个是等效的:

from ctypes import *windll.LoadLibrary('user32.dll').MessageBoxW(0, u'内容', u'标题',0)windll.LoadLibrary('user32').MessageBoxW(0, u'内容', u'标题',0)windll.user32.MessageBoxW(0, u'内容', u'标题',0)

下面三个也一样

from ctypes import *cdll.LoadLibrary('msvcrt.dll').printf('hello world !/n')cdll.LoadLibrary('msvcrt').printf('hello world !/n')cdll.msvcrt.printf('hello world !/n')

2. 隐式类型转换

int、str、unicode,这三种类型是可以直接作为参数传递的,不用指明对应的C类型

from ctypes import *cdll.msvcrt.printf('%d, %s/n', 100, 'abc')

3. 显式类型转换

int、str、unicode 之外的类型在作为参数时要做显式类型转换,指出对应的C类型

from ctypes import *cdll.msvcrt.printf('%.2f/n', c_double(3.14))

4. 指针和引用

byref() 可以引用内存地址,相当于 C 的 &

from ctypes import *i = c_int(0)cdll.msvcrt.sscanf('100', '%d', byref(i))print i.value

pointer() 也可以达到相同的效果

from ctypes import *i = c_int(0)cdll.msvcrt.sscanf('100', '%d', pointer(i))print i.value

二者的区别在于,pointer() 会构造一个指针对象,而 byref() 只是一个函数,所以 byref() 的开销较小。

如果只是用于传递参数,那么用 byref() 就足够了。

5. 字符串指针

直接用 create_string_buffer() 和 create_unicode_buffer()

from ctypes import *s = create_string_buffer('abc') #初始值abccdll.msvcrt.sscanf('def', '%s', s)print s.value #现在是def

6. 回调函数

为了测试回调函数,先编写一个 test.dll,下面是 C 代码

 #include <stdio.h> typedef void __stdcall (*MyCallback)(int);__declspec(dllexport) void __stdcall TestMyCallback( int i, MyCallback mycallback){    printf("TestMyCallback:%d/n", i);    mycallback(i*10);}

上面这个函数接受两个参数,第一个是整型变量 i,第二个是一个回调函数。

首先打印变量 i,然后再把 i*10 传给回调函数。

( __declspec(dllexport) 关键字用于从dll导出函数,详见这里 )

下面的Python代码中实现了回调函数,并传给了test.TestMyCallbackPython

from ctypes import *def mycallback( i ):    print 'mycallback:', ic_mycallback = WINFUNCTYPE(None,         #返回值类型 None 代表 void                           c_int         #第一个参数类型 int                           )(mycallback) #括号内是需要被包装的Python函数windll.test.TestMyCallback(100, c_mycallback)

WINFUNCTYPE 的用法实际包括两个步骤:

  • 首先要声明一个 C 函数原型

    c_function_type = WINFUNCTYPE(返回值类型,第一个参数类型,第二个参数类型...)
  • 然后再用这个原型包装 Python 函数

    c_function = c_function_type(Python函数)

WINFUNCTYPE 遵守 __stdcall 调用规范,对应的 __cdecl 版本是 CFUNCTYPE


发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表