python ctypes常用方法

来源:互联网 发布:苹果免费office软件 编辑:程序博客网 时间:2024/05/22 17:22
Python中ctypes的使用遇到的问题
ctypes是Python的一个外部库,提供和C语言兼容的数据类型,可以很方便地调用C DLL中的函数。ctypes的官方文档在https://docs.python.org/3/library/ctypes.html。

1. ctypes基本数据类型映射表

参数类型预先设定好,或者在调用函数时再把参数转成相应的c_***类型。ctypes的类型对应如下:

ctypes typeC typePython Typec_charchar1-character stringc_wcharwchar_t1-character unicode stringc_bytecharint/longc_ubyteunsigned charint/longc_boolboolboolc_shortshortint/longc_ushortunsigned shortint/longc_intintint/longc_uintunsigned intint/longc_longlongint/longc_ulongunsigned longint/longc_longlong__int64 or longlongint/longc_ulonglongunsigned __int64 or unsigned long longint/longc_floatfloatfloatc_doubledoublefloatc_longdoublelong double floatfloatc_char_pchar *string or Nonec_wchar_pwchar_t *unicode or Nonec_void_pvoid *int/long or None 

对应的指针类型是在后面加上"_p",如int*是c_int_p等等。在python中要实现c语言中的结构,需要用到类。

2. 加载DLL

访问dll,首先需引入ctypes库 
from ctypes import *   

假设你已经有了一个的DLL(名字是add.dll),且该DLL有一个符合cdecl(这里强调调用约定是因为,stdcall调用约定和cdecl调用约定声明的导出函数,在使用python加载时使用的加载函数是不同的,后面会有说明)调用约定的导出函数Add。
stdcall调用约定:两种加载方式

[python] view plain copy
 print?
  1. Objdll = ctypes.windll.LoadLibrary("dllpath")    
  2. Objdll = ctypes.WinDLL("dllpath")     

cdecl调用约定:也有两种加载方式

[python] view plain copy
 print?
  1. Objdll = ctypes.cdll.LoadLibrary("dllpath")    
  2. Objdll = ctypes.CDLL("dllpath")    

其实windll和cdll分别是WinDLL类和CDll类的对象。 


3. 调用DLL方法

加载dll后会返回一个DLL对象,使用其中的函数方法则相当于操作该对象的对应属性。


注意,经过stdcall声明的方法,如果不是用def文件声明的导出函数或者extern “C” 声明的话,编译器会对函数名进行修改


函数参数申明,通过设置函数的argtypes属性


函数返回类型,函数默认返回c_int类型,如果需要返回其他类型,需要设置函数的restype属性

如下:

test.c(动态库源代码)

[cpp] view plain copy
  1. // 编译生成动态库: gcc -g -fPIC -shared -o libtest.so test.c  
  2.   
  3. #include <stdio.h>  
  4. #include <string.h>  
  5. #include <stdlib.h>  
  6.   
  7. typedef struct StructPointerTest  
  8. {  
  9.     char name[20];  
  10.     int age;  
  11. }StructPointerTest, *StructPointer;  
  12.   
  13. StructPointer test()    // 返回结构体指针  
  14. {   
  15.     StructPointer p = (StructPointer)malloc(sizeof(StructPointerTest));   
  16.     strcpy(p->name, "Joe");  
  17.     p->age = 20;  
  18.       
  19.     return p;   
  20. }  

编译gcc -g -fPIC -shared -o libtest.so test.c


call.py(python调用c语言生成的动态库):

[python] view plain copy
  1. #!/bin/env python  
  2. # coding=UTF-8  
  3.   
  4. from ctypes import *  
  5.   
  6. #python中结构体定义  
  7. class StructPointer(Structure):  
  8.     _fields_ = [("name", c_char * 20), ("age", c_int)]  
  9.   
  10. if __name__ == "__main__":  
  11.     lib = cdll.LoadLibrary("./libtest.so")  
  12.     lib.test.restype = POINTER(StructPointer)  
  13.     p = lib.test()  
  14.   
  15.     print "%s: %d" %(p.contents.name, p.contents.age)  

最后运行结果:

[plain] view plain copy
  1. [zcm@c_py #112]$make clean  
  2. rm -f *.o libtest.so  
  3. [zcm@c_py #113]$make  
  4. gcc -g -fPIC -shared -o libtest.so test.c  
  5. [zcm@c_py #114]$./call.py   
  6. Joe: 20  
  7. [zcm@c_py #115]$  

4. 指针与引用

常用的通过调用ctypes类型的指针函数来创建指针实例:

[python] view plain copy
 print?
  1. from ctype import *  
  2. i = c_int(1)  
  3. pi = POINTER(i)  

对指针实例赋值只会改变其指向的内存地址,而不是改变内存的内容,与其他语言类似,如需要可改变内容的字符串,可须使用create_string_buffer()

[python] view plain copy
 print?
  1. >>> p = create_string_buffer("Hello"10)  # create a 10 byte buffer  
  2. >>> print sizeof(p), repr(p.raw)  
  3. 10 'Hello/x00/x00/x00/x00/x00'  

不带参数的调用指针类型创建一个NULL指针, NULL指针有一个False布尔值

[python] view plain copy
 print?
  1. >>> null_ptr = POINTER(c_int)()   
  2. >>> print bool(null_ptr)   
  3. False   

指针实例有一个contents属性,返回这个指针所指向的对象。

另外,byref()是用来传递引用参数,pointer()作为传参通常会创建一个实际的指针对象,当不需要实际指针对象时,则可使用byref()

byref(n)返回的相当于C的指针右值&n,本身没有被分配空间:

>>> from ctypes import *
>>> n = c_int(0)
>>> p = byref(n)
>>> pp = byref(p)
Traceback (most recent call last):
  File "<pyshell#4>", line 1, in <module>
    pp = byref(p)
TypeError: byref() argument must be a ctypes instance, not 'CArgObject'

 

pointer返回的相当于指针左值T* p=&n,可以改变,可以取地址:

>>> from ctypes import *
>>> n = c_int(0)

>>> q = pointer(n)
>>> q.contents = c_int(1)
>>> qq = byref(q)
>>> dir(qq)
['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '_obj']
>>> type(qq)
<class 'CArgObject'>                  #'CArgObject'是什么对象???

>>> qq
<cparam 'P' (010AD238)>
>>> q
<__main__.LP_c_long object at 0x010AD210>
>>> q.contents
c_long(1)

对于T**参数,通常你得构造一个pointer,然后byref传进去


 

5. 结构体类型处理

Structures和Unions必须继承Structure和Union基础类,它们都在ctypes模块中定义,每一个子类必须定义个_fields_属性,_fields_是一个二维的tuples列表,包含着每个field的name及type,这field类型必须是一个ctypes类型,如c_int,或者任何其他的继承ctypes的类型,如Structure, Union, Array, 指针等。

例如有一个简单结构,包含两个整型x和y,可如下初始化一个结构:

[python] view plain copy
 print?
  1. from ctypes import *  
  2. import types  
  3. class Test(Structure):  
  4.     _fields_ = [('x', c_int),  
  5.                 ('y', c_char)]  
  6. test1 = Test(12)  

另外,如结构体用于链表操作,即包含指向结构体指针时,若直接定义:

[python] view plain copy
 print?
  1. from ctypes import *  
  2. import types  
  3. class Test(Structure):  
  4.     _fields_ = [('x', c_int),  
  5.                 ('y', c_char),  
  6.                 ('next', Test)]  

则python会报错type未定义,:

[python] view plain copy
 print?
  1. from ctypes import *  
  2. import types  
  3. class Test(Structure):  
  4.     pass  
  5. Test._fields_ = [('x', c_int),  
  6.                 ('y', c_char),  
  7.                 ('next', POINTER(Test))]  

6、回调函数:

1. 定义回调函数类型,类似C中的函数指针,如void ( * event_cb )( void * data, void * user_data ),定义为:

event_cb = CFUNCTYPE( None, c_void_p, c_void_p )

None表示返回值是void,也可以为其它类型,剩余俩个参数与C中的回调参数一致。

2. 定义Python回调函数:

def event_arrived( data, user_data ):
    #parsing data
    #...
    #return something or None

3. 注册回调函数:

register_events_listener( event_cb( event_arrived ), c_void_p( 0 ))


7、传递结构体指针:

1. 定义结构体:

class MyStructure( Structure )
    _fields_ = [
        ( 'x', c_int ),
        ( 'y', c_int )]

2. 传递结构体:

_local = MyStructure( 1, 2 )
process_this_structure( byref( _local ))


8、函数参数为char**或者数组指针

对应c函数为fun(char ** ppdata, int*idlist)

python对应的参数设置:fun.py

patterns=["qwerqwer", "12eweqwe", "asdfdgfgg"]

ids= [1,2,3,4,5]

a_patterns = (c_char_p * len(patterns))()
a_patterns[:] = patterns

ids = (c_uint * len(ids))(*ids)

lib = ctypes.cdll.LoadLibrary(libpath)

lib.fun.argtypes = [(c_char_p*len(patterns)),  POINTER(c_uint)]

lib.fun(a_patterns,ids)

持续更新。。。




原创粉丝点击