最近要使用python调用C++编译生成的DLL动态链接库,因此学习了一下ctypes库的基本使用。

ctypes是一个用于Python的外部函数库,它提供C兼容的数据类型,并允许在DLL或共享库中调用函数。

一、Python调用DLL里面的导出函数

1.VS生成dll

1.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生成dll

1.1 添加 mainClass 类,内容如下:

mainClass.h:

#pragma once

extern int dta;
class mainClass
{
public:
mainClass();
~mainClass();
void produceData();
};

mainClass.cpp:

#include "stdafx.h"
#include "mainClass.h" int dta = ; mainClass::mainClass()
{
} mainClass::~mainClass()
{
} void mainClass::produceData() {
dta = ;
}

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位dll

2.Python导入dll动态链接库

明显可以看出,在C++里设置的全局变量的值已经从0变为10了,说明python可以通过调用dll里面的实例方法来更新全局变量值

三、Python_ctypes 指定函数参数类型和返回类型

前面两个例子C++动态链接库导出函数的返回类型都是int型,而Python 默认函数的参数类型和返回类型为 int 型,所以Python 理所当然的 以为 dll导出函数返回了一个 int 类型的值。但是如果C++动态链接库导出的函数返回类型不是int型,而是特定类型,就需要指定ctypes的函数返回类型 restype 。同样,通过ctypes给函数传递参数时,参数类型默认为int型,如果不是int型,而是特定类型,就需要指定ctypes的函数形参类型 argtypes 。

接下来,我将举一个简单例子来说明一下

myTest.cpp:

#include "stdafx.h"
#define DLLEXPORT extern "C" __declspec(dllexport) //放在 #include "stdafx.h" 之后
#include <string> //使用string类型 需要包含头文件 <string>
using namespace std; //string类是一个模板类,位于名字空间std中
//字符串
DLLEXPORT char *getRandData(char *arg) {
return arg;
}

python代码:

from ctypes import *
pDll = CDLL("./myTest.dll") ########## 指定 函数的参数类型 #################
pDll.getRandData.argtypes = [c_char_p]
#第一个参数
arg1 = c_char_p(bytes("hello", 'utf-8')) ########## 指定 函数的返回类型 #################
pDll.getRandData.restype = c_char_p ########### 调用动态链接库函数 ##################
res = pDll.getRandData(arg1) #打印返回结果
print(res.decode()) #返回的是utf-8编码的数据,需要解码

或者如下形式:

from ctypes import *
pDll = CDLL("./myTest.dll") ########## 指定 函数的返回类型 #################
pDll.getRandData.restype = c_char_p ########### 调用动态链接库函数 ##################
res = pDll.getRandData(b'hello') # 或者变量.encode() #打印返回结果
print(res.decode()) #返回的是utf-8编码的数据,需要解码

运行结果:

四、Python_ctypes dll返回数组_结构体

在ctypes里,可以把数组指针传递给dll,但是我们无法通过dll获取到c++返回的数组指针。由于python中没有对应的数组指针类型,因此,要获取dll返回的数组,我们需要借助结构体。

myTest.cpp:

#include "stdafx.h"
#define DLLEXPORT extern "C" __declspec(dllexport) //放在 #include "stdafx.h" 之后
#include <string> //使用string类型 需要包含头文件 <string>
using namespace std; //string类是一个模板类,位于名字空间std中 typedef struct StructPointerTest
{
char name[];
int age;
int arr[];
int arrTwo[][];
}StructTest, *StructPointer; //sizeof(StructTest)就是求 struct StructPointerTest 这个结构体占用的字节数
//malloc(sizeof(StructTest))就是申请 struct StructPointerTest 这个结构体占用字节数大小的空间
//(StructPointer)malloc(sizeof(StructTest))就是将申请的空间的地址强制转化为 struct StructPointerTest * 指针类型
//StructPointer p = (StructPointer)malloc(sizeof(StructTest))就是将那个强制转化的地址赋值给 p
StructPointer p = (StructPointer)malloc(sizeof(StructTest)); //字符串
DLLEXPORT StructPointer test() // 返回结构体指针
{
strcpy_s(p->name, "Lakers");
p->age = ;
p->arr[] = ;
p->arr[] = ;
p->arr[] = ; for (int i = ; i < ; i++)
for (int j = ; j < ; j++)
p->arrTwo[i][j] = i*+j; return p;
}

python代码:

# 返回结构体
import ctypes path = r'./myTest.dll'
dll = ctypes.WinDLL(path) #定义结构体
class StructPointer(ctypes.Structure): #Structure在ctypes中是基于类的结构体
_fields_ = [("name", ctypes.c_char * 20), #定义一维数组
("age", ctypes.c_int),
("arr", ctypes.c_int * 3), #定义一维数组
("arrTwo", (ctypes.c_int * 3) * 2)] #定义二维数组 #设置导出函数返回类型
dll.test.restype = ctypes.POINTER(StructPointer) # POINTER(StructPointer)表示一个结构体指针
#调用导出函数
p = dll.test() print(p.contents.name.decode()) #p.contents返回要指向点的对象 #返回的字符串是utf-8编码的数据,需要解码
print(p.contents.age)
print(p.contents.arr[0]) #返回一维数组第一个元素
print(p.contents.arr[:]) #返回一维数组所有元素
print(p.contents.arrTwo[0][:]) #返回二维数组第一行所有元素
print(p.contents.arrTwo[1][:]) #返回二维数组第二行所有元素

运行结果:

最新文章

  1. GitHub 实现多人协同提交代码并且权限分组管理
  2. 话说C++中的左值、纯右值、将亡值
  3. K-均值聚类及其在生物信息中的应用
  4. IE7浏览器下CSS属性选择器二三事
  5. js解决快速回车重复订单提交(客户端方式)
  6. 【策略】HDOJ-1205-吃糖果
  7. int组成时间值
  8. DFS - leetcode [深度优先遍历]
  9. Android 工程集成React Native 0.44 注意点
  10. &lt;TCP/IP&gt;记一次关于IP地址和MAC物理地址的思考
  11. git clone Failed to connect to 127.0.0.1 port 43213: Connection refused
  12. ubuntu配置lua环境,并进行c与lua的相互调用
  13. angularjs入门(二)
  14. ASM X86&&X64 Registers 对寄存器ESP和EBP的一些理解
  15. Creazy Ideas 智能汽车和智能交通
  16. HDU1074(KB12-D 状态压缩dp)
  17. origin里用c语言编程
  18. 中南月赛F ZZY and his little friends
  19. webapi应用架构详解
  20. Oracle profile含义、修改、新增

热门文章

  1. Cucumber:启动类配置
  2. Java-javaFx库运用-自动弹跳的球
  3. Typora--终于找到一个能够解决将csdn文章同步到hexo的完美编辑器(解决csdn图片防盗链导致无法直接复制文章的问题)。
  4. project2_login(登录窗口)
  5. (转)python基础之迭代器协议和生成器(一)
  6. 20130317 如何批量把文件名称写入txt文档
  7. java通过传送地址获取坐标
  8. vue中excal表格的导入和导出
  9. 3.2_springBoot2.1.x检索之JestClient操作ElasticSearch
  10. 6-MySQL高级-索引