已阅读5页,还剩18页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
内容: .加载动态链接库 .从已加载的dll中引用函数 .调用函数1 .基本的数据类型 .调用函数2 .用自己的数据类型调用函数 .确认需要的参数类型(函数原型) .返回值 .传递指针 .结构和联合 .结构或联合的对齐方式和字节的顺序 .结构和联合中的位 .数组 .指针 .类型转换 .未完成的类型 .回调函数 .访问dlls导出的值 .可变长度的数据类型 .bugs 将要做的和没有做的事情 注意:本文中的代码例子使用doctest确保他们能够实际工作。一些代码例子在linux和windows以及苹果机上执行有一定的差别 注意:一些代码引用了ctypes的c_int类型。它是c_long在32位机子上的别名,你不应该变得迷惑,如果你期望 的是c_int类型,实事上打印的是c_long,它们实事上是相同的类型。 加载动态链接库 ctypes加载动态链接库,导出cdll和在windows上同样也导出windll和oledll对象。 加载动态链接库后,你可以像使用对象的属性一样使用它们。cdll加载使用标准的cdecl调用约定的链接库, 而windll库使用stdcall调用约定,oledll也使用stdcall调用约定,同时确保函数返回一个windows HRESULT错误代码。这错误 代码自动的升为WindowsError Python exceptions,当这些函数调用失败时。 这有一些windows例子,msvcrt是微软的c标准库,包含大部分的标准c函数,同时使用cdecl调用约定。 注:cdecl和stdcall的区别请见 /log-20.html from ctypes import * print windll.kernel32 # doctest: +WINDOWS print cdll.msvcrt # doctest: +WINDOWS libc = cdll.msvcrt # doctest: +WINDOWS windows自动添加常用的.dll文件后缀名 在linux上,需要使用包含扩展名的文件名来加载一个库,因此属性操作不能正常使用。 或者使用dll加载器的 LoadLibrary方法,或者通过CDLL构造函数创建一个CDLL的一个实例 cdll.LoadLibrary(libc.so.6) # doctest: +LINUX libc = CDLL(libc.so.6) # doctest: +LINUX libc # doctest: +LINUX 加载dll使用其中函数 使用函数就像对象的属性 from ctypes import * libc.printf print windll.kernel32.GetModuleHandleA # doctest: +WINDOWS print windll.kernel32.MyOwnFunction # doctest: +WINDOWS Traceback (most recent call last): File , line 1, in ? File ctypes.py, line 239, in _getattr_ func = _StdcallFuncPtr(name, self) AttributeError: function MyOwnFunction not found 00 注意 win32系统dll像kernel32和user32大部分导出ANSI和UNICODE版本函数,UNICODE版本以一个W结尾导出 而ANSI版本则以一个A结尾导出的。win32 GetModuleHandle函数,返回一个指定的module名字的module句柄 下面c原型,GetModuleHandle的macro,依赖于它是不是UNICODE。 /* ANSI version */ HMODULE GetModuleHandleA(LPCSTR lpModuleName); /* UNICODE version */ HMODULE GetModuleHandleW(LPCWSTR lpModuleName); windll不会神奇的自已去选择一个,你必须显式确认GetModuleHandleA或者GetModuleHandleW去使用它们 去处理一般字符串或unicode字符串。 有时候,dll导出函数名,python无法识别 ,像?2YAPAXIZ. 在这情况下,你必须使用getattr去使用这些函数 getattr(cdll.msvcrt, ?2YAPAXIZ) # doctest: +WINDOWS 在windows上,一些dllS不是导出函数名,而是以顺序,这些函数可以将这些数字当作dll对象的索引来使用 这些函数。 cdll.kernel321 # doctest: +WINDOWS cdll.kernel320 # doctest: +WINDOWS Traceback (most recent call last): File , line 1, in ? File ctypes.py, line 310, in _getitem_ func = _StdcallFuncPtr(name, self) AttributeError: function ordinal 0 not found 调用函数 你可调用这些函数,像其它的python函数一样,下面的例子使用time()函数,它以秒为单位返回从unix新纪元的系统时间 ,GetModuleHandleA()函数,返回一个win32模块句柄。 下面的例子用空指针调用函数(None作为空指针) print libc.time(None) # doctest: +SKIP 1150640792 print hex(windll.kernel32.GetModuleHandleA(None) # doctest: +WINDOWS 0x1d000000 ctypes尝试阻止你以错误的参数数量或调用约定来调用函数。不幸的是,这些仅仅能在windows上工作 它在函数返回后不会去检查这栈,尽管在调用函数后有错误发生。 windll.kernel32.GetModuleHandleA() # doctest: +WINDOWS Traceback (most recent call last): File , line 1, in ? ValueError: Procedure probably called with not enough arguments (4 bytes missing) windll.kernel32.GetModuleHandleA(0, 0) # doctest: +WINDOWS Traceback (most recent call last): File , line 1, in ? ValueError: Procedure probably called with too many arguments (4 bytes in excess) 产生了同样的exception,当你用cdecl调用约定去使用一个stdcall函数,反之亦然。 cdll.kernel32.GetModuleHandleA(None) # doctest: +WINDOWS Traceback (most recent call last): File , 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 , line 1, in ? ValueError: Procedure probably called with too many arguments (4 bytes in excess) 找到正确的调用约定,你必须检查c头文件或者你想调用的函数的文档。 在windows,ctypes使用win32结构exception处理一般的保护错误阻止crashes,当调用无效的参数值 windll.kernel32.GetModuleHandleA(32) # doctest: +WINDOWS Traceback (most recent call last): File , line 1, in ? WindowsError: exception: access violation reading 0x00000020 然而有很多种可能性会发生crash,你使用python的ctypes,你应该足够的小心。 None,integers,longs,byte strings 和unicode string是python内置对象,可以直接的作为这些函数的参数 ,None作为一个空指针,byte string和unicode string 当作一个内存块,它包含这些数据(char* 或者 wchar_t*) 。python的int和long长整形作为c int类型,它们的值对应着c类型。 在调用带有参数的函数之前,我们必须学习更多的关于ctypes数据类型。 基础的数据类型 ctypes定义了许多主要的c兼容有数据类型 数据类型请见/log-21.html 使用它们和一个可选的正确的值去创建所有的这些类型 c_int() c_long(0) c_char_p(Hello, World) c_char_p(Hello, World) c_ushort(-3) c_ushort(65533) 因此这些类型是可变的,他们的值在后面也是可以改变的 分配一个新值到这些指针类型c_char_p, c_wchar_p, 和 c_void_p的一个实例上,改变它们指向的 内存位置,而不是这些内存块的内容(当然,因为python的string是不可改变的) s = 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 你应该注意,不要期望传给函数指针是指向可变的内存。如果你需要可变的内存块,ctypes有一个 create_string_buffer 函数。这内存块内容通过这raw属性来访问或改变,如果你想访问一个以null结束 的字符串,使用这string属性 from ctypes import * p = create_string_buffer(3) # create a 3 byte buffer, initialized to NUL bytes print sizeof(p), repr(p.raw) 3 x00x00x00 p = create_string_buffer(Hello) # create a buffer containing a NUL terminated string print sizeof(p), repr(p.raw) 6 Hellox00 print repr(p.value) Hello p = create_string_buffer(Hello, 10) # create a 10 byte buffer print sizeof(p), repr(p.raw) 10 Hellox00x00x00x00x00 p.value = Hi print sizeof(p), repr(p.raw) 10 Hix00lox00x00x00x00x00 create_string_buffer代替c_buffer(这个到目前是以别名存在的)函数,就像早期版本ctypes的c_string 函数。create_unicode_buffe函数创建一个包含c类型wchar_t的unicode字符的可变的内存块 调用函数2 注意这printf打印到实际的标准的输出管道,而不是sys.stdout,所以这些例子仅仅工作在console下,而不能在 idle或pythonwin下工作。 printf = libc.printf printf(Hello, %sn, World!) Hello, World! 14 printf(Hello, %S, uWorld!) Hello, World! 13 printf(%d bottles of beern, 42) 42 bottles of beer 19 printf(%f bottles of beern, 42.5) Traceback (most recent call last): File , line 1, in ? ArgumentError: argument 2: exceptions.TypeError: Dont know how to convert parameter 2 就像以前提到的,所有的python类型除了integer,string,unicode string以外必须以一个合适的ctypes类型包装, 这样它们才能转换成需要的c数据类型。 printf(An int %d, a double %fn, 1234, c_double(3.14) Integer 1234, double 3.1400001049 31 使用你自定义的数据类型调用函数 你可以自定义ctypes参数,来让你自己的classes的实例作为函数的参数。ctypes寻找_as_parameter_ 属性 作为函数的参数。当然,它必须是整形,字符串,或unicode class Bottles(object): . def _init_(self, number): . self._as_parameter_ = number . bottles = Bottles(42) printf(%d bottles of beern, bottles) 42 bottles of beer 19 如果你不想保存实例的数据在_as_parameter_ 实例的变量中,你应该定义一个属性使这个数据有效。 定义需要的参数类型(函数原型) 可以通过设置argtypes属性,定义从dll中导出的需要的参数类型。argtypes必须是c数据类型的一个列表 (这里的printf可能不是很好的例子,因为它是一个取决于format字符串的可变数目的,需要不同类型的参数 ,使用这种特性是一种不错的特性) printf.argtypes = c_char_p, c_char_p, c_int, c_double printf(String %s, Int %d, Double %fn, Hi, 10, 2.2) String Hi, Int 10, Double 2.200000 37 定义一个format阻止了不兼容的参数类型(仅仅是一个c函数的原型),尝试转换这些参数到合适的类型。 printf(%d %d %d, 1, 2, 3) Traceback (most recent call last): File , line 1, in ? ArgumentError: argument 2: exceptions.TypeError: wrong type printf(%s %d %f, X, 2, 3) X 2 3.00000012 12 如果你定义了你自己的类去传给调用函数,你必须使用from_param类方法,这样就可以在argtypes列表中使用它们。 from_param类方法接收传给函数的Python对象,它做一个类型检测,或者确保这个对象是可接受的就返回这个对象本身 ,它的_as_parameter_属性,或者你想作为参数传递给c函数的任何东西。这结果必须是一个整数,字符串,unicode,一个 ctypes的实例,或者任何有_as_parameter_属性的对象。 返回值 主要的函数都返回c int类型,其它的返回类型可以通过设置function对象的restype属性 这有一个更好的例子,它使用strchr函数,它期望一个string指针和一个char,同时返回一个指向string的 指针。 strchr = libc.strchr strchr(abcdef, ord(d) # doctest: +SKIP 8059983 strchr.restype = c_char_p # c_char_p is a pointer to a string strchr(abcdef, ord(d) def print strchr(abcdef, ord(x) None 如果你想避免上面的ord(x)调用,你可以设置argtypes属性,这第二个参数会从一个python字符到一个c char字符 strchr.restype = c_char_p strchr.argtypes = c_char_p, c_char strchr(abcdef, d) def strchr(abcdef, def) Traceback (most recent call last): File , line 1, in ? ArgumentError: argument 2: exceptions.TypeError: one character string expected print strchr(abcdef, x) None strchr(abcdef, d) def 你可以使用一个可以调用的python对象(一个函数或者一个class)作为这restype属性的值,如果外部函数 返回的是一个整数,这个可调用对象将调用这c函数返回的整数,这个可调用对象的结果将作为这个函数调用的结果,这个对于检查 返回值,同时产生一个错误很有帮助。 GetModuleHandle = windll.kernel32.GetModuleHandleA # doctest: +WINDOWS def ValidHandle(value): . if value = 0: . raise WinError() . return value . GetModuleHandle.restype = ValidHandle # doctest: +WINDOWS GetModuleHandle(None) # doctest: +WINDOWS 486539264 GetModuleHandle(something silly) # doctest: +WINDOWS Traceback (most recent call last): File , line 1, in ? File , line 3, in ValidHandle WindowsError: Errno 126 The specified module could not be found. WinError是一个函数,它调用Windows的FormatMessage() API函数去获取一个错误码的描述,同时返回一个exception 。WinError采取一个可选择的错误代码参数,如果没有,它调用GetLastError()。 请注意大量的错误检测机制是可行的,通过errcheck属性,更多的细节请参考手册。 传递指针和传递引用 有时候,一个c API函数期望一个指向某个数据类型的指针作为参数,可能要写入一些数据到相应的位置,如果这数据 太大而不能传值。这同样适用于通过引用传递参数。 ctypes导出这byref函数,它用作通过引用传递参数。使用指针可以达到相同的效果,尽管指针可以做更多的事情 ,因为它初始化了一个指针对象,它比使用 byref要快,但在python中你不需要指针对象 i = c_int() f = c_float() s = create_string_buffer(000 * 32) print i.value, f.value, repr(s.value) 0 0.0 libc.sscanf(1 3.14 Hello, %d %f %s, . byref(i), byref(f), s) 3 print i.value, f.value, repr(s.value) 1 3.1400001049 Hello 结构和联合 Structures和unions必须继承Structure和Union基础类,它们都在ctypes模块中定义,每一个子类必须定义一个 _fields_属性,_fields_是一个2维tuples的列表,包含这field的name和field的type 这field类型必须是一个ctypes类型,像c_int,或者任何其它的继承ctypes的类型,structure,union,array,指针。 这里有一个简单的POINT结构,包含两个整型x和y,同样它也显示了如何在构造函数中初始化一个结构。 from ctypes import * class POINT(Structure): . _fields_ = (x, c_int), . (y, c_int) . point = POINT(10, 20) print point.x, point.y 10 20 point = POINT(y=5) print point.x, point.y 0 5 POINT(1, 2, 3) Traceback (most recent call last): File , line 1, in ? ValueError: too many initializers 你可以创建很多更复杂的结构。结构可以通过包含一个结构类型的field来包含其它的结构。 下面是一外RECT结构,包含两个POINT名为upperleft和lowerright。 r = RECT(POINT(1, 2), POINT(3, 4) r = RECT(1, 2), (3, 4) Field描述可以从类中找到,这在调试中是很有用的,它可以提供非常有用的信息。 print POINT.x print POINT.y Structure/union对齐和byte顺序 默认Structure/union的field对齐和c编译器的方式相同。通过在子类中定义一个_pack_ class 属性可以覆盖这个行为。这必须设置一个正数和为fields对齐设置一个最大值。这就是 #pragma pack(n)在 MSVC中的作用。 ctypes使用结构和联合原始的byte顺序。使用一个非原始的byte顺序创建结构,你可以使用BigEndianStructure, LittleEndianStructure,BigEndianUnion,LittleEndianUnion中的一个作为基础类。这些类不包含pointer的field。 Structure/union的byte field 它是可以创建包含byte field的structures 和 unions byte field仅仅可以适合整型field,这bit的width通过 这 _fields_元组中第三项来指定。 class Int(Structure): . _fields_ = (first_16, c_int, 16), . (second_16, c_int, 16) . print Int.first_16 print Int.second_16 Array Array是对列,包含一个固定数目的相同类型的实例。 被推荐的方法去创建array类型是对过一个数据类型与一个正数相乘。 TenPointsArrayType = POINT * 10 下面是一个人造数据类型的例子,一个结构包含4个POINT和一些其它的东西。 from ctypes import * class POINT(Structure): . _fields_ = (x, c_int), (y, c_int) . class MyStruct(Structure): . _fields_ = (a, c_int), . (b, c_float), . (point_array, POINT * 4) print len(MyStruct().point_array) 4 使用一般的方法创建实例,通过调用class arr = TenPointsArrayType() for pt in arr: print pt.x, pt.y 这上面打印了一系列的0,0行,因为这队列的内容都初始化为0。 正确类型的初始化也可以指定。 from ctypes import * TenIntegers = c_int * 10 ii = TenIntegers(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) print ii for i in ii: print i, . 1 2 3 4 5 6 7 8 9 10 指针 通过调用ctypes类型的指针函数来创建指针实例 from ctypes import * i = c_int(42) pi = pointer(i) 指针实例有一个contents属性,返回这个指针指向的对象,上面的i对象。 pi.contents c_long(42) 注意 这ctypes不包含面向对象,它每次查看一个属性时就创建一个全新的等价的对象。 pi.contents is i False pi.contents is pi.contents False 分配另外一个c_int的实例到pointer的内容属性,将导致指针指向存储它本身的内存位置。 i = c_int(99) pi.contents = i pi.contents c_long(99) 指针的实例也可以用整数来索引 pi0 99 分配一个整数索引将改变它指向的指针 print i c_long(99) pi0 = 22 print i c_long(22) 你可以使用不同于0整型的索引的指针,但是你必须知道你正在做什么,就像在c里面,你可以访访问或武断改变内存位置 ,一般而言,当你从一个c函数接收一个指针,同时你知道这个指针实例指向的是一个array而不是单个项。 在这种场景下,这指针函数比简单的创建一个指针实例要困难多,它首先必须通过POINTER函数创建一个可以 接收任何ctypes类型的指针类型,返回一个新类型。 PI = POINTER(c_int) PI PI(42) Traceback (most recent call last): File , line 1, in ? TypeError: expected c_long instead of int PI(c_int(42) 不带参数的调用这指针类型创建一个NULL指针,NULL指针有一个False 布尔值。 null_ptr = POINTER(c_int)() print bool(null_ptr) False ctypes检查NULL非引用指针(非引用非关联指针会crash Python). null_ptr0 Traceback (most recent call last): . ValueError: NULL pointer access null_ptr0 = 1234 Traceback (most recent call last): . ValueError: NULL pointer access 类型转换 一般而言,ctypes进行严格的类型检查,如果在函数参数列表中有一个POINT(c_int)类型或者 在作为结构定义中一个成员field,仅仅接收这相同类型的实例。有一些exception应用在这些规则上 当ctypes接收其它类型的对象。例如你传递一个兼容的array实例,代替指针类型。这样对于POINTER(c_int) ,ctypes可以接收c_int型array class Bar(Structure): . _fields_ = (count, c_int), (values, POINTER(c_int) . bar = Bar() bar.values = (c_int * 3)(1, 2, 3) bar.count = 3 for i in range(bar.count): . print bar.valuesi . 1 2 3 设置一个指针类型为空,你可以分配None: bar.values = None 有时候,你有一些不兼容类型的实例。在C中,你可以强制转换一个类型到另一个类型。ctypes提供了一个有相同作用的转换 函数。这Bar structure定义了一个POINTER(c_int)指针,或者c_int的array的field,但是没有其它类型。 bar.values = (c_byte * 4)() Traceback (most recent call last): File , line 1, in ? TypeError: incompatible types, c_byte_Array_4 instance instead of LP_c_long instance 对于这些例子,cast函数是很不错的。 使用cast函数将一个ctypes实例转换成一个不同的ctypes数据类型的指针。cast对象有两个参数,一个ctypes对象 是或者可以转换成某些类型的指针,或一个ctypes指针类型。它返回这第二个参数的实例,它引用第一个参数相同的内存块。 a = (c_byte * 4)() cast(a, POINTER(c_int) 因此,cast可以使用来分配Bar的值域 bar = Bar() bar.values = cast(c_byte * 4)(), POINTER(c_int) print bar.values0 0 未完成的类型 完成的类型包括成员还没有定义的structure或union,array.在c,他们是在前面声明 ,而在后面定义的: struct cell; /* forward declaration */ struct char *name; struct cell *next; cell; 直接转换为ctypes像这样,但是它不能工作: class cell(Structure): . _fields_ = (name, c_char_p), . (next, POINTER(cell) . Traceback (most recent call last): File , line 1, in ? File , line 2, in cell NameError: name cell is not defined 但是,新类cell在它自己的类声明中是无效的。在ctypes,可以定义cell类,同时在类声明中设置_fields_属性 from ctypes import * class cell(Structure): . pass . cell._fields_ = (name, c_char_p), . (next, POINTER(cell) 尝试一下,我们可以创建cell的两个实例,让他们指向他们自己。 c1 = cell() = foo c2 = cell() = bar c1.next = pointer(c2) c2.next = pointer(c1) p = c1 for i in range(8): . print , . p = p.next0 . foo bar foo bar foo bar foo bar 回调函数 ctypes可以从python可调用对象中创建一个c可调用的函数指针。这些通常被称为回调函数。 首先,你需要为回调函数创建一个类,这类知道这调用约定,返回类型,以及这函数需要接收的参数个数。 CFUNCTYPE工厂函数使用正常的cdecl调用约定创建回调函数的类型,同时在windows上,WINFUNCTYPE工厂函数 使用stdcall调用给定为回调函数创建类型。 这两个工厂函数的结果类型作为第一个参数,这些期望的参数类型回调函数作为这剩下的参数。 这而使用标准的c库qsort函数,它使用一个回调函数来帮助排序items。qsort将使用来排序一个整数array. IntArray5 = c_int * 5 ia = IntArray5(5, 1, 7, 33, 99) qsort = libc.qsort qsort.restype = None 必须接收一个指向数据的指针来调用qsort函数排序,同时一个比较函数的指针,也就是回调函数。这回调函数 接收两个items的指针,如果第一个小于第二个它返回一个负数,如果相等,返回0,其它的就返回一个正数。 我们的回调函数接收
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2024年建筑工程公司与施工方分包合同
- 2024年庆典花卉租赁合同
- 2024年度环保设备生产与安装合同
- 2024年企业间关于虚拟现实技术研发合同
- 2024年度BIM模型能耗分析与优化服务合同
- 2024国有林业企业与农村集体组织土地承包合同
- 2024年家庭遗产分配协议
- 2024年度金融科技合作协议
- 2024酒店布草采购合同
- 2024年度离婚财产分配合同:涉及三个未成年子女的抚养权
- 小作坊食品安全管理制度(3篇)
- 孕期焦虑测评
- 光伏电站施工组织设计
- 全人教版四年级英语上册期中考试知识点汇总-必背的重点
- 2023年1月高三英语试题(浙江卷)+听力+答案+作文
- 汉字听写大赛汇总成语
- 体位引流课件
- 市政工程项目部管理制度及岗位职责
- 第9章-庭院生态工程
- 《特殊儿童早期干预》教学大纲
- GB/T 5456-2009纺织品燃烧性能垂直方向试样火焰蔓延性能的测定
评论
0/150
提交评论