调用SI_GetProductStringSafe成功却触发访问违例,如何从非托管void*取字符串?
解决SI_GetProductStringSafe调用的AccessViolationException问题
你的代码触发System.AccessViolationException的核心原因是对lpvDeviceString参数的声明完全错误:
非托管函数中,lpvDeviceString是调用者预先分配的缓冲区指针,函数会将产品字符串写入这个缓冲区;但你用了out IntPtr,相当于要求函数返回一个指针,这会导致函数尝试往未初始化的内存地址写入数据,直接触发内存访问冲突。虽然返回值显示SI_SUCCESS,但这只是函数执行逻辑层面的返回,内存操作已经违规。
修正步骤及代码
1. 修正DllImport参数声明
- 将
out IntPtr lvpDeviceString改为IntPtr lpvDeviceString(传入我们分配的缓冲区指针) - 匹配参数类型:
dwDeviceNum、dwFlags对应非托管DWORD,用C#uint更准确;DeviceStringLenInBytes对应size_t,用UIntPtr适配32/64位系统
2. 预先分配缓冲区并调用函数
提前分配足够大小的非托管缓冲区,传入函数后再从中读取字符串,最后释放缓冲区避免内存泄漏。
完整代码示例:
using System; using System.Runtime.InteropServices; class UsbDeviceTest { // 定义SI_STATUS枚举(根据SDK实际定义补充其他状态值) public enum SI_STATUS : uint { SI_SUCCESS = 0, SI_DEVICE_IO_FAILED = 1, SI_DEVICE_NOT_FOUND = 2 } [DllImport("SiUSBXp.dll", CharSet = CharSet.Auto, EntryPoint = "SI_GetProductStringSafe")] private static extern SI_STATUS SI_GetProductStringSafe( uint dwDeviceNum, IntPtr lpvDeviceString, UIntPtr deviceStringLenInBytes, uint dwFlags); static void Main(string[] args) { const int bufferSize = 100; // 缓冲区大小,需足够容纳产品字符串+结束符 IntPtr buffer = Marshal.AllocHGlobal(bufferSize); try { SI_STATUS result = SI_GetProductStringSafe(0, buffer, (UIntPtr)bufferSize, 0); if (result == SI_STATUS.SI_SUCCESS) { string productStr = Marshal.PtrToStringAuto(buffer); Console.WriteLine($"产品字符串: {productStr}"); } else { Console.WriteLine($"调用失败,错误码: {result}"); } } finally { // 必须释放非托管内存 Marshal.FreeHGlobal(buffer); } } }
额外说明
CharSet.Auto会自动适配系统的ANSI/Unicode编码,与非托管函数的字符串处理逻辑匹配;如果明确知道函数是宽字符版本,可指定CharSet.Unicode- 缓冲区大小需根据实际产品字符串长度设置,避免溢出;如果不确定长度,可先调用函数获取所需长度(参考AN169文档中的相关说明)
- 必须在
finally块中释放非托管缓冲区,防止内存泄漏
内容的提问来源于stack exchange,提问作者Piglet
相关产品推荐
相关产品推荐

