【大型軟體開發】開發日誌(五).net框架與C++的融合:CLR——C++呼叫C#的DLL

軒先生。發表於2023-03-03

做什麼?

先說一下場景,現在正在開發一個Qt ActiveServer,也就是用一個應用程式去向其他的元件暴露介面,以達到提供服務的目的。

然後新版的框架要提供大部分功能,也就是要重做大部分模組。現在有一個問題,就是有一個用於提供向web傳送post請求,獲得回執並解析的模組,叫LBD_WebApiInterface

然後現在,我要將這個模組整合到新的Qt ActiveServer裡面去,做這個的時候我踩了巨多的坑,做了差不多快一個星期才勉強搞清楚這個怎麼做。

其實是很簡單的,但是它並不會告訴你錯在哪,你只能自己猜謎,這點是最頭疼的。

其次,如果有可能,如果是在Windows環境下,我是及其推薦你把C#的DLL改裝成COM元件,並註冊進系統內,這樣可以省去非常非常多的麻煩,不會以至於你在開發的時候跨過和我一樣多的坑。

然後我發現CLR這一塊完全是一片空白,國內基本上找不到什麼中文資料和討論,真要被這玩意搞得頭暈了。

怎麼做?

首先我們要知道CLR是什麼,知道的 就知道了,不知道的你也只能自己去百度了。

實際上,CLR語言就是個C#語言和C++語言的結合體,具體語法我這裡不會過多闡述,只說應用

因為我這裡大部分的類都是以單例的形式對外暴露的,具體是否需要使用單例可以視情況而定,總之我這裡就要用單例:

注:其中這個LBD_WebApiInterface的namespace就是來自我需要呼叫的那個C#的DLL

image

這個類裡面我只是需要獲得一個單例,返回我們在這個CLR專案中,由C#DLL生成的單例,僅此而已。

甚至不需要一個類去包裹,當然了最好還是有一個類去管理gc,不然我也不知道會發生什麼。

然後就是寫介面了,寫一個DLL 對外暴露的類:TeachInfo

image

這個宏的目的是為了複用這個標頭檔案,這樣就可以在呼叫方直接引用這個標頭檔案而不需要修改了

如果想要這個宏正常工作的話,則需要原始檔在呼叫這個標頭檔案的上方加入宏定義

#ifndef _IWEB_H
#define _IWEB_H

// 透過宏來控制是匯入還是匯出
#ifdef _IWEB
#define WEB_API __declspec(dllexport)
#else
#define WEB_API __declspec(dllimport)
#endif
//..程式碼塊
#endif

先來看下轉換資料型別的標頭檔案Trans.h

image
因為System::Sring型別相當於是託管型別,不能直接轉換成std::string,所以必須要聽過msclr\marshal_cppstd.h裡面提供的這個marshal_as函式進行轉換
這裡有一個比較坑的點,就是你必須要把這個

#include <msclr/gcroot.h> //gcroot
#include <msclr\marshal_cppstd.h>

這兩行 放在

using namespace msclr::interop;

的前面,否則會報錯:

"*"不能再型別"IServiceProvider"上使用此間接定址。

除此之外

#include "windows.h"

這一行的引用和上述引用的位置也可能導致類似的問題,這裡就不細說了

然後我們來看一個簡單的示例

image

注:CLR型別需要在原始檔的開頭引用

#include "pch.h"

標頭檔案,這個標頭檔案將決定哪些內容會被編譯,如果你不包含這個標頭檔案的話就不會編譯了,然後就有可能報錯LNK2019

然後就是,除了標頭檔案之外,你的原始檔中的每一個函式都需要一個WEB_API的標記,否則呼叫方可能會報錯LNK2019

來看下呼叫方

呼叫方的話 其實也比較簡單,我之前犯了一個錯誤害得我搞了好久,都沒能解決問題

首先需要知道的一點是,#pragma comment(lib,"xxx.lib")這條宏和你在vs中對專案的設定是不一樣的,如果你在VS中設定的比如連結庫地址,但是你#pragma comment(lib,"xxx.lib")的位置是錯誤的,編譯器會優先嚐試匯入你寫的這條宏,這會導致

LNK 2019

然後還有一點就是,你是可以用#pragma comment(lib,"xxx.lib")引用這個lib檔案,但是如果這個lib依賴的庫不在應用程式的根資料夾下,有可能這個應用程式編譯之後會有兩個問題

1.LNK2019
2.ActiveServer 註冊失敗

我只提供一個範例,具體問題我不再分析

image

image

相關文章