环境:python2.7
下面是枚举Windows所有进程,根据已知进程名获取进程信息(pid)的实例。
1 | def getPidByName(processName): |
基本数据类型
了解一种编程语言的数据类型是通往这条编程大道的基本入门法则,下面是ctypes中python与C对应类型表:
|ctypes type|C type|Python type|
| :———| :—-| :———|
| c_bool | _Bool | bool (1) |
| c_char | char | 1-character string |
| c_wchar | wchar_t | 1-character unicode string |
| c_byte | char | int/long |
| c_ubyte | unsigned char | int/long |
| c_short | short | int/long |
| c_ushort | unsigned short | int/long |
| c_int | int | int/long |
| c_uint | unsigned int | int/long |
| c_long | long | int/long |
| c_ulong | unsigned long | int/long |
| c_longlong | __int64 or long long | int/long |
| c_ulonglong | unsigned __int64 or unsigned long long | int/long |
| c_float | float | float |
| c_double | double | float |
| c_longdouble | long double | float |
| c_char_p | char * (NUL terminated) | string or None |
| c_wchar_p | wchar_t * (NUL terminated) | unicode or None |
| c_void_p | void * | int/long or None |
先简单看下基本类型说明:
初始化及构造
基本类型都可以通过类似于C构造函数的方式来声明和初始化。
比如要声明一个c_bool,cb = c_bool(True)None对应C中的NULL。
NULL在win32编程中经常用到,所以这一条还是很有用的。实际上,用0也能表示,毕竟在Windows中NULL是0用宏来实现的。c_char_p、 c_wchar_p、 c_void_p 构建出的指针,其内容是不可变的,赋新值时实际上是指向了新地址。官方的例子很生动,引用下:
1
2
3
4
5
6
7
8
9
10s = "Hello, World"
c_s = c_char_p(s)
print c_s
c_char_p('Hello, World')
c_s.value = "Hi, there"
print c_s
c_char_p('Hi, there')
print s # first string is unchanged
Hello, World
>>>
可以看出s最终是没有被修改的。也证实了一点,Python中string类型是不可变(immutable)的。
- 创建可变的string buffer。
Win32编程中经常要用到字符数组。比如传递空的字符数组到某API中,获取信息并保存到数组中。那么这里肯定需要可变的string buffer了。
ctypes中可使用create_string_buffer()和create_unicode_buffer()两个方法。
其中,create_string_buffer()生成c_char类型的字符数组,而create_unicode_buffer()生成c_wchar类型的。
普通数组
沿用最上面的枚举进程的代码,这个进程id数组声明如下:aProcesses = (wintypes.DWORD * 1024)()
可以看出,基本格式是: (类型 * 大小)()
数组可以直接作为指针传递
指针与引用
Python ctypes中
指针: pointer()
引用: byref()
指针相当于C中的左值, 本身可以被修改,被操作(取地址):
1 | from ctypes import * |
注意:pointer(t)相当于POINTER(type(t)), 也就是说可以用POINTER()来生成(可以理解为注册)新指针类型。
1 | from ctypes import * |
在Win32编程中,一般有两种情况需要用到ctypes的指针和引用。
一种是普通的一维指针传递,这时使用byref()即可。还一种是多维指针,这里就需要用到pointer()构建指针对象,最后使用byref()传递。
例如**T这种二维指针。
类型转换
使用cast()来进行类型转换(强转)。
例如:
1 | from ctypes import * |
DLL加载
说到Win32编程,肯定是需要调用各种DLL的,比如系统库,C运行库等。
用法比较简单,两种方式:
ctypes.LibraryLoader.LoadLibrary(dllname)
调用该dll的方法较多时,可先保存module再直接使用变量调用。自定义dll使用此方式,dllname即为dll路径ctypes.LibraryLoader.dllname
在调用方法较少的情况下使用比较方便。
注意:如果dll不存在,C/C++下加载dll可以判断句柄是否为空,但这里两种方式都会抛异常。可以使用find_library(name)先查找,如果存在时再加载。
回调函数
回调机制在Win32编程中十分重要,很多API都需要传入一个回调函数作为参数。
在ctypes中,有几种声明回调函数的方式。我们来看看它们有什么区别。
CFUNCTYPE(restype, *argtypes, use_errno=False, use_last_error=False)
一般C回调函数,__stdcall的调用方式。WINFUNCTYPE(restype, *argtypes, use_errno=False, use_last_error=False)
Window API专用回调函数,和C中WINAPI这个宏有异曲同工之妙。自然是__cdecl的调用方式了。PYFUNCTYPE(restype, *argtypes)
Python回调函数,混和编程中较少使用。
扩展:__cdecl和__stdcall有什么区别呢?
__stdcall:参数由右向左压入堆栈;堆栈由函数本身清理。
__cdecl:参数也是由右向左压入堆栈;但堆栈由调用者清理。
两者在同一名字修饰约定下,编译过后变量和函数的名字也不一样。