首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 更多程式語言 >> 程式設計綜合問答

C# 調用 C++ DLL 結構體嵌套指針封送失敗:class 改 struct 解決 TypeLoadException

     編輯:程式設計綜合問答

問題現象

C++ 導出函數接收嵌套指針結構體:

// C++ DLL
struct A {
    int X;
    int Y;
    A* a;  // 自引用指針
};
extern "C" __declspec(dllexport) int fun1(A* a);

C# 端定義對應類型並動態調用:

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
class A {  // ❌ 這裡用了 class
    public int X;
    public int Y;
    public A a;
}

[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
delegate int dofun(ref A a1);

// 動態加載、獲取委托、調用...
A a1 = new A(); a1.X = 100; a1.Y = 20;
A a2 = new A(); a1.a = a2;
dofun1(ref a1); // 拋出 TypeLoadException: 無法封送處理類型為 "TestDll.A" 的字段 "a"

根因分析

C# 中 class 是引用類型,佈局為指向堆對象的指釈;而 C++ struct A 是值類型,內存中直接包含成員,嵌套指針 A* a 也是值類型指針(4/8 字節)。

  • C# class A 字段 public A a 是**托管引用**,無法直接映射為非托管指針。
  • 封送處理器嘗試將托管對象引用轉換為非托管指針,但 class 類型不支持直接作為結構體字段進行 LayoutKind.Sequential 封送,導致 TypeLoadException。

解決方案:改用 struct + IntPtr 模擬指針

1. 將 C# 類型改為 struct

[StructLayout(LayoutKind.Sequential)]
struct A {
    public int X;
    public int Y;
    public IntPtr a; // 用 IntPtr 對應 C++ 的 A*
}
  • struct 是值類型,內存佈局連續,與 C++ struct 一致。
  • IntPtr 大小與平台指針一致(32 位 4 字節,64 位 8 字節),完美映射 A*。

2. 調用時手動構建指針鏈


// 分配非托管內存塊,模擬 C++ 結構體數組/鏈表
IntPtr pA1 = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(A)));
IntPtr pA2 = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(A)));

A a1 = new A { X = 100, Y = 20, a = pA2 };
A a2 = new A { X = 0, Y = 0, a = IntPtr.Zero };

Marshal.StructureToPtr(a1, pA1, false);
Marshal.StructureToPtr(a2, pA2, false);

// 委托簽名改為接收 IntPtr
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
delegate int dofun(IntPtr a1);

dofun1(pA1); // 調用

// 釋放非托管內存
Marshal.FreeHGlobal(pA1);
Marshal.FreeHGlobal(pA2);

3. 若 C++ 函數修改結構體且需回傳

調用後用 Marshal.PtrToStructure(pA1, typeof(A)) 讀回數據。

關鍵對照表

C++ 類型C# 封送類型說明
struct A { int x; A* next; }struct A { int x; IntPtr next; }值類型嵌套指針
class A (引用類型)**不可直接封送**托管堆對象,佈局不兼容
A** (指針的指針)ref IntPtr / IntPtr*雙重指針

總結

C# 與 C++ 互操作時,**凡是涉及非托管結構體嵌套指針,C# 必須用 struct + IntPtr**,並手動管理非托管內存(AllocHGlobal/FreeHGlobal)。class 僅用於純托管場景,不可用於 StructLayout.Sequential 封送。此修改即可解決 TypeLoadException,實現正確調用。

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