1. 程式人生 > >Qt程式中呼叫C#編寫的dll

Qt程式中呼叫C#編寫的dll

最近在使用Qt整合以前的一個工具時,發現有幾個dll是採用C#寫的,當然可以把C#寫的dll重新在Qt中用C++寫,雖然這是幾個C#寫的dll的函式介面使用的引數都比較簡單,只用了int和string型別等,但在函式內部卻還使用了C#的專用一些類,如果重寫還是很麻煩,就查找了一些文章,多數都是說採用把C#寫的dll採用COM註冊方式讓Qt呼叫,但是這樣又要重新編譯以前的C#專案,實在是很麻煩,所以是否有種方式可以直接利用以前C#寫的dll呢?

C#寫的dll是沒有dllMain入口函式的,是一種中間語言,需要.Net執行時進行做本地化工作,因此如果要呼叫C#寫的dll,需要依賴.Net執行時,然而Qt中還無法直接呼叫.Net執行時,最好的方式是能夠在Qt中直接呼叫C#dll的函式,但是Qt明顯只能呼叫C++寫的dll,所以就只能通過編寫一個C++的dll匯出介面供Qt呼叫,這個C++編寫的dll對C#寫的dll進行封裝,這個C++的dll可以採用/CLR方式對C#編寫的dll進行引用的,即將C++編寫的dll中生成的.lib檔案供Qt進行連結,由於該介面符合C++規範,所以Qt可以連結到對應的C++編寫的dll。為此,寫一篇Demo,希望對查詢該資料的人有幫助吧。demo的過程如下:

編寫環境:Win7,Qt5.7.1(MSVC2013_x86),VS2013

1.開啟VS2013,新建一個C#的Class Library專案(這裡選擇的是.Net Framework 4),專案名為CSharpDll


2.由於預設沒有引入Forms等UI庫,先在reference中新增引用System.Windows.Forms以便可以在測試中使用MessageBox等



3.最終C#編寫的dll如下圖,名稱空間為CSharpDll,公共類為CSharpClass

裡面包含一個加法add,一個減法substract(為了測試指標,所以在減法的返回型別是void,而把計算結果通過ref引數c給返回),一個showBox方法(裡面採用C#的MessageBox對話方塊顯示使用者輸入的引數字串)


4.對project進行release build,在release目錄下生成了CSharpDll.dll(待會用到)


5.關閉CSharpDll專案,另外新建一個C++ CLR型別的Class Library專案(選擇與C#專案相同的.Net Framework 4),專案名稱為CppDll


6.選擇Project->CppDll Properties...,在彈出的屬性頁面選擇“Add New Reference..”,點選“browsing.”後選擇CSharpDll專案中release目錄下的CSharpDll.dll



7.選擇CSharpDll.dll後,可以看到在專案屬性的References中出現了CSharpDll這個Library


8.在CppDll專案中的CppDll.h中利用__declspec(dllexport)匯出對應的3個介面函式add,substract,showBox,這裡為便於區分,前面加上api_字首對這3個函式封裝。需要using namespace System::Reflection,對於這裡的api_showBox方法,其引數不能採用CSharpDll裡面的showBox引數的string型別,而是使用const char* 型別


9.選擇release方式build CppDll專案,在release資料夾中生成了CppDll.lib和CppDll.dll檔案,可以看到同時其也將引用的CSharpDll.dll也給拷貝到release資料夾中了


10.接下來在Qt中進行呼叫,在QtCreator中新建一個TestCSharpDll專案(MSVC2013 32bit)


11.將CppDll.lib拷貝到Qt的TestCSharpDll專案資料夾內


12.在mainwindow.ui中,設計呼叫api_add函式,api_substract函式和api_showBox函式的介面


13.分別對add,sub和showBox按鈕的clicked訊號進行槽函式編寫,其呼叫對應的api_add,api_substract和api_showBox函式,需要注意兩點:

第一點是需要在mainwindow.cpp檔案前通過#pragma comment(lib,“lib檔案的絕對路徑”),這裡的lib檔案的絕對路徑需根據CppDll.lib的實際位置進行設定(在這裡本機上為'E:/SourceCode/Qt/TestCSharpDll/CppDll.lib')(絕對路徑的分隔符最好採用'/',而不是'\')

第二點是需要在開始處通過 __declspec(dllimport)的方式將匯出函式進行宣告,連結器會在CppDll.lib中去查詢這3個函式的動態連結庫dll的入口                                                                                           

14.選擇Build對Qt中的TestCSharpDll專案進行Debug構建,將CSharpDll.dll和CppDll.dll拷貝到專案生成的Debug目錄中


15.執行TestCSharpDll.exe,輸入add函式的兩個加法引數,substract函式的兩個減法引數,以及輸入showBox的QString引數(這裡是QString,在槽函式裡面會通過QString的toLocal8Bit().data()函式轉換為char*引數)進行測試,可看出呼叫成功,並且在showBox中,彈出的對話方塊標題是CSharpDll中寫的“這是C#的MessageBox”,而在內容中則是Qt中輸入的“這個字串是Qt介面輸入的”




16.後記:Qt中實際上是通過連結中間層C++ CLR編寫的.lib,而C++ CLR可以直接呼叫C#的dll,因此需要在Qt與C# dll中構建一箇中間層,這個中間層既能被Qt中的C++程式碼進行連結,同時該中間層又能訪問C# dll中的函式。所以最終Qt生成的專案中同一目錄下需要CppDll.dll和CSharpDll.dll。

自然會有人想到如果將CppDll專案採用生成靜態庫static library(.lib)形式,通過在Qt中連結該靜態庫,最終執行Qt程式時就可以不用在同一目錄下放置CppDll.dll,而只需要放置CSharp.dll即可。理論上是這樣沒錯,但是經過嘗試發現Qt還是無法通過靜態連結CppDll的方式。

具體實際原因不太確定,不過我猜原因可能是,Qt程式是純native c++程式碼(unmanaged程式碼),而CppDll.dll和CSharpDll.dll是managed程式碼,因此Qt程式通過動態連結到CppDll.dll時,Qt程式本身是以native c++程式碼執行,而在呼叫CppDll.dll中的程式碼時,CppDll.dll中程式碼的載入是以.Net執行時進行託管,也即CppDll.dll被載入時,是被.Net Framework進行host啟動的,然後CppDll.dll中的程式碼可以進一步訪問CSharpDll.dll(此時CSharpDll.dll也是被.Net Framework進行host啟動的)。所以如果在Qt程式中將CppDll進行靜態連結,其生成的Qt程式中最後必然包括maneged程式碼(即Qt程式中既有native又有managed程式碼),最終就會造成Qt程式在執行中無法明確其啟動方式(按navtive啟動,裡面的執行都是navtive方式,遇到managed程式碼,無法正常呼叫;如果按.Net進行host啟動,則其native程式碼無法被.Net host識別),從而導致連結失敗,當然,至於是否有其他方式繞過managed和unmanaged的限制並能以這種靜態連結方式進行構建,還需進一步查閱了。同時在將managed程式碼編譯時,編譯器選項需要指定'/clr',然而這個'/clr'與qt需要的編譯指令'/EH'衝突,無法相容,所以也無法靜態連結。

不過,估計這種靜態連結的做法也應該沒多少必要性了,無論是從維護角度還是執行角度。

相關原始碼下載:連結: https://pan.baidu.com/s/1vuns3yKiqD-CFj4ZXM116w 密碼: i6p2

相關推薦

Qt程式呼叫C#編寫dll

最近在使用Qt整合以前的一個工具時,發現有幾個dll是採用C#寫的,當然可以把C#寫的dll重新在Qt中用C++寫,雖然這是幾個C#寫的dll的函式介面使用的引數都比較簡單,只用了int和string型別等,但在函式內部卻還使用了C#的專用一些類,如果重寫還是很麻煩,就查找了

筆記:在C#程式呼叫C++編寫的類

假設C++中有一個類c定義如下:class c { private: int count; public: void add(int n); int get(); }; 其中add函式定義如下:void C:: add(int n){ count +

擴充套件Python之在Python呼叫C編寫的函式模組

目錄 編寫Python擴充套件 1. 建立應用程式碼 2. 根據樣板編寫封裝程式碼 2.1 包含Python標頭檔案 2.2 為每一個模組函式新增形如PyObject* *Module_func()* 的封裝函式

C#呼叫C++的dll的引數為指標型別的匯出函式(包括二級指標的情況)

一:首先什麼是IntPtr 先來看看MSDN上說的:用於表示指標或控制代碼的平臺特定型別。這個其實說出了這樣兩個事實,IntPtr 可以用來表示指標或控制代碼、它是一個平臺特定型別。對於它的解釋,這個哥們寫的比較好:It's a class that wraps a

在Java程式呼叫C函式--列印"HelloWorld"

本文是將書中的第二章單獨抽出來,紅色部分為譯者注. 1.概述 這個列印的過程是用JDK或Java 2 SDK寫一個簡單的Java程式,程式會呼叫一個C函式列印"HelloWorld".這個過程將包括以下步驟: 建立一個Java類(HelloWorld.java),以及定義一

java呼叫C/C++編寫的生成的動態連線庫dll檔案介面的簡單例子

1、首先用java編寫一個簡單類 public class Hello { public native void sayHello(); static { System.loadLibrary("HelloDll"); } public static void

Unity3D使用C#呼叫C++編寫DLL

前言 Unity3D是一款非常優秀的遊戲引擎,可以使用C#作為指令碼語言進行程式設計。但是有的時候我們需要執行一些第三方的程式,例如C++實現的一些演算法。我們需要將C++的程式碼以DLL的形式嵌入到

C#呼叫C++編寫DLL函式引數傳遞

                        &nb

MATLAB呼叫.C程式

需要把 .c  變成 MATLAB 可以識別的程式碼 在MATLAB中進入 .c 檔案所在的當前目錄,然後  mex  BP_General_C.c 。該路徑下生成BP_General_C.mexa64檔案,即為MATLAB可以呼叫的檔案。 mex 

C#動態呼叫C++編寫DLL函式

C#動態呼叫C++編寫的DLL函式 動態載入DLL需要使用Windows API函式:LoadLibrary、GetProcAddress以及FreeLibrary。我們可以使用DllImport在C#中使用這三個函式。 [DllImport(“Kernel32”)] publ

[轉]在C#呼叫C語言函式(靜態呼叫Native DLL,Windows & Microsoft.Net平臺)

原文:https://blog.csdn.net/yapingxin/article/details/7288325   對於不太瞭解.Net的人,如果想要了解.Net,我必須給他介紹P/Invoke。P/Invoke是什麼呢?簡單地說,就是在.Net中呼叫原生代碼(Native code)的一

C#呼叫C++編寫DLL函式各種引數傳遞問題

[System.Security.SuppressUnmanagedCodeSecurity] // We won't use this maliciously   [DllImport("User32.dll", CharSet=CharSet.Auto)]   public static extern

通過qt在linux系統下對c++進行封裝獲得so庫並在在其他程式呼叫共享庫

在qt下封裝.so 開啟qt新建專案,選擇這個 2、生成了兩個.h檔案和一個main.c檔案,這裡參照.h檔案構建標頭檔案,宣告函式變數等,也就是展現給客戶使用的部分。 之後在.c檔案中對自己的函式進行定義 點編譯,就可以了,可以看到在專案資料夾中生成了.s

C#/WPF 應用程式A 呼叫WPF編寫的應用程式B的exe時,引數傳遞問題

經過驗證該方法可行,注意:引數可以多個傳入,以空格分隔開的; WPF中, 應用程式A 呼叫WPF編寫的應用程式B的exe,引數傳遞給B的 主要方法: 1、在App.xaml.cs中的App類中,過載

C#專案呼叫C++生成的Dll 入門教程

1、首先建立一個C++ 的dll 專案(Dll_Cpp)並生成,會得到這三個檔案: Dll_Cpp.dll   //動態庫 Dll_Cpp.lib   //靜態庫 Dll_Cpp.h    //標頭檔案 其中Dll_Cpp.h的程式碼如下: // 下列 ifdef 塊是建

.net WinForm程式呼叫Visual Studio IDE 資料連線配置介面 (一個DLL搞定)

玩過直接調 資料庫配置介面的一定對如下三個東西不陌生 Microsoft.Data.ConnectionUI.Dialog.dll -- 微軟自帶的資料連線配置介面庫 Microsoft.Data.ConnectionUI.dll --微軟自帶的資料連線配置介面庫 Micr

Qt呼叫CDLL

在QT開發時,有裡需要呼叫已經寫好的DLL來實現一些功能,那要如何呼叫呢?我們先建一個DLL。 在VS2012中建立C++ WIN32的DLL空專案DllTest,然後新增標頭檔案和CPP檔案,具體如下 Calculate.h #ifdef __cplusplus #

C++呼叫C# DLL

首先寫個Com的DLL 工程如下 Security.cs程式碼 Com 介面程式碼 工程生成配置  選擇為Com互註冊 編譯 後生成 XGSharpLib.dll檔案 下面編寫C++測試程式 這樣 我們就可以在C++程式 呼叫C# .net自帶的加密演算法了

VB.net呼叫C\C++ 的DLL

只需要宣告一句話,比如呼叫mfc_Calc.dll中的calc函式 Public Declare Function calc Lib "mfc_Calc" (ByValaAs Integer, ByV

c++程式呼叫c編譯器編譯後的函式,為什麼要加extern "c"?

首先,被它修飾的目標是“extern”的。也就是告訴編譯器,其宣告的函式和變數可以在本模組或其他模組中使用。通常,在模組的標頭檔案中對本模組提供給其他模組引用的函式和全域性變數以關鍵字extern宣告。 其次,被它修飾的目標是“c”,意思是其修飾的變數和函式是按照c語言方式