Python使用ctypes模块调用DLL函数之传递结构体参数
Python使用ctypes模块调用DLL函数之传递结构体
参数
引言
在Python语言中,可以使用ctypes模块调用其它如C++语言编写的动态链接库DLL文件中的函数,在提高软件运行效率的同时,也可以充分利用目前市面上各种第三方的DLL库函数,以扩充Python软件的功能及应用领域,减少重复编写代码、重复造轮子的工作量,这也充分体现了Python语言作为一种胶水语言所特有的优势。
上篇已经讲了传递数值、指针、字符串参数的例子,详细细节请参考:
这次讲一下在Python中使用ctypes模块调用DLL中的库函数传递结构体参数的情况。
同样,操作系统环境是win7 64位,Python使用的版本是python2.7.14,函数约定的调用方式为C调用(cdecl)方式。
例1:简单的结构体传递参数示例
这个例子的功能是打印一个学生的信息,并返回一个指向字
符串的指针。
其中,学生信息定义为一个结构体类型,包括学生的姓名、年龄及分数信息,该函数的声明及结构体定义如下图所示:
函数printStudentInfo的具体C语言实现代码如下:
在Python中的调用方式如下:
从上面的代码中可以看出,对于结构体参数的传递,在Python中需要使用Structure作为基类定义一个与之对应的结构体类Student,在类的_fields中定义结构体中每个成员变量的名称和数据类型。
然后定义结构体类的一个实例对象student,对每个成员变量逐个赋值,以byref传址的方式进行调用就可以了。
例2:复杂结构体传递参数示例
如果一个结构体里面包括了另一个结构体、指针等综合要素,情况就要复杂些了。
对上面的例子改进一下,定义一个People类,里面包含了一个指向Student类型的指针变量和一个整形变量,在这个结构体中存储多个学生的信息及学生的总数信息。
设计一个函数getPeopleInfo获取每个学生的信息,并返回所有学生的个数,该函数的声明及结构体定义如下图所示:
函数getPeopleInfo的具体C语言实现代码如下:
在Python中的调用方式如下:
在上面的代码中,第43行定义了一个指向Student结构体的指针变量。
第48行调用该函数并打印返回值,第49-50行,分别打印两个学生的具体信息。
完整的测试代码
完整的测试代码如下图所示:
运行结果如下图所示:
在上图中,第1行输出的是函数printStudentInfo内部的打印信息。
python调用C++DLL传参技巧
python调⽤C++DLL传参技巧准备⼯作:C++⽂件(cpp):(注意在函数声明上加上extern "C" 的修饰)#include <stdio.h>extern"C" {__declspec(dllexport) int Double(int x);__declspec(dllexport) float floatAdd(float a,float b);__declspec(dllexport) void HelloWorld(char * str);__declspec(dllexport) void Ints(int * arr,int n);}int Double(int x){return x*2;}float floatAdd(float a,float b) {return a+b;}void HelloWorld(char * str){puts(str);}void Ints(int * arr,int n){for(int i=0;i<n;i++){printf("%d ",arr[i]);}puts("");}⽤g++(mingw64位)编译为dll:g++ dlltest.cpp -shared -o dlltest.dll -Wl,--out-implib,dlltest.libpause在python脚本中加载dll :from ctypes import *dll = cdll.LoadLibrary('DLL/dlltest.dll')1.如果不加任何修饰,默认传⼊参数为int,传出参数也为int2.对于int以外的类型(如float),需要声明python函数的传⼊参数类型,传出参数类型fun.argtypes=[c_float,c_float] #定义传参类型fun.restype=c_float #定义返回值类型a=fun(c_float(1.4),c_float(1.2))print(type(a))print(a)输出:<class'float'>2.59999990463256843.对于字符串char* ,在声明传⼊参数类型时,需要声明为字符指针,然后分配⼀块char数组,最后把这个数组强制转换为字符指针并且,在把python脚本中的数据结构导⼊c++中时,需要把str转换为bytes或者bytesarray类型,并且进⾏迭代器分解hello=dll.HelloWorldhello.argtypes=[POINTER(c_char)] #传⼊参数为字符指针STR=(c_char * 100)(*bytes("相信你还在这⾥",'utf-8')) #把⼀组100个的字符定义为STRcast(STR, POINTER(c_char))hello(STR)输出:相信你还在这⾥4.对于其他数据类型的数组,(例如int*),操作相似:Ints=dll.IntsInts.argtypes=[POINTER(c_int),c_int]INT=(c_int * 100)(*[1,2,3]) #把列表传⼊变长参数args*中cast(INT, POINTER(c_int))Ints(INT,c_int(3))输出:1 2 35.对于返回值为数组的情况,可以直接使⽤索引去访问,但是下标操作[]不是从迭代器中取对象,⽽是地址偏移:def fillHoleCpp(im):dll = cdll.LoadLibrary("bfs.dll")bfs=dll.bfsbfs.argtypes = [POINTER(c_int),c_int]bfs.restype = POINTER(c_int)a = np.asarray(range(16), dtype=np.int32).reshape([4, 4])if not a.flags['C_CONTIGUOUS']:a = np.ascontiguous(a, dtype=a.dtype) # 如果不是C连续的内存,必须强制转换IMG = cast(a.ctypes.data, POINTER(c_int)) # 转换为ctypes,这⾥转换后的可以直接利⽤ctycast(IMG, POINTER(c_int))length=a.sizeans=bfs(IMG,c_int(length))print(type(ans))for i in range(0,length):print(ans[i],end='')。
python调用c++ctypelist传数组或者返回数组的方法
python调⽤c++ctypelist传数组或者返回数组的⽅法⽰例1:pycallclass.cpp:#include <iostream>using namespace std;typedef unsigned char BYTE;#define MAX_COUNT 20struct tagOutCardResult_py{BYTE cbCardCount;BYTE cbResultCard1;BYTE cbResultCard2;BYTE cbResultCard3;BYTE cbResultCard4;BYTE cbResultCard5;BYTE cbResultCard6;BYTE cbResultCard7;BYTE cbResultCard8;BYTE cbResultCard9;BYTE cbResultCard10;BYTE cbResultCard11;BYTE cbResultCard12;BYTE cbResultCard13;BYTE cbResultCard14;BYTE cbResultCard15;BYTE cbResultCard16;BYTE cbResultCard17;BYTE cbResultCard18;BYTE cbResultCard19;BYTE cbResultCard20;};struct tagOutCardResult{BYTE cbCardCount;BYTE cbResultCard[MAX_COUNT];void clear(){cbCardCount = 0;for (int nIdx = 0;nIdx < MAX_COUNT;++nIdx){cbResultCard[nIdx] = 0;}}void topy(tagOutCardResult_py* ppy){cout<<"topy function begin"<<endl;ppy->cbCardCount = cbCardCount;cout<<"topy function 1"<<endl;ppy->cbResultCard1 = cbResultCard[1 - 1];cout<<"topy function 2"<<endl;ppy->cbResultCard2 = cbResultCard[2 - 1];ppy->cbResultCard3 = cbResultCard[3 - 1];ppy->cbResultCard4 = cbResultCard[4 - 1];ppy->cbResultCard5 = cbResultCard[5 - 1];ppy->cbResultCard6 = cbResultCard[6 - 1];ppy->cbResultCard7 = cbResultCard[7 - 1];ppy->cbResultCard8 = cbResultCard[8 - 1];ppy->cbResultCard9 = cbResultCard[9 - 1];ppy->cbResultCard10 = cbResultCard[10 - 1];ppy->cbResultCard11 = cbResultCard[11 - 1];ppy->cbResultCard12 = cbResultCard[12 - 1];ppy->cbResultCard13 = cbResultCard[13 - 1];ppy->cbResultCard14 = cbResultCard[14 - 1];ppy->cbResultCard15 = cbResultCard[15 - 1];ppy->cbResultCard16 = cbResultCard[16 - 1];ppy->cbResultCard17 = cbResultCard[17 - 1];ppy->cbResultCard18 = cbResultCard[18 - 1];ppy->cbResultCard19 = cbResultCard[19 - 1];ppy->cbResultCard20 = cbResultCard[20 - 1];cout<<"topy function end"<<endl;}};class TestLib{public:void display(tagOutCardResult& ret);};void TestLib::display(tagOutCardResult& ret) {ret.cbCardCount = 3;ret.cbResultCard[0] = 1;ret.cbResultCard[1] = 50;ret.cbResultCard[2] = 100;cout<<"First display aaa ";cout<<"hello ";cout<<"world ";}extern "C" {TestLib oGameLogic;void display(tagOutCardResult_py* ret_py) {tagOutCardResult oRet;oGameLogic.display(oRet);cout<<"before topy"<<endl;oRet.topy(ret_py);cout<<"after topy"<<endl;cout<<"in cpp:ret_py->cbCardCount:"<<ret_py->cbCardCount<<endl;cout<<"in cpp:ret_py->cbResultCard1:"<<ret_py->cbResultCard1<<endl;cout<<" this:" << ret_py << endl;}}编译脚本:g++ -o libpycallclass.so -shared -fPIC pycallclass.cpp -I/usr/include/python2.6 -L/usr/lib64/python2.6/config Game.py调⽤部分。
Python使用ctypes库调用外部DLL
Python:使用ctypes库调用外部DLL一、Python之ctypesctypes是Python的一个外部库,提供和C语言兼容的数据类型,可以很方便地调用C DLL中的函数。
在Python2.5官方安装包都带有ctypes 1.1版。
ctypes的官方文档在这里。
ctypes的使用非常简明,如调用cdecl方式的DLL只需这样:view sourceprint?1 from ctypes import *;2 h=CDLL('msvcrt.dll')3 h.printf('a=%d,b=%d,a+b=%d',1,2,1+2);以上代码运行后输出 a=1,b=2,a+b=3。
二、加载库和普通函数的调用官方API提供的库中有几个主要的函数:view sourceprint?01 //初始化02 int DCSPCLIENTDLL InitInterface( const char *pCenterIP, const unsigned short nUpLinkSvrPort,const unsigned short nDownLinkSvrPort );0304 //释放资源05 int DCSPCLIENTDLL FiniInterface( void );0607 //登录08 int DCSPCLIENTDLL Login( const unsigned int uiBranchPlatformID, const unsigned int nUserID, const char *pPassword );09 //注销10 int DCSPCLIENTDLL Logout( const unsigned int uiBranchPlatformID, const unsigned int nUserID, const char *pPassword );1112 //发车辆实时定位数据13 int DCSPCLIENTDLL SendUPRealLocation( const char * const pDeviceId, const char cDeviceColor,14 const unsigned short nMsgCode, const _stBPDynamicData * const pStGpsData );在Python中加载使用:view sourceprint?01 from ctypes import *0203 #加载API库04 api = CDLL('DCSPClientDLL.dll');05 #初始化函数的参数类型06 api.InitInterface.argtypes=[c_char_p,c_ushort,c_ushort]07 api.Login.argtypes=[c_uint,c_uint,c_char_p]08 api.Logout.argtypes=[c_uint,c_uint,c_char_p]0910 #初始化并登录11 api.InitInterface(u"中心服务器地址" , u'上行服务端端口' , u'下行客户端端口')12 api.Login(platformID,userID,password);13 #.....其它操作14 api.Logout(platformID,userID,password); #注销参数类型可以像上面的代码一样预先设定好,或者在调用函数时再把参数转成相应的c_***类型。
python-ctypes模块中文帮助文档
内容:.加载动态链接库.从已加载的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<WinDLL 'kernel32', handle ... at ...>>>> print cdll.msvcrt # doctest: +WINDOWS<CDLL 'msvcrt', handle ... at ...>>>> libc = cdll.msvcrt # doctest: +WINDOWS>>>windows自动添加常用的.dll文件后缀名在linux上,需要使用包含扩展名的文件名来加载一个库,因此属性操作不能正常使用。
ctypes返回带构造函数的结构体
ctypes返回带构造函数的结构体
ctypes可以用来调用动态链接库中的函数,并且可以处理返回结构体指针的情况。
下面是一个示例代码,展示如何使用ctypes返回带构造函数的结构体指针:```python
import ctypes
# 定义一个结构体指针类
class StructPointerTest(ctypes.Structure):
_fields_ = [("x", ctypes.c_int), ("y", ctypes.c_int)]
# 加载动态链接库
lib = ctypes.CDLL("libtest.so")
# 调用动态链接库中的函数,并将返回的结构体指针赋值给变量p
p = lib.test()
# 访问结构体中的成员变量
print(p.contents.x)
```
在上述示例中,我们首先定义了一个结构体指针类`StructPointerTest`,其中包含两个整型成员变量`x`和`y`。
然后,我们使用`ctypes.CDLL`函数加载动态链接库,并设置返回类型为结构体指针。
接下来,我们调用动态链接库中的`test`函数,并将返回的结构体指针赋值给变量`p`。
最后,我们可以通过`p.contents.x`来访问结构体中的成员变量`x`的值。
python调用dll动态链接库结构体参数及回调函数等示例
python调⽤dll动态链接库结构体参数及回调函数等⽰例结构体⽰例:这⾥是 C 代码的部分,主要是结构体的声明和回调函数定义。
// 新版本定义typedef enum {DevCard,DevLocator,DevReader} DevType;typedef enum {MsgLocate, // 定位信号MsgCut, // 剪断信号MsgHeartBeat, // ⼼跳信号MsgCall, // 呼叫信号MsgShake, // 震动信号MsgLeave, // 离开信号MsgOffAlarm, // 报警灯关闭信号MsgCheckPower, // 电量检测信号MsgHeartRate, // ⼼率信号MsgExtra, // 补发信号MsgNoPower, // 没电信号MsgReset, // 重置信号MsgBoundary, // 跨界信号// 此消息会跟随着MsgLocate消息出现MsgPower,// 当checkOffline设置为1时有效。
如果在offlineTime时间内没有收到此设备的信号,则发出离线信号MsgOffline,MsgLast} MsgType;// 由于消息类型的不同,不是所有的值都有意义typedef struct {MsgType type;DevType device; // 硬件类型int32_t cardID; // 标签/腕带ID,当DevType为DevCard时有意义int32_t readerID; // 阅读器ID,总是有意义int32_t locatorID; // ⼤于0时有意义,表⽰定位器IDint32_t readerRSSI; // 阅读器信号强度,由于硬件版本的差异,此值要么全有意义,要么全没意义(总是0)int32_t locatorRssiX; // 当MsgType为[MsgLocate, MsgExtra]时,此值有意义,表⽰定位器的信号强度int32_t locatorRssiY; // 定位器的信号强度uint8_t isCut; // 如果此值为1,表⽰硬件被破坏。
python调用dll方法
python 调用 dll 方法 来自 /lf8289/article/details/2322550
分类: python 2008-04-24 12:27 6833 人阅读 评论(6) 收藏 举报
在 python 中调用 dll 文件中的接口比较简单,实例代码如下:
2. import ctypes
dll = ctypes.WinDll( ' test.dll ' )
其中 ctypes.windll 为 ctypes.WinDll 类的一个对象,已经在 ctypes 模块中定义好的。在 test.dll 中有 test 接口,可直接用 dll 调用即可 nRst = dll.test( ) print nRst
如我们有一个 test.dll 文件,内部定义如下: extern " C "
{
int __stdcall test( void * p, int len) { return len; }
}
在 python 中我们可以用以下两种方式载入 1. import ctypes
dll = ctypes.windll.LoadLibrary( ' test.dll ' )
2. import ctypes
dll = ctypes.CDll( ' test.dll ' )
#方法二: test = dll.test
test.argtypes = [ctypes.c_char_p, ctypes.c_int] test.restypes = ctypes.c_int nRst = test(sBuf, len(sBuf))
Python调用windows下DLL详解 - ctypes库的使用
Python调用windows下DLL详解- ctypes库的使用分类:Python 2008-10-14 19:19 17710人阅读评论(12) 收藏举报pythondllwindowsstructurecclass作者:童磊(magictong)P.S. 之前的排版乱掉了,这里做一下排版,顺便改一下里面的一些用词错误。
2011-08-04在python中某些时候需要C做效率上的补充,在实际应用中,需要做部分数据的交互。
使用python中的ctypes模块可以很方便的调用windows的dll(也包括linux下的so等文件),下面将详细的讲解这个模块(以windows平台为例子),当然我假设你们已经对windows下怎么写一个DLL是没有问题的。
引入ctypes库[python] view plaincopyprint?from ctypes import *from ctypes import *假设你已经有了一个的DLL(名字是add.dll),且该DLL有一个符合cdecl(这里强调调用约定是因为,stdcall调用约定和cdecl调用约定声明的导出函数,在使用python加载时使用的加载函数是不同的,后面会有说明)调用约定的导出函数Add。
建立一个Python文件DllCall.py测试:[python] view plaincopyprint?from ctypes import *dll = CDLL("add.dll")print dll.Add(1, 102)from ctypes import *dll = CDLL("add.dll")print dll.Add(1, 102)结果:103上面是一个简单的例子。
下面简单聊一下调用流程:1、加载DLL上面已经说过,加载的时候要根据你将要调用的函数是符合什么调用约定的。
stdcall调用约定:两种加载方式[python] view plaincopyprint?Objdll = ctypes.windll.LoadLibrary("dllpath")Objdll = ctypes.WinDLL("dllpath")Objdll = ctypes.windll.LoadLibrary("dllpath")Objdll = ctypes.WinDLL("dllpath")cdecl调用约定:也有两种加载方式[python] view plaincopyprint?Objdll = ctypes.cdll.LoadLibrary("dllpath")Objdll = ctypes.CDLL("dllpath")<SPAN style="FONT-FAMILY: Microsoft YaHei">其实windll和cdll分别是WinDLL类和CDll类的对象。
python函数调用时参数传递方式
python函数调⽤时参数传递⽅式python函数调⽤时参数传递⽅式C/C++参数传递⽅式对于C程序员来说,我们都知道C在函数调⽤时,采⽤的是值传递,即形参和实参分配不同的内存地址,在调⽤时将实参的值传给实参,在这种情况下,在函数内修改形参并不会影响到实参,但是这样带来⼀个问题,如果我们需要刻意地对实参进⾏修改,就不得不传递实参的指针到函数,然后在函数中修改指针指向的数据,以达到修改实参的⽬的。
后来,C++中引⼊了引⽤这个概念,即在函数定义时,在形参前加⼀个&符号,表⽰传递参数的引⽤,在写法上,除了多出⼀个&符号,其他部分和C中传值调⽤⼀样,但是实际确是达到了可以在函数内修改实参内容的⽬的。
这种参数传递的⽅式被称为传引⽤。
python的参数传递说完了C/C++的参数传递⽅式,那么python中参数传递到底是传值还是传引⽤呢?我们来看两个实例:test1.py:def test(num):num += 10x = 1test(x)print x输出结果:1test2.py:def test(lst):lst[0] = 4lst[1] = 5tlist = [1,2]test(tlist)print tlist输出结果:[4,5]可以看到,在上述代码test1.py中,在函数中修改传⼊的x的值,函数执⾏完之后,x并没有改变,⾄少对于int型变量⽽⾔,python函数调⽤为传值。
在代码test2.py中,在函数中修改传⼊的tlist的值,函数执⾏完,list的内容却被函数修改了,从这⾥⼜可以看出,对于list类型⽽⾔,python函数调⽤为传引⽤。
看到这⾥就很疑惑了,python的函数调⽤到底是传值还是传引⽤?python的变量内存模型要搞清楚python的函数调⽤时传值还是传引⽤,这还得从python的变量内存模型说起,作为⼀个C/C++程序员,对于变量的理解就是CPU会为每个变量分配独⽴的内存空间,在变量⽣存周期结束时内存空间被收回。
Python调用DLL动态链接库——ctypes使用
Python调⽤DLL动态链接库——ctypes使⽤最近要使⽤python调⽤C++编译⽣成的DLL动态链接库,因此学习了⼀下ctypes库的基本使⽤。
ctypes是⼀个⽤于Python的外部函数库,它提供C兼容的数据类型,并允许在DLL或共享库中调⽤函数。
⼀、Python调⽤DLL⾥⾯的导出函数1.VS⽣成dll1.1 新建动态链接库项⽬1.2 在myTest.cpp中输⼊以下内容:// myTest.cpp : 定义 DLL 应⽤程序的导出函数。
//#include "stdafx.h"#define DLLEXPORT extern "C" __declspec(dllexport) //放在 #include "stdafx.h" 之后//两数相加DLLEXPORT int sum(int a, int b) {return a + b;}注意:导出函数前⾯要加 extern "C" __declspec(dllexport) ,这是因为ctypes只能调⽤C函数。
如果不⽤extern "C",构建后的动态链接库没有这些函数的符号表。
采⽤C++的⼯程,导出的接⼝需要extern "C",这样python中才能识别导出的函数。
1.3⽣成dll动态链接库因为我的python3是64位的,所以VS⽣成的dll要选择64位的,如下所⽰:点击标题栏的⽣成 -> ⽣成解决⽅案1.4 查看⽣成的dll动态链接库2.Python导⼊dll动态链接库⽤python将动态链接库导⼊,然后调⽤动态链接库的函数。
为此,新建main.py⽂件,输⼊如下内容:from ctypes import *#----------以下四种加载DLL⽅式皆可—————————# pDLL = WinDLL("./myTest.dll")# pDll = windll.LoadLibrary("./myTest.dll")# pDll = cdll.LoadLibrary("./myTest.dll")pDll = CDLL("./myTest.dll")#调⽤动态链接库函数res = pDll.sum(1,2)#打印返回结果print(res)运⾏结果如下所⽰:⼆、Python调⽤DLL⾥⾯的实例⽅法更新全局变量值1.VS⽣成dll1.1 添加 mainClass 类,内容如下:mainClass.h:#pragma onceextern int dta;class mainClass{public:mainClass();~mainClass();void produceData();};mainClass.cpp:#include "stdafx.h"#include "mainClass.h"int dta = 0;mainClass::mainClass(){}mainClass::~mainClass(){}void mainClass::produceData() {dta = 10;}1.2 更改 myTest.cpp 内容myTest.cpp:#include "stdafx.h"#define DLLEXPORT extern "C" __declspec(dllexport) //放在 #include "stdafx.h" 之后#include "mainClass.h"//返回实例⽅法⾥⾯更新数据后的值DLLEXPORT int getRandData() {mainClass dataClass = mainClass();dataClass.produceData();return dta;}1.3 ⽣成64位dll2.Python导⼊dll动态链接库明显可以看出,在C++⾥设置的全局变量的值已经从0变为10了,说明python可以通过调⽤dll⾥⾯的实例⽅法来更新全局变量值三、Python_ctypes 指定函数参数类型和返回类型前⾯两个例⼦C++动态链接库导出函数的返回类型都是int型,⽽Python 默认函数的参数类型和返回类型为 int 型,所以Python 理所当然的以为 dll导出函数返回了⼀个 int 类型的值。
