处理c#中指向结构的指针

Handling pointers to structure in C#

本文关键字:结构 指针 处理      更新时间:2023-10-16

我正在做一个项目,其中包括用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互操作助手工具将您的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();

很有魅力。:)