以往我們經常是需要使用C#來呼叫C++的dll,這通過PInvoke就能實現。現在在實際的專案過程中,有時會遇到在C++的專案中呼叫某個C#的dll來完成特定的某個功能,我們都知道,Native C++是沒辦法直接呼叫.NET平臺的dll的。那有沒有辦法來做到這一點了?答案是肯定的。
我們以OMCS實時音視訊框架為例,OMCS WinPC 版的SDK是C#開發的,有些使用C++ QT開發Windows應用的客戶,想呼叫OMCS來進行語音視訊對談或遠端桌面等功能,那該怎麼做了?
雖然,Native C++是呼叫不了C#的dll的,但是Managed C++(C++/CLI)可以呼叫C#的dll,所以,可以使用C++/CLI作為橋樑來將 Native C++ 和 C#連線起來。
(1)新建一個C++/CLI的庫(比如名稱為OmcsWrap),新增參照並呼叫OMCS.dll,使用Managed C++語法呼叫OMCS.dll中的元件,並暴露出標準的C/C++介面。
(2)編譯C++/CLI庫得到 OmcsWrap.dll 和 OmcsWrap.lib。該庫的介面是符合C++規範的。
(3)在Native C++專案(如QT)中,連結 OmcsWrap.dll、OmcsWrap.lib即可。
如果瞭解OMCS的基本用法(不瞭解的可以檢視:OMCS入門Demo:語音視訊、電子白板、遠端桌面 功能展現),那麼下面的C++/CLI的呼叫程式碼就很容易理解了。
我們新建一個C++/CLI的控制檯專案,來演示如何通過OMCS的攝像頭聯結器連線到任意一個線上使用者的攝像頭,拿到攝像頭的視訊影象Bitmap資料。
using namespace System; using namespace System::Drawing; using namespace OMCS::Passive; using namespace OMCS::Passive::Video; ref class Tester { private: DynamicCameraConnector^ dynamicCameraConnector; int frameCount = 0; public: Tester() { IMultimediaManager^ mgr = MultimediaManagerFactory::GetSingleton(); //登陸 aa09 mgr->Initialize("aa09", "", "127.0.0.1", 9900); Console::WriteLine(L"登入OMCS伺服器成功!"); } void Start() { dynamicCameraConnector = gcnew DynamicCameraConnector(); dynamicCameraConnector->NewFrameReceived += gcnew ESBasic::CbGeneric<array<unsigned char, 1>^>(this, &Tester::OnNewFrameReceived); dynamicCameraConnector->ConnectEnded += gcnew ESBasic::CbGeneric<OMCS::Passive::ConnectResult>(this, &Tester::OnConnectEnded); //連線自己的攝像頭 dynamicCameraConnector->BeginConnect(L"aa09"); } //攝像頭影象資料 void OnNewFrameReceived(array<unsigned char, 1>^ rgb24) { Bitmap^ bm = ESBasic::Helpers::ImageHelper::ConstructRGB24Bitmap(rgb24, dynamicCameraConnector->VideoSize.Width, dynamicCameraConnector->VideoSize.Height); ++this->frameCount; Console::WriteLine(L"收到影象幀:" + this->frameCount.ToString("00000")); } //連線攝像頭的結果 void OnConnectEnded(OMCS::Passive::ConnectResult result) { if (result == ConnectResult::Succeed) //連線成功 { Console::WriteLine(L"連線攝像頭成功!"); } else { Console::WriteLine(L"連線攝像頭失敗!原因:" + result.ToString()); } } void Stop() { if (this->dynamicCameraConnector != nullptr) { if (this->dynamicCameraConnector->Connected) { this->dynamicCameraConnector->Disconnect(); //斷開到目標攝像頭的連線 Console::WriteLine(L"斷開攝像頭聯結器!"); } this->dynamicCameraConnector = nullptr; } } };
(1)這裡僅僅是將收到的攝像頭視訊影象幀的幀數列印出來,真實的使用場景中,可以將影象幀回撥傳給QT,QT就可以在UI控制元件上將影象渲染出來。這樣就可以看到視訊了。
(2)這裡是以攝像頭為例,桌面也是完全一樣的模式,使用DynamicDesktopConnector。
(3)對於麥克風聲音,則更簡單一下,因為其不需要UI渲染,所以直接在C++/CLI中呼叫MicrophoneConnector就可以了。連線目標麥克風成功,本地電腦就會自動播放其聲音。
啟動OMCS伺服器端(可從文末下載)後,執行本文的控制檯程式,執行效果如下截圖所示:
這裡只是簡單的示意一下C++/CLI呼叫OMCS的方式,至於封裝一個給Native C++來呼叫C++/CLI庫,這個庫要提供哪些API,則取決於具體的專案需求,這裡就不舉例了。
1. C++/CLI呼叫OMCS Demo:CppCli-CallOMCS-Demo.rar
2. Demo 伺服器端+C#使用者端:OMCS.Demos.Simplest.rar
關於OMCS實時視訊功能的demo介紹,請參見這裡。
最後提一下,還有一種 Native C++ 呼叫C#的dll的方式,是使用COM元件。
這種方式是在C#的dll外再封裝一層,將其介面全部轉換成COM介面,如此,標準的COM元件就可以被Native C++呼叫了。
COM元件這種做法已經很古老,而且相當繁瑣,所以不是迫不得已,一般不會採用這種方式。