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 相关文章推荐
Python操作Word批量生成文章的方法
Jul 28 Python
python简单线程和协程学习心得(分享)
Jun 14 Python
python进行文件对比的方法
Dec 24 Python
python SQLAlchemy 中的Engine详解
Jul 04 Python
python读写csv文件方法详细总结
Jul 05 Python
运用PyTorch动手搭建一个共享单车预测器
Aug 06 Python
python命名空间(namespace)简单介绍
Aug 10 Python
Python进程池Pool应用实例分析
Nov 27 Python
用openCV和Python 实现图片对比,并标识出不同点的方式
Dec 19 Python
Python print不能立即打印的解决方式
Feb 19 Python
Python3标准库glob文件名模式匹配的问题
Mar 13 Python
python自动统计zabbix系统监控覆盖率的示例代码
Apr 03 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 str_replace的替换漏洞
2008/03/15 PHP
php学习之 认清变量的作用范围
2010/01/26 PHP
php 定义404页面的实现代码
2012/11/19 PHP
PHP中变量引用与变量销毁机制分析
2014/11/15 PHP
textarea的value是html文件源代码,存成html文件的代码
2007/04/20 Javascript
js 弹出框 替代浏览器的弹出框
2010/10/29 Javascript
基于KMP算法JavaScript的实现方法分析
2013/05/03 Javascript
js调用iframe实现打印页面内容的方法
2014/03/04 Javascript
js图片滚动效果时间可随意设定当鼠标移上去时停止
2014/06/26 Javascript
JavaScript使用concat连接数组的方法
2015/04/06 Javascript
JavaScript遍历Json串浏览器输出的结果不统一问题
2016/11/03 Javascript
原生javascript实现图片放大镜效果
2017/01/18 Javascript
JavaScript表单验证完美代码
2017/03/02 Javascript
JS判断非空至少输入两个字符的简单实现方法
2017/06/23 Javascript
vue初尝试--项目结构(推荐)
2018/01/30 Javascript
理解Koa2中的async&amp;await的用法
2018/02/05 Javascript
vue-cli常用设置总结
2018/02/24 Javascript
element-ui表格数据转换的示例代码
2018/08/24 Javascript
Vue实现可移动水平时间轴
2020/06/29 Javascript
vue中如何添加百度统计代码
2020/12/19 Vue.js
[01:14:34]DOTA2上海特级锦标赛C组资格赛#2 LGD VS Newbee第一局
2016/02/28 DOTA
[43:18]NB vs Infamous 2019国际邀请赛淘汰赛 败者组 BO3 第一场 8.22
2019/09/05 DOTA
python中去空格函数的用法
2014/08/21 Python
Python查找相似单词的方法
2015/03/05 Python
解决Python2.7中IDLE启动没有反应的问题
2018/11/30 Python
python pyenv多版本管理工具的使用
2019/12/23 Python
学会python自动收发邮件 代替你问候女友
2020/05/20 Python
澳大利亚领先的在线礼品网站:Gifts Australia
2020/08/15 全球购物
建筑经济管理专业求职信分享
2014/01/06 职场文书
自我评价格式
2014/01/06 职场文书
2014年十一国庆向国旗敬礼寄语
2014/04/11 职场文书
爱心捐书活动总结
2014/07/05 职场文书
导游词之山东孔庙
2019/11/04 职场文书
MySQL系列之十三 MySQL的复制
2021/07/02 MySQL
Javascript的promise,async和await的区别详解
2022/03/24 Javascript
MySQL数据库如何使用Shell进行连接
2022/04/12 MySQL