在C#項(xiàng)目中調(diào)用C++編寫的動態(tài)庫的三種方式
場景和優(yōu)點(diǎn)
在以下場景下,可能會使用C#調(diào)用C++編寫的dll:
- C++庫已經(jīng)存在并且經(jīng)過了充分測試和驗(yàn)證,需要被C#項(xiàng)目重復(fù)使用時(shí);
- C++編寫的庫中包含高性能計(jì)算、海量數(shù)據(jù)處理等需要使用底層語言實(shí)現(xiàn)的操作時(shí),可以考慮將這些操作封裝為動態(tài)鏈接庫供C#調(diào)用;
- 在跨平臺開發(fā)時(shí),C++可在多個平臺上運(yùn)行,通過封裝為dll,可以讓C#項(xiàng)目也能夠在多個平臺上運(yùn)行;
- 需要將不同的功能模塊拆分成獨(dú)立的組件,C++編寫的dll可以作為一個獨(dú)立的組件,供C#項(xiàng)目或其他語言的項(xiàng)目調(diào)用。
此外,使用C#調(diào)用C++編寫的dll還有以下優(yōu)點(diǎn):
- C#具有較高的開發(fā)效率和易用性,通過調(diào)用C++編寫的dll可以兼顧高性能和高開發(fā)效率。
- C#可以使用.NET Framework提供的強(qiáng)大工具和庫,如LINQ、異步編程等等,這些工具和庫可以提高開發(fā)效率,同時(shí)也可以利用C++的性能優(yōu)勢。
- C#可以與其他語言,如Java、Python等配合使用,借助各種技術(shù),如SOAP、WCF、gRPC等實(shí)現(xiàn)多語言之間的互操作。
- C++作為一種系統(tǒng)級編程語言,可以訪問系統(tǒng)底層資源,如內(nèi)存、磁盤、網(wǎng)絡(luò)等,C#調(diào)用C++編寫的dll可以實(shí)現(xiàn)訪問這些底層資源的功能,從而提供更多的功能。
C#調(diào)用C++編寫的動態(tài)庫的方式
在C#中調(diào)用C++編寫的動態(tài)庫有以下幾種方式:
1. 使用DllImport特性
使用DllImport特性可以直接引入動態(tài)鏈接庫中的C++函數(shù),并在C#中進(jìn)行調(diào)用。
下面是一個簡單的示例:
首先,我們在C++中編寫一個簡單的dll,里面包含一個計(jì)算兩數(shù)之和的函數(shù)addition:
c++Copy Code// file: mylib.cpp #include "pch.h" #include "mylib.h" int addition(int a, int b) { return a + b; }
然后,我們在C++中將其封裝為一個dll,并導(dǎo)出addition函數(shù):
// file: mylib.h #ifdef MYLIB_EXPORTS #define MYLIB_API __declspec(dllexport) #else #define MYLIB_API __declspec(dllimport) #endif extern "C" MYLIB_API int addition(int a, int b); // export the function
接著,在C#項(xiàng)目中使用DllImport特性導(dǎo)入這個dll,并調(diào)用其中的函數(shù):
using System.Runtime.InteropServices; class Program { [DllImport("MyLib.dll", CallingConvention = CallingConvention.Cdecl)] public static extern int addition(int a, int b); static void Main(string[] args) { int result = addition(1, 2); Console.WriteLine("The sum is: " + result); } }
在上述示例中,我們使用DllImport特性聲明了一個addition方法,將其與C++中的addition函數(shù)進(jìn)行綁定。在Main函數(shù)中,我們調(diào)用了這個方法,并輸出計(jì)算結(jié)果。
需要注意的是,在使用DllImport特性時(shí),需要指定正確的dll名稱和函數(shù)調(diào)用規(guī)約,否則可能會出現(xiàn)運(yùn)行時(shí)錯誤。
2. 使用C++/CLI
另一種實(shí)現(xiàn)方式是使用C++/CLI(C++/Common Language Infrastructure)。
C++/CLI是一種結(jié)合了C++和CLR(Common Language Runtime)的語言,它可以編寫針對.NET Framework/CLR的代碼,同時(shí)也可以訪問C++的底層資源。因此,我們可以使用C++/CLI來封裝C++庫,并將其作為dll供C#調(diào)用。
下面是一個簡單的示例:
首先,在C++/CLI中編寫一個類LibraryWrapper,里面包含一個使用C++庫計(jì)算兩數(shù)之和的方法Addition:
// file: LibraryWrapper.h #pragma once namespace MyLibrary { public ref class LibraryWrapper { private: Library* lib; // the C++ object we want to wrap public: LibraryWrapper(); // constructor ~LibraryWrapper(); // destructor int Addition(int a, int b); // method used to add two numbers }; }
其中,Library是我們需要封裝的C++庫中的一個類。
然后,在實(shí)現(xiàn)文件LibraryWrapper.cpp中實(shí)現(xiàn)類的構(gòu)造函數(shù)、析構(gòu)函數(shù)和Addition方法:
// file: LibraryWrapper.cpp #include "pch.h" #include "LibraryWrapper.h" #include "Library.h" using namespace MyLibrary; LibraryWrapper::LibraryWrapper() { lib = new Library(); // create a new Library object } LibraryWrapper::~LibraryWrapper() { delete lib; // release the memory } int LibraryWrapper::Addition(int a, int b) { return lib->addition(a, b); // call the addition method in C++ library }
這里我們實(shí)例化了一個C++庫中的對象,然后在Addition方法中調(diào)用了它的addition方法。
最后,在C++/CLI項(xiàng)目中發(fā)布dll,并在C#項(xiàng)目中引用。在C#項(xiàng)目中,我們可以創(chuàng)建一個LibraryWrapper對象,并調(diào)用其中的Addition方法:
using System; using System.Runtime.InteropServices; namespace CppCLILibraryTest { class Program { static void Main(string[] args) { MyLibrary.LibraryWrapper wrapper = new MyLibrary.LibraryWrapper(); int result = wrapper.Addition(1, 2); Console.WriteLine("The sum is: " + result); } } }
需要注意的是,當(dāng)使用C++/CLI封裝C++庫時(shí),我們需要確保兩者所使用的Runtime是相同的。比如,如果C++庫是使用靜態(tài)連接的方式與CRT(C Runtime)鏈接的,那么我們需要在C++/CLI項(xiàng)目的屬性中設(shè)置“/MT”選項(xiàng),以保證代碼使用相同的CRT版本。
3. 使用COM組件
另一種實(shí)現(xiàn)方式是使用COM組件。COM是微軟推出的一種二進(jìn)制接口標(biāo)準(zhǔn),它可以讓不同的應(yīng)用程序之間以二進(jìn)制碼互相通信。
下面是一個簡單的示例:
首先,在C++中編寫一個簡單的dll,里面包含一個計(jì)算兩數(shù)之和的函數(shù)addition:
// file: MyLibrary.h #pragma once #ifdef MYLIBRARY_EXPORTS #define MYLIBRARY_API __declspec(dllexport) #else #define MYLIBRARY_API __declspec(dllimport) #endif namespace MyLibrary { class MyMath { public: static int Addition(int a, int b); }; }
然后,我們將這個dll封裝為一個COM組件。我們需要創(chuàng)建一個類,其中包含COM接口和類工廠:
// file: MathCOM.h #pragma once #include "MyLibrary.h" class MathCOM : public IUnknown { private: ULONG m_cRef; public: MathCOM(); ~MathCOM(); // IUnknown methods STDMETHODIMP QueryInterface(REFIID riid, void** ppv); STDMETHODIMP_(ULONG) AddRef(void); STDMETHODIMP_(ULONG) Release(void); // COM interface method STDMETHODIMP Addition(int a, int b, int* result); }; class MathClassFactory : public IClassFactory { private: ULONG m_cRef; public: MathClassFactory(); ~MathClassFactory(); // IUnknown methods STDMETHODIMP QueryInterface(REFIID riid, void** ppv); STDMETHODIMP_(ULONG) AddRef(void); STDMETHODIMP_(ULONG) Release(void); // IClassFactory methods STDMETHODIMP CreateInstance(IUnknown* pUnknownOuter, REFIID riid, void** ppvObject); STDMETHODIMP LockServer(BOOL fLock); };
在實(shí)現(xiàn)文件MathCOM.cpp中,我們需要為這些接口方法提供具體的實(shí)現(xiàn):
// file: MathCOM.cpp #include "stdafx.h" #include "MathCOM.h" MathCOM::MathCOM() { m_cRef = 1; } MathCOM::~MathCOM() {} STDMETHODIMP MathCOM::QueryInterface(REFIID riid, void** ppv) { *ppv = NULL; if (riid == IID_IUnknown || riid == IID_IDispatch) *ppv = this; if (*ppv != NULL) { ((LPUNKNOWN)*ppv)->AddRef(); return S_OK; } return E_NOINTERFACE; } STDMETHODIMP_(ULONG) MathCOM::AddRef() { return InterlockedIncrement((LONG*)&m_cRef); } STDMETHODIMP_(ULONG) MathCOM::Release() { ULONG cRef = InterlockedDecrement((LONG*)&m_cRef); if (cRef == 0) delete this; return cRef; } STDMETHODIMP MathCOM::Addition(int a, int b, int* result) { *result = MyLibrary::MyMath::Addition(a, b); return S_OK; } MathClassFactory::MathClassFactory() { m_cRef = 1; } MathClassFactory::~MathClassFactory() {} STDMETHODIMP MathClassFactory::QueryInterface(REFIID riid, void** ppv) { *ppv = NULL; if (riid == IID_IUnknown || riid == IID_IClassFactory) *ppv = this; if (*ppv != NULL) { ((LPUNKNOWN)*ppv)->AddRef(); return S_OK; } return E_NOINTERFACE; } STDMETHODIMP_(ULONG) MathClassFactory::AddRef() { return InterlockedIncrement((LONG*)&m_cRef); } STDMETHODIMP_(ULONG) MathClassFactory::Release() { ULONG cRef = InterlockedDecrement((LONG*)&m_cRef); if (cRef == 0) delete this; return cRef; } STDMETHODIMP MathClassFactory::CreateInstance(IUnknown* pUnknownOuter, REFIID riid, void** ppvObject) { if (pUnknownOuter) return CLASS_E_NOAGGREGATION; MathCOM* pMathCOM = new MathCOM(); if (!pMathCOM) return E_OUTOFMEMORY; HRESULT hResult = pMathCOM->QueryInterface(riid, ppvObject); pMathCOM->Release(); return hResult; } STDMETHODIMP MathClassFactory::LockServer(BOOL fLock) { return S_OK; }
在項(xiàng)目中使用C++編譯器生成COM組件dll之后,在C#項(xiàng)目中使用COM互操作性來調(diào)用這個COM組件,代碼如下:
using System.Runtime.InteropServices; namespace COMTest { [ComImport, Guid("B9D43B8A-61F3-4668-AB30-C2BE194AD0AA")] [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] public interface IMathCOM { [PreserveSig] int Addition(int a, int b, out int result); } [ComImport, Guid("8CFD0B22-24A3-4490-9127-9DB3FD53E15F")] class MathCOM { } class Program { static void Main(string[] args) { IMathCOM mathCOM = (IMathCOM)new MathCOM(); int result = 0; mathCOM.Addition(1, 2, out result); Console.WriteLine("The sum is: " + result); } } }
在這個示例中,我們聲明了一個用來調(diào)用COM組件的接口IMathCOM,然后實(shí)例化MathCOM類并把它轉(zhuǎn)換為IMathCOM類型,就可以調(diào)用其中的Addition方法了。
以上三種方式都可用于調(diào)用C++編寫的動態(tài)庫,選擇使用哪種方式應(yīng)該根據(jù)具體的場景和需求來決定。
到此這篇關(guān)于在C#項(xiàng)目中調(diào)用C++編寫的動態(tài)庫的三種方式的文章就介紹到這了,更多相關(guān)C#調(diào)用C++動態(tài)庫內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Unity TextMeshPro實(shí)現(xiàn)富文本超鏈接默認(rèn)字體追加字體
這篇文章主要為大家介紹了Unity TextMeshPro實(shí)現(xiàn)富文本超鏈接默認(rèn)字體追加字體示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-01-01C#使用doggleReport生成pdf報(bào)表的方法
這篇文章主要介紹了C#使用doggleReport生成pdf報(bào)表的方法,結(jié)合實(shí)例形式分析了C# doggleReport安裝及使用具體操作技巧,需要的朋友可以參考下2017-06-06C#使用FileSystemWatcher控件實(shí)現(xiàn)的文件監(jiān)控功能示例
這篇文章主要介紹了C#使用FileSystemWatcher控件實(shí)現(xiàn)的文件監(jiān)控功能,結(jié)合實(shí)例形式分析了C# FileSystemWatcher組件的功能及監(jiān)控文件更改情況的具體使用技巧,需要的朋友可以參考下2017-08-08C#調(diào)用存儲過程詳解(帶返回值、參數(shù)輸入輸出等)
這篇文章主要介紹了C#調(diào)用存儲過程的方法,結(jié)合實(shí)例形式詳細(xì)分析了各種常用的存儲過程調(diào)用方法,包括帶返回值、參數(shù)輸入輸出等,需要的朋友可以參考下2016-06-06C#實(shí)現(xiàn)單例模式的幾種方法總結(jié)
這篇文章主要介紹了C#實(shí)現(xiàn)單例模式的幾種方法,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-01-01