Python調用DLL庫

轉自https://www.cnblogs.com/FHC1994/p/11421229.html,僅爲保存記錄,作者寫的比較全面,感謝!!!

如有侵權,聯繫立刪。

Python調用DLL動態鏈接庫——ctypes使用

最近要使用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 = 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動態鏈接庫

明顯可以看出,在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 "mainClass.h"

//返回實例方法裏面更新數據後的值
DLLEXPORT int getRandData() {
    mainClass dataClass = mainClass();
    dataClass.produceData();
    return dta;
}

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[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][:]) #返回二維數組第二行所有元素
 運行結果:

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章