C++呼叫C#的動態庫dll

2022-06-06 12:05:25

  以往我們經常是需要使用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即可。

二.C++/CLI呼叫C#版的OMCS範例

  如果瞭解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,則取決於具體的專案需求,這裡就不舉例了。

三. Demo 原始碼下載

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元件這種做法已經很古老,而且相當繁瑣,所以不是迫不得已,一般不會採用這種方式。