Return 从 C++ DLL 到 C# 的字节数组
Return byte array from C++ DLL to C#
我正在实现一个 C++ DLL,它需要 read/write 数据到串行线路。
此 DLL 的用法是在 C# 应用程序中。
目前,我无法在使用 C++ 读取代码时从 C# 应用程序读取数据(没有 C# 包装器,读取功能可以正常工作)。
C++代码:
extern "C" __declspec(dllexport) int Read(void *Buffer, unsigned int MaxNbBytes, unsigned int TimeOut_ms)
{
return uart.Read(Buffer, MaxNbBytes, TimeOut_ms);
}
C#代码
[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);
在 运行 之后,我不断收到 System.AccessViolationException
。
我该如何解决这个问题?
备注:我不能使用 C# Serial class。我的 C++ 串行函数运行良好。
uart.Read(void *Buffer, unsigned int MaxNbBytes, unsigned int TimeOut_ms)
参考:
\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
错误是您使用了 out
关键字。如果您需要被调用方分配一个新数组并将 return 分配给您,就会使用它。这是一个额外的间接级别。
所以你可以使用下面的p/invoke:
[DllImport("RS232LIB.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int Read(byte[] bytesRead, uint maxNbBytes, uint timeOutMs);
这样称呼它:
var bytes = new byte[4];
Read(bytes, (uint)bytes.Length, timeOutMs);
注意 byte
是 blittable,所以 byte[]
是 blittable。这意味着该框架将简单地固定您的阵列。因此它编组为 [In,Out
]。如果你想更明确地说明你可以写的意图:
[DllImport("RS232LIB.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int Read([Out] byte[] bytesRead, uint maxNbBytes, uint timeOutMs);
但行为不会有任何不同。该数组仍将被固定,并且从语义上讲,该参数将为 [In,Out]
.
我还删除了不必要的 CharSet
规范并将其他两个参数更改为 uint
以匹配 unsigned int
。当然,使用 uint
可能会引入额外的强制转换,您可能会觉得这很烦人。为了方便起见,您可能会在 p/invoke 声明中坚持使用 int
。
我正在实现一个 C++ DLL,它需要 read/write 数据到串行线路。 此 DLL 的用法是在 C# 应用程序中。 目前,我无法在使用 C++ 读取代码时从 C# 应用程序读取数据(没有 C# 包装器,读取功能可以正常工作)。
C++代码:
extern "C" __declspec(dllexport) int Read(void *Buffer, unsigned int MaxNbBytes, unsigned int TimeOut_ms)
{
return uart.Read(Buffer, MaxNbBytes, TimeOut_ms);
}
C#代码
[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);
在 运行 之后,我不断收到 System.AccessViolationException
。
我该如何解决这个问题?
备注:我不能使用 C# Serial class。我的 C++ 串行函数运行良好。
uart.Read(void *Buffer, unsigned int MaxNbBytes, unsigned int TimeOut_ms)
参考:
\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
错误是您使用了 out
关键字。如果您需要被调用方分配一个新数组并将 return 分配给您,就会使用它。这是一个额外的间接级别。
所以你可以使用下面的p/invoke:
[DllImport("RS232LIB.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int Read(byte[] bytesRead, uint maxNbBytes, uint timeOutMs);
这样称呼它:
var bytes = new byte[4];
Read(bytes, (uint)bytes.Length, timeOutMs);
注意 byte
是 blittable,所以 byte[]
是 blittable。这意味着该框架将简单地固定您的阵列。因此它编组为 [In,Out
]。如果你想更明确地说明你可以写的意图:
[DllImport("RS232LIB.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int Read([Out] byte[] bytesRead, uint maxNbBytes, uint timeOutMs);
但行为不会有任何不同。该数组仍将被固定,并且从语义上讲,该参数将为 [In,Out]
.
我还删除了不必要的 CharSet
规范并将其他两个参数更改为 uint
以匹配 unsigned int
。当然,使用 uint
可能会引入额外的强制转换,您可能会觉得这很烦人。为了方便起见,您可能会在 p/invoke 声明中坚持使用 int
。