获取设备deviceId时StringBuff参数溢出
思路:
用SetupDiGetDeviceRegistryProperty函数获取已打开设备的deviceId,然后比较是否含有指定pid,vid,然后再获取该设备的firendlyName
函数:
<DllImport("setupapi.dll", CharSet:=CharSet.Auto, SetLastError:=True)> _
Private Shared Function SetupDiGetDeviceRegistryProperty( _
ByVal lpInfoSet As IntPtr _
, ByVal DeviceInfoData As SP_DEVINFO_DATA _
, ByVal MyProperty As UInt32 _
, ByVal PropertyRegDataType As UInt32 _
, ByVal PropertyBuffer As StringBuilder _
, ByVal PropertyBufferSize As UInt32 _
, ByVal RequiredSize As IntPtr _
) As Boolean
End Function
该函数的第五个参数PropertyBuffer原本是这样:
Dim szDeviceID As New StringBuilder("")
szDeviceID.Capacity = 60
当前系统中符合该设备类Guid的设备有2个,依次获取,第一次获取到deviceId="ACPI\PNP0501",不符合pid|Vid(但是获取正常,此时szDeviceID长度为 12)
第二次获取出错,提示为第五个参数溢出:
未处理IndexOutOfRangeException
警告: 非托管代码已经在 StringBuilder 缓冲区溢出。进程可能变得不稳定。在进行封送处理之前,未给 StringBuilder 分配足够的容量。
事后,依次改动szDeviceID.Capacity直至实验至500,才发现可以正常获取,读到deviceId="USB\Vid_10C4&Pid_ea60&Rev_0100&Mi_00&0s_NT"
此时szDeviceID长度为 42,Capacity为500
问题:小弟不明白,这个StringBuff用了才42,原本Capacity设成60,怎么就出错了呢?
改到500才可以,非常不明白啊?????
[最优解释]
该回复于2012-03-11 09:04:48被版主删除
[其他解释]
而且这个错误不是setupapi.dll抛出的吧,不然应该是获取不成功,返回一个false,我在用getLastError去查看的
这个貌似是.net运行时编译器的毛病?
[其他解释]
找到一些资料,这个问题应该是CLR对StringBuilder在托管代码与非托管代码之间的封送处理弄得,网上看的建议都是将其容量设的足够大。。。这不是浪费资源么。。。
附上一些资料:
托管代码与非托管代码之间StringBuilder 和封送处理
CLR 封送拆收器具有内置的 StringBuilder 类型知识,并且处理它的方式与处理其他类型不同。默认情况下,StringBuilder 作为 [InAttribute, OutAttribute] 传递。StringBuilder 很特别,因为具有 Capacity 属性(该属性可以在运行时确定必需缓冲区的大小),并且它可被动态地更改。因此,在封送过程中,CLR 可以固定 StringBuilder,直接传递在 StringBuilder 中使用的内部缓冲区的地址,并允许适当的本机代码更改该缓冲区的内容。
为了充分利用 StringBuilder,您将需要遵循下列所有规则:
不要通过引用传递 StringBuilder(使用 out 或 ref)。否则,CLR 会认为该参数的签名是 wchar_t **,而不是 wchar_t *,并且它将无法固定 StringBuilder 的内部缓冲区。性能会大大降低。
当非托管代码使用 Unicode 时使用 StringBuilder。否则,CLR 将不得不复制该字符串,并将它在 Unicode 和 ANSI 之间转换,这样会降低性能。通常情况下,您应将 StringBuilder 作为 Unicode 字符的 LPARRAY 或作为 LPWSTR 封送。
始终提前指定 StringBuilder 的容量,并确保该容量对存放缓冲区而言足够大。在非托管代码端的最佳做法是接受字符串缓冲区的大小作为参数,以避免缓冲区溢出。在 COM 中,您还可以使用 IDL 中的 size_is 来指定大小。
[其他解释]
感情我送分来了。