Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Handling pointers to structure in C#

I'm working on project which includes DLL written in C++ and a C# code. Lets say that that DLL has a function:

MyStruct* GetPointer(); // returns pointer to MyStruct structure

MyStruct structure looks like this:

struct MyStruct
{
    OtherStruct1 *data1;
    OtherStruct2 *data2;
};

And OtherStruct1 and OtherStruct2 structures look like this:

struct OtherStruct1
{
public:
    double x;
    char y;
};

struct OtherStruct2
{
public:
    float a;
    float b;
    float c;
    float d;
};

My question is - what's the best way to handle all of these pointers in a C# code? By "handling" i mean operations of reading from and writing to memory. Structures in C# can't simply contain pointer variables. What am I supposed to do? What's the most elegant way?

like image 589
Gont.M Avatar asked Jan 06 '23 02:01

Gont.M


2 Answers

Ok, I rezlized a way to do it.

[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;
};

And then:

unsafe
{
    MyStruct *tmp = (MyStruct*) GetPointer();
    tmp->data2[0].a = 1.0F;
}


[DllImport(DLL_PATH, CallingConvention = CallingConvention.Cdecl)]
    unsafe public static extern MyStruct* GetPointer();

Works like a charm. :)

like image 125
Gont.M Avatar answered Jan 07 '23 14:01

Gont.M


You can use the Microsoft (now open source) PInvoke Interop Assistant tool to convert your C/C++ code to C# or VB. Running your example code gives:

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

}

Replace "Unknown" in the DllImportAttribute with your dll name, and make sure it's referenced in the project. You should now be able to access your structs in managed code.

Then to read/write from/to memory you will need to use the methods in the System.Runtime.InteropServices.Marshal namespace. The following code snippet shows how to use your GetPointer function to get your unmanaged struct into a managed one:

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

And here is how you would pass a managed struct to an unmanaged method:

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! ***
}
like image 41
BitBot Avatar answered Jan 07 '23 16:01

BitBot