python使用ctypes库调用DLL动态链接库


Posted in Python onOctober 22, 2020

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

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

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

1.VS生成dll

1.1 新建动态链接库项目

python使用ctypes库调用DLL动态链接库

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位的,如下所示:

python使用ctypes库调用DLL动态链接库

点击标题栏的 生成 -> 生成解决方案 

python使用ctypes库调用DLL动态链接库

1.4 查看生成的dll动态链接库

python使用ctypes库调用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使用ctypes库调用DLL动态链接库

二、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 = 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位dll

2.Python导入dll动态链接库

python使用ctypes库调用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动态链接库

四、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[20];
  int age;
  int arr[3];
  int arrTwo[2][3];
}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 = 20;
  p->arr[0] = 3;
  p->arr[1] = 5;
  p->arr[2] = 10;
  
  for (int i = 0; i < 2; i++)
    for (int j = 0; j < 3; j++)
      p->arrTwo[i][j] = i*10+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][:]) #返回二维数组第二行所有元素

运行结果:

python使用ctypes库调用DLL动态链接库

以上就是python使用ctypes库调用DLL动态链接库的详细内容,更多关于python 调用DLL动态链接库的资料请关注三水点靠木其它相关文章!

Python 相关文章推荐
使用Django的模版来配合字符串翻译工作
Jul 27 Python
Python实现简单的多任务mysql转xml的方法
Feb 08 Python
Python cookbook(数据结构与算法)对切片命名清除索引的方法
Mar 13 Python
一条命令解决mac版本python IDLE不能输入中文问题
May 15 Python
Python文件打开方式实例详解【a、a+、r+、w+区别】
Mar 30 Python
python仿evething的文件搜索器实例代码
May 13 Python
解决Python3.8用pip安装turtle-0.0.2出现错误问题
Feb 11 Python
Python递归函数特点及原理解析
Mar 04 Python
Jupyter notebook如何实现指定浏览器打开
May 13 Python
Python logging日志模块 配置文件方式
Jul 12 Python
Python操作word文档插入图片和表格的实例演示
Oct 25 Python
Python如何使用循环结构和分支结构
Apr 13 Python
Python通过len函数返回对象长度
Oct 22 #Python
python 还原梯度下降算法实现一维线性回归
Oct 22 #Python
利用Pycharm + Django搭建一个简单Python Web项目的步骤
Oct 22 #Python
python处理写入数据代码讲解
Oct 22 #Python
基于Python爬取股票数据过程详解
Oct 21 #Python
OpenCV利用python来实现图像的直方图均衡化
Oct 21 #Python
Python实现手势识别
Oct 21 #Python
You might like
PHP获取163、gmail、126等邮箱联系人地址【已测试2009.10.10】
2009/10/11 PHP
php dirname(__FILE__) 获取当前文件的绝对路径
2011/06/28 PHP
php数组合并的二种方法
2014/03/21 PHP
php设计模式之单例模式实例分析
2015/02/25 PHP
详解PHP实现异步调用的4种方法
2016/03/14 PHP
php实现的一段简单概率相关代码
2016/05/30 PHP
php实现压缩合并js的方法【附demo源码下载】
2016/09/22 PHP
PHP之将POST数据转化为字符串的实现代码
2016/11/03 PHP
JS类库Bindows1.3中的内存释放方式分析
2007/03/08 Javascript
JavaScript 图像动画的小demo
2012/05/23 Javascript
值得分享的Bootstrap Ace模板实现菜单和Tab页效果
2015/12/30 Javascript
原生javascript实现自动更新的时间日期
2016/02/12 Javascript
javascript拖拽效果延伸学习
2016/04/04 Javascript
JavaScript解八皇后问题的方法总结
2016/06/12 Javascript
Js 获取当前函数参数对象的实现代码
2016/06/20 Javascript
JS实现的二叉树算法完整实例
2017/04/06 Javascript
js+html5实现手机九宫格密码解锁功能
2018/07/30 Javascript
vue防止花括号{{}}闪烁v-text和v-html、v-cloak用法示例
2019/03/13 Javascript
JavaScript进制转换实现方法解析
2020/01/18 Javascript
[01:31:03]DOTA2完美盛典全回顾 见证十五项大奖花落谁家
2017/11/28 DOTA
[01:00] DOTA2英雄背景故事第五期之重力引力法则谜团
2020/07/16 DOTA
python批量替换页眉页脚实例代码
2018/01/22 Python
python 实现对文件夹内的文件排序编号
2018/04/12 Python
python3写的简单本地文件上传服务器实例
2018/06/04 Python
python实现批量解析邮件并下载附件
2018/06/19 Python
加拿大最大的钻石商店:Peoples Jewellers
2018/01/01 全球购物
泰海淘:泰国king Power王权免税集团旗下跨境海淘综合型电商
2020/07/26 全球购物
static全局变量与普通的全局变量有什么区别
2014/05/27 面试题
安全教育心得体会
2013/12/29 职场文书
关于安全的标语
2014/06/10 职场文书
私人房屋买卖协议书
2014/10/04 职场文书
开票员岗位职责
2015/02/12 职场文书
敬老院志愿者活动总结
2015/05/06 职场文书
幼师辞职信范文大全
2015/05/12 职场文书
未婚证明范本
2015/06/15 职场文书
Pandas实现DataFrame的简单运算、统计与排序
2022/03/31 Python