存在的問題:數組
問題1:C++ 與 C# 一樣定義的結構體在內存佈局上有時並不一致;函數
問題2:C# 中引入了垃圾自動回收機制,其垃圾回收器可能會從新定位指針所指向的結構體變量。佈局
解決方案:spa
問題1方案:強制指定 C++、C# 結構體的內存佈局,使其一致(二者都固定爲:結構體的成員按其聲明時出現的順序依次佈局,結構體成員的內存對齊爲1字節對齊);指針
爲題2方案:C# 調用時將待傳遞的結構體轉化爲字節數組,並使用 fixed 語句將該字節數組固定住。內存
示例代碼以下:it
一、C++結構體定義:io
#pragma pack(1)
struct Person
{
#define Count_FavoriteNumbers 6
int id;
float favoriteNumbers[Count_FavoriteNumbers];
};
#pragma pack() // #pragma pack(1) endclass
C++ 導出函數:變量
#define DllExport extern "C" __declspec(dllexport)
DllExport void __stdcall InitPersonInfo_DLL(Person* p_Person)
{
p_Person->id = 0;
for (int i = 1; i <= Count_FavoriteNumbers; i++)
{
p_Person->favoriteNumbers[i - 1] = 1.11 * i;
}
}
二、C# 結構體定義:
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public class Person
{
public const int Count_FavoriteNumbers = 6;
public int id;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = Count_FavoriteNumbers, ArraySubType = UnmanagedType.U4)]
public float[] favoriteNumbers = new float[Count_FavoriteNumbers];
}
C# 調用(WPF窗體程序):
public partial class MainWindow : Window
{
[DllImport("MFCLibrary_ExportFunction.dll", EntryPoint = "InitPersonInfo_DLL")]
static extern unsafe void InitPersonInfo_DLL(byte* p_Person);
public unsafe void InitPersonInfo(ref Person person)
{
byte[] structBytes = StructToBytes(person);
fixed (byte* p_Person = &structBytes[0])
{
InitPersonInfo_DLL(p_Person);
person = (Person)BytesToStruct(structBytes, person.GetType());
}
public MainWindow()
{
InitializeComponent();
Person zhangSan = new Person();
InitPersonInfo(ref zhangSan);
}
#region // 結構體與 byte[] 互轉
// Struct 轉換爲 byte[]
public static byte[] StructToBytes(object structure)
{
int size = Marshal.SizeOf(structure);
IntPtr buffer = Marshal.AllocHGlobal(size);
try
{
Marshal.StructureToPtr(structure, buffer, false);
byte[] bytes = new byte[size];
Marshal.Copy(buffer, bytes, 0, size);
return bytes;
}
finally
{
Marshal.FreeHGlobal(buffer);
}
}
// byte[] 轉換爲 Struct
public static object BytesToStruct(byte[] bytes, Type strcutType)
{
int size = Marshal.SizeOf(strcutType);
IntPtr buffer = Marshal.AllocHGlobal(size);
try
{
Marshal.Copy(bytes, 0, buffer, size);
return Marshal.PtrToStructure(buffer, strcutType);
}
finally
{
Marshal.FreeHGlobal(buffer);
}
}
#endregion
}