DllImport) 調用純 C 風格導出函數,無法直接實例化 C++ 類、調用成員函數、處理 C++ 異常或 STL 類型。ref class,編譯為 .NET 組件,C# 像調用原生 .NET 類一樣使用。
C# 專案 → 引用 ManagedWrapper.dll (C++/CLI)
↓ 內部持有指針
NativeLib.dll (原生 C++)
Visual Studio → 新建專案 → CLR 類庫,命名 ManagedWrapper。目標框架建議 .NET Framework 4.7.2+ 或 .NET Core 3.1+ (需 SDK 支持)。
將原生類頭文件 NativeClass.h、實現文件複製到專案目錄,不加入 CLR 編譯(屬性 → C/C++ → General → Common Language Runtime Support → No Common Language Runtime Support),保持純原生編譯。
// NativeClass.h
#pragma once
class __declspec(dllexport) NativeClass {
int nCount;
public:
NativeClass() : nCount(0) {}
int GetCount() const { return nCount; }
void Increase() { ++nCount; }
void Clear() { nCount = 0; }
};
// ManagedWrapper.h
#pragma once
#include "NativeClass.h"
using namespace System;
namespace ManagedWrapper {
public ref class NativeClassEx {
private:
NativeClass* m_pNative; // 原生指針
public:
NativeClassEx() : m_pNative(new NativeClass()) {}
~NativeClassEx() { this->!NativeClassEx(); } // 托管析構 → 調用終結器
!NativeClassEx() { delete m_pNative; m_pNative = nullptr; } // 終結器
int GetCount() { return m_pNative->GetCount(); }
void Increase() { m_pNative->Increase(); }
void Clear() { m_pNative->Clear(); }
};
}
關鍵點:
ref class:托管類,運行在 CLR 堆上,受 GC 管理。~ClassName():托管析構函數,確定性釋放,實現 IDisposable。!ClassName():終結器,非確定性 GC 回收時調用,雙重保障釋放原生資源。生成模式選 Release/x64(或 x86,需與 C# 專案一致),輸出 ManagedWrapper.dll。
ManagedWrapper.dll。using ManagedWrapper;
using System.Diagnostics;
var obj = new NativeClassEx();
Debug.WriteLine("Count: " + obj.GetCount()); // 0
obj.Increase(); obj.Increase(); obj.Increase();
Debug.WriteLine("Count: " + obj.GetCount()); // 3
obj.Clear();
Debug.WriteLine("Count: " + obj.GetCount()); // 0
| 問題 | 解決方案 |
|---|---|
| CPU 架構不匹配 | C++/CLI、C# 專案統一 x64 或 x86,勿用 Any CPU(C++/CLI 不支持)。 |
| STL/複雜類型跨邊界 | 封裝層只暴露 POD 類型(int、double、bool、枚舉),複雜類型在內部轉換。 |
| 原生異常穿透到 CLR | 封裝層所有公共方法 捕獲所有異常,轉換為托管異常或錯誤碼返回。 |
| 第三方原生 DLL 依賴 | 確保原生 DLL 與托管 DLL 同目錄,或放入系統 PATH;必要時用 SetDllDirectory。 |
| OpenCV 等大型庫封裝失敗 | 優先考慮 P/Invoke 純 C 接口 或現成 NuGet 包,避免 C++/CLI 複雜依賴鏈。 |
C++/CLI 是連接非托管 C++ 與 .NET 生態的橋樑,核心模式是 ref class 持有原生指針 + 雙重析構模式保證資源釋放。掌握此模式,即可將任意 C++ 庫優雅地引入 C# 專案。