4

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?

4
  • Are you expecting the objects that get modified in the C# code to also update the c++ code that are looking at the same pointers? Is it ok if you had to make a call like Marshal.StructureToPtr before the pointer object gets updated? Commented Nov 8, 2016 at 20:03
  • I'm expecting a way to change values of objects given by DLL directly in the memory, so the C++ code could keep working on these objects changed by C# code. It's like DLL gives pointers, C# is filling them with initial data and DLL works on it. Then C# code keeps reading data modified by DLL and drawing results on canvas. Commented Nov 8, 2016 at 20:14
  • Is it possible to make GetPointer a C# method so that the managed code is the one that creates the shared memory space? And/Or can you use a C++/Cli shim to help bridge the gap? Commented Nov 8, 2016 at 20:16
  • The C++ library is already written so it would be better if there's a solution which assumes allocating shared memory space by DLL and getting access to it in C#. Commented Nov 8, 2016 at 20:26

2 Answers 2

6

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! ***
}
Sign up to request clarification or add additional context in comments.

3 Comments

Thanks, that's the more elegant way, without using 'unsafe' block :)
I edited to show how to free the unmanaged memory after you are finished with it. This is a must, or you will get a memory leak!
Shared memory is free'd with usage of DLL's Clear() function :) But thanks anyway, that's pretty useful knowledge for the future :)
6

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. :)

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.