欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

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

程序员文章站 2022-03-02 12:38:18
最近要使用python调用c++编译生成的dll动态链接库,因此学习了一下ctypes库的基本使用。ctypes是一个用于python的外部函数库,它提供c兼容的数据类型,并允许在dll或共享库中调用...

最近要使用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动态链接库的资料请关注其它相关文章!