調用 SetupDiEnumDeviceInfo 等 SetupAPI 函數時,XP 系統正常,但 Win7 及以上系統返回失敗、數據異常或拋出異常。
// 錯誤簽名:SP_DEVINFO_DATA 結構體大小未正確初始化
[DllImport("setupapi.dll", SetLastError = true)]
public static extern bool SetupDiEnumDeviceInfo(IntPtr lpInfoSet, uint dwIndex, SP_DEVINFO_DATA devInfoData);
// 正確簽名:必須傳遞指標,且結構體需設定 cbSize
[DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Auto)]
public static extern bool SetupDiEnumDeviceInfo(
IntPtr DeviceInfoSet,
uint MemberIndex,
ref SP_DEVINFO_DATA DeviceInfoData); // 注意:ref 關鍵字
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct SP_DEVINFO_DATA
{
public uint cbSize; // 必須 = Marshal.SizeOf(typeof(SP_DEVINFO_DATA))
public Guid ClassGuid;
public uint DevInst;
public IntPtr Reserved;
}
// 使用前必須初始化
SP_DEVINFO_DATA data = new SP_DEVINFO_DATA();
data.cbSize = (uint)Marshal.SizeOf(typeof(SP_DEVINFO_DATA));
枚舉設備資訊、安裝驅動需要管理員權限。
using System;
using System.Runtime.InteropServices;
public static class SetupApi
{
[DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Auto)]
public static extern IntPtr SetupDiGetClassDevs(
ref Guid ClassGuid,
string Enumerator,
IntPtr hwndParent,
uint Flags);
[DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Auto)]
public static extern bool SetupDiEnumDeviceInfo(
IntPtr DeviceInfoSet,
uint MemberIndex,
ref SP_DEVINFO_DATA DeviceInfoData);
[DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Auto)]
public static extern bool SetupDiDestroyDeviceInfoList(IntPtr DeviceInfoSet);
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct SP_DEVINFO_DATA
{
public uint cbSize;
public Guid ClassGuid;
public uint DevInst;
public IntPtr Reserved;
}
public const uint DIGCF_PRESENT = 0x02;
public const uint DIGCF_ALLCLASSES = 0x04;
}
public static void EnumerateDevices()
{
Guid guid = Guid.Empty; // GUID_DEVCLASS_ALL
IntPtr hDevInfo = SetupApi.SetupDiGetClassDevs(
ref guid, null, IntPtr.Zero,
SetupApi.DIGCF_PRESENT | SetupApi.DIGCF_ALLCLASSES);
if (hDevInfo == IntPtr.Zero)
throw new System.ComponentModel.Win32Exception();
try
{
var deviceData = new SetupApi.SP_DEVINFO_DATA();
deviceData.cbSize = (uint)Marshal.SizeOf(deviceData);
for (uint i = 0; SetupApi.SetupDiEnumDeviceInfo(hDevInfo, i, ref deviceData); i++)
{
Console.WriteLine($"Device Instance: {deviceData.DevInst:X8}, Class GUID: {deviceData.ClassGuid}");
}
int error = Marshal.GetLastWin32Error();
if (error != 259) // ERROR_NO_MORE_ITEMS
throw new System.ComponentModel.Win32Error(error);
}
finally
{
SetupApi.SetupDiDestroyDeviceInfoList(hDevInfo);
}
}
Marshal.GetLastWin32Error() 獲取真實錯誤碼winerror.h 解析錯誤含義(如 122=緩衝區不足,87=參數錯誤)Win7/XP 兼容性問題本質上是 **P/Invoke 簽名不精確** 與 **架構不匹配** 導致。嚴格遵循:ref 傳遞結構體、正確設定 cbSize、編譯目標平台與系統一致、以管理員權限運行,即可實現跨版本穩定調用。