4

I have some C++ dll with struct description and some methods:

struct MSG_STRUCT {  
    unsigned long dataSize;
    unsigned char* data;
}

And function for example:

unsigned long ReadMsg( unsigned long msgId, MSG_STRUCT* readMsg)
{
    readMsg->dataSize = someDataSize;
    readMsg->data = someData;
}

So I want to call this function from C#:

[StructLayout(LayoutKind.Sequential)]
struct MSG_STRUCT 
{
    UInt32 dataSize;
    byte[] data;
}

[DllImport("mydll.dll")]
public static Int32 ReadMsg( UInt32 msgId, ref MSG_STRUCT readMsg);

So I tried to call C# function like:

var readMsg = new MSG_STRUCT();
readMsg.data = new byte[4128];
Int32 res = ReadMsg( someMsgId, ref readMsg);

But I didn't get smth normal in data. I also tried to call ReadMsg with IntPrt type parameter, but Marshal.PtrToStructure gave me AccessViolationException sometimes.

I don't have any ideas how to pass a pointer to MSG_STRUCT from C# to C++ and receive the result as filled MSG_STRUCT.data

The final solutionthat worked for me: I used a part of solution offered by xanatos: I set CallingConvention = CallingConvention.Cdecl for my DllImport function. I found out that I also need to change:

[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4128)]
public byte[] Data;

Thanks everyone for your help

2
  • "But i didn't get smth normal in data." What did you get, what were you expecting? Is data supposed to be a string or a byte array? Commented Mar 18, 2016 at 8:49
  • Data is supposed to be a byte array. Commented Mar 18, 2016 at 11:13

2 Answers 2

3

You could try with:

[StructLayout(LayoutKind.Sequential)]
public struct MSG_STRUCT 
{
    int dataSize;
    IntPtr data;

    public byte[] GetData() 
    {
        var bytes = new byte[dataSize];
        Marshal.Copy(data, bytes, 0, dataSize);
        return bytes;
    }
}

[DllImport("NativeLibrary.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern uint ReadMsg(uint msgId, ref MSG_STRUCT readMsg);

and then:

MSG_STRUCT msg = new MSG_STRUCT();
uint res = ReadMsg(123, ref msg);
byte[] bytes = msg.GetData();

Your C function is reassigning the data pointer, so you have to marshal it back to C#. The easiest way (for me) is to simply pass a IntPtr and do some explicit Marshal.Copy(...).

An alternative is to have data a byte[], but then in C-side you have to memcpy(readMsg->data, someData, someDataSize) instead of simply assigning readMsg->data = someData.

Sign up to request clarification or add additional context in comments.

Comments

2

Try to change attribute from

[StructLayout(LayoutKind.Sequential)]

to

[StructLayout(LayoutKind.Sequential, Pack=X)]

Where X is 1,2,4,8 ..

Default packing in c++ is 8, so try to set Pack = 8

1 Comment

I have edited my answer. It may depend on different packing of structures in C++ and C#.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.