ctypes调用dll

王朝学院·作者佚名  2016-08-27
窄屏简体版  字體: 小  |  中  |  大  |  超大  

1. 加载 Windows API 和 C 运行库先看例子

from ctypes import *

u32 = windll.LoadLibrary('user32.dll') #加载user32.dll

u32.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') #初始值abc

cdll.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:', i

c_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

 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
 
© 2005- 王朝網路 版權所有 導航