gr1d3r
gr1d3r

Reputation: 109

Return byte array from C++ DLL to C#

I'm implementing a C++ DLL that needs to read/write data to the serial line. The usage of this DLL is in a C# application. Currently I can't manage to read data from the C# application while i'm using the C++ read code (without the C# wrapper the read function works properly).

C++ code:

extern "C" __declspec(dllexport) int Read(void *Buffer, unsigned int MaxNbBytes, unsigned int TimeOut_ms)
{
    return uart.Read(Buffer, MaxNbBytes, TimeOut_ms);
}

C# Code

[DllImport("RS232LIB.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)]
public static extern int Read(out byte[] bytesRead, int maxNbBytes, int timeOutMs);

var bytes = new byte[4];
Read(out bytes, 4, 10);

After running those line i keeps getting the System.AccessViolationException. How can I solve this issue?

Remark: I can't use the C# Serial class. My C++ serial function works well.

The uart.Read(void *Buffer, unsigned int MaxNbBytes, unsigned int TimeOut_ms) reference:

\Buffer : array of bytes read from the serial device
\MaxNbBytes : maximum allowed number of bytes read
\TimeOut_ms : delay of timeout before giving up the reading

Upvotes: 3

Views: 7015

Answers (1)

David Heffernan
David Heffernan

Reputation: 612983

The mistake is your use of the out keyword. That would be used if you needed the callee to allocate a new array and return it to you. That is an extra level of indirection.

So you can use the following p/invoke:

[DllImport("RS232LIB.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int Read(byte[] bytesRead, uint maxNbBytes, uint timeOutMs);

Call it like this:

var bytes = new byte[4];
Read(bytes, (uint)bytes.Length, timeOutMs);

Note that byte is blittable, and so byte[] is blittable. This means that the framework will simply pin your array. Consequently it marshals as [In,Out]. If you wanted to be more explicit about the intent you could write:

[DllImport("RS232LIB.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int Read([Out] byte[] bytesRead, uint maxNbBytes, uint timeOutMs);

But the behaviour would not be any different. The array will still be pinned and semantically the argument will be [In,Out].

I also removed the needless CharSet specification and changed the other two arguments to uint to match unsigned int. Of course, using uint may introduce extra casts that you may find irksome. You would likely be forgiven for sticking to int in the p/invoke declaration for the sake of convenience.

Upvotes: 7

Related Questions