[447]python ctypes调用.dll and .so

爱⌒轻易说出口 提交于 2021-02-18 15:46:02

ctypes是Python的一个外部库,提供和C语言兼容的数据类型,可以很方便地调用CDLL中的函数。ctypes的官方文档在这里。

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

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

ctypes type C type Python Type
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_bool bool bool
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 longlong 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 float
c_char_p char * string or None
c_wchar_p wchar_t * unicode or None
c_void_p void * 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调用约定:两种加载方式

Objdll = ctypes.windll.LoadLibrary("dllpath")  
Objdll = ctypes.WinDLL("dllpath")   

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

Objdll = ctypes.cdll.LoadLibrary("dllpath")  
Objdll = ctypes.CDLL("dllpath")  

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

3. 调用DLL方法

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

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

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

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

4. 指针与引用

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

from ctypes import *
i = c_int(1)
pi = pointer(i)

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

>>> p = create_string_buffer("Hello", 10)  # create a 10 byte buffer
>>> print sizeof(p), repr(p.raw)
10 'Hello/x00/x00/x00/x00/x00'

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

>>> null_ptr = pointer(c_int)() 
>>> print(bool(null_ptr))
False 

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

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

5. 结构体类型处理

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

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

from ctypes import *
import types
class Test(Structure):
    _fields_ = [('x', c_int),
                ('y', c_char)]
test1 = Test(1, 2)

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

from ctypes import *
import types
class Test(Structure):
    _fields_ = [('x', c_int),
                ('y', c_char),
                ('next', Test)]

则python会报错type未定义

from ctypes import *
import types
class Test(Structure):
    pass
Test._fields_ = [('x', c_int),
                ('y', c_char),
                ('next', POINTER(Test))]

6. 数组定义

数组包含一个固定数目的相同类型的实例,常用创建数组类型是对一个数据类型与一个正数相乘,例如:

ArrayType = Test * 10

初始化和使用数组:

>>> from ctypes import *
>>> TenIntegers = c_int * 10
>>> ii = TenIntegers(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
>>> print ii
<c_long_Array_10 object at 0x...>
>>> for i in ii: print i,

7. 回调函数

ctypes可以从python可调用对象中创建一个c可调用的函数指针,这些通常被称为回调函数。

import platform
from ctypes import *

if platform.system() == 'Windows':
    libc = cdll.LoadLibrary('msvcrt.dll')
elif platform.system() =='Linux':
    libc = cdll.LoadLibrary('libc.so.6')

来源:https://blog.csdn.net/linda1000/article/details/12623527

本文同步分享在 博客“周小董”(CSDN)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!