tags:

views:

37

answers:

1

I have two C++ structures that I have to send as parameters when calling a DLL method from C#.

For example, lets define them as:

struct A
{
    int data;
}

struct B
{
    int MoreData;
    A * SomeData;
}

A method that I need to call from C# has the following signature:

int operation (B * data);

(Please note that I do not have the control over these C++ structures nor methods. )

In C# I define these structures as classes:

[StructLayout(LayoutKind.Sequential)]
class A
{
    public int data;
}

[StructLayout(LayoutKind.Sequential)]
class B
{
    public int MoreData;

    [MarshalAs(UnmanagedType.Struct)]
    public A SomeData;
}

I have created a "debugging dll" to call from C# in order to ensure all the data is received correctly in C++ methods. So far only the data that comes before the nested structure pointer is sent correctly.

When I try to read data from the nested structure (B->A->data) I get a read violation error (AccessViolationException).

How can I marshal the nested structure so I will be able to read it in the C++ method?

+2  A: 

Your C# declaration is not equivalent, it generates the SomeData field inline. In other words, the equivalent native declaration would be:

struct B
{
    int MoreData;
    A SomeData;
}

The P/Invoke marshaller cannot deal with a member that's a pointer. It is a memory management problem, it is very murky who owns the pointer and is responsible for deleting it. To make this work at all, you have to declare the structure like this:

    struct B {
        public int MoreData;
        public IntPtr SomeData;
    }

And marshal it yourself. Like this:

        var b = new B();
        b.MoreData = 0x12345678;
        var a = new A();
        a.Data = 0x789abcde;
        int len = Marshal.SizeOf(a);
        b.SomeData = Marshal.AllocCoTaskMem(len);
        try {
            Marshal.StructureToPtr(a, b.SomeData, false);
            someFunction(ref b);
        }
        finally {
            Marshal.FreeCoTaskMem(b.SomeData);
        }

Implicit in this code is that the pointer is owned by the managed code and that it releases the memory (FreeCoTaskMem call). If the native code copies the passed structure then this will be a problem, it is going to read bad data or bomb when it tries to dereference the pointer. Not freeing the memory isn't an option either, that produces an unpluggable memory leak. Because the native code cannot use the free() function to release it. If you end up in that mine field then you will have to write a wrapper in the C++/CLI language so that you can use the CRT's malloc() function.

Hans Passant
This came moments too late, just finished marshaling it myself. Still, this is the correct answer. Thank you anyway!
Rekreativc