SetupDiGetClassDevsExW: implement DIGCF_ALLCLASSES and DIGCF_DEVICEINTERFACE. use deviceset and enumstr parameters if provided
Set right last error (if needed) in SetupDiCreateDeviceInfoListExW, SetupDiEnumDeviceInfo, SetupDiEnumDeviceInterfaces, SetupDiGetDeviceRegistryPropertyW
Replace some constants by constant names
Modified: trunk/reactos/lib/setupapi/devinst.c

Modified: trunk/reactos/lib/setupapi/devinst.c
--- trunk/reactos/lib/setupapi/devinst.c	2005-07-06 21:24:11 UTC (rev 16467)
+++ trunk/reactos/lib/setupapi/devinst.c	2005-07-06 21:32:16 UTC (rev 16468)
@@ -76,7 +76,21 @@
     PWSTR InterfaceName; /* "COMx:" */
     LPGUID InterfaceGuid; /* GUID_DEVINTERFACE_SERENUM_BUS_ENUMERATOR */
 #else
-    WCHAR DeviceInstance[0];
+    BOOL IsDevice; /* This entry is a device or an interface */
+    union
+    {
+        struct
+        {
+            GUID ClassGuid;
+            WCHAR RegistryKey[0]; /* "0000", "0001"... */
+        } Device;
+        struct
+        {
+            GUID ClassGuid;
+            GUID InterfaceGuid;
+            WCHAR DeviceInstance[0]; /* "ACPI\PNP0501\4&2658d0a0&0"... */
+        } Interface;
+    };
 #endif
 } DeviceInfo;
 
@@ -85,7 +99,7 @@
 typedef struct _DeviceInfoList
 {
     DWORD magic;
-    GUID ClassGuid;
+    GUID ClassGuid; /* Only devices related of this class are in the device list */
     HWND hWnd;
     HKEY HKLM; /* Local or distant HKEY_LOCAL_MACHINE registry key */
     DWORD numberOfEntries;
@@ -154,7 +168,7 @@
         LPCWSTR MachineName,
         PVOID Reserved)
 {
-    WCHAR szKeyName[40];
+    WCHAR szKeyName[MAX_GUID_STRING_LEN + 1];
     HKEY hClassesKey;
     HKEY hClassKey;
     DWORD dwLength;
@@ -179,7 +193,7 @@
 
     for (dwIndex = 0; ; dwIndex++)
     {
-	dwLength = 40;
+	dwLength = MAX_GUID_STRING_LEN + 1;
 	lError = RegEnumKeyExW(hClassesKey,
 			       dwIndex,
 			       szKeyName,
@@ -359,7 +373,7 @@
         LPCWSTR MachineName,
         PVOID Reserved)
 {
-    WCHAR szKeyName[40];
+    WCHAR szKeyName[MAX_GUID_STRING_LEN + 1];
     WCHAR szClassName[256];
     HKEY hClassesKey;
     HKEY hClassKey;
@@ -383,7 +397,7 @@
 
     for (dwIndex = 0; ; dwIndex++)
     {
-	dwLength = 40;
+	dwLength = MAX_GUID_STRING_LEN + 1;
 	lError = RegEnumKeyExW(hClassesKey,
 			       dwIndex,
 			       szKeyName,
@@ -636,7 +650,10 @@
 
   list = HeapAlloc(GetProcessHeap(), 0, sizeof(DeviceInfoList));
   if (!list)
+  {
+    SetLastError(ERROR_NOT_ENOUGH_MEMORY);
     return (HDEVINFO)INVALID_HANDLE_VALUE;
+  }
 
   list->magic = SETUP_DEV_INFO_LIST_MAGIC;
   list->hWnd = hwndParent;
@@ -676,7 +693,7 @@
         if (list->magic != SETUP_DEV_INFO_LIST_MAGIC)
             SetLastError(ERROR_INVALID_HANDLE);
         else if (DeviceInfoData->cbSize != sizeof(SP_DEVINFO_DATA))
-            SetLastError(ERROR_INVALID_PARAMETER);
+            SetLastError(ERROR_INVALID_USER_BUFFER);
         else if (MemberIndex >= list->numberOfEntries)
             SetLastError(ERROR_NO_MORE_ITEMS);
         else
@@ -686,20 +703,27 @@
             while (MemberIndex-- > 0)
                 ItemList = ItemList->Flink;
             DevInfo = (DeviceInfo *)ItemList;
+            if (DevInfo->IsDevice)
+            {
 #ifdef __WINE__
-            memcpy(&DeviceInfoData->ClassGuid,
-                DevInfo->InterfaceGuid,
-                sizeof(GUID));
-            DeviceInfoData->DevInst = 0; /* FIXME */
-            DeviceInfoData->Reserved = 0;
+                memcpy(&DeviceInfoData->ClassGuid,
+                    DevInfo->InterfaceGuid,
+                    sizeof(GUID));
+                DeviceInfoData->DevInst = 0; /* FIXME */
+                DeviceInfoData->Reserved = 0;
 #else
-            memcpy(&DeviceInfoData->ClassGuid,
-                &list->ClassGuid, /* FIXME */
-                sizeof(GUID));
-            DeviceInfoData->DevInst = 0; /* FIXME */
-            DeviceInfoData->Reserved = 0;
+                memcpy(&DeviceInfoData->ClassGuid,
+                    &DevInfo->Device.ClassGuid,
+                    sizeof(GUID));
+                DeviceInfoData->DevInst = 0; /* FIXME */
+                DeviceInfoData->Reserved = 0;
 #endif
-            ret = TRUE;
+                ret = TRUE;
+            }
+            else
+            {
+                SetLastError(ERROR_INVALID_PARAMETER);
+            }
         }
     }
     else
@@ -1125,28 +1149,25 @@
 }
 #endif /* __WINE__ */
 
-static HDEVINFO SETUP_CreateDevListFromClass(
+static LONG SETUP_CreateDevListFromClass(
+       DeviceInfoList *list,
        PCWSTR MachineName,
-       LPGUID class)
+       LPGUID class,
+       PCWSTR Enumerator)
 {
-    HDEVINFO hDeviceInfo;
     HKEY KeyClass;
     LONG rc;
     DWORD subKeys = 0, maxSubKey;
     DeviceInfo* deviceInfo;
     DWORD i;
 
-    hDeviceInfo = SetupDiCreateDeviceInfoListExW(class, NULL, MachineName, NULL);
-    if (hDeviceInfo == INVALID_HANDLE_VALUE)
-        return INVALID_HANDLE_VALUE;
-
     KeyClass = SetupDiOpenClassRegKeyExW(class, KEY_ENUMERATE_SUB_KEYS | KEY_QUERY_VALUE, DIOCR_INSTALLER, MachineName, NULL);
     if (KeyClass == INVALID_HANDLE_VALUE)
-    {
-        SetupDiDestroyDeviceInfoList(hDeviceInfo);
-        return INVALID_HANDLE_VALUE;
-    }
+        return GetLastError();
 
+    if (Enumerator)
+        FIXME("Enumerator parameter ignored\n");
+
     rc = RegQueryInfoKeyW(
         KeyClass,
         NULL, NULL,
@@ -1157,35 +1178,185 @@
     if (rc != ERROR_SUCCESS)
     {
         RegCloseKey(KeyClass);
-        SetLastError(rc);
-        return INVALID_HANDLE_VALUE;
+        return rc;
     }
 
     for (i = 0; i < subKeys; i++)
     {
-        deviceInfo = HeapAlloc(GetProcessHeap(), 0, sizeof(DeviceInfo) + maxSubKey * sizeof(WCHAR));
+        deviceInfo = HeapAlloc(GetProcessHeap(), 0, FIELD_OFFSET(DeviceInfo, Device.RegistryKey) + maxSubKey * sizeof(WCHAR));
         if (!deviceInfo)
         {
-           SetupDiDestroyDeviceInfoList(hDeviceInfo);
-           SetLastError(ERROR_NO_SYSTEM_RESOURCES);
-           return INVALID_HANDLE_VALUE;
+           RegCloseKey(KeyClass);
+           return ERROR_NO_SYSTEM_RESOURCES;
         }
-        rc = RegEnumKeyW(KeyClass, i, &deviceInfo->DeviceInstance[0], maxSubKey);
+        rc = RegEnumKeyW(KeyClass, i, &deviceInfo->Device.RegistryKey[0], maxSubKey);
         if (rc == ERROR_NO_MORE_ITEMS)
             break;
         if (rc != ERROR_SUCCESS)
         {
-           SetupDiDestroyDeviceInfoList(hDeviceInfo);
-           SetLastError(rc);
-           return INVALID_HANDLE_VALUE;
+           RegCloseKey(KeyClass);
+           return rc;
         }
-        InsertTailList(&((DeviceInfoList*)hDeviceInfo)->ListHead, &deviceInfo->ItemEntry);
-        ((DeviceInfoList*)hDeviceInfo)->numberOfEntries++;
+        deviceInfo->IsDevice = TRUE;
+        memcpy(&deviceInfo->Device.ClassGuid, class, sizeof(GUID));
+        InsertTailList(&list->ListHead, &deviceInfo->ItemEntry);
+        list->numberOfEntries++;
     }
 
     RegCloseKey(KeyClass);
+    return ERROR_SUCCESS;
+}
 
-    return hDeviceInfo;
+static LONG SETUP_CreateInterfaceList(
+       DeviceInfoList *list,
+       PCWSTR MachineName,
+       LPGUID InterfaceGuid,
+       PCWSTR DeviceInstance /* OPTIONAL */)
+{
+    HKEY hInterfaceKey, hEnumKey, hKey;
+    LONG rc;
+    WCHAR KeyBuffer[max(MAX_PATH, MAX_GUID_STRING_LEN) + 1];
+    DWORD i;
+    DWORD dwLength, dwRegType;
+    DeviceInfo *deviceInfo;
+
+    /* Open registry key related to this interface */
+    hInterfaceKey = SetupDiOpenClassRegKeyExW(InterfaceGuid, KEY_ENUMERATE_SUB_KEYS, DIOCR_INTERFACE, MachineName, NULL);
+    if (hInterfaceKey == INVALID_HANDLE_VALUE)
+        return GetLastError();
+
+    /* Enumerate sub keys */
+    i = 0;
+    while (TRUE)
+    {
+        dwLength = sizeof(KeyBuffer) / sizeof(KeyBuffer[0]);
+        rc = RegEnumKeyExW(hInterfaceKey, i, KeyBuffer, &dwLength, NULL, NULL, NULL, NULL);
+        if (rc == ERROR_NO_MORE_ITEMS)
+            break;
+        if (rc != ERROR_SUCCESS)
+        {
+            RegCloseKey(hInterfaceKey);
+            return rc;
+        }
+        i++;
+
+        /* Open sub key */
+        rc = RegOpenKeyEx(hInterfaceKey, KeyBuffer, 0, KEY_QUERY_VALUE, &hKey);
+        if (rc != ERROR_SUCCESS)
+        {
+            RegCloseKey(hInterfaceKey);
+            return rc;
+        }
+
+        /* Read DeviceInstance value */
+        rc = RegQueryValueExW(hKey, L"DeviceInstance", NULL, &dwRegType, NULL, &dwLength);
+        if (rc != ERROR_SUCCESS )
+        {
+            RegCloseKey(hKey);
+            RegCloseKey(hInterfaceKey);
+            return rc;
+        }
+        if (dwRegType != REG_SZ)
+        {
+            RegCloseKey(hKey);
+            RegCloseKey(hInterfaceKey);
+            return ERROR_GEN_FAILURE;
+        }
+
+        /* Allocate memory for list entry and read DeviceInstance */
+        deviceInfo = HeapAlloc(GetProcessHeap(), 0, FIELD_OFFSET(DeviceInfo, Interface.DeviceInstance) + dwLength + sizeof(UNICODE_NULL));
+        if (!deviceInfo)
+        {
+            RegCloseKey(hKey);
+            RegCloseKey(hInterfaceKey);
+            return ERROR_NO_SYSTEM_RESOURCES;
+        }
+        rc = RegQueryValueExW(hKey, L"DeviceInstance", NULL, NULL, (LPBYTE)&deviceInfo->Interface.DeviceInstance[0], &dwLength);
+        if (rc != ERROR_SUCCESS)
+        {
+            HeapFree(GetProcessHeap(), 0, deviceInfo);
+            RegCloseKey(hKey);
+            RegCloseKey(hInterfaceKey);
+            return rc;
+        }
+        deviceInfo->Interface.DeviceInstance[dwLength / sizeof(WCHAR)] = UNICODE_NULL;
+        TRACE("DeviceInstance %S\n", deviceInfo->Interface.DeviceInstance);
+        RegCloseKey(hKey);
+
+        if (DeviceInstance)
+        {
+            /* Check if device enumerator is not the right one */
+            if (wcscmp(DeviceInstance, deviceInfo->Interface.DeviceInstance) != 0)
+            {
+                HeapFree(GetProcessHeap(), 0, deviceInfo);;
+                continue;
+            }
+        }
+
+        /* Find class GUID associated to the device instance */
+        rc = RegOpenKeyEx(
+            HKEY_LOCAL_MACHINE,
+            L"SYSTEM\\CurrentControlSet\\Enum",
+            0, /* Options */
+            KEY_ENUMERATE_SUB_KEYS,
+            &hEnumKey);
+        if (rc != ERROR_SUCCESS)
+        {
+            HeapFree(GetProcessHeap(), 0, deviceInfo);
+            RegCloseKey(hInterfaceKey);
+            return rc;
+        }
+        rc = RegOpenKeyEx(
+            hEnumKey,
+            deviceInfo->Interface.DeviceInstance,
+            0, /* Options */
+            KEY_QUERY_VALUE,
+            &hKey);
+        RegCloseKey(hEnumKey);
+        if (rc != ERROR_SUCCESS)
+        {
+            HeapFree(GetProcessHeap(), 0, deviceInfo);
+            RegCloseKey(hInterfaceKey);
+            return rc;
+        }
+        dwLength = sizeof(KeyBuffer) - sizeof(UNICODE_NULL);
+        rc = RegQueryValueExW(hKey, L"ClassGUID", NULL, NULL, (LPBYTE)KeyBuffer, &dwLength);
+        if (rc != ERROR_SUCCESS)
+        {
+            HeapFree(GetProcessHeap(), 0, deviceInfo);
+            RegCloseKey(hKey);
+            RegCloseKey(hInterfaceKey);
+            return rc;
+        }
+        KeyBuffer[dwLength / sizeof(WCHAR)] = UNICODE_NULL;
+        RegCloseKey(hKey);
+        KeyBuffer[37] = UNICODE_NULL; /* Replace the } by a NULL character */
+        if (UuidFromStringW(&KeyBuffer[1], &deviceInfo->Interface.ClassGuid) != RPC_S_OK)
+        {
+            HeapFree(GetProcessHeap(), 0, deviceInfo);
+            return ERROR_GEN_FAILURE;
+        }
+        TRACE("ClassGUID %S\n", KeyBuffer);
+
+        /* If current device matches the list GUID (if any), append the entry to the list */
+        if (IsEqualIID(&list->ClassGuid, &GUID_NULL) || IsEqualIID(&list->ClassGuid, &deviceInfo->Interface.ClassGuid))
+        {
+            TRACE("Entry found\n");
+            deviceInfo->IsDevice = FALSE;
+            memcpy(
+                &deviceInfo->Interface.InterfaceGuid,
+                InterfaceGuid,
+                sizeof(deviceInfo->Interface.InterfaceGuid));
+            InsertTailList(&list->ListHead, &deviceInfo->ItemEntry);
+            list->numberOfEntries++;
+        }
+        else
+        {
+            HeapFree(GetProcessHeap(), 0, deviceInfo);
+        }
+    }
+    RegCloseKey(hInterfaceKey);
+    return ERROR_SUCCESS;
 }
 
 /***********************************************************************
@@ -1200,38 +1371,141 @@
        LPCWSTR machine,
        PVOID reserved)
 {
-    HDEVINFO ret = (HDEVINFO)INVALID_HANDLE_VALUE;
+    HDEVINFO hDeviceInfo = INVALID_HANDLE_VALUE;
+    DeviceInfoList *list;
+    LPGUID pClassGuid;
+    LPGUID ClassGuidList;
+    DWORD RequiredSize;
+    LONG i;
+    LONG rc;
 
     TRACE("%s %s %p 0x%08lx %p %s %p\n", debugstr_guid(class), debugstr_w(enumstr),
      parent, flags, deviceset, debugstr_w(machine), reserved);
 
+    /* Create the deviceset if not set */
+    if (deviceset)
+    {
+        list = (DeviceInfoList *)deviceset;
+        if (list->magic != SETUP_DEV_INFO_LIST_MAGIC)
+        {
+            SetLastError(ERROR_INVALID_HANDLE);
+            return INVALID_HANDLE_VALUE;
+        }
+        hDeviceInfo = deviceset;
+    }
+    else
+    {
+         hDeviceInfo = SetupDiCreateDeviceInfoListExW(
+             flags & DIGCF_DEVICEINTERFACE ? NULL : class,
+             NULL, machine, NULL);
+         if (hDeviceInfo == INVALID_HANDLE_VALUE)
+             return INVALID_HANDLE_VALUE;
+         list = (DeviceInfoList *)hDeviceInfo;
+    }
+
+    if (IsEqualIID(&list->ClassGuid, &GUID_NULL))
+        pClassGuid = NULL;
+    else
+        pClassGuid = &list->ClassGuid;
+
     if (flags & DIGCF_PRESENT)
         FIXME(": flag DIGCF_PRESENT ignored\n");
     if (flags & DIGCF_PROFILE)
         FIXME(": flag DIGCF_PROFILE ignored\n");
-    if (deviceset)
-       FIXME(": deviceset ignored\n");
 
-    if (enumstr)
-        FIXME(": unimplemented for enumerator strings (%s)\n",
-         debugstr_w(enumstr));
-    else if (flags & DIGCF_ALLCLASSES)
-        FIXME(": unimplemented for DIGCF_ALLCLASSES\n");
+    if (flags & DIGCF_ALLCLASSES)
+    {
+        /* Get list of device classes */
+        SetupDiBuildClassInfoList(0, NULL, 0, &RequiredSize);
+        if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
+        {
+            if (!deviceset)
+                SetupDiDestroyDeviceInfoList(hDeviceInfo);
+            return INVALID_HANDLE_VALUE;
+        }
+        ClassGuidList = HeapAlloc(GetProcessHeap(), 0, RequiredSize * sizeof(GUID));
+        if (!ClassGuidList)
+        {
+            if (!deviceset)
+                SetupDiDestroyDeviceInfoList(hDeviceInfo);
+            SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+            return INVALID_HANDLE_VALUE;
+        }
+        if (!SetupDiBuildClassInfoListExW(0, ClassGuidList, RequiredSize, &RequiredSize, machine, NULL))
+        {
+            HeapFree(GetProcessHeap(), 0, ClassGuidList);
+            if (!deviceset)
+                SetupDiDestroyDeviceInfoList(hDeviceInfo);
+            return INVALID_HANDLE_VALUE;
+        }
+
+        /* Enumerate devices in each device class */
+        for (i = 0; i < RequiredSize; i++)
+        {
+            if (pClassGuid == NULL || IsEqualIID(pClassGuid, &ClassGuidList[i]))
+            {
+                rc = SETUP_CreateDevListFromClass(list, machine, &ClassGuidList[i], enumstr);
+                if (rc != ERROR_SUCCESS)
+                {
+                    HeapFree(GetProcessHeap(), 0, ClassGuidList);
+                    SetLastError(rc);
+                    if (!deviceset)
+                        SetupDiDestroyDeviceInfoList(hDeviceInfo);
+                    return INVALID_HANDLE_VALUE;
+                }
+            }
+        }
+        HeapFree(GetProcessHeap(), 0, ClassGuidList);
+        return hDeviceInfo;
+    }
     else if (flags & DIGCF_DEVICEINTERFACE)
-        FIXME(": unimplemented for DIGCF_DEVICEINTERFACE\n");
-    else
     {
+        if (class == NULL)
+        {
+            SetLastError(ERROR_INVALID_PARAMETER);
+            if (!deviceset)
+                SetupDiDestroyDeviceInfoList(hDeviceInfo);
+            return INVALID_HANDLE_VALUE;
+        }
+
 #ifdef __WINE__
-        if (IsEqualIID(class, &GUID_DEVINTERFACE_SERENUM_BUS_ENUMERATOR))
-            ret = SETUP_CreateSerialDeviceList();
-        else
-#else
+        /* Special case: find serial ports by calling QueryDosDevice */
+        if (IsEqualIID(class, &GUID_DEVINTERFACE_SERENUM_BUS_ENUMERATOR)
         {
-            ret = SETUP_CreateDevListFromClass(machine, (LPGUID)class);
+            rc = SETUP_CreateSerialDeviceList();
+            if (rc != ERROR_SUCCESS)
+            {
+                SetLastError(rc);
+                if (!deviceset)
+                    SetupDiDestroyDeviceInfoList(hDeviceInfo);
+                return INVALID_HANDLE_VALUE;
+            }
+            return hDeviceInfo;
         }
 #endif
+
+        rc = SETUP_CreateInterfaceList(list, machine, (LPGUID)class, enumstr);
+        if (rc != ERROR_SUCCESS)
+        {
+            SetLastError(rc);
+            if (!deviceset)
+                SetupDiDestroyDeviceInfoList(hDeviceInfo);
+            return INVALID_HANDLE_VALUE;
+        }
+        return hDeviceInfo;
     }
-    return ret;
+    else
+    {
+        rc = SETUP_CreateDevListFromClass(list, machine, (LPGUID)class, enumstr);
+        if (rc != ERROR_SUCCESS)
+        {
+            SetLastError(rc);
+            if (!deviceset)
+                SetupDiDestroyDeviceInfoList(hDeviceInfo);
+            return INVALID_HANDLE_VALUE;
+        }
+        return hDeviceInfo;
+    }
 }
 
 /***********************************************************************
@@ -1268,25 +1542,32 @@
                     ItemList = ItemList->Flink;
                 DevInfo = (DeviceInfo *)ItemList;
 
-                DeviceInterfaceData->cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
+                if (!DevInfo->IsDevice)
+                {
+                    DeviceInterfaceData->cbSize = sizeof(SP_DEVICE_INTERFACE_DATA); /* FIXME: verify instead of setting */
 #ifdef __WINE__
-                /* FIXME: this assumes the only possible enumeration is of serial
-                 * ports.
-                 */
-                memcpy(&DeviceInterfaceData->InterfaceClassGuid,
-                 DevInfo->InterfaceGuid,
-                 sizeof(DeviceInterfaceData->InterfaceClassGuid));
-                DeviceInterfaceData->Flags = 0;
-                /* Note: this appears to be dangerous, passing a private
-                 * pointer a heap-allocated datum to the caller.  However, the
-                 * expected lifetime of the device data is the same as the
-                 * HDEVINFO; once that is closed, the data are no longer valid.
-                 */
-                DeviceInterfaceData->Reserved = (ULONG_PTR)DevInfo->InterfaceName;
+                    /* FIXME: this assumes the only possible enumeration is of serial
+                     * ports.
+                     */
+                    memcpy(&DeviceInterfaceData->InterfaceClassGuid,
+                        DevInfo->InterfaceGuid,
+                        sizeof(DeviceInterfaceData->InterfaceClassGuid));
+                    DeviceInterfaceData->Flags = 0;
+                    /* Note: this appears to be dangerous, passing a private
+                     * pointer a heap-allocated datum to the caller.  However, the
+                     * expected lifetime of the device data is the same as the
+                     * HDEVINFO; once that is closed, the data are no longer valid.
+                     */
+                    DeviceInterfaceData->Reserved = (ULONG_PTR)DevInfo->InterfaceName;
 #else
-                FIXME("implemented\n");
+                    FIXME("unimplemented\n");
 #endif
-                ret = TRUE;
+                    ret = TRUE;
+                }
+                else
+                {
+                    SetLastError(ERROR_INVALID_PARAMETER);
+                }
             }
         }
         else
@@ -1513,6 +1794,7 @@
     FIXME("%04lx %p %ld %p %p %ld %p\n", (DWORD)devinfo, DeviceInfoData,
         Property, PropertyRegDataType, PropertyBuffer, PropertyBufferSize,
         RequiredSize);
+    SetLastError(ERROR_GEN_FAILURE);
     return FALSE;
 }