Reputation: 109
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
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