3

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

6
  • I don't think that's your problem, but in the C++ part you use unsigned int - shouldn't the C# counterpart then use uint? Commented Mar 15, 2018 at 8:30
  • This is not the issue here. Even when I'm setting hard codded values to the length ans timeout I've got this exception. Commented Mar 15, 2018 at 8:33
  • I think, you will find the answer to this post helpful: stackoverflow.com/questions/15862983/… Commented Mar 15, 2018 at 8:39
  • 2
    That C++ function is written properly, it does not actually return an array. It uses the array you pass and fills it with bytes. You must delete out. Pass bytes.Length as the 2nd argument so you can never get it wrong. And don't ignore the return value, it probably tells you how many bytes it copied into your array. Commented Mar 15, 2018 at 8:43
  • After reading your comments i changed the uint signature to int and removed the out . There is no exception but i'm getting garbage data from the line. Commented Mar 15, 2018 at 8:47

1 Answer 1

7

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.

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

3 Comments

David, i tried your soulution. Currenly i'm still getting the same 4 bytes of junk data. While reading more then 4 bytes the rest of them are 0x00.
Presumably that's just what the DLL is sending you. This question concerns the marshalling, and I have answered that accurately. You will be able to verify that by writing a simple DLL with the same function signature, that populates the array with known values. When you verify that those values turn up in your C# program you will know that the marhsalling is correct and that the issue lies elsewhere. This really is Debugging 101. Narrow the problem down until the issue becomes obvious.
Your soulutions works. My problem was in the serial device.

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.