【问题标题】:Return byte array from C++ DLL to C#将字节数组从 C++ DLL 返回到 C#
【发布时间】:2018-03-15 08:24:59
【问题描述】:

我正在实现一个需要将数据读/写到串行线路的 C++ DLL。 此 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 类。我的 C++ 串行函数运行良好。

uart.Read(void *Buffer, unsigned int MaxNbBytes, unsigned int TimeOut_ms) 参考:

\Buffer : 从串行设备读取的字节数组
\MaxNbBytes : 允许读取的最大字节数
\TimeOut_ms : 放弃读取前的超时延迟

【问题讨论】:

  • 我不认为这是你的问题,但在 C++ 部分你使用unsigned int - C# 对应部分不应该使用uint?
  • 这不是这里的问题。即使我将硬编码值设置为长度和超时,我也遇到了这个异常。
  • 我想,你会发现这篇文章的答案很有帮助:stackoverflow.com/questions/15862983/…
  • 那个 C++ 函数写得很好,它实际上并没有返回一个数组。它使用您传递的数组并用字节填充它。您必须删除out。传递 bytes.Length 作为第二个参数,这样你就永远不会出错。并且不要忽略返回值,它可能会告诉您它复制到数组中的字节数。
  • 阅读您的 cmets 后,我将 uint 签名更改为 int 并删除了 out 。也不例外,但我正在从线路中获取垃圾数据。

标签: c# c++ dllimport dllexport


【解决方案1】:

错误是您使用了out 关键字。如果您需要被调用者分配一个新数组并将其返回给您,则可以使用该方法。这是一个额外的间接级别。

所以你可以使用下面的 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。

【讨论】:

  • 大卫,我试过你的解决方案。目前我仍然得到相同的 4 字节垃圾数据。读取超过 4 个字节时,其余为 0x00。
  • 大概这正是 DLL 发送给您的内容。这个问题与编组有关,我已经准确地回答了这个问题。您将能够通过编写具有相同函数签名的简单 DLL 来验证这一点,该 DLL 将使用已知值填充数组。当您验证这些值是否出现在您的 C# 程序中时,您将知道编组是正确的并且问题出在其他地方。这真的是调试 101。缩小问题范围,直到问题变得明显。
  • 您的解决方案有效。我的问题出在串行设备上。
猜你喜欢
  • 1970-01-01
  • 2013-01-16
  • 2018-11-07
  • 2017-06-28
  • 2012-04-22
  • 1970-01-01
  • 2013-05-03
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多