【问题标题】:unsigned char* causes Access Violation Exceptionunsigned char* 导致访问冲突异常
【发布时间】:2016-04-05 15:10:54
【问题描述】:

我正在开发一个包装库,允许我的项目在任何 CPU 环境中使用 x86 C++ dll 库,我无法控制 dll,因此我在 C# 中使用 DllImport。

在 C++ 中声明了一个提供的函数: int __stdcall Func(int V, unsigned char *A) 并在VB中提供了一个示例声明:Private Declare Function Func Lib "lib.dll" Alias "_Func@8" (ByVal V As Long, A As Any) As Long

此函数将请求设备通过将 Convert.ToInt64(decimalValue) 作为 V 传递给卡/从卡中添加/减去一个值,并在 A 中传递一些自定义信息。

这里是A的描述:

  • 它是一个包含 7 个字节的字节指针。
  • 前 5 个字节用于存储将传递到卡日志的信息(收据编号的后 4 位应包含在前 2 个字节中,其他 3 个可能是 A3A4A5)
  • 最后 2 个字节用于存储将传递给设备的信息(收据编号的最后 4 位)
  • 返回时,A 包含一个 32 字节的数据。

经过数小时的研究和尝试,除了“访问冲突异常”之外,我无法得出其他结果。请参阅以下代码草案:

[DllImport("lib.dll", EntryPoint="_Func@8")]
public static external Int64 Func(Int64 V, StringBuilder sb);

string ReceiptNum = "ABC1234";
decimal Amount = 10m;
byte[] A = new byte[32];
A[0] = Convert.ToByte(ReceiptNum.Substring(3, 2));
A[1] = Convert.ToByte(ReceiptNum.Substring(5));
A[2] = Convert.ToByte("A3");
A[3] = Convert.ToByte("A4");
A[4] = Convert.ToByte("A5");
A[5] = Convert.ToByte(ReceiptNum.Substring(3, 2));
A[6] = Convert.ToByte(ReceiptNum.Substring(5));
StringBuilder sb = new StringBuilder(
    new ASCIIEncoding().GetString(A), A.Length
);
Int64 Result = Func(Convert.ToInt64(Amount), sb);

此时它会抛出异常。我尝试过传递 IntPtr、byte*、byte (by A[0])、byval、byref,但它们都不起作用。 (也尝试部署为 x86 CPU)

不胜感激!感谢您的宝贵时间!


PS - 使用 StringBuilder 的原因是该库包含一个接受“char *Data”参数的函数,导致相同的异常,解决方案是使用 StringBuilder 作为指针传递,该函数的 VB 声明为:Private声明 Function Func1 Lib "lib.dll" Alias "_Func1@12(ByVal c As Byte, ByVal o As Byte, ByVal Data As String) As Long

【问题讨论】:

  • 请记住,ASCIIEncoding 仅支持 7 位字符,因此它不会返回十六进制值 0xA30xA40xA5Convert.ToByte(string) 也只处理十进制数字 0-9; Convert.ToByte("A3") 将抛出 FormatException。所以我不知道你是怎么打电话给Func()的。
  • 嗨,迈克尔!由于我现在无法连接到我的办公室开发服务器,所以我只是从内存中编写代码,将尝试 Luaan 解决方案 tmr:)

标签: c# c++ .net dll access-violation


【解决方案1】:

你的外部定义是错误的。

StringBuilder 是一个包含 c# 字符数组的复杂结构。

c# 字符是 utf-16(具有复杂规则的双字节,用于解码 unicode 多字符字符)。可能不是您想要的。

如果您的数据是原始字节缓冲区,您应该选择 byte[]

Int64 也是 c# long。

【讨论】:

    【解决方案2】:

    好吧,您的本机方法签名采用int,而您正试图传递long long。那是行不通的,很明显。返回值也是如此。不要假设 VB 清楚地映射到 VB.NET,更不用说 C# - Long 在 VB 中表示 32 位整数,但在 .NET 中不是。原生代码是一个非常复杂的环境,在尝试与原生代码交互时,您最好知道自己在做什么。

    StringBuilder 只能用于字符数据。那不是你的情况,你应该改用byte[]。无论您在做什么有趣的事情,您都在尝试传递无效的 unicode 数据而不是原始字节。混淆可能是因为 C 不区分 byte[]string - 通常都表示为 char*

    此外,我不明白您希望这个包装器如何在 AnyCPU 环境中工作。如果本机 DLL 是 32 位的,则只能从 32 位进程中使用它。 AnyCPU 不是魔术,它只是将位数的决定推迟到运行时,而不是编译时。

    【讨论】:

    • 嗨 Luaan,非常感谢您的帮助!我做了一个快速的研究,发现 VB 中的 Long 意味着 Int32,所以我想我必须对 C++ 中标记为 int 的所有变量使用 Int32,对吧?而且由于 VB 将 ByRef 视为默认的参数传递方法,因此我也应该将 ref 添加到外部函数中。我手头没有设备,所以请允许我尽快回复您,再次感谢!
    • @ZayLau 它比这更复杂,但我认为您现在可以放心地忽略这些差异 - 假设 int 映射到 int 应该足够好。请注意,无论如何,第一个参数在 VB 中是通过 ByVal 传递的,所以没有 ref。第二个参数将被编组,它是一个数组,而不是指向数组的指针,所以再一次,没有ref..
    • @ZayLau:“Long”在 VB6 中是 32 位。在现代版本的 VB 中,它是 64 位的。
    猜你喜欢
    • 2018-12-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-11-05
    • 1970-01-01
    相关资源
    最近更新 更多