我正在开发一个项目,其中包括用 C++ 编写的 DLL 和 C# 代码。假设该 DLL 有一个函数:
MyStruct* GetPointer(); // returns pointer to MyStruct structure
MyStruct 结构如下所示:
struct MyStruct
{
OtherStruct1 *data1;
OtherStruct2 *data2;
};
OtherStruct1 和 OtherStruct2 结构如下所示:
struct OtherStruct1
{
public:
double x;
char y;
};
struct OtherStruct2
{
public:
float a;
float b;
float c;
float d;
};
我的问题是 - 在 C# 代码中处理所有这些指针的最佳方法是什么?我所说的“处理”是指读取和写入内存的操作。 C# 中的结构不能简单地包含指针变量。我应该做些什么?最优雅的方式是什么?
您可以使用 Microsoft(现已开源)PInvoke Interop Assistant 工具将 C/C++ 代码转换为 C# 或 VB。运行示例代码会给出:
[System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)]
public struct MyStruct {
/// OtherStruct1*
public System.IntPtr data1;
/// OtherStruct2*
public System.IntPtr data2;
}
[System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)]
public struct OtherStruct1 {
/// double
public double x;
/// char
public byte y;
}
[System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)]
public struct OtherStruct2 {
/// float
public float a;
/// float
public float b;
/// float
public float c;
/// float
public float d;
}
public partial class NativeMethods {
/// Return Type: MyStruct*
[System.Runtime.InteropServices.DllImportAttribute("<Unknown>", EntryPoint="GetPointer")]
public static extern System.IntPtr GetPointer() ;
}
将 DllImportAttribute 中的“Unknown”替换为您的 dll 名称,并确保在项目中引用它。您现在应该能够在托管代码中访问您的结构。
然后,要从内存中读取/写入内存,您将需要使用 System.Runtime.InteropServices.Marshal 命名空间中的方法。以下代码片段展示了如何使用 GetPointer 函数将非托管结构转换为托管结构:
IntPtr myPtr = NativeMethods.GetPointer(); // Call native code to retrieve pointer to unmanaged memory where the struct lives
MyStruct myStruct = new MyStruct(); // Create a new managed struct
myStruct = Marshal.PtrToStructure<MyStruct>(myPtr);
以下是将托管结构传递给非托管方法的方法:
MyStruct myStruct = new MyStruct(); // Create the managed struct
myStruct.data1 = new OtherStruct1(); // Create a managed OtherStruct1
myStruct.data2 = new OtherStruct2(); // Create a managed OtherStruct2
IntPtr myStructPtr = Marshal.AllocHGlobal(Marshal.SizeOf<MyStruct>()); // Allocate unmanaged memory for the struct
Marshal.StructureToPtr<MyStruct>(myStruct, myStructPtr, false);
try
{
NativeMethodThatUsesMyStructPtr(myStructPtr);
}
finally
{
Marshal.FreeHGlobal(myStructPtr); // *** You have to free unmanaged memory manually, or you will get a memory leak! ***
}
好吧,我重新找到了方法。
[StructLayout(LayoutKind.Sequential)]
struct MyStruct
{
OtherStruct1 *data1;
OtherStruct2 *data2;
};
[StructLayout(LayoutKind.Sequential)]
struct OtherStruct1
{
public:
double x;
char y;
};
[StructLayout(LayoutKind.Sequential)]
struct OtherStruct2
{
public:
float a;
float b;
float c;
float d;
};
然后:
unsafe
{
MyStruct *tmp = (MyStruct*) GetPointer();
tmp->data2[0].a = 1.0F;
}
[DllImport(DLL_PATH, CallingConvention = CallingConvention.Cdecl)]
unsafe public static extern MyStruct* GetPointer();
就像魅力一样。 :)
对@BitBot 答案的补充。
如果您想获取结构体的修改值,则需要将其值复制回托管内存,如下所示:
var modifiedStruct = Marshal.PtrToStructure<MyStruct>(myStructPtr);