无法调用CsWin32生成的SetupDiGetDeviceRegistryProperty正确重载
问题
测试C#/Win32项目时,试图通过自动生成的PInvoke包装器调用Win32 Setup API的SetupDiGetDeviceRegistryProperty函数,但编译器始终匹配错误的重载,导致编译失败。
生成的两个SetupDiGetDeviceRegistryProperty重载如下(希望调用第一个“易用”重载):
[SupportedOSPlatform("windows5.0")] internal static unsafe winmdroot.Foundation.BOOL SetupDiGetDeviceRegistryProperty(SafeHandle DeviceInfoSet, in winmdroot.Devices.DeviceAndDriverInstallation.SP_DEVINFO_DATA DeviceInfoData, winmdroot.Devices.DeviceAndDriverInstallation.SETUP_DI_REGISTRY_PROPERTY Property, uint* PropertyRegDataType, Span<byte> PropertyBuffer, uint* RequiredSize) { // 生成代码省略,目标调用此重载 } [DllImport("SETUPAPI.dll", ExactSpelling = true, EntryPoint = "SetupDiGetDeviceRegistryPropertyW", SetLastError = true),DefaultDllImportSearchPaths(DllImportSearchPath.System32)] [SupportedOSPlatform("windows5.0")] internal static extern unsafe winmdroot.Foundation.BOOL SetupDiGetDeviceRegistryProperty(winmdroot.Devices.DeviceAndDriverInstallation.HDEVINFO DeviceInfoSet, winmdroot.Devices.DeviceAndDriverInstallation.SP_DEVINFO_DATA* DeviceInfoData, winmdroot.Devices.DeviceAndDriverInstallation.SETUP_DI_REGISTRY_PROPERTY Property, [Optional] uint* PropertyRegDataType, [Optional] byte* PropertyBuffer, uint PropertyBufferSize, [Optional] uint* RequiredSize);
测试代码中步骤3的调用始终匹配第二个重载,编译报错:
internal unsafe bool TryGetUsbDeviceRegistryString( string deviceInstance, SETUP_DI_REGISTRY_PROPERTY prop, out string name) { name = string.Empty; var guidDevinterfaceUsbDevice = new Guid("{A5DCBF10-6530-11D2-901F-00C04FB951ED}"); SETUP_DI_GET_CLASS_DEVS_FLAGS flags = SETUP_DI_GET_CLASS_DEVS_FLAGS.DIGCF_DEVICEINTERFACE | SETUP_DI_GET_CLASS_DEVS_FLAGS.DIGCF_PRESENT; // 获取DeviceInfoList的SafeHandle SetupDiDestroyDeviceInfoListSafeHandle info = PInvoke.SetupDiGetClassDevs(guidDevinterfaceUsbDevice, deviceInstance, HWND.Null, flags); uint dwSize = 0; var did = new SP_DEVINFO_DATA{ cbSize = (uint)Marshal.SizeOf<SP_DEVINFO_DATA>()}; uint memberIndex = 0; uint propType = 0; Span<byte> buffer = stackalloc byte[1]; // 遍历设备 while (PInvoke.SetupDiEnumDeviceInfo(info, memberIndex, ref did)) { // 尝试获取注册表属性所需的缓冲区大小 // *** 编译时匹配了错误的重载 *** PInvoke.SetupDiGetDeviceRegistryProperty(info, did, prop, ref propType, buffer, ref dwSize); // 后续代码省略... memberIndex++; } return false; }
编译错误信息:
Build started at 12:20 PM... 1>------ Build started: Project: TestPInvoke, Configuration: Debug x64 ------ 1>C:\Users\joe\source\repos\TestPInvoke\TestPInvoke\DeviceInfoSet.cs(39,50,39,54): error CS1503: Argument 1: cannot convert from 'Windows.Win32.SetupDiDestroyDeviceInfoListSafeHandle' to 'Windows.Win32.Devices.DeviceAndDriverInstallation.HDEVINFO' 1>C:\Users\joe\source\repos\TestPInvoke\TestPInvoke\DeviceInfoSet.cs(39,74,39,82): error CS1615: Argument 4 may not be passed with the 'ref' keyword 1>C:\Users\joe\source\repos\TestPInvoke\TestPInvoke\DeviceInfoSet.cs(39,90,39,96): error CS1503: Argument 6: cannot convert from 'System.Span<byte>' to 'uint' 1>C:\Users\joe\source\repos\TestPInvoke\TestPInvoke\DeviceInfoSet.cs(39,102,39,108): error CS1615: Argument 7 may not be passed with the 'ref' keyword 1>Done building project "TestPInvoke.csproj" -- FAILED. Build has been canceled. ========== Elapsed 00.993 seconds ==========
解决方法
问题核心是你传递的参数与目标重载的签名不匹配,导致编译器无法定位到第一个“易用”重载,转而匹配第二个原始重载。修正参数即可解决:
- 指针参数传递方式错误:目标重载的第4个参数(
PropertyRegDataType)和第6个参数(RequiredSize)类型是uint*,但你使用了ref关键字传递,不符合签名要求。在unsafe上下文里,应该用&运算符获取变量的指针。 - 第二个参数优化(可选):目标重载的第二个参数是
in SP_DEVINFO_DATA(只读引用),直接传递did虽然可以,但加上in关键字更贴合签名语义,避免不必要的值拷贝。
修正后的调用代码:
PInvoke.SetupDiGetDeviceRegistryProperty(info, in did, prop, &propType, buffer, &dwSize);
这样编译器就能正确匹配到第一个“易用”重载,解决所有编译错误。
内容的提问来源于stack exchange,提问作者Joe
相关产品推荐
相关产品推荐

