在 C# 中处理指向结构的指针

问题描述 投票:0回答:3

我正在开发一个项目,其中包括用 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# 中的结构不能简单地包含指针变量。我应该做些什么?最优雅的方式是什么?

c# c++ pointers memory dll
3个回答
6
投票

您可以使用 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! ***
}

6
投票

好吧,我重新找到了方法。

[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();

就像魅力一样。 :)


0
投票

对@BitBot 答案的补充。

如果您想获取结构体的修改值,则需要将其值复制回托管内存,如下所示:

var modifiedStruct = Marshal.PtrToStructure<MyStruct>(myStructPtr);
© www.soinside.com 2019 - 2024. All rights reserved.