4. 需要将不同的功能模块拆分成独立的组件,C++编写的dll可以作为一个独立的组件,供C#项目或其他语言的项目调用。
3. C#可以与其他语言,如Java、Python等配合使用,借助各种技术,如SOAP、WCF、gRPC等实现多语言之间的互操作。
4. C++作为一种系统级编程语言,可以访问系统底层资源,如内存、磁盘、网络等,C#调用C++编写的dll可以实现访问这些底层资源的功能,从而提供更多的功能。
c++Copy Code// file: mylib.cpp #include "pch.h" #include "mylib.h" int addition(int a, int b) { return a + b; }然后,我们在C++中将其封装为一个dll,并导出addition函数:
// 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#项目中使用DllImport特性导入这个dll,并调用其中的函数:
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函数进行绑定。在Main函数中,我们调用了这个方法,并输出计算结果。
// 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++库中的一个类。
// 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 }这里我们实例化了一个C++库中的对象,然后在Addition方法中调用了它的addition方法。最后,在C++/CLI项目中发布dll,并在C#项目中引用。在C#项目中,我们可以创建一个LibraryWrapper对象,并调用其中的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); } } }需要注意的是,当使用C++/CLI封装C++库时,我们需要确保两者所使用的Runtime是相同的。比如,如果C++库是使用静态连接的方式与CRT(C Runtime)链接的,那么我们需要在C++/CLI项目的属性中设置“/MT”选项,以保证代码使用相同的CRT版本。
// 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组件。我们需要创建一个类,其中包含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); };在实现文件MathCOM.cpp中,我们需要为这些接口方法提供具体的实现:
// 堆代码 duidaima.com // 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; }在项目中使用C++编译器生成COM组件dll之后,在C#项目中使用COM互操作性来调用这个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); } } }在这个示例中,我们声明了一个用来调用COM组件的接口IMathCOM,然后实例化MathCOM类并把它转换为IMathCOM类型,就可以调用其中的Addition方法了。