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 字節)。
class A 字段 public A a 是**托管引用**,無法直接映射為非托管指針。class 類型不支持直接作為結構體字段進行 LayoutKind.Sequential 封送,導致 TypeLoadException。struct + IntPtr 模擬指針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*。
// 分配非托管內存塊,模擬 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);
調用後用 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,實現正確調用。