2

I am working with a C DLL which accepts parameters of the form of nested pointed structures. It's simplified C# form is something like this:

struct Point
{
    public double X;
    public double Y;
}

struct Rectangle
{
    public unsafe Point* LowLeft;
    public unsafe Point* TopRight;
}

The question arises when I want to instantiate struct Rectangle. How can I create an instance of Point on the heap and then assign its address to LowLeft and TopRight fields?

Some wrong ways:

  • Using C# new keyword directly is syntactically wrong:

    r.LowLeft = new Point();

  • Using a dummy variable seems to be wrong because it is allocated on the heap and thus is freed when we leave the scope:

    var dummy = new Point(); r.LowLeft = @dummy;

melmi
  • 988
  • 3
  • 9
  • 27
  • Possible duplicate of https://stackoverflow.com/questions/4853213/are-structs-always-stack-allocated-or-sometimes-heap-allocated – Dan Rayson Aug 08 '17 at 13:38
  • @Sinatr This question is not the same of what you mentioned. – melmi Aug 08 '17 at 13:44
  • @DanRayson This question is related but not the same. I know where a structure might be allocated. But this is not the point. What I exactly want is a pointer to that structure. – melmi Aug 08 '17 at 13:45
  • 1
    I found `Marshal.StructureToPtr`. Using this guy we should get memory previously. – melmi Aug 08 '17 at 13:48
  • My bad, that question was regarding receiving `struct`, not passing. See [msdn](https://learn.microsoft.com/en-us/dotnet/framework/interop/passing-structures), the idea is - you don't care, just create proper structure and the rest is a marshal job. – Sinatr Aug 08 '17 at 13:49
  • @Sinatr You are right. Actually, my mistake was that I was seeking for a one line command to do that. But in a managed environment we should work more. Anyway, I am working on it. – melmi Aug 08 '17 at 14:08

1 Answers1

2

Going off what M.Elmi said in the comments, I had a go at putting a struct into unmanaged memory. Unfortunately I don't know how to check that it's on the heap or stack or whatnot, but it's certainly sat in unmanaged memory now...

    static void Main(string[] args)
    {
        SuperStruct myStruct = new SuperStruct() { MyName = "Dan", MyNumber = 1 };
        SuperStruct myOtherStruct;

        IntPtr pointer = Marshal.AllocHGlobal(Marshal.SizeOf<SuperStruct>());

        Marshal.StructureToPtr(myStruct, pointer, false);

        myOtherStruct = Marshal.PtrToStructure<SuperStruct>(pointer);

        Console.WriteLine($"Name: {myOtherStruct.MyName}, Number: {myOtherStruct.MyNumber}.");
        Console.ReadLine();
    }
    struct SuperStruct
    {
        public int MyNumber;
        public string MyName;
    }

Overall, the process is: 1) Set up some unmanaged memory ready to contain the data by using Marshal.AllocHGlobal. 2) Set that memory using the Marshal.StructureToPtr method. 3) Retreive that struct from unmanaged memory using Marshal.PtrToStructure<T> method.

It's also been pointed out (by M.Elmi in the comments) that we could declare a typed pointer, as long as there isn't an managed type included in the struct definition, like this:

        unsafe
        {
            SuperStruct* ptr = (SuperStruct*)pointer.ToPointer();
        }

In my specific example, the string field prevents you from using this declaration, but it's worth putting here for posterior.

Dan Rayson
  • 1,315
  • 1
  • 14
  • 37
  • Please add that the pointer to structure can simply be constructed by `SuperStruct* ptr = (SuperStruct *)pointer.ToPointer();` – melmi Aug 08 '17 at 14:16
  • @M.Elmi When I add `SuperStruct*` somewhere, it gives me a compiler error of `Cannot take the address of, get the size of, or declare a pointer to a managed type ('Program.SuperStruct')` It won't even compile with unsafe flag :( – Dan Rayson Aug 08 '17 at 14:21
  • 1
    IMHO this is because of the `string` field in your `struct`. `string`s are managed classes and cause problems when going into `unsafe` field. – melmi Aug 08 '17 at 18:28