首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

C# 調用 C++ DLL 進階:托管 C++/CLI 封裝非托管類完整實戰

     編輯:關於C語言

背景與痛點

  • C# 只能通過 P/Invoke (DllImport) 調用純 C 風格導出函數,無法直接實例化 C++ 類、調用成員函數、處理 C++ 異常或 STL 類型。
  • 解決方案:編寫 C++/CLI 托管封裝層,將非托管類封裝為 ref class,編譯為 .NET 組件,C# 像調用原生 .NET 類一樣使用。

架構圖解

C# 專案 → 引用 ManagedWrapper.dll (C++/CLI)
                ↓ 內部持有指針
          NativeLib.dll (原生 C++)

步驟 1:建立 CLR 類庫專案

Visual Studio → 新建專案 → CLR 類庫,命名 ManagedWrapper。目標框架建議 .NET Framework 4.7.2+ 或 .NET Core 3.1+ (需 SDK 支持)。

步驟 2:引入非托管原生類

將原生類頭文件 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; }
};

步驟 3:編寫托管封裝類

// 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 回收時調用,雙重保障釋放原生資源。
  • 原生指針作為私有字段,生命週期與托管對象綁定。

步驟 4:編譯生成 ManagedWrapper.dll

生成模式選 Release/x64(或 x86,需與 C# 專案一致),輸出 ManagedWrapper.dll。

步驟 5:C# 專案引用並調用

  1. 右鍵「引用」→「添加引用」→ 瀏覽選中 ManagedWrapper.dll。
  2. 代碼調用:
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# 專案。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved