通過vs可以導出動態鏈接庫(dll文件)給其他c++項目、c#項目、python項目使用。本案例實現將vs項目導出為動態鏈接庫,給c++項目與python項目使用。涉及全局變量、函數、自定義類的導出。
項目創建完成後會得到以下結構, 可以將核心代碼寫在dllmain.cpp裡面(原先的內容可以不用管),頭文件信息寫入在pch.h裡面
以下內容可以全部拷貝到pch.h中(博主的代碼涉及到了cuda,所以需要配置以下cuda,cuda的配置可以參考libtorch顯存管理示例_萬裡鵬程轉瞬至的博客-CSDN博客,各位也可以將cuda相關的代碼刪掉)。博主在這裡定義了導出一個全局變量、兩個函數和一個自定義類。
#pragma once
//extern 表示該函數為全局函數,可以在其他地方調用
//“C”表示按照C語言方式進行編譯和鏈接 python下導入dll只支持C函數
//__declspec(dllexport)告訴編譯器這是一個導出函數
#ifdef BUILD_MYDLL
#define API_SYMBOL __declspec(dllexport)
#else
#define API_SYMBOL __declspec(dllimport)
#endif // BUILD_MYDLL
// 導入庫
#include <iostream>
#include <cuda_runtime_api.h>
// 導出全局變量
extern "C" API_SYMBOL int globa_times;
// 導出函數
extern "C" API_SYMBOL float get_cuda_use();
extern "C" API_SYMBOL int reset_cuda();
//導出類
extern "C" API_SYMBOL class MyClass
{
public:
MyClass();
~MyClass();
int FunSub(int a, int b);
private:
};
上述代碼中的關鍵就是extern "C" API_SYMBOL class_type fun_name; 其中API_SYMBOL是一個define的常量,在本項目中的值為 __declspec(dllexport),在其他項目導入本dll庫時的值為__declspec(dllimport),這是根據常量BUILD_MYDLL自動判斷的。
以下內容可以全部拷貝到dllmain.cpp中。常量BUILD_MYDLL也是定義在以下代碼中。
// dllmain.cpp : 定義 DLL 應用程序的入口點。
#include "pch.h"
using namespace std;
#define BUILD_MYDLL // 定義為導出函數
int globa_times = 999;
float get_cuda_use()
{
size_t free_byte;
size_t total_byte;
cudaError_t cuda_status = cudaMemGetInfo(&free_byte, &total_byte);
if (cudaSuccess != cuda_status) {
printf("Error: cudaMemGetInfo fails, %s \n", cudaGetErrorString(cuda_status));
return float(-1.0);
}
else {
double free_db = (double)free_byte;
double total_db = (double)total_byte;
float used_db_1 = (total_db - free_db) / 1024.0 / 1024.0;
std::cout << "Now used GPU memory " << used_db_1 << " MB\n";
return float(used_db_1);
}
}
int reset_cuda() {
globa_times++;
cudaDeviceReset();
float res=get_cuda_use();
return int(res);
}
MyClass::MyClass()
{
std::cout << "MyClass init" << std::endl;
}
MyClass::~MyClass()
{
}
int MyClass::FunSub(int a, int b)
{
return a - b;
}
點擊運行後,即可生成動態鏈接庫(xxx.dll和xxx.lib)。
c++項目導入動態鏈接庫通常是需要頭文件(.h文件)、庫文件(.lib文件)、動態鏈接文件(.dll文件),上述流程中的xxx.dll和xxx.lib與.h可以單獨整理出來形成一個目錄。其中,需要注意的是,dll文件要放在可執行文件同級下才行(或者將dll文件的路徑添加到系統環境變量path中)。
導入自己的動態鏈接庫與正常庫是一樣的步驟,具體如下圖所示。此外,還需要在鏈接器-》輸入-》附加依賴項中配置dll_export.dll。由於博主在構造自己的動態鏈接庫時使用到了cuda,因此這個項目也要配置cuda環境。
測試代碼如下所示
#include <iostream>
#include <pch.h>
int main()
{
globa_times = 66;
//print_cuda_use();
int res= reset_cuda();
std::cout << "Now CUDA mem = " << res << std::endl;
MyClass myc;
int sub = myc.FunSub(100, 10);
std::cout << "myc.FunSub(100, 10) = " << sub << std::endl;
return 1;
}
運行結果如下所示,可見一切正常
python導入dll庫只需要dll文件路徑即可,但是當dll文件存在其他依賴的dll時,需要使用os.add_dll_directory補充其他dll的路徑。下面代碼中lib.reset_cuda(),就是調用最前面寫的c函數,釋放顯存。
import ctypes
import os
#os.environ['Path']+=r'C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v11.1\bin'
#python3.7版本以上使用下列代碼添加依賴項dll的路徑
os.add_dll_directory(r'C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v11.1\bin')
lib = ctypes.cdll.LoadLibrary(os.getcwd()+ "/dll_export.dll")
#win32api.FreeLibrary(libc._handle) #發現程序運行結束時無法正常退出dll,需要顯式釋放dll
lib.reset_cuda()
博主使用的是jupyter lab,因此在執行命令中,可以在控制台看到相應的c++代碼的輸出。
更多python調用c++ dll庫的用法可以參考C/C++代碼生成DLL & Python調用C/C++生成的dll_紅顏時光的博客-CSDN博客_c生成dll