1. 程式人生 > 程式設計 >python使用ctypes庫呼叫DLL動態連結庫

python使用ctypes庫呼叫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動態連結庫的資料請關注我們其它相關文章!