C++與.NET之間的互相調(diào)用的幾種常見方式
C++ 與 .NET 之間的互操作性是開發(fā)混合應(yīng)用程序時(shí)的常見需求。下面我將展示幾種常見的互操作方式和示例:
1. 使用 C++/CLI 作為橋梁
C++/CLI 是微軟提供的一種中間語言,專門用于連接原生 C++ 和 .NET 代碼。這是實(shí)現(xiàn)互操作性最靈活的方式。
cpp-dotnet-interop C++/CLI 互操作代碼示例
NativeLibrary.h - 原生 C++ 庫
// NativeLibrary.h - 原生 C++ 庫 #pragma once extern "C" { __declspec(dllexport) int Add(int a, int b); __declspec(dllexport) void* CreatePerson(const char* name, int age); __declspec(dllexport) const char* GetPersonName(void* person); __declspec(dllexport) int GetPersonAge(void* person); __declspec(dllexport) void DestroyPerson(void* person); }
NativeLibrary.cpp - 原生 C++ 庫實(shí)現(xiàn)
// NativeLibrary.cpp - 原生 C++ 庫實(shí)現(xiàn) #include "NativeLibrary.h" #include <string> #include <memory> // 簡單的 Person 類 struct Person { std::string name; int age; }; int Add(int a, int b) { return a + b; } void* CreatePerson(const char* name, int age) { auto person = new Person{name, age}; return person; } const char* GetPersonName(void* person) { return reinterpret_cast<Person*>(person)->name.c_str(); } int GetPersonAge(void* person) { return reinterpret_cast<Person*>(person)->age; } void DestroyPerson(void* person) { delete reinterpret_cast<Person*>(person); }
BridgeLibrary.h - C++/CLI 橋接頭文件
// BridgeLibrary.h - C++/CLI 橋接頭文件 #pragma once using namespace System; namespace BridgeLibrary { public ref class Calculator { public: static int Add(int a, int b); }; public ref class PersonWrapper { private: void* nativePerson; public: PersonWrapper(String^ name, int age); ~PersonWrapper(); !PersonWrapper(); String^ GetName(); int GetAge(); }; }
BridgeLibrary.cpp - C++/CLI 橋接實(shí)現(xiàn)
// BridgeLibrary.cpp - C++/CLI 橋接實(shí)現(xiàn) #include "BridgeLibrary.h" #include "../NativeLibrary/NativeLibrary.h" #include <msclr/marshal_cppstd.h> namespace BridgeLibrary { int Calculator::Add(int a, int b) { return ::Add(a, b); } PersonWrapper::PersonWrapper(String^ name, int age) { msclr::interop::marshal_context context; const char* nativeName = context.marshal_as<const char*>(name); nativePerson = ::CreatePerson(nativeName, age); } PersonWrapper::~PersonWrapper() { this->!PersonWrapper(); } PersonWrapper::!PersonWrapper() { if (nativePerson != nullptr) { ::DestroyPerson(nativePerson); nativePerson = nullptr; } } String^ PersonWrapper::GetName() { const char* nativeName = ::GetPersonName(nativePerson); return gcnew String(nativeName); } int PersonWrapper::GetAge() { return ::GetPersonAge(nativePerson); } }
DotNetClient.cs - .NET 客戶端調(diào)用示例
// DotNetClient.cs - .NET 客戶端調(diào)用示例 using System; using BridgeLibrary; class Program { static void Main() { // 調(diào)用 C++ 計(jì)算函數(shù) int result = Calculator.Add(5, 3); Console.WriteLine("5 + 3 = " + result); // 使用 C++ 中的 Person 類 using (var person = new PersonWrapper("John Doe", 30)) { Console.WriteLine("Name: " + person.GetName()); Console.WriteLine("Age: " + person.GetAge()); } } }
2. 使用 P/Invoke 從 .NET 調(diào)用原生 C++
如果不想使用 C++/CLI,也可以通過 P/Invoke 直接從 .NET 調(diào)用原生 C++ 函數(shù)。
cpp-dotnet-pinvoke p/Invoke調(diào)用代碼示例
NativeLibrary.h - 原生 C++ 庫
// NativeLibrary.h - 原生 C++ 庫 #pragma once extern "C" { __declspec(dllexport) int Add(int a, int b); __declspec(dllexport) void* CreatePerson(const char* name, int age); __declspec(dllexport) const char* GetPersonName(void* person); __declspec(dllexport) int GetPersonAge(void* person); __declspec(dllexport) void DestroyPerson(void* person); }
NativeLibrary.cpp - 原生 C++ 庫實(shí)現(xiàn)
// NativeLibrary.cpp - 原生 C++ 庫實(shí)現(xiàn) #include "NativeLibrary.h" #include <string> #include <memory> // 簡單的 Person 類 struct Person { std::string name; int age; }; int Add(int a, int b) { return a + b; } void* CreatePerson(const char* name, int age) { auto person = new Person{name, age}; return person; } const char* GetPersonName(void* person) { return reinterpret_cast<Person*>(person)->name.c_str(); } int GetPersonAge(void* person) { return reinterpret_cast<Person*>(person)->age; } void DestroyPerson(void* person) { delete reinterpret_cast<Person*>(person); }
DotNetClient.cs - .NET 客戶端 P/Invoke 調(diào)用示例
// DotNetClient.cs - .NET 客戶端 P/Invoke 調(diào)用示例 using System; using System.Runtime.InteropServices; class Program { // 導(dǎo)入原生 C++ 函數(shù) [DllImport("NativeLibrary.dll", CallingConvention = CallingConvention.Cdecl)] public static extern int Add(int a, int b); [DllImport("NativeLibrary.dll", CallingConvention = CallingConvention.Cdecl)] public static extern IntPtr CreatePerson(string name, int age); [DllImport("NativeLibrary.dll", CallingConvention = CallingConvention.Cdecl)] [return: MarshalAs(UnmanagedType.LPStr)] public static extern string GetPersonName(IntPtr person); [DllImport("NativeLibrary.dll", CallingConvention = CallingConvention.Cdecl)] public static extern int GetPersonAge(IntPtr person); [DllImport("NativeLibrary.dll", CallingConvention = CallingConvention.Cdecl)] public static extern void DestroyPerson(IntPtr person); static void Main() { // 調(diào)用 C++ 計(jì)算函數(shù) int result = Add(5, 3); Console.WriteLine("5 + 3 = " + result); // 使用 C++ 中的 Person 類 IntPtr personPtr = CreatePerson("John Doe", 30); try { string name = GetPersonName(personPtr); int age = GetPersonAge(personPtr); Console.WriteLine("Name: " + name); Console.WriteLine("Age: " + age); } finally { // 釋放資源 DestroyPerson(personPtr); } } }
3. 從 C++ 調(diào)用 .NET 代碼
從原生 C++ 調(diào)用 .NET 代碼需要使用 CLR Hosting API。下面是一個簡單的示例:
CppHost.cpp - C++ 調(diào)用 .NET 代碼示例
// CppHost.cpp - C++ 調(diào)用 .NET 代碼示例 #include <windows.h> #include <metahost.h> #include <string> #include <iostream> #pragma comment(lib, "mscoree.lib") int main() { // 初始化 CLR ICLRMetaHost* pMetaHost = nullptr; ICLRRuntimeInfo* pRuntimeInfo = nullptr; ICLRRuntimeHost* pClrRuntimeHost = nullptr; HRESULT hr = CLRCreateInstance(CLSID_CLRMetaHost, IID_ICLRMetaHost, (LPVOID*)&pMetaHost); if (FAILED(hr)) { std::cerr << "Failed to create CLR instance" << std::endl; return 1; } hr = pMetaHost->GetRuntime(L"v4.0.30319", IID_ICLRRuntimeInfo, (LPVOID*)&pRuntimeInfo); if (FAILED(hr)) { std::cerr << "Failed to get runtime info" << std::endl; pMetaHost->Release(); return 1; } BOOL fLoadable; hr = pRuntimeInfo->IsLoadable(&fLoadable); if (FAILED(hr) || !fLoadable) { std::cerr << "Runtime is not loadable" << std::endl; pRuntimeInfo->Release(); pMetaHost->Release(); return 1; } hr = pRuntimeInfo->GetInterface(CLSID_CLRRuntimeHost, IID_ICLRRuntimeHost, (LPVOID*)&pClrRuntimeHost); if (FAILED(hr)) { std::cerr << "Failed to get runtime host" << std::endl; pRuntimeInfo->Release(); pMetaHost->Release(); return 1; } hr = pClrRuntimeHost->Start(); if (FAILED(hr)) { std::cerr << "Failed to start CLR" << std::endl; pClrRuntimeHost->Release(); pRuntimeInfo->Release(); pMetaHost->Release(); return 1; } // 調(diào)用 .NET 方法 DWORD pReturnValue; hr = pClrRuntimeHost->ExecuteInDefaultAppDomain( L"DotNetLibrary.dll", L"DotNetLibrary.Calculator", L"Add", L"5,3", &pReturnValue); if (FAILED(hr)) { std::cerr << "Failed to execute .NET method" << std::endl; } else { std::cout << "Result from .NET: " << pReturnValue << std::endl; } // 清理資源 pClrRuntimeHost->Stop(); pClrRuntimeHost->Release(); pRuntimeInfo->Release(); pMetaHost->Release(); return 0; }
DotNetLibrary.cs - .NET 庫示例
// DotNetLibrary.cs - .NET 庫示例 namespace DotNetLibrary { public class Calculator { public static int Add(string args) { string[] numbers = args.Split(','); int a = int.Parse(numbers[0]); int b = int.Parse(numbers[1]); return a + b; } } }
互操作方法比較
C++/CLI:
- 優(yōu)點(diǎn):完全支持 .NET 功能,可直接訪問 .NET 類庫
- 缺點(diǎn):需要安裝 .NET Framework/.NET Core
- 適用場景:需要深度集成 .NET 功能的 C++ 應(yīng)用
P/Invoke:
- 優(yōu)點(diǎn):簡單直接,不需要中間層
- 缺點(diǎn):類型映射復(fù)雜,不支持面向?qū)ο筇匦?/li>
- 適用場景:從 .NET 調(diào)用簡單的 C/C++ 函數(shù)
CLR Hosting:
- 優(yōu)點(diǎn):允許原生 C++ 代碼調(diào)用 .NET 代碼
- 缺點(diǎn):實(shí)現(xiàn)復(fù)雜,性能開銷大
- 適用場景:需要在 C++ 應(yīng)用中嵌入 .NET 功能
到此這篇關(guān)于C++與.NET之間的互相調(diào)用的幾種常見方式的文章就介紹到這了,更多相關(guān)C++ .NET互相調(diào)用內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
實(shí)現(xiàn)一個random?shuffle算法示例
這篇文章主要為大家介紹了實(shí)現(xiàn)一個random?shuffle算法示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-05-05使用Visual Studio 2010/2013編譯V8引擎步驟分享
這篇文章主要介紹了使用Visual Studio 2013編譯V8引擎步驟分享,需要的朋友可以參考下2015-08-08用c語言實(shí)現(xiàn)HUP信號重啟進(jìn)程的方法
本篇文章是對使用c語言實(shí)現(xiàn)HUP信號重啟進(jìn)程的方法進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下2013-05-05C++詳細(xì)分析講解函數(shù)參數(shù)的擴(kuò)展
在C++中,定義函數(shù)時(shí)可以給形參指定一個默認(rèn)的值,這樣調(diào)用函數(shù)時(shí)如果沒有給這個形參賦值(沒有對應(yīng)的實(shí)參),那么就使用這個默認(rèn)的值。也就是說,調(diào)用函數(shù)時(shí)可以省略有默認(rèn)值的參數(shù)2022-04-04OpenMP task construct 實(shí)現(xiàn)原理及源碼示例解析
這篇文章主要為大家介紹了OpenMP task construct 實(shí)現(xiàn)原理及源碼示例解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-03-03C++ OpenCV實(shí)現(xiàn)圖像雙三次插值算法詳解
圖像雙三次插值的原理,就是目標(biāo)圖像的每一個像素都是由原圖上相對應(yīng)點(diǎn)周圍的4x4=16個像素經(jīng)過加權(quán)之后再相加得到的。本文主要介紹了通過C++ OpenCV實(shí)現(xiàn)圖像雙三次插值算法,需要的可以參考一下2021-12-12C++?OpenCV實(shí)現(xiàn)物體尺寸測量示例詳解
本文主要介紹了利用OpenCV對物體的尺寸進(jìn)行測量,即先定位到待測物體的位置,然后測量物體的寬高。感興趣的同學(xué)可以跟隨小編一起學(xué)習(xí)學(xué)習(xí)2022-01-01