2

原始的 VB.net,运行良好:

Declare Function HolderName Lib "myCard.dll" (ByVal buf As String) As Integer
Declare Function Photo Lib "myCard.dll" (ByRef photo As Byte) As Integer

...

buff = Space(200) : res = HolderName(buff)
ShowMsg("HolderName():" & IIf(res = 0, "OK:" & Trim(buff), "FAIL"))

photobuf = New Byte(4096) {}
res = Photo(photobuf(0))
ShowMsg("Photo():" & IIf(res = 0, "OK", "FAIL"))
If res = 0 Then
    Dim ms As New MemoryStream(photobuf)
    picImage.Image = Image.FromStream(ms)
End If

转换后的代码现在在 C# 中(使用http://converter.telerik.com/

using System.Runtime.InteropServices;

...

[DllImport("myCard.dll", CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
public static extern int HolderName(String dBuff);

[DllImport("myCard.dll", CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
public static extern int Photo(ref byte photo);

//...

buff = new String(' ', 200);
res = HolderName(buff);
// buff remains UNALTERED!
ShowMsg("HolderName():" + (res == 0 ? "OK:" + Strings.Trim(buff) : "FAIL"));

photobuf = new byte[4096];
res = Photo(ref photobuf[0]);
ShowMsg("Photo():" + (res == 0 ? "OK" : "FAIL"));

// photobuf successfully receives the data bytes from Photo function
if (res == 0)
{
    MemoryStream ms = new MemoryStream(photobuf);
    picImage.Image = Image.FromStream(ms);
}

buff即使该HolderName函数实际上返回了一些值(使用 USB 监视器观察),问题仍然没有改变。为什么会发生这种情况,我该如何解决?

4

2 回答 2

4

看起来这段代码是在 VB6 中开始的。它可以用那种语言工作,但今天有严重的麻烦。两个 [DllImport] 声明都是错误的。

您让本机 HolderName() 函数修改字符串的内容。这是非法的,.NET 中的字符串是不可变的。您必须改为将参数声明为 StringBuilder。并在您拨打电话之前将其容量设置得足够高。请注意,这是一个有风险的功能,您不能告诉它避免写入超出分配的容量。

当它这样做时,它会破坏 GC 堆,这是一个非常讨厌的调试问题。

Photo() 的问题在于它的参数实际上是byte[],没有 ref。倾向于意外工作,这是一个非常常见的意外。但是最大的问题是 pinvoke marshaller 不知道数组需要被固定。当 Photo() 运行时垃圾收集器运行时,它会猛拉地板垫并将阵列移动到其他地方。Photo() 不知道并继续使用旧地址。

当它这样做时,它会破坏 GC 堆,这是一个非常讨厌的调试问题。

否则没有什么奇妙的理由为什么它可以在 VB.NET 中工作但不能在 C# 中工作。我怀疑框架更改是更好的解释。但一个必要的出发点当然是消除错误,这类腐败问题很难推理。

于 2015-09-16T13:36:42.330 回答
-1

最后,我回答了我自己的问题。StringBuilder 就是答案!这是代码...

        [DllImport("mykaddll.dll", CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
        public static extern int HolderName([Out] StringBuilder dBuff);

        //....

        StringBuilder sbBuff = new StringBuilder(200);
        res = HolderName(sbBuff);
        buf = sbBuff.ToString().Trim();
        ShowMsg("HolderName():" + (res == 0 ? "OK:" + buf : "FAIL"));
于 2015-09-16T15:42:08.263 回答