h4 { font-size: 14px; font-weight: bold; margin: 10px 0; background-color: rgba(152, 213, 229, 1); border-radius: 6px; height: 35px; line-height: 35px; border: 1px solid rgba(57, 154, 178, 1); padding-left: 6px } h5 { font-size: 14px; font-weight: bold; margin: 10px 0; background-color: rgba(152, 213, 229, 1); border-radius: 6px; height: 35px; line-height: 35px; border: 1px solid rgba(57, 154, 178, 1); padding-left: 6px; width: 50% }
ctypes 是一个Python 标准库中的一个库.为了实现调用 DLL,或者共享库等C数据类型而设计.它可以把这些C库包装后在纯Python环境下调用.
加载动态链接库
dll:动态链接库 系统生成时的调用约定不同 cdll、windll和oledll
CDLL:代码方式 cdecl 。 WINDLL:代码方式win32 stdcall 。 oledll使用win32调用代码方式 且返回值是windows里返回的hresult值,双字节的值说明函数执行结果,其最高bit位为0则执行成功,1则为执行失败。
详细见http://www.blogjava.net/JAVA-HE/archive/2010/01/04/308134.html。 cdecl和stdcall异同,参数入栈顺序均是从右向左,不同的是栈的清除工作,cdecl是由调用者负责清除,stdcall由被调用者清除。
注意 linux 平台上使用 cdll 的,而不是 windll。
from ctypes import windll # 首先导入 ctypes 模块的 windll 子模块 somelibc = windll.LoadLibrary(some.dll) # 使用 windll 模块的 LoadLibrary 导入动态链接库
from ctypes import cdll # 首先导入 ctypes 模块的 cdll 子模块,注意 linux 平台上使用 cdll 的,而不是 windll。 somelibc = cdll.LoadLibrary(“./some.so”) # 使用 cdll 模块的 LoadLibrary 导入动态链接库
stdcall调用约定和cdecl调用约定声明的导出函数,在使用python加载时使用的加载函数是不同的 加载的时候要根据你将要调用的函数是符合什么调用约定的 cdecl调用约定:也有两种加载方式 Objdll = ctypes.cdll.LoadLibrary("dllpath") Objdll = ctypes.CDLL("dllpath") /*其实windll和cdll分别是WinDLL类和CDll类的对象。*/
from ctypes import * print(windll.kernel32) # 使用windll约定方式导出 kernel32.dll 中的功能和信息 print(cdll.msvcrt) # 使用cdll约定方式导出 msvcrt.dll 中的功能和信息 print(cdll.LoadLibrary('libc.so.6')) print(CDLL('libc.so.6')) # Linux动态链接库的编译与windows不同,后缀也不同,通常为 .so 文件,放在 usr/lib 文件夹下 # windows的dll大多放在Windows\System32文件夹下.
有些时候 dll 导出的函数不是Python中的有效值,比如 "??2@YAPAXI@Z". 在这种情况下你必须使用函数 getattr() 来获取函数: getattr(cdll.msvcrt, "??2@YAPAXI@Z") # 因为 cdll.msvcrt.??2@YAPAXI@Z 不合法,变量名(属性)不可以这样定义
获取函数
在加载dll的时候会返回一个DLL对象(假设名字叫Objdll),利用该对象就可以调用dll中的方法。 e.g.
如果dll中有个方法名字叫Add(注意如果经过stdcall声明的方法,如果不是用def文件声明的导出函数或者extern “C” 声明的话,
编译器会对函数名进行修改,这个要注意,我想你们懂的。) 调用:nRet = Objdll.Add(12, 15) 即完成一次调用。
有些时候 dll 导出的函数不是Python中的有效值,比如 “??2@YAPAXI@Z”. 在这种情况下你必须使用函数 getattr() 来获取函数:
getattr(cdll.msvcrt, "??2@YAPAXI@Z") # 因为 cdll.msvcrt.??2@YAPAXI@Z 不合法,变量名(属性)不可以这样定义
在windows中,一些dll的导出函数并不是按名字来的,而是下标数字. 这些函数可以用下标序号来获取,比如:
>>> 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 >>>
基本数据类型
ctypes type C type Python type c_bool _Bool bool(1) c_char char 1-character bytes object c_wchar wchar_t 1-charactor string ① c_byte char int c_ubyte unsigned char int c_short short int c_ushort unsigned short int c_int int int ② c_uint unsigned int int c_long long int c_ulong unsigned long int c_longlong __int64 or long long int c_ulonglong unsigned __int64 or unsigned long long int c_size_t size_t int c_ssize_t ssize_t or Py_ssize_t int c_float float float c_double double float c_longdouble long double float
# 指针类型 c_char_p char * (NUL terminated) bytes object or None ③ c_wchar_p wchar_t * (NUL terminated) string or None c_void_p void * int or None
Null None ④
只有移上四种两者间可以直接使用,其余都要经由ctypes包装
Bottles
自定义数据类型
class Bottles: ... def __init__(self, number): ... self._as_parameter_ = number # ,它必须是Python支持的四种类型之一
ctypes所有类型都是一个对象包装
c_int() # ctypes 中 c_int只是 c_long的别名而已
你应该小心,不要把这些指针传给试图改变内存的函数. 如果你确实需要改变内存数据而非替换指针地址, ctypes提供了create_string_buffer()函数.
from ctypes import * s = "Hello, World" c_s = c_wchar_p(s) print(c_s) # c_wchar_p(4765728) c_wchar_p('Hello, World') c_s.value = "Hi, there" print(c_s) # c_wchar_p(45431616) c_wchar_p('Hi, there') print(s) # Hello, World # first object is unchanged
from ctypes import * p = create_string_buffer(3) # create a 3 byte buffer, initialized to NUL bytes print(sizeof(p), str(p.raw)) # 3 b'\x00\x00\x00' p = create_string_buffer(b"Hello") # create a buffer containing a NUL terminated string print(sizeof(p), str(p.raw)) # 6 b'Hello\x00' print(repr(p.value)) # b'Hello' p = create_string_buffer(b"Hello", 10) # create a 10 byte buffer print(sizeof(p), str(p.raw)) # 10 b'Hello\x00\x00\x00\x00\x00' p.value = b"Hi" # 这里注意 p.value = b'HI'并不是把value替换成常量b'HI'的指针,而是直接修改了buffer print(sizeof(p), repr(p.raw)) # 10 b'Hi\x00lo\x00\x00\x00\x00\x00' # 从这里看得到,确实是 buffer 被修改了,上一次的值Hello 中的lo还在内存之中.
create_string_buffer() 函数代替了以前的 c_buffer() 函数(现在依旧可用,作为别名). 为了创建可修改的unicode wchar_t类型内存块, 请使用 create_unicode_buffer() 函数.
返回值类型
>>> strchr = libc.strchr >>> strchr(b"abcdef", ord("d")) 8059983 # 这里ctypes并不知道返回的是什么,所以默认直接就把指针地址打印了出来(int型) >>> strchr.restype = c_char_p # c_char_p is a pointer to a string >>> strchr(b"abcdef", ord("d")) b'def' # 这里设置过了,ctypes知道返回的是c_char_p类型,所以打印该指针指向的字符串数据 >>> print(strchr(b"abcdef", ord("x"))) None >>>
>>> strchr.restype = c_char_p >>> strchr.argtypes = [c_char_p, c_char] >>> strchr(b"abcdef", b"d") 'def'
参数类型
printf.argtypes = [c_char_p, c_char_p, c_int, c_double] # 指定4个参数,按顺序
>>> dll.addf.argtypes = (c_float, c_float) # addf 有两个形参,都是 float 类型 或者是下面这样,但是,你知道的,查找元组的效率略高:) >>> dll.addf.argtypes = [c_float, c_float] # addf 有两个形参,都是 float 类型
结构体
typedef struct _SimpleStruct { int nNo; float fVirus; char szBuffer[512]; } SimpleStruct, *PSimpleStruct; typedef const SimpleStruct* PCSimpleStruct; extern "C"int __declspec(dllexport) PrintStruct(PSimpleStruct simp); int PrintStruct(PSimpleStruct simp) { printf ("nMaxNum=%f, szContent=%s", simp->fVirus, simp->szBuffer); return simp->nNo; }
from ctypes import * class SimpStruct(Structure): _fields_ = [ ("nNo", c_int), ("fVirus", c_float), ("szBuffer", c_char * 512)] dll = CDLL("AddDll.dll") simple = SimpStruct(); simple.nNo = 16 simple.fVirus = 3.1415926 simple.szBuffer = "magicTong/0" print dll.PrintStruct(byref(simple))
指针
ctypes内和指针有关的三个参数
函数 | 说明 |
byref(x [, offset]) | 返回 x 的地址,x 必须为 ctypes 类型的一个实例。相当于 c 的 &x 。 offset 表示偏移量。 |
pointer(x) | 创建并返回一个指向 x 的指针实例, x 是一个实例对象。 |
POINTER(type) | 返回一个类型,这个类型是指向 type 类型的指针类型, type 是 ctypes 的一个类型。 |
byref 很好理解,传递参数的时候就用这个,用 pointer 创建一个指针变量也行,不过 byref 更快。
而 pointer 和 POINTER 的区别是,pointer 返回一个实例,POINTER 返回一个类型。甚至你可以用 POINTER 来做 pointer 的工作:
>>> a = c_int(66) # 创建一个 c_int 实例 >>> b = pointer(a) # 创建指针 >>> c = POINTER(c_int)(a) # 创建指针 >>> b <__main__.LP_c_long object at 0x00E12AD0> >>> c <__main__.LP_c_long object at 0x00E12B20> >>> b.contents # 输出 a 的值 c_long(66) >>> c.contents # 输出 a 的值 c_long(66)
pointer 创建的指针貌似没方法修改指向的 ctypes 类型值。
该说的都说了,接下来就要调用 print_point 函数了:
>>> dll.print_point.argtypes = (POINTER(Point),) # 指明函数的参数类型 >>> dll.print_point.restype = None # 指明函数的返回类型 >>> >>> p = Point(32.4, -92.1) # 实例化一个 Point >>> dll.print_point(byref(p)) # 调用函数 position x 32.400002 y -92.099998>>>
>>> dll.print_point(pointer(p)) # 调用函数 position x 32.400002 y -92.099998>>>
今天的文章ctypes_hyper-competitive分享到此就结束了,感谢您的阅读,如果确实帮到您,您可以动动手指转发给其他人。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/47102.html