【发布时间】:2017-12-17 06:17:00
【问题描述】:
我为 QuickUsb 编写了一个包装类来访问 c# 中的非托管库。有关完整的实现,请参阅gist。
这个问题的主要兴趣点是以下部分:
public class QuickUsbPort
{
private class SafeQuickUsbHandle : SafeHandleZeroOrMinusOneIsInvalid
{
[DllImport("QuickUsb.dll", CharSet = CharSet.Ansi)] static extern
int QuickUsbClose(IntPtr handle);
public SafeQuickUsbHandle(IntPtr handle) : base(true)
{
SetHandle(handle);
}
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
protected override bool ReleaseHandle()
{
return QuickUsbClose(handle) != 0;
}
}
private static class NativeLib
{
[DllImport("QuickUsb.dll", CharSet = CharSet.Ansi)] static extern
int QuickUsbOpen(out SafeQuickUsbHandle handle, string deviceName);
public static SafeQuickUsbHandle Open(string deviceName)
{
if (QuickUsbOpen(out SafeQuickUsbHandle handle, deviceName) == 0)
{
throw new QuickUsbException("Open", new List<Tuple<string, string>>
{
new Tuple<string, string>("deviceName", deviceName),
});
}
return handle;
}
}
}
在调用Open() 时,将handle 编组为SafeQuickUsbHandle 似乎存在问题,此代码会引发MissingMethodException。但是,以下修改不会引发此类异常:
[DllImport("QuickUsb.dll", CharSet = CharSet.Ansi)] static extern
int QuickUsbOpen(out IntPtr handle, string deviceName);
public static SafeQuickUsbHandle Open(string deviceName)
{
if (QuickUsbOpen(out IntPtr handle, deviceName) == 0)
{
throw new QuickUsbException("Open", new List<Tuple<string, string>>
{
new Tuple<string, string>("deviceName", deviceName),
});
}
return new SafeQuickUsbHandle(handle);
}
所以我想知道我是否遗漏了 SafeQuickUsbHandle 实现的某些方面,以允许 c# 正确编组和处置句柄。
请注意,在 dll 中,handle 是指向句柄的指针:
/// <param name="handle">
/// A PQHANDLE that points to a QHANDLE in which to place the new device ID.
/// If successful, hDevice will contain the new QHANDLE</param>
【问题讨论】:
-
任何变量都需要内存。调用方法(或 dll)时,参数列表被放入执行堆栈,当函数返回时,执行堆栈被释放。因此,您丢失了任何返回变量,因为该变量在堆栈上。因此,任何 OUT 声明都必须在调用方法中分配内存。我想查看 c 语言代码,因为我怀疑您使用的是指针,这就是代码使用第二种方法的原因。
-
@jdweng 我无权访问 c 代码,但是是的,它是一个指针。 QuickUsbOpen 创建一个句柄并将其作为输出参数而不是返回值返回。返回值表示成功。通过将 typename 放在函数调用中是预先定义句柄然后将其传递给函数以便它在堆栈上的简写。也许这就是问题所在。除了我做的以外,也许没有办法解决这个问题