將標準C++動態連結庫封裝到.NET程式集dll全攻略
#ifdef CPPDLL_EXPORTS
#define CPPDLL_API __declspec(dllexport)
#else
#define CPPDLL_API __declspec(dllimport)
#endif
class CPPDLL_API CcppDll {
public :
CcppDll( void );
// TODO: 在此新增您的方法。
void ChangeValue( int i);
int GetValue();
private :
int var;
CcppDll
} ;
extern CPPDLL_API int ncppDll;
CPPDLL_API int fncppDll( void );
首先,在標準C++中使用標準C++dll的通常做法是預編譯時匯入lib檔案,於是有人希望能夠開一個managed C++ dll,用這種方法匯入標準C++ dll,然後再在 Winform等其他.NET程式中呼叫,實際上這是不可能的,因為.NET程式在呼叫這個庫時根本找不到入口.下面我一2個例子詳細說明,其中標準C++庫(cppDll)一個,C#dll(CSDll)一個,以及一個測試的C#WinForm(CSFormTest)一個
實際上 標準C++生成的dll檔案本身是可以查詢匯出函式入口的,這一點比較麻煩,如果是C函式,還強一點,因為可以在前面宣告extern "C" 這樣 Dll裡面的函式名就是入口,只要標頭檔案就可以知道入口了.例如這樣
{
return42;
}
但是在類中的成員函式就不能宣告為extern "C"了,因此我們必須查詢他的入口,舉個簡單例子
/***********cppDll.cpp********/
#include "stdafx.h"
#include "cppDll.h"
#ifdef _MANAGED
#pragma managed(push, off)
#endif
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
#ifdef _MANAGED
#pragma managed(pop)
#endif
/***************cppDll.cpp*******/
// 這是匯出變數的一個示例
CPPDLL_API int ncppDll=0;
CPPDLL_API struct VS *vs;
// 這是匯出函式的一個示例。
CPPDLL_API int fncppDll(void)
{
return42;
}
// 這是已匯出類的建構函式。
// 有關類定義的資訊,請參閱 cppDll.h
CcppDll::CcppDll()
{
return;
}
void CcppDll::ChangeValue(int i)
{
var=i;
}
int CcppDll::GetValue()
{
return var;
}
CcppDll * CcppDll::CreateCcppDll()
{
CcppDll * cs=new CcppDll();
return cs;
}
/***************************/
前頭的外部函式和變數您不用看了,相信您只要會用DllImport的基本語法就明白怎麼導,這裡主要看看3個CcppDll的成員函式怎麼封裝.注意這個CcppDll::CreateCcppDll()這裡相當於顯式呼叫建構函式,這個函式是我們封裝到.NET的關鍵,必須有,至於為什麼後文再說.我寫了幾個讀寫變數的函式以便您新建幾個物件測試.
然後簡要介紹一下DllImport在C#中的語法
2007年1月10日
DllImport的第一個引數是庫完整檔名,第二個就是函式入口了,正如剛才所說C++類成員函式不能被宣告為extern "C" 因此入口不是dll中定義的函式名,而是如下
6 5 000111D6 [email protected]@@[email protected] = @ILT+465([email protected]@@[email protected])
而匯入以後的函式定義部分的函式名可以任意指定.
從問號開始等號以前是剛才哪個CCppDll::ChangeValue的真實入口,其中@後是類明,@@後是編譯器產生的.
檢視這些入口可以用VS的dumpbin命令,您可以選擇標準輸出或者輸出到檔案,輸出到檔案的好處我最後在討論.
注意:使用
dumpbin -exports 檔案完整路徑
不要用/exports否則看不到入口
下面是C#的dll程式碼
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
namespace CSDLL
{
publicclass Class1
{
[DllImport(@"D:/dotNetApps/Client Applications/cppDll/debug/cppDll.dll", EntryPoint ="[email protected]@@[email protected]", CallingConvention = CallingConvention.ThisCall)]
privateexternstaticvoid ChangeValue(IntPtr pThis,int i);
[DllImport(@"D:/dotNetApps/Client Applications/cppDll/debug/cppDll.dll", EntryPoint ="[email protected]@@QAEHXZ", CallingConvention = CallingConvention.ThisCall)]
privateexternstaticint GetValue(IntPtr pThis);
[DllImport(@"D:/dotNetApps/Client Applications/cppDll/debug/cppDll.dll", EntryPoint ="[email protected]@@[email protected]", CallingConvention = CallingConvention.Winapi)]
privateexternstatic IntPtr CreateCppDll();
public Class1()
{
p = CreateCppDll();
}
publicint Value
{
get{ return GetValue(p); }
set{ ChangeValue(p, (int)value); }
}
public IntPtr p;
}
}
其中using System.Runtime.InteropServices;使您能夠使用DllImport
下面我們來分析一下這段程式碼.在匯入函式部分,三個外部函式均聲明瞭庫的絕對路徑,和入口全稱,還有就是呼叫方式.您可能認為就算函式可以匯入,全部宣告為static extern 還如何以面向物件方式呼叫? 難道只能當作靜態函式,全域性只用一個物件,顯然不能實現原有的C++庫的功能.解決這個問題關鍵就在呼叫方式.
CallingConvention列舉有4個值,我們主要使用2種,一種是預設的CallingConvention.StdCall(windows下與Winapi等效),相當於靜態呼叫,另一種則是CallingConvention.ThisCall其中 CreateCppDll相當於建構函式,是為物件開闢記憶體空間的,因此在呼叫它以前物件還沒有被分配,所以它必須使用這種呼叫方式,
privateexternstatic IntPtr CreateCppDll();
在封裝這個標準C++類的C#類中,建構函式就應呼叫它以得到一個指向原C++物件的指標
public Class1(){
p = CreateCppDll();
}
public IntPtr p;
而Class1的成員變數p就是用來儲存這個物件指標的,注意由於安全程式碼C#物件中禁止使用指標,這裡無論C++是何種型別 C#一律使用平臺指標IntPtr,定義外部函式也是如此.
然後其他所有的成員非靜態函式則使用 CallingConvention.ThisCall進行匯入,如下
[DllImport(@"D:/dotNetApps/Client Applications/cppDll/debug/cppDll.dll", EntryPoint = "[email protected]@@[email protected]", CallingConvention = CallingConvention.ThisCall)]
private extern static void ChangeValue(IntPtr pThis,int i);
[DllImport(@"D:/dotNetApps/Client Applications/cppDll/debug/cppDll.dll", EntryPoint = "[email protected]@@QAEHXZ", CallingConvention = CallingConvention.ThisCall)]
private extern static int GetValue(IntPtr pThis);
注意到剛才我們定義標準C++庫時ChangeValue有一個引數 GetValue沒有引數,而這次宣告的時候ChangeValue有兩個引數,GetValue有一個引數.這個多處來的引數就是真實的物件指標,因此,在Class1中封裝這些函式時,每次呼叫都把p傳給這些函式(注意,前提是p已經分配)如下(這裡為了符合C#風格,乾脆封到屬性裡面去了,反正性質是一樣的):
{
get{ return GetValue(p); }
set{ ChangeValue(p, (int)value); }
}
這樣就完全做到了形式上用C函式呼叫,實際上是物件在呼叫自己的成員函式,由於然後經過這一道封裝後,在呼叫這個庫的應用程式中所看到的仍然是面向物件的類方式.這樣就完全達到了我們預期的目的.
下面是一個C#WinForm呼叫這個dll的示例:
//....................預設包含的庫我省了
namespace CSFormTest
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
privatevoid button1_Click(object sender, EventArgs e)
{
Class1 c1 =new Class1();
Class1 c2 =new Class1();
c1.Value =3;
c2.Value =4;
MessageBox.Show("c1="+ c1.Value +",c2="+ c2.Value);
}
}
}
執行結果:物件c1,c2的屬性Value都得到改變,證明他們是2個不同物件呼叫各自成員函式的結果,
由此證明我們的思路完全正確
相關推薦
將標準C++動態連結庫封裝到.NET程式集dll全攻略
/**/ /* ********cppDll.h********* */ #ifdef CPPDLL_EXPORTS #define CPPDLL_API __declspec(dllexport) #else #define CPPDLL_API __declspec(
python中呼叫 C#動態連結庫問題記錄
程式[摘自https://blog.csdn.net/LTG01/article/details/80700513] import clr clr.FindAssembly("PythonNetTest.dll") ## 載入c#dll檔案 from PythonNetTest import *
python3使用ctypes在windows中訪問C和C++動態連結庫函式示例
python3使用ctypes在windows中訪問C和C++動態連結庫函式示例 這是我們的第一個示例,我們儘量簡單,不傳參,不返回,不訪問其他的動態連結庫 一 測試環境介紹和準備 測試環境: 作業系統:windows10 Python版本:3.7.0 VS版本:vs2015社群版(免費) 相關
C++動態連結庫的製作
輸入函式__declspec(dllimport) 與輸出函式__declspec(dllexport) 有什麼區別呢?我知道他們不同,但差別在哪呢?我用的全是__declspec(dllexport) , __declspec(dllimport)一般在
tensorflow之編譯使用c/c++動態連結庫
tensorflow主流介面雖然是python,但是其也支援C語言的介面供大家呼叫,並且對於影象處理等方面c++是一種更為合適的語音。因此本文主要介紹一下如何安裝並使用c版本的tensorflow。 一、 編譯。 編譯的目的主要是為了產生對應的標頭檔案和動態連結so檔案。編譯
JAVA通過JNI呼叫C++動態連結庫CLL(一)
簡介 本文筆者,詳細的演示JAVA通過JNI呼叫C++動態連結庫CLL的開發過程和涉及到知識點介紹,入門級簡單易懂 (一)Java本機介面(Java Native Interfa
Unity呼叫c++動態連結庫注意事項
Unity通過ndk呼叫java,java通過jni呼叫c++ .so 1:Jni的.so是獨立的,還是依賴其他.so庫或靜態庫? 答:都可以,但具體的.so拷貝到unity中後,是否還能正常呼叫,有待測試。 2:C++中jni與java回撥函式,這裡只舉例靜態函式呼叫
C#呼叫C/C++動態連結庫(.dll)詳解
第一篇編譯C的動態連線庫 在實際工作中,我們經常會將C語言中的.lib和.h檔案(靜態庫)編譯成動態連線庫.dll檔案(這裡只提供這兩種檔案,沒有完整的工程),以提供給其他語言平臺呼叫。 1,必須有.lib檔案,只有.h檔案是無法編譯動態連線庫的。 2,我使用的是V
python3.2下呼叫C動態連結庫
python和C,我覺得這簡直是無敵的組合啊。一般性的業務邏輯用python快速出模型,而碰到python執行緩慢的操作,則可以通過呼叫C編譯好的連結庫來完成。在python3.2下,可以通過ctype模組單純的訪問C連結庫,也可以通過傳統方式訪問。ctype模組固然方便,但
淺談JAVA呼叫C++動態連結庫
如: public native 返回型別 方法名(引數列表); 注意,這些方法沒有方法體。。 程式中呼叫這裡的方法和呼叫平常的方法的方式是一樣的。 --------------------------------------------------------------------------
electron 使用 node-ffi 呼叫 C++ 動態連結庫(DLL)
一、為什麼需要使用DLL 需要使用系統 API 操作或擴充套件應用程式; 需要呼叫第三方的介面API,特別是與硬體裝置進行通訊,而這些介面 API 基本上都是通過 C++ 動態連結庫(DLL)實現的; 需要呼叫C++實現的一些複雜演算法等。 二、node-ffi 是什麼 n
Ubuntu X86編譯tensorflow C++動態連結庫
以下方法在x86上親測通過,在Nvidia TX 系列第三步會出錯。但是會生成動態連結庫,有興趣的可以試試能不能用,我測試是可以用的。 環境(16.04LTS cuda8.0 cudnn6.0.10 tf1.3 python2 ) 1.安裝依賴項
C++ 動態連結庫和靜態連結庫
typedef int (*DllFunc)(int, int); int _tmain(int argc, _TCHAR* argv[]) { DllFunc dllFunc; HINSTANCE hInstLib = LoadLibrary(L"DllTest.dll"); if (hInstLi
java 呼叫c++動態連結庫
JNI其實是Java Native Interface的簡稱,也就是java本地介面。它提供了若干的API實現了和Java和其他語言的通訊(主要是C&C++)。也許不少人覺得Java已經足夠強大,為什麼要需要JNI這種東西呢?我們知道Java是一種
Linux環境下使用eclipse開發C++動態連結庫程式
Linux中也有類似windows中DLL的變成方法,只不過名稱不同而已。在Linux中,動態連結叫做Standard Object,生成的動態連結檔案為*.so。詳細請參考相關文件。 開發環境:Eclipse 3.4.2 G++:4.3.2 1. 建立動態連結庫
lua——alien庫實現lua呼叫C動態連結庫(dll、so)
我們知道,lua通過lua_State堆疊可以很方便的與C語言進行互動 也可以呼叫專門為lua呼叫而封裝的C庫。 具體步驟: 1.原C檔案中引入lua相關標頭檔案 #include "lua.h" #include "lualib.h" #include "lauxli
C#總結(七)動態載入C++動態連結庫
C#呼叫C++ 連結庫的方式分為靜態呼叫和動態呼叫這兩種方式。靜態呼叫之前的文章裡面都有介紹,使用.net 提供的DllImport 匯入相關的C++ 庫即可。請看之前的文章,https://www.cnblogs.com/zhangweizhong/p/8119340.html 。 今天介紹動
Native C++藉助CLR動態載入並呼叫.NET程式集
Native C++程式碼和託管.NET程式碼互操作並不是什麼難事, 資料也很多, 但是有些方法複雜繁瑣, 本文介紹了一種簡單的可行、支援動態載入的基於CLR的互動方法. 1.首先是動態載入目標程式集和類: try { auto assembly = Ass
將訓練好的caffe模型封裝成動態連結庫提供C++API
<<2018.12.11 照例先po出兩篇部落格 https://blog.csdn.net/maweifei/article/details/72811413 https://blog.csdn.net/jiongnima/article/details/70199480
ApolloStudio高手之路(8):用Python呼叫.Net(C#、VB.Net等)開發的動態連結庫(DLL庫檔案)實現相互協作
ApolloStudio是基於.Net與Python雙架構下的實現,這樣的架構體系使得其具有傳統定製軟體無法比擬的超強拓展性,在本文中我們將介紹這兩者是如何在ApolloStudio平臺上實現優勢互補的。由於在ApolloStudio中使用的更易學習的Python作為主導指令碼語言,這裡我們將介