ctypes使用指南

ctypes是Python的一个外部函数库,从Python2.5开始引入,用于调用动态链接库/共享库中的函数。本文档详细介绍了如何加载动态链接库、访问函数、调用函数、使用基本数据类型、传递指针、处理结构和联合等内容,适用于Windows和Linux环境。此外,还讨论了回调函数、访问动态链接库导出的值等高级话题。
摘要由CSDN通过智能技术生成

ctypes使用指南

1  ctypes简介

从Python2.5开始引入。

ctypes是Python的外部函数库。它提供了C兼容的数据类型,并且允许调用动态链接库/共享库中的函数。它可以将这些库包装起来给Python使用。

2  ctypes入门

本入门中的代码使用doctest确保可用。不过一些代码在linux/windows/mac os x中的行为可能略有差异,这在其doctest的注释中有所表示。

少数代码示例引用了ctypes的c_int类型。这个类型是32bit系统中c_long类型的别名。所以你在期待c_int而显示c_long时不必疑惑,他们是一样的。

2.1  载入动态链接库

ctypes导出了 cdll,在windows上还有 windll 和 oledll 对象用于载入动态链接库。

载入动态链接库可以直接存取其属性。 cdll 载入导出函数符合cdecl调用规范的库,而 windll 载入导出函数符合 stdcall 调用规范的库, oledll 也使用 stdcall 调用规范,并假设函数返回Windows的HRESULT错误码。错误码用于在出错时自动抛出WindowsError这个Python异常。

如下是Windows的例子,主意msvcrt是MS标准C库,包含了大部分标准C函数,并且使用cdecl调用规范:

>>> from ctypes import *

>>> print windll.kernel32

<WinDLL 'kernel32', handle ... at ...>

>>> print cdll.msvcrt

<CDLL 'msvcrt', handle ... at ...>

>>> libc=cdll.msvcrt

>>>

 

Windows通常使用".dll"作为动态链接库的扩展名。

Linux上需要指定包含扩展名的文件名来载入动态库,所以属性存取方式就失效了。你可以使用 LoadLibrary 方法,或者创建CDLL的实例来载入:

>>> cdll.LoadLibrary("libc.so.6")

<CDLL 'libc.so.6', handle ... at ...>

>>> libc==CDLL("libc.so.6")

>>> libc

<CDLL 'libc.so.6', handle ... at ...>

>>>

 

2.2  从载入的动态链接库中访问函数

函数是作为dll对象的属性来存取的:

>>> from ctypes import *

>>> libc.printf

<_FuncPtr object at 0x...>

>>> print windll.kernel32.GetModuleHandleA

<_FuncPtr object at 0x...>

>>> print windll.kernel32.MyOwnFunction

Traceback (most recent call last):

  File "<stdin>", line 1, in ?

  File "ctypes.py", line 239, in __getattr__

    func = _StdcallFuncPtr(name,self)

AttributeError: function 'MyOwnFunction' not found

>>>

 

注意win32系统动态链接库,如kernel32和user32经常同时导出ANSI和UNICODE版本的函数。UNICODE版本的会在名字末尾加"W",而ANSI版本的加上"A"。Win32版本的 GetModuleHandle 函数,返回给定模块名的句柄,有如下C原型,还有一个宏用于暴露其中一个作为 GetModuleHandle ,依赖于UNICODE定义与否:

/* ANSI version */

HMODULE GetModuleHandleA(LPCSTR lpModuleName);

/* UNICODE version */

HMODULE GetModuleHandleW(LPCWSTR lpModuleName);

 

windll 并不会自动选择调用某个版本,所以你必须指定要调用的,传递的时候也要指定正确的字符串参数类型。

有时动态链接库导出函数并不是有效的Python标识符,例如 "??2@YAPAXI@Z" 。这种情况下,你必须使用getattr 获取函数:

>>> getattr(cdll.msvcrt,"??2@YAPAXI@Z")

<_FuncPtr object at 0x...>

>>>

 

在Windows上,有些动态链接库导出函数不是用名字,而是用序号(ordinal)。这些函数通过索引存取:

>>> cdll.kernel32[1]

<_FuncPtr object at 0x...>

>>> cdll.kernel32[0]

Traceback (most recent call last):

  File "<stdin>", line 1, in ?

  File "ctypes.py", line 310, in __getitem__

    func = _StdcallFuncPtr(name,self)

AttributeError: function ordinal 0 not found

>>>

 

2.3  调用函数

你可以像正常的Python函数一样调用这些函数。这里用 time() 函数示例,返回Unix epoch系统时间,和GetModuleHandleA() 函数,返回win32模块句柄。

这个例子调用函数时附带NULL指针(None作为NULL指针):

>>> print libc.time(None)

1150640792

>>> print hex(windll.kernel32.GetModuleHandleA(None))

0x1d000000

>>>

 

在调用函数时,如果使用了错误的参数数量和调用规范时,ctypes尝试保护调用。不幸的是该功能仅在Windows上有用。它通过检查函数返回栈来实现,所以尽管发生了错误,但是函数还是调用了:

>>> windll.kernel32.GetModuleHandleA()

Traceback (most recent call last):

  File "<stdin>", line 1, in ?

ValueError: Procedure probably called with not enough argument (4 bytes missing)

>>> windll.kernel.GetModuleHandleA(0,0)

Traceback (most recent call last):

  File "<stdin>", line 1, in ?

ValueError: Procedure probably called with too many argument (4 bytes in excess)

 

这在你使用了错误的调用规范时同样会发生:

>>> cdll.kernel32.GetModuleHandleA(None) # doctest: +WINDOWS

Traceback (most recent call last):

  File "<stdin>", line 1, in ?

ValueError: Procedure probably called with not enough arguments (4 bytes missing)

>>>

 

>>> windll.msvcrt.printf("spam") # doctest: +WINDOWS

Traceback (most recent call last):

  File "<stdin>", line 1, in ?

ValueError: Procedure probably called with too many arguments (4 bytes in excess)

>>>

 

想要找到正确的调用规范,你必须查看C头文件或者函数的文档。

在Windows,ctypes使用win32结构异常处理,避免无保护的挂掉:

>>> windll.kernel32.GetModuleHandleA(32)

Traceback (most recent call last):

  File "<stdin>", line 1, in ?

WindowsError: exception: access violation reading 0x00000020

 

尽管如此,仍然有很多方法用ctypes挂掉Python,所以你必须很小心的使用。

None、整数、长整数、字节串和unicode字符串是可以作为本地Python对象直接传递给函数调用的。None是作为C的NULL指针,字 节串和unicode字符串作为内存块指针传递(char* 或 wchar_t*)。Python整数和长整数作为平台相关的C类型传递。

在调用更多的函数之前,必须了解关于ctypes数据类型的知识。

2.4  基本数据类型

ctypes定义了一系列基本C数据类型:

ctypes类型

C类型

Python类型

c_char

char

1个字符的字符串

c_wchar

wchar_t

1个字符的unicode字符串

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 或 long long

int/long

c_ulonglong

unsigned __int64 或 unsigned long long

int/long

c_float

float

float

c_double

double

float

c_char_p

char * (NUL结尾字符串)

string或None

c_wchar_p

wchar_t * (NUL结尾字符串)

unicode或None

c_void_p

void *

int/long或None

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值