shlwapi/tests: Skip StrRStrI tests on older Win9x platforms.
[wine] / dlls / setupapi / devinst.c
index 3a4c130..31269ff 100644 (file)
@@ -33,9 +33,9 @@
 #include "winnls.h"
 #include "setupapi.h"
 #include "wine/debug.h"
+#include "wine/list.h"
 #include "wine/unicode.h"
 #include "cfgmgr32.h"
-#include "initguid.h"
 #include "winioctl.h"
 #include "rpc.h"
 #include "rpcdce.h"
@@ -71,6 +71,7 @@ static const WCHAR Enum[] = {'S','y','s','t','e','m','\\',
                                   'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
                                  'E','n','u','m',0};
 static const WCHAR DeviceDesc[] = {'D','e','v','i','c','e','D','e','s','c',0};
+static const WCHAR DeviceInstance[] = {'D','e','v','i','c','e','I','n','s','t','a','n','c','e',0};
 static const WCHAR HardwareId[] = {'H','a','r','d','w','a','r','e','I','D',0};
 static const WCHAR CompatibleIDs[] = {'C','o','m','p','a','t','i','b','l','e','I','d','s',0};
 static const WCHAR Service[] = {'S','e','r','v','i','c','e',0};
@@ -83,6 +84,8 @@ static const WCHAR Capabilities[] = {'C','a','p','a','b','i','l','i','t','i','e'
 static const WCHAR UINumber[] = {'U','I','N','u','m','b','e','r',0};
 static const WCHAR UpperFilters[] = {'U','p','p','e','r','F','i','l','t','e','r','s',0};
 static const WCHAR LowerFilters[] = {'L','o','w','e','r','F','i','l','t','e','r','s',0};
+static const WCHAR Phantom[] = {'P','h','a','n','t','o','m',0};
+static const WCHAR SymbolicLink[] = {'S','y','m','b','o','l','i','c','L','i','n','k',0};
 
 /* is used to identify if a DeviceInfoSet pointer is
 valid or not */
@@ -94,45 +97,398 @@ struct DeviceInfoSet
     GUID ClassGuid;
     HWND hwndParent;
     DWORD cDevices;
-    SP_DEVINFO_DATA *devices;
+    struct list devices;
+};
+
+struct DeviceInstance
+{
+    struct list entry;
+    SP_DEVINFO_DATA data;
+};
+
+/* Pointed to by SP_DEVICE_INTERFACE_DATA's Reserved member */
+struct InterfaceInfo
+{
+    LPWSTR           referenceString;
+    LPWSTR           symbolicLink;
+    PSP_DEVINFO_DATA device;
+};
+
+/* A device may have multiple instances of the same interface, so this holds
+ * each instance belonging to a particular interface.
+ */
+struct InterfaceInstances
+{
+    GUID                      guid;
+    DWORD                     cInstances;
+    DWORD                     cInstancesAllocated;
+    SP_DEVICE_INTERFACE_DATA *instances;
+    struct list               entry;
 };
 
 /* Pointed to by SP_DEVINFO_DATA's Reserved member */
 struct DeviceInfo
 {
-    HKEY   key;
-    LPWSTR instanceId;
+    struct DeviceInfoSet *set;
+    HKEY                  key;
+    BOOL                  phantom;
+    DWORD                 devId;
+    LPWSTR                instanceId;
+    struct list           interfaces;
 };
 
-static struct DeviceInfo *SETUPDI_AllocateDeviceInfo(LPCWSTR instanceId)
+static void SETUPDI_GuidToString(const GUID *guid, LPWSTR guidStr)
+{
+    static const WCHAR fmt[] = {'{','%','0','8','X','-','%','0','4','X','-',
+        '%','0','4','X','-','%','0','2','X','%','0','2','X','-','%','0','2',
+        'X','%','0','2','X','%','0','2','X','%','0','2','X','%','0','2','X','%',
+        '0','2','X','}',0};
+
+    sprintfW(guidStr, fmt, guid->Data1, guid->Data2, guid->Data3,
+        guid->Data4[0], guid->Data4[1], guid->Data4[2], guid->Data4[3],
+        guid->Data4[4], guid->Data4[5], guid->Data4[6], guid->Data4[7]);
+}
+
+static void SETUPDI_FreeInterfaceInstances(struct InterfaceInstances *instances)
+{
+    DWORD i;
+
+    for (i = 0; i < instances->cInstances; i++)
+    {
+        struct InterfaceInfo *ifaceInfo =
+            (struct InterfaceInfo *)instances->instances[i].Reserved;
+
+        if (ifaceInfo->device && ifaceInfo->device->Reserved)
+        {
+            struct DeviceInfo *devInfo =
+                (struct DeviceInfo *)ifaceInfo->device->Reserved;
+
+            if (devInfo->phantom)
+                SetupDiDeleteDeviceInterfaceRegKey(devInfo->set,
+                        &instances->instances[i], 0);
+        }
+        HeapFree(GetProcessHeap(), 0, ifaceInfo->referenceString);
+        HeapFree(GetProcessHeap(), 0, ifaceInfo->symbolicLink);
+        HeapFree(GetProcessHeap(), 0, ifaceInfo);
+    }
+    HeapFree(GetProcessHeap(), 0, instances->instances);
+}
+
+/* Finds the interface with interface class InterfaceClassGuid in the device.
+ * Returns TRUE if found, and updates *interface to point to device's
+ * interfaces member where the given interface was found.
+ * Returns FALSE if not found.
+ */
+static BOOL SETUPDI_FindInterface(const struct DeviceInfo *devInfo,
+        const GUID *InterfaceClassGuid, struct InterfaceInstances **iface_ret)
+{
+    BOOL found = FALSE;
+    struct InterfaceInstances *iface;
+
+    TRACE("%s\n", debugstr_guid(InterfaceClassGuid));
+
+    LIST_FOR_EACH_ENTRY(iface, &devInfo->interfaces, struct InterfaceInstances,
+            entry)
+    {
+        if (IsEqualGUID(&iface->guid, InterfaceClassGuid))
+        {
+            *iface_ret = iface;
+            found = TRUE;
+            break;
+        }
+    }
+    TRACE("returning %d (%p)\n", found, found ? *iface_ret : NULL);
+    return found;
+}
+
+/* Finds the interface instance with reference string ReferenceString in the
+ * interface instance map.  Returns TRUE if found, and updates instanceIndex to
+ * the index of the interface instance's instances member
+ * where the given instance was found.  Returns FALSE if not found.
+ */
+static BOOL SETUPDI_FindInterfaceInstance(
+        const struct InterfaceInstances *instances,
+        LPCWSTR ReferenceString, DWORD *instanceIndex)
+{
+    BOOL found = FALSE;
+    DWORD i;
+
+    TRACE("%s\n", debugstr_w(ReferenceString));
+
+    for (i = 0; !found && i < instances->cInstances; i++)
+    {
+        SP_DEVICE_INTERFACE_DATA *ifaceData = &instances->instances[i];
+        struct InterfaceInfo *ifaceInfo =
+            (struct InterfaceInfo *)ifaceData->Reserved;
+
+        if (!ReferenceString && !ifaceInfo->referenceString)
+        {
+            *instanceIndex = i;
+            found = TRUE;
+        }
+        else if (ReferenceString && ifaceInfo->referenceString &&
+                !lstrcmpiW(ifaceInfo->referenceString, ReferenceString))
+        {
+            *instanceIndex = i;
+            found = TRUE;
+        }
+    }
+    TRACE("returning %d (%d)\n", found, found ? *instanceIndex : 0);
+    return found;
+}
+
+static LPWSTR SETUPDI_CreateSymbolicLinkPath(LPCWSTR instanceId,
+        const GUID *InterfaceClassGuid, LPCWSTR ReferenceString)
+{
+    static const WCHAR fmt[] = {'\\','\\','?','\\','%','s','#','%','s',0};
+    WCHAR guidStr[39];
+    DWORD len;
+    LPWSTR ret;
+
+    SETUPDI_GuidToString(InterfaceClassGuid, guidStr);
+    /* omit length of format specifiers, but include NULL terminator: */
+    len = lstrlenW(fmt) - 4 + 1;
+    len += lstrlenW(instanceId) + lstrlenW(guidStr);
+    if (ReferenceString && *ReferenceString)
+    {
+        /* space for a hash between string and reference string: */
+        len += lstrlenW(ReferenceString) + 1;
+    }
+    ret = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
+    if (ret)
+    {
+        int printed = sprintfW(ret, fmt, instanceId, guidStr);
+        LPWSTR ptr;
+
+        /* replace '\\' with '#' after the "\\\\?\\" beginning */
+        for (ptr = strchrW(ret + 4, '\\'); ptr; ptr = strchrW(ptr + 1, '\\'))
+            *ptr = '#';
+        if (ReferenceString && *ReferenceString)
+        {
+            ret[printed] = '\\';
+            lstrcpyW(ret + printed + 1, ReferenceString);
+        }
+    }
+    return ret;
+}
+
+/* Adds an interface with the given interface class and reference string to
+ * the device, if it doesn't already exist in the device.  If iface is not
+ * NULL, returns a pointer to the newly added (or already existing) interface.
+ */
+static BOOL SETUPDI_AddInterfaceInstance(PSP_DEVINFO_DATA DeviceInfoData,
+        const GUID *InterfaceClassGuid, LPCWSTR ReferenceString,
+        SP_DEVICE_INTERFACE_DATA **ifaceData)
+{
+    struct DeviceInfo *devInfo = (struct DeviceInfo *)DeviceInfoData->Reserved;
+    BOOL newInterface = FALSE, ret;
+    struct InterfaceInstances *iface = NULL;
+
+    TRACE("%p %s %s %p\n", devInfo, debugstr_guid(InterfaceClassGuid),
+            debugstr_w(ReferenceString), iface);
+
+    if (!(ret = SETUPDI_FindInterface(devInfo, InterfaceClassGuid, &iface)))
+    {
+        iface = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
+                sizeof(struct InterfaceInstances));
+        if (iface)
+        {
+            list_add_tail(&devInfo->interfaces, &iface->entry);
+            newInterface = TRUE;
+        }
+    }
+    if (iface)
+    {
+        DWORD instanceIndex = 0;
+
+        if (!(ret = SETUPDI_FindInterfaceInstance(iface, ReferenceString,
+                        &instanceIndex)))
+        {
+            SP_DEVICE_INTERFACE_DATA *instance = NULL;
+
+            if (!iface->cInstancesAllocated)
+            {
+                iface->instances = HeapAlloc(GetProcessHeap(), 0,
+                        sizeof(SP_DEVICE_INTERFACE_DATA));
+                if (iface->instances)
+                    instance = &iface->instances[iface->cInstancesAllocated++];
+            }
+            else if (iface->cInstances == iface->cInstancesAllocated)
+            {
+                iface->instances = HeapReAlloc(GetProcessHeap(), 0,
+                        iface->instances,
+                        (iface->cInstancesAllocated + 1) *
+                        sizeof(SP_DEVICE_INTERFACE_DATA));
+                if (iface->instances)
+                    instance = &iface->instances[iface->cInstancesAllocated++];
+            }
+            else
+                instance = &iface->instances[iface->cInstances];
+            if (instance)
+            {
+                struct InterfaceInfo *ifaceInfo = HeapAlloc(GetProcessHeap(),
+                        0, sizeof(struct InterfaceInfo));
+
+                if (ifaceInfo)
+                {
+                    ret = TRUE;
+                    ifaceInfo->device = DeviceInfoData;
+                    ifaceInfo->symbolicLink = SETUPDI_CreateSymbolicLinkPath(
+                            devInfo->instanceId, InterfaceClassGuid,
+                            ReferenceString);
+                    if (ReferenceString)
+                    {
+                        ifaceInfo->referenceString =
+                            HeapAlloc(GetProcessHeap(), 0,
+                                (lstrlenW(ReferenceString) + 1) *
+                                sizeof(WCHAR));
+                        if (ifaceInfo->referenceString)
+                            lstrcpyW(ifaceInfo->referenceString,
+                                    ReferenceString);
+                        else
+                            ret = FALSE;
+                    }
+                    else
+                        ifaceInfo->referenceString = NULL;
+                    if (ret)
+                    {
+                        HKEY key;
+
+                        iface->cInstances++;
+                        instance->cbSize =
+                            sizeof(SP_DEVICE_INTERFACE_DATA);
+                        instance->InterfaceClassGuid = *InterfaceClassGuid;
+                        instance->Flags = SPINT_ACTIVE; /* FIXME */
+                        instance->Reserved = (ULONG_PTR)ifaceInfo;
+                        if (newInterface)
+                            iface->guid = *InterfaceClassGuid;
+                        key = SetupDiCreateDeviceInterfaceRegKeyW(devInfo->set,
+                                instance, 0, KEY_WRITE, NULL, NULL);
+                        if (key != INVALID_HANDLE_VALUE)
+                        {
+                            RegSetValueExW(key, SymbolicLink, 0, REG_SZ,
+                                    (BYTE *)ifaceInfo->symbolicLink,
+                                    lstrlenW(ifaceInfo->symbolicLink) *
+                                    sizeof(WCHAR));
+                            RegCloseKey(key);
+                        }
+                        if (ifaceData)
+                            *ifaceData = instance;
+                    }
+                    else
+                        HeapFree(GetProcessHeap(), 0, ifaceInfo);
+                }
+            }
+        }
+        else
+        {
+            if (ifaceData)
+                *ifaceData = &iface->instances[instanceIndex];
+        }
+    }
+    else
+        ret = FALSE;
+    TRACE("returning %d\n", ret);
+    return ret;
+}
+
+static BOOL SETUPDI_SetInterfaceSymbolicLink(SP_DEVICE_INTERFACE_DATA *iface,
+        LPCWSTR symbolicLink)
+{
+    struct InterfaceInfo *info = (struct InterfaceInfo *)iface->Reserved;
+    BOOL ret = FALSE;
+
+    if (info)
+    {
+        HeapFree(GetProcessHeap(), 0, info->symbolicLink);
+        info->symbolicLink = HeapAlloc(GetProcessHeap(), 0,
+                (lstrlenW(symbolicLink) + 1) * sizeof(WCHAR));
+        if (info->symbolicLink)
+        {
+            lstrcpyW(info->symbolicLink, symbolicLink);
+            ret = TRUE;
+        }
+    }
+    return ret;
+}
+
+static HKEY SETUPDI_CreateDevKey(struct DeviceInfo *devInfo)
+{
+    HKEY enumKey, key = INVALID_HANDLE_VALUE;
+    LONG l;
+
+    l = RegCreateKeyExW(HKEY_LOCAL_MACHINE, Enum, 0, NULL, 0, KEY_ALL_ACCESS,
+            NULL, &enumKey, NULL);
+    if (!l)
+    {
+        RegCreateKeyExW(enumKey, devInfo->instanceId, 0, NULL, 0,
+                KEY_READ | KEY_WRITE, NULL, &key, NULL);
+        RegCloseKey(enumKey);
+    }
+    return key;
+}
+
+static HKEY SETUPDI_CreateDrvKey(struct DeviceInfo *devInfo)
 {
-    struct DeviceInfo *devInfo = HeapAlloc(GetProcessHeap(), 0,
-            sizeof(struct DeviceInfo));
+    static const WCHAR slash[] = { '\\',0 };
+    WCHAR classKeyPath[MAX_PATH];
+    HKEY classKey, key = INVALID_HANDLE_VALUE;
+    LONG l;
+
+    lstrcpyW(classKeyPath, ControlClass);
+    lstrcatW(classKeyPath, slash);
+    SETUPDI_GuidToString(&devInfo->set->ClassGuid,
+            classKeyPath + lstrlenW(classKeyPath));
+    l = RegCreateKeyExW(HKEY_LOCAL_MACHINE, classKeyPath, 0, NULL, 0,
+            KEY_ALL_ACCESS, NULL, &classKey, NULL);
+    if (!l)
+    {
+        static const WCHAR fmt[] = { '%','0','4','u',0 };
+        WCHAR devId[10];
+
+        sprintfW(devId, fmt, devInfo->devId);
+        RegCreateKeyExW(classKey, devId, 0, NULL, 0, KEY_READ | KEY_WRITE,
+                NULL, &key, NULL);
+        RegCloseKey(classKey);
+    }
+    return key;
+}
+
+static struct DeviceInfo *SETUPDI_AllocateDeviceInfo(struct DeviceInfoSet *set,
+        DWORD devId, LPCWSTR instanceId, BOOL phantom)
+{
+    struct DeviceInfo *devInfo = NULL;
+    HANDLE devInst = GlobalAlloc(GMEM_FIXED, sizeof(struct DeviceInfo));
+    if (devInst)
+        devInfo = GlobalLock(devInst);
 
     if (devInfo)
     {
+        devInfo->set = set;
+        devInfo->devId = (DWORD)devInst;
+
         devInfo->instanceId = HeapAlloc(GetProcessHeap(), 0,
                 (lstrlenW(instanceId) + 1) * sizeof(WCHAR));
         if (devInfo->instanceId)
         {
-            HKEY enumKey;
-            LONG l;
-
             devInfo->key = INVALID_HANDLE_VALUE;
+            devInfo->phantom = phantom;
             lstrcpyW(devInfo->instanceId, instanceId);
             struprW(devInfo->instanceId);
-            l = RegCreateKeyExW(HKEY_LOCAL_MACHINE, Enum, 0, NULL, 0,
-                    KEY_ALL_ACCESS, NULL, &enumKey, NULL);
-            if (!l)
+            devInfo->key = SETUPDI_CreateDevKey(devInfo);
+            if (devInfo->key != INVALID_HANDLE_VALUE)
             {
-                RegCreateKeyExW(enumKey, devInfo->instanceId, 0, NULL, 0,
-                        KEY_ALL_ACCESS, NULL, &devInfo->key, NULL);
-                RegCloseKey(enumKey);
+                if (phantom)
+                    RegSetValueExW(devInfo->key, Phantom, 0, REG_DWORD,
+                            (LPBYTE)&phantom, sizeof(phantom));
             }
+            list_init(&devInfo->interfaces);
+            GlobalUnlock(devInst);
         }
         else
         {
-            HeapFree(GetProcessHeap(), 0, devInfo);
+            GlobalUnlock(devInst);
+            GlobalFree(devInst);
             devInfo = NULL;
         }
     }
@@ -141,53 +497,73 @@ static struct DeviceInfo *SETUPDI_AllocateDeviceInfo(LPCWSTR instanceId)
 
 static void SETUPDI_FreeDeviceInfo(struct DeviceInfo *devInfo)
 {
+    struct InterfaceInstances *iface, *next;
+
     if (devInfo->key != INVALID_HANDLE_VALUE)
         RegCloseKey(devInfo->key);
-    HeapFree(GetProcessHeap(), 0, devInfo->instanceId);
-    HeapFree(GetProcessHeap(), 0, devInfo);
-}
-
-static void SETUPDI_GuidToString(const GUID *guid, LPWSTR guidStr)
-{
-    static const WCHAR fmt[] = {'{','%','0','8','X','-','%','0','4','X','-',
-        '%','0','4','X','-','%','0','2','X','%','0','2','X','-','%','0','2',
-        'X','%','0','2','X','%','0','2','X','%','0','2','X','%','0','2','X','%',
-        '0','2','X','}',0};
+    if (devInfo->phantom)
+    {
+        HKEY enumKey;
+        LONG l;
 
-    sprintfW(guidStr, fmt, guid->Data1, guid->Data2, guid->Data3,
-        guid->Data4[0], guid->Data4[1], guid->Data4[2], guid->Data4[3],
-        guid->Data4[4], guid->Data4[5], guid->Data4[6], guid->Data4[7]);
+        l = RegCreateKeyExW(HKEY_LOCAL_MACHINE, Enum, 0, NULL, 0,
+                KEY_ALL_ACCESS, NULL, &enumKey, NULL);
+        if (!l)
+        {
+            RegDeleteTreeW(enumKey, devInfo->instanceId);
+            RegCloseKey(enumKey);
+        }
+    }
+    HeapFree(GetProcessHeap(), 0, devInfo->instanceId);
+    LIST_FOR_EACH_ENTRY_SAFE(iface, next, &devInfo->interfaces,
+            struct InterfaceInstances, entry)
+    {
+        list_remove(&iface->entry);
+        SETUPDI_FreeInterfaceInstances(iface);
+        HeapFree(GetProcessHeap(), 0, iface);
+    }
+    GlobalFree((HANDLE)devInfo->devId);
 }
 
 /* Adds a device with GUID guid and identifer devInst to set.  Allocates a
  * struct DeviceInfo, and points the returned device info's Reserved member
- * to it.
+ * to it.  "Phantom" devices are deleted from the registry when closed.
  * Returns a pointer to the newly allocated device info.
  */
 static BOOL SETUPDI_AddDeviceToSet(struct DeviceInfoSet *set,
         const GUID *guid,
         DWORD devInst,
         LPCWSTR instanceId,
+        BOOL phantom,
         SP_DEVINFO_DATA **dev)
 {
     BOOL ret = FALSE;
-    struct DeviceInfo *devInfo = SETUPDI_AllocateDeviceInfo(instanceId);
+    struct DeviceInfo *devInfo = SETUPDI_AllocateDeviceInfo(set, set->cDevices,
+            instanceId, phantom);
+
+    TRACE("%p, %s, %d, %s, %d\n", set, debugstr_guid(guid), devInst,
+            debugstr_w(instanceId), phantom);
 
     if (devInfo)
     {
-        if (set->devices)
-            set->devices = HeapReAlloc(GetProcessHeap(), 0, set->devices,
-                    (set->cDevices + 1) * sizeof(SP_DEVINFO_DATA));
-        else
-            set->devices = HeapAlloc(GetProcessHeap(), 0,
-                    sizeof(SP_DEVINFO_DATA));
-        if (set->devices)
+        struct DeviceInstance *devInst =
+                HeapAlloc(GetProcessHeap(), 0, sizeof(struct DeviceInstance));
+
+        if (devInst)
         {
-            *dev = &set->devices[set->cDevices++];
-            (*dev)->cbSize = sizeof(SP_DEVINFO_DATA);
-            memcpy(&(*dev)->ClassGuid, guid, sizeof(GUID));
-            (*dev)->DevInst = devInst;
-            (*dev)->Reserved = (ULONG_PTR)devInfo;
+            WCHAR classGuidStr[39];
+
+            list_add_tail(&set->devices, &devInst->entry);
+            set->cDevices++;
+            devInst->data.cbSize = sizeof(SP_DEVINFO_DATA);
+            devInst->data.ClassGuid = *guid;
+            devInst->data.DevInst = devInfo->devId;
+            devInst->data.Reserved = (ULONG_PTR)devInfo;
+            SETUPDI_GuidToString(guid, classGuidStr);
+            SetupDiSetDeviceRegistryPropertyW(set, &devInst->data,
+                SPDRP_CLASSGUID, (const BYTE *)classGuidStr,
+                lstrlenW(classGuidStr) * sizeof(WCHAR));
+            if (dev) *dev = &devInst->data;
             ret = TRUE;
         }
         else
@@ -465,8 +841,6 @@ BOOL WINAPI SetupDiClassGuidsFromNameExA(
     LPWSTR MachineNameW = NULL;
     BOOL bResult;
 
-    FIXME("\n");
-
     ClassNameW = MultiByteToUnicode(ClassName, CP_ACP);
     if (ClassNameW == NULL)
         return FALSE;
@@ -747,7 +1121,7 @@ SetupDiCreateDeviceInfoListExA(const GUID *ClassGuid,
     {
         MachineNameW = MultiByteToUnicode(MachineName, CP_ACP);
         if (MachineNameW == NULL)
-            return (HDEVINFO)INVALID_HANDLE_VALUE;
+            return INVALID_HANDLE_VALUE;
     }
 
     hDevInfo = SetupDiCreateDeviceInfoListExW(ClassGuid, hwndParent,
@@ -764,7 +1138,7 @@ SetupDiCreateDeviceInfoListExA(const GUID *ClassGuid,
  * Create an empty DeviceInfoSet list.
  *
  * PARAMS
- *   ClassGuid [I] if not NULL only devices with GUID ClcassGuid are associated
+ *   ClassGuid [I] if not NULL only devices with GUID ClassGuid are associated
  *                 with this list.
  *   hwndParent [I] hwnd needed for interface related actions.
  *   MachineName [I] name of machine to create emtpy DeviceInfoSet list, if NULL
@@ -791,20 +1165,20 @@ SetupDiCreateDeviceInfoListExW(const GUID *ClassGuid,
     {
         FIXME("remote support is not implemented\n");
         SetLastError(ERROR_INVALID_MACHINENAME);
-        return (HDEVINFO)INVALID_HANDLE_VALUE;
+        return INVALID_HANDLE_VALUE;
     }
 
     if (Reserved != NULL)
     {
         SetLastError(ERROR_INVALID_PARAMETER);
-        return (HDEVINFO)INVALID_HANDLE_VALUE;
+        return INVALID_HANDLE_VALUE;
     }
 
     list = HeapAlloc(GetProcessHeap(), 0, size);
     if (!list)
     {
         SetLastError(ERROR_NOT_ENOUGH_MEMORY);
-        return (HDEVINFO)INVALID_HANDLE_VALUE;
+        return INVALID_HANDLE_VALUE;
     }
 
     list->magic = SETUP_DEVICE_INFO_SET_MAGIC;
@@ -813,9 +1187,122 @@ SetupDiCreateDeviceInfoListExW(const GUID *ClassGuid,
             ClassGuid ? ClassGuid : &GUID_NULL,
             sizeof(list->ClassGuid));
     list->cDevices = 0;
-    list->devices = NULL;
+    list_init(&list->devices);
+
+    return list;
+}
+
+/***********************************************************************
+ *              SetupDiCreateDevRegKeyA (SETUPAPI.@)
+ */
+HKEY WINAPI SetupDiCreateDevRegKeyA(
+        HDEVINFO DeviceInfoSet,
+        PSP_DEVINFO_DATA DeviceInfoData,
+        DWORD Scope,
+        DWORD HwProfile,
+        DWORD KeyType,
+        HINF InfHandle,
+        PCSTR InfSectionName)
+{
+    PWSTR InfSectionNameW = NULL;
+    HKEY key;
+
+    TRACE("%p %p %d %d %d %p %s\n", DeviceInfoSet, DeviceInfoData, Scope,
+            HwProfile, KeyType, InfHandle, debugstr_a(InfSectionName));
+
+    if (InfHandle)
+    {
+        if (!InfSectionName)
+        {
+            SetLastError(ERROR_INVALID_PARAMETER);
+            return INVALID_HANDLE_VALUE;
+        }
+        else
+        {
+            InfSectionNameW = MultiByteToUnicode(InfSectionName, CP_ACP);
+            if (InfSectionNameW == NULL) return INVALID_HANDLE_VALUE;
+        }
+    }
+    key = SetupDiCreateDevRegKeyW(DeviceInfoSet, DeviceInfoData, Scope,
+            HwProfile, KeyType, InfHandle, InfSectionNameW);
+    MyFree(InfSectionNameW);
+    return key;
+}
+
+/***********************************************************************
+ *              SetupDiCreateDevRegKeyW (SETUPAPI.@)
+ */
+HKEY WINAPI SetupDiCreateDevRegKeyW(
+        HDEVINFO DeviceInfoSet,
+        PSP_DEVINFO_DATA DeviceInfoData,
+        DWORD Scope,
+        DWORD HwProfile,
+        DWORD KeyType,
+        HINF InfHandle,
+        PCWSTR InfSectionName)
+{
+    struct DeviceInfoSet *set = DeviceInfoSet;
+    struct DeviceInfo *devInfo;
+    HKEY key = INVALID_HANDLE_VALUE;
+
+    TRACE("%p %p %d %d %d %p %s\n", DeviceInfoSet, DeviceInfoData, Scope,
+            HwProfile, KeyType, InfHandle, debugstr_w(InfSectionName));
 
-    return (HDEVINFO)list;
+    if (!DeviceInfoSet || DeviceInfoSet == INVALID_HANDLE_VALUE)
+    {
+        SetLastError(ERROR_INVALID_HANDLE);
+        return INVALID_HANDLE_VALUE;
+    }
+    if (set->magic != SETUP_DEVICE_INFO_SET_MAGIC)
+    {
+        SetLastError(ERROR_INVALID_HANDLE);
+        return INVALID_HANDLE_VALUE;
+    }
+    if (!DeviceInfoData || DeviceInfoData->cbSize != sizeof(SP_DEVINFO_DATA)
+            || !DeviceInfoData->Reserved)
+    {
+        SetLastError(ERROR_INVALID_PARAMETER);
+        return INVALID_HANDLE_VALUE;
+    }
+    devInfo = (struct DeviceInfo *)DeviceInfoData->Reserved;
+    if (devInfo->set != set)
+    {
+        SetLastError(ERROR_INVALID_PARAMETER);
+        return INVALID_HANDLE_VALUE;
+    }
+    if (Scope != DICS_FLAG_GLOBAL && Scope != DICS_FLAG_CONFIGSPECIFIC)
+    {
+        SetLastError(ERROR_INVALID_FLAGS);
+        return INVALID_HANDLE_VALUE;
+    }
+    if (KeyType != DIREG_DEV && KeyType != DIREG_DRV)
+    {
+        SetLastError(ERROR_INVALID_FLAGS);
+        return INVALID_HANDLE_VALUE;
+    }
+    if (devInfo->phantom)
+    {
+        SetLastError(ERROR_DEVINFO_NOT_REGISTERED);
+        return INVALID_HANDLE_VALUE;
+    }
+    if (Scope != DICS_FLAG_GLOBAL)
+        FIXME("unimplemented for scope %d\n", Scope);
+    switch (KeyType)
+    {
+        case DIREG_DEV:
+            key = SETUPDI_CreateDevKey(devInfo);
+            break;
+        case DIREG_DRV:
+            key = SETUPDI_CreateDrvKey(devInfo);
+            break;
+        default:
+            WARN("unknown KeyType %d\n", KeyType);
+    }
+    if (InfHandle)
+        SetupInstallFromInfSectionW(NULL, InfHandle, InfSectionName, SPINST_ALL,
+                NULL, NULL, SP_COPY_NEWER_ONLY, NULL, NULL, DeviceInfoSet,
+                DeviceInfoData);
+    return key;
 }
 
 /***********************************************************************
@@ -861,7 +1348,8 @@ BOOL WINAPI SetupDiCreateDeviceInfoA(
 static DWORD SETUPDI_DevNameToDevID(LPCWSTR devName)
 {
     LPCWSTR ptr;
-    DWORD devNameLen = lstrlenW(devName), devInst = 0;
+    int devNameLen = lstrlenW(devName);
+    DWORD devInst = 0;
     BOOL valid = TRUE;
 
     TRACE("%s\n", debugstr_w(devName));
@@ -892,7 +1380,7 @@ BOOL WINAPI SetupDiCreateDeviceInfoW(
        DWORD CreationFlags,
        PSP_DEVINFO_DATA DeviceInfoData)
 {
-    struct DeviceInfoSet *set = (struct DeviceInfoSet *)DeviceInfoSet;
+    struct DeviceInfoSet *set = DeviceInfoSet;
     BOOL ret = FALSE, allocatedInstanceId = FALSE;
     LPCWSTR instanceId = NULL;
 
@@ -905,7 +1393,7 @@ BOOL WINAPI SetupDiCreateDeviceInfoW(
         SetLastError(ERROR_INVALID_DEVINST_NAME);
         return FALSE;
     }
-    if (!DeviceInfoSet || DeviceInfoSet == (HDEVINFO)INVALID_HANDLE_VALUE)
+    if (!DeviceInfoSet || DeviceInfoSet == INVALID_HANDLE_VALUE)
     {
         SetLastError(ERROR_INVALID_HANDLE);
         return FALSE;
@@ -938,12 +1426,12 @@ BOOL WINAPI SetupDiCreateDeviceInfoW(
 
             if (set->cDevices)
             {
-                DWORD i, highestDevID = 0;
+                DWORD highestDevID = 0;
+                struct DeviceInstance *devInst;
 
-                for (i = 0; i < set->cDevices; i++)
+                LIST_FOR_EACH_ENTRY(devInst, &set->devices, struct DeviceInstance, entry)
                 {
-                    struct DeviceInfo *devInfo =
-                        (struct DeviceInfo *)set->devices[i].Reserved;
+                    struct DeviceInfo *devInfo = (struct DeviceInfo *)devInst->data.Reserved;
                     LPCWSTR devName = strrchrW(devInfo->instanceId, '\\');
                     DWORD id;
 
@@ -975,14 +1463,13 @@ BOOL WINAPI SetupDiCreateDeviceInfoW(
     }
     else
     {
-        DWORD i;
+        struct DeviceInstance *devInst;
 
         ret = TRUE;
         instanceId = DeviceName;
-        for (i = 0; ret && i < set->cDevices; i++)
+        LIST_FOR_EACH_ENTRY(devInst, &set->devices, struct DeviceInstance, entry)
         {
-            struct DeviceInfo *devInfo =
-                (struct DeviceInfo *)set->devices[i].Reserved;
+            struct DeviceInfo *devInfo = (struct DeviceInfo *)devInst->data.Reserved;
 
             if (!lstrcmpiW(DeviceName, devInfo->instanceId))
             {
@@ -996,16 +1483,23 @@ BOOL WINAPI SetupDiCreateDeviceInfoW(
         SP_DEVINFO_DATA *dev = NULL;
 
         ret = SETUPDI_AddDeviceToSet(set, ClassGuid, 0 /* FIXME: DevInst */,
-                instanceId, &dev);
-        if (ret && DeviceInfoData)
+                instanceId, TRUE, &dev);
+        if (ret)
         {
-            if (DeviceInfoData->cbSize != sizeof(SP_DEVINFO_DATA))
+            if (DeviceDescription)
+                SetupDiSetDeviceRegistryPropertyW(DeviceInfoSet,
+                    dev, SPDRP_DEVICEDESC, (const BYTE *)DeviceDescription,
+                    lstrlenW(DeviceDescription) * sizeof(WCHAR));
+            if (DeviceInfoData)
             {
-                SetLastError(ERROR_INVALID_USER_BUFFER);
-                ret = FALSE;
+                if (DeviceInfoData->cbSize != sizeof(SP_DEVINFO_DATA))
+                {
+                    SetLastError(ERROR_INVALID_USER_BUFFER);
+                    ret = FALSE;
+                }
+                else
+                    *DeviceInfoData = *dev;
             }
-            else
-                memcpy(DeviceInfoData, dev, sizeof(SP_DEVINFO_DATA));
         }
     }
     if (allocatedInstanceId)
@@ -1015,38 +1509,109 @@ BOOL WINAPI SetupDiCreateDeviceInfoW(
 }
 
 /***********************************************************************
- *             SetupDiEnumDeviceInfo (SETUPAPI.@)
+ *             SetupDiRegisterDeviceInfo (SETUPAPI.@)
  */
-BOOL WINAPI SetupDiEnumDeviceInfo(
-        HDEVINFO  devinfo,
-        DWORD  index,
-        PSP_DEVINFO_DATA info)
+BOOL WINAPI SetupDiRegisterDeviceInfo(
+        HDEVINFO DeviceInfoSet,
+        PSP_DEVINFO_DATA DeviceInfoData,
+        DWORD Flags,
+        PSP_DETSIG_CMPPROC CompareProc,
+        PVOID CompareContext,
+        PSP_DEVINFO_DATA DupDeviceInfoData)
 {
-    BOOL ret = FALSE;
+    struct DeviceInfoSet *set = DeviceInfoSet;
+    struct DeviceInfo *devInfo;
 
-    TRACE("%p %d %p\n", devinfo, index, info);
+    TRACE("%p %p %08x %p %p %p\n", DeviceInfoSet, DeviceInfoData, Flags,
+            CompareProc, CompareContext, DupDeviceInfoData);
 
-    if(info==NULL)
+    if (!DeviceInfoSet || DeviceInfoSet == INVALID_HANDLE_VALUE)
+    {
+        SetLastError(ERROR_INVALID_HANDLE);
         return FALSE;
-    if(info->cbSize < sizeof(*info))
+    }
+    if (set->magic != SETUP_DEVICE_INFO_SET_MAGIC)
+    {
+        SetLastError(ERROR_INVALID_HANDLE);
         return FALSE;
-    if (devinfo && devinfo != (HDEVINFO)INVALID_HANDLE_VALUE)
+    }
+    if (!DeviceInfoData || DeviceInfoData->cbSize != sizeof(SP_DEVINFO_DATA)
+            || !DeviceInfoData->Reserved)
     {
-        struct DeviceInfoSet *list = (struct DeviceInfoSet *)devinfo;
-        if (list->magic == SETUP_DEVICE_INFO_SET_MAGIC)
-        {
-            if (index < list->cDevices)
-            {
-                if (info->cbSize == sizeof(SP_DEVINFO_DATA))
-                {
-                    memcpy(info, &list->devices[index], info->cbSize);
-                    ret = TRUE;
-                }
-                else
-                    SetLastError(ERROR_INVALID_USER_BUFFER);
-            }
-            else
-                SetLastError(ERROR_NO_MORE_ITEMS);
+        SetLastError(ERROR_INVALID_PARAMETER);
+        return FALSE;
+    }
+    devInfo = (struct DeviceInfo *)DeviceInfoData->Reserved;
+    if (devInfo->set != set)
+    {
+        SetLastError(ERROR_INVALID_PARAMETER);
+        return FALSE;
+    }
+    if (devInfo->phantom)
+    {
+        devInfo->phantom = FALSE;
+        RegDeleteValueW(devInfo->key, Phantom);
+    }
+    return TRUE;
+}
+
+/***********************************************************************
+ *              SetupDiRemoveDevice (SETUPAPI.@)
+ */
+BOOL WINAPI SetupDiRemoveDevice(
+        HDEVINFO devinfo,
+        PSP_DEVINFO_DATA info)
+{
+    FIXME("(%p, %p): stub\n", devinfo, info);
+    SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
+    return FALSE;
+}
+
+/***********************************************************************
+ *             SetupDiEnumDeviceInfo (SETUPAPI.@)
+ */
+BOOL WINAPI SetupDiEnumDeviceInfo(
+        HDEVINFO  devinfo,
+        DWORD  index,
+        PSP_DEVINFO_DATA info)
+{
+    BOOL ret = FALSE;
+
+    TRACE("%p %d %p\n", devinfo, index, info);
+
+    if(info==NULL)
+    {
+        SetLastError(ERROR_INVALID_PARAMETER);
+        return FALSE;
+    }
+    if (devinfo && devinfo != INVALID_HANDLE_VALUE)
+    {
+        struct DeviceInfoSet *list = devinfo;
+        if (list->magic == SETUP_DEVICE_INFO_SET_MAGIC)
+        {
+            if (index < list->cDevices)
+            {
+                if (info->cbSize == sizeof(SP_DEVINFO_DATA))
+                {
+                    struct DeviceInstance *devInst;
+                    DWORD i = 0;
+
+                    LIST_FOR_EACH_ENTRY(devInst, &list->devices,
+                            struct DeviceInstance, entry)
+                    {
+                        if (i++ == index)
+                        {
+                            *info = devInst->data;
+                            break;
+                        }
+                    }
+                    ret = TRUE;
+                }
+                else
+                    SetLastError(ERROR_INVALID_USER_BUFFER);
+            }
+            else
+                SetLastError(ERROR_NO_MORE_ITEMS);
         }
         else
             SetLastError(ERROR_INVALID_HANDLE);
@@ -1122,7 +1687,7 @@ BOOL WINAPI SetupDiGetDeviceInstanceIdW(
        DWORD DeviceInstanceIdSize,
        PDWORD RequiredSize)
 {
-    struct DeviceInfoSet *set = (struct DeviceInfoSet *)DeviceInfoSet;
+    struct DeviceInfoSet *set = DeviceInfoSet;
     struct DeviceInfo *devInfo;
 
     TRACE("%p %p %p %d %p\n", DeviceInfoSet, DeviceInfoData, DeviceInstanceId,
@@ -1145,8 +1710,13 @@ BOOL WINAPI SetupDiGetDeviceInstanceIdW(
         return FALSE;
     }
     devInfo = (struct DeviceInfo *)DeviceInfoData->Reserved;
+    if (devInfo->set != set)
+    {
+        SetLastError(ERROR_INVALID_PARAMETER);
+        return FALSE;
+    }
     TRACE("instance ID: %s\n", debugstr_w(devInfo->instanceId));
-    if (DeviceInstanceIdSize < lstrlenW(devInfo->instanceId) + 1)
+    if (DeviceInstanceIdSize < strlenW(devInfo->instanceId) + 1)
     {
         SetLastError(ERROR_INSUFFICIENT_BUFFER);
         if (RequiredSize)
@@ -1290,8 +1860,27 @@ BOOL WINAPI SetupDiGetClassDescriptionExA(
         PCSTR MachineName,
         PVOID Reserved)
 {
-  FIXME("\n");
-  return FALSE;
+    HKEY hKey;
+    DWORD dwLength;
+    BOOL ret;
+
+    hKey = SetupDiOpenClassRegKeyExA(ClassGuid,
+                                     KEY_ALL_ACCESS,
+                                     DIOCR_INSTALLER,
+                                     MachineName,
+                                     Reserved);
+    if (hKey == INVALID_HANDLE_VALUE)
+    {
+       WARN("SetupDiOpenClassRegKeyExA() failed (Error %u)\n", GetLastError());
+       return FALSE;
+    }
+
+    dwLength = ClassDescriptionSize;
+    ret = !RegQueryValueExA( hKey, NULL, NULL, NULL,
+                             (LPBYTE)ClassDescription, &dwLength );
+    if (RequiredSize) *RequiredSize = dwLength;
+    RegCloseKey(hKey);
+    return ret;
 }
 
 /***********************************************************************
@@ -1307,6 +1896,7 @@ BOOL WINAPI SetupDiGetClassDescriptionExW(
 {
     HKEY hKey;
     DWORD dwLength;
+    BOOL ret;
 
     hKey = SetupDiOpenClassRegKeyExW(ClassGuid,
                                      KEY_ALL_ACCESS,
@@ -1319,38 +1909,12 @@ BOOL WINAPI SetupDiGetClassDescriptionExW(
        return FALSE;
     }
 
-    if (RequiredSize != NULL)
-    {
-       dwLength = 0;
-       if (RegQueryValueExW(hKey,
-                            NULL,
-                            NULL,
-                            NULL,
-                            NULL,
-                            &dwLength))
-       {
-           RegCloseKey(hKey);
-           return FALSE;
-       }
-
-       *RequiredSize = dwLength / sizeof(WCHAR);
-    }
-
     dwLength = ClassDescriptionSize * sizeof(WCHAR);
-    if (RegQueryValueExW(hKey,
-                        NULL,
-                        NULL,
-                        NULL,
-                        (LPBYTE)ClassDescription,
-                        &dwLength))
-    {
-       RegCloseKey(hKey);
-       return FALSE;
-    }
-
+    ret = !RegQueryValueExW( hKey, NULL, NULL, NULL,
+                             (LPBYTE)ClassDescription, &dwLength );
+    if (RequiredSize) *RequiredSize = dwLength / sizeof(WCHAR);
     RegCloseKey(hKey);
-
-    return TRUE;
+    return ret;
 }
 
 /***********************************************************************
@@ -1371,18 +1935,394 @@ HDEVINFO WINAPI SetupDiGetClassDevsA(
         enumstrW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
         if (!enumstrW)
         {
-            ret = (HDEVINFO)INVALID_HANDLE_VALUE;
+            ret = INVALID_HANDLE_VALUE;
             goto end;
         }
         MultiByteToWideChar(CP_ACP, 0, enumstr, -1, enumstrW, len);
     }
-    ret = SetupDiGetClassDevsW(class, enumstrW, parent, flags);
+    ret = SetupDiGetClassDevsExW(class, enumstrW, parent, flags, NULL, NULL,
+            NULL);
     HeapFree(GetProcessHeap(), 0, enumstrW);
 
 end:
     return ret;
 }
 
+/***********************************************************************
+ *               SetupDiGetClassDevsExA (SETUPAPI.@)
+ */
+HDEVINFO WINAPI SetupDiGetClassDevsExA(
+        const GUID *class,
+        PCSTR enumstr,
+        HWND parent,
+        DWORD flags,
+        HDEVINFO deviceset,
+        PCSTR machine,
+        PVOID reserved)
+{
+    HDEVINFO ret;
+    LPWSTR enumstrW = NULL, machineW = NULL;
+
+    if (enumstr)
+    {
+        int len = MultiByteToWideChar(CP_ACP, 0, enumstr, -1, NULL, 0);
+        enumstrW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
+        if (!enumstrW)
+        {
+            ret = INVALID_HANDLE_VALUE;
+            goto end;
+        }
+        MultiByteToWideChar(CP_ACP, 0, enumstr, -1, enumstrW, len);
+    }
+    if (machine)
+    {
+        int len = MultiByteToWideChar(CP_ACP, 0, machine, -1, NULL, 0);
+        machineW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
+        if (!machineW)
+        {
+            HeapFree(GetProcessHeap(), 0, enumstrW);
+            ret = INVALID_HANDLE_VALUE;
+            goto end;
+        }
+        MultiByteToWideChar(CP_ACP, 0, machine, -1, machineW, len);
+    }
+    ret = SetupDiGetClassDevsExW(class, enumstrW, parent, flags, deviceset,
+            machineW, reserved);
+    HeapFree(GetProcessHeap(), 0, enumstrW);
+    HeapFree(GetProcessHeap(), 0, machineW);
+
+end:
+    return ret;
+}
+
+static void SETUPDI_AddDeviceInterfaces(SP_DEVINFO_DATA *dev, HKEY key,
+        const GUID *guid)
+{
+    DWORD i, len;
+    WCHAR subKeyName[MAX_PATH];
+    LONG l = ERROR_SUCCESS;
+
+    for (i = 0; !l; i++)
+    {
+        len = sizeof(subKeyName) / sizeof(subKeyName[0]);
+        l = RegEnumKeyExW(key, i, subKeyName, &len, NULL, NULL, NULL, NULL);
+        if (!l)
+        {
+            HKEY subKey;
+            SP_DEVICE_INTERFACE_DATA *iface = NULL;
+
+            if (*subKeyName == '#')
+            {
+                /* The subkey name is the reference string, with a '#' prepended */
+                SETUPDI_AddInterfaceInstance(dev, guid, subKeyName + 1, &iface);
+                l = RegOpenKeyExW(key, subKeyName, 0, KEY_READ, &subKey);
+                if (!l)
+                {
+                    WCHAR symbolicLink[MAX_PATH];
+                    DWORD dataType;
+
+                    len = sizeof(symbolicLink);
+                    l = RegQueryValueExW(subKey, SymbolicLink, NULL, &dataType,
+                            (BYTE *)symbolicLink, &len);
+                    if (!l && dataType == REG_SZ)
+                        SETUPDI_SetInterfaceSymbolicLink(iface, symbolicLink);
+                    RegCloseKey(subKey);
+                }
+            }
+            /* Allow enumeration to continue */
+            l = ERROR_SUCCESS;
+        }
+    }
+    /* FIXME: find and add all the device's interfaces to the device */
+}
+
+static void SETUPDI_EnumerateMatchingInterfaces(HDEVINFO DeviceInfoSet,
+        HKEY key, const GUID *guid, LPCWSTR enumstr)
+{
+    struct DeviceInfoSet *set = DeviceInfoSet;
+    DWORD i, len;
+    WCHAR subKeyName[MAX_PATH];
+    LONG l;
+    HKEY enumKey = INVALID_HANDLE_VALUE;
+
+    TRACE("%s\n", debugstr_w(enumstr));
+
+    l = RegCreateKeyExW(HKEY_LOCAL_MACHINE, Enum, 0, NULL, 0, KEY_READ, NULL,
+            &enumKey, NULL);
+    for (i = 0; !l; i++)
+    {
+        len = sizeof(subKeyName) / sizeof(subKeyName[0]);
+        l = RegEnumKeyExW(key, i, subKeyName, &len, NULL, NULL, NULL, NULL);
+        if (!l)
+        {
+            HKEY subKey;
+
+            l = RegOpenKeyExW(key, subKeyName, 0, KEY_READ, &subKey);
+            if (!l)
+            {
+                WCHAR deviceInst[MAX_PATH * 3];
+                DWORD dataType;
+
+                len = sizeof(deviceInst);
+                l = RegQueryValueExW(subKey, DeviceInstance, NULL, &dataType,
+                        (BYTE *)deviceInst, &len);
+                if (!l && dataType == REG_SZ)
+                {
+                    TRACE("found instance ID %s\n", debugstr_w(deviceInst));
+                    if (!enumstr || !lstrcmpiW(enumstr, deviceInst))
+                    {
+                        HKEY deviceKey;
+
+                        l = RegOpenKeyExW(enumKey, deviceInst, 0, KEY_READ,
+                                &deviceKey);
+                        if (!l)
+                        {
+                            WCHAR deviceClassStr[40];
+
+                            len = sizeof(deviceClassStr);
+                            l = RegQueryValueExW(deviceKey, ClassGUID, NULL,
+                                    &dataType, (BYTE *)deviceClassStr, &len);
+                            if (!l && dataType == REG_SZ &&
+                                    deviceClassStr[0] == '{' &&
+                                    deviceClassStr[37] == '}')
+                            {
+                                GUID deviceClass;
+                                SP_DEVINFO_DATA *dev;
+
+                                deviceClassStr[37] = 0;
+                                UuidFromStringW(&deviceClassStr[1],
+                                        &deviceClass);
+                                if (SETUPDI_AddDeviceToSet(set, &deviceClass,
+                                        0 /* FIXME: DevInst */, deviceInst,
+                                        FALSE, &dev))
+                                    SETUPDI_AddDeviceInterfaces(dev, subKey, guid);
+                            }
+                            RegCloseKey(deviceKey);
+                        }
+                    }
+                }
+                RegCloseKey(subKey);
+            }
+            /* Allow enumeration to continue */
+            l = ERROR_SUCCESS;
+        }
+    }
+    if (enumKey != INVALID_HANDLE_VALUE)
+        RegCloseKey(enumKey);
+}
+
+static void SETUPDI_EnumerateInterfaces(HDEVINFO DeviceInfoSet,
+        const GUID *guid, LPCWSTR enumstr, DWORD flags)
+{
+    HKEY interfacesKey = SetupDiOpenClassRegKeyExW(guid, KEY_READ,
+            DIOCR_INTERFACE, NULL, NULL);
+
+    TRACE("%p, %s, %s, %08x\n", DeviceInfoSet, debugstr_guid(guid),
+            debugstr_w(enumstr), flags);
+
+    if (interfacesKey != INVALID_HANDLE_VALUE)
+    {
+        if (flags & DIGCF_ALLCLASSES)
+        {
+            DWORD i, len;
+            WCHAR interfaceGuidStr[40];
+            LONG l = ERROR_SUCCESS;
+
+            for (i = 0; !l; i++)
+            {
+                len = sizeof(interfaceGuidStr) / sizeof(interfaceGuidStr[0]);
+                l = RegEnumKeyExW(interfacesKey, i, interfaceGuidStr, &len,
+                        NULL, NULL, NULL, NULL);
+                if (!l)
+                {
+                    if (interfaceGuidStr[0] == '{' &&
+                            interfaceGuidStr[37] == '}')
+                    {
+                        HKEY interfaceKey;
+                        GUID interfaceGuid;
+
+                        interfaceGuidStr[37] = 0;
+                        UuidFromStringW(&interfaceGuidStr[1], &interfaceGuid);
+                        l = RegOpenKeyExW(interfacesKey, interfaceGuidStr, 0,
+                                KEY_READ, &interfaceKey);
+                        if (!l)
+                        {
+                            SETUPDI_EnumerateMatchingInterfaces(DeviceInfoSet,
+                                    interfaceKey, &interfaceGuid, enumstr);
+                            RegCloseKey(interfaceKey);
+                        }
+                    }
+                }
+            }
+        }
+        else
+        {
+            /* In this case, SetupDiOpenClassRegKeyExW opened the specific
+             * interface's key, so just pass that long
+             */
+            SETUPDI_EnumerateMatchingInterfaces(DeviceInfoSet,
+                    interfacesKey, guid, enumstr);
+        }
+        RegCloseKey(interfacesKey);
+    }
+}
+
+static void SETUPDI_EnumerateMatchingDeviceInstances(struct DeviceInfoSet *set,
+        LPCWSTR enumerator, LPCWSTR deviceName, HKEY deviceKey,
+        const GUID *class, DWORD flags)
+{
+    DWORD i, len;
+    WCHAR deviceInstance[MAX_PATH];
+    LONG l = ERROR_SUCCESS;
+
+    TRACE("%s %s\n", debugstr_w(enumerator), debugstr_w(deviceName));
+
+    for (i = 0; !l; i++)
+    {
+        len = sizeof(deviceInstance) / sizeof(deviceInstance[0]);
+        l = RegEnumKeyExW(deviceKey, i, deviceInstance, &len, NULL, NULL, NULL,
+                NULL);
+        if (!l)
+        {
+            HKEY subKey;
+
+            l = RegOpenKeyExW(deviceKey, deviceInstance, 0, KEY_READ, &subKey);
+            if (!l)
+            {
+                WCHAR classGuid[40];
+                DWORD dataType;
+
+                len = sizeof(classGuid);
+                l = RegQueryValueExW(subKey, ClassGUID, NULL, &dataType,
+                        (BYTE *)classGuid, &len);
+                if (!l && dataType == REG_SZ)
+                {
+                    if (classGuid[0] == '{' && classGuid[37] == '}')
+                    {
+                        GUID deviceClass;
+
+                        classGuid[37] = 0;
+                        UuidFromStringW(&classGuid[1], &deviceClass);
+                        if ((flags & DIGCF_ALLCLASSES) ||
+                                IsEqualGUID(class, &deviceClass))
+                        {
+                            static const WCHAR fmt[] =
+                             {'%','s','\\','%','s','\\','%','s',0};
+                            LPWSTR instanceId;
+
+                            instanceId = HeapAlloc(GetProcessHeap(), 0,
+                                (lstrlenW(enumerator) + lstrlenW(deviceName) +
+                                lstrlenW(deviceInstance) + 3) * sizeof(WCHAR));
+                            if (instanceId)
+                            {
+                                sprintfW(instanceId, fmt, enumerator,
+                                        deviceName, deviceInstance);
+                                SETUPDI_AddDeviceToSet(set, &deviceClass,
+                                        0 /* FIXME: DevInst */, instanceId,
+                                        FALSE, NULL);
+                                HeapFree(GetProcessHeap(), 0, instanceId);
+                            }
+                        }
+                    }
+                }
+                RegCloseKey(subKey);
+            }
+            /* Allow enumeration to continue */
+            l = ERROR_SUCCESS;
+        }
+    }
+}
+
+static void SETUPDI_EnumerateMatchingDevices(HDEVINFO DeviceInfoSet,
+        LPCWSTR parent, HKEY key, const GUID *class, DWORD flags)
+{
+    struct DeviceInfoSet *set = DeviceInfoSet;
+    DWORD i, len;
+    WCHAR subKeyName[MAX_PATH];
+    LONG l = ERROR_SUCCESS;
+
+    TRACE("%s\n", debugstr_w(parent));
+
+    for (i = 0; !l; i++)
+    {
+        len = sizeof(subKeyName) / sizeof(subKeyName[0]);
+        l = RegEnumKeyExW(key, i, subKeyName, &len, NULL, NULL, NULL, NULL);
+        if (!l)
+        {
+            HKEY subKey;
+
+            l = RegOpenKeyExW(key, subKeyName, 0, KEY_READ, &subKey);
+            if (!l)
+            {
+                TRACE("%s\n", debugstr_w(subKeyName));
+                SETUPDI_EnumerateMatchingDeviceInstances(set, parent,
+                        subKeyName, subKey, class, flags);
+                RegCloseKey(subKey);
+            }
+            /* Allow enumeration to continue */
+            l = ERROR_SUCCESS;
+        }
+    }
+}
+
+static void SETUPDI_EnumerateDevices(HDEVINFO DeviceInfoSet, const GUID *class,
+        LPCWSTR enumstr, DWORD flags)
+{
+    HKEY enumKey;
+    LONG l;
+
+    TRACE("%p, %s, %s, %08x\n", DeviceInfoSet, debugstr_guid(class),
+            debugstr_w(enumstr), flags);
+
+    l = RegCreateKeyExW(HKEY_LOCAL_MACHINE, Enum, 0, NULL, 0, KEY_READ, NULL,
+            &enumKey, NULL);
+    if (enumKey != INVALID_HANDLE_VALUE)
+    {
+        if (enumstr)
+        {
+            HKEY enumStrKey;
+
+            l = RegOpenKeyExW(enumKey, enumstr, 0, KEY_READ,
+                    &enumStrKey);
+            if (!l)
+            {
+                SETUPDI_EnumerateMatchingDevices(DeviceInfoSet, enumstr,
+                        enumStrKey, class, flags);
+                RegCloseKey(enumStrKey);
+            }
+        }
+        else
+        {
+            DWORD i, len;
+            WCHAR subKeyName[MAX_PATH];
+
+            l = ERROR_SUCCESS;
+            for (i = 0; !l; i++)
+            {
+                len = sizeof(subKeyName) / sizeof(subKeyName[0]);
+                l = RegEnumKeyExW(enumKey, i, subKeyName, &len, NULL,
+                        NULL, NULL, NULL);
+                if (!l)
+                {
+                    HKEY subKey;
+
+                    l = RegOpenKeyExW(enumKey, subKeyName, 0, KEY_READ,
+                            &subKey);
+                    if (!l)
+                    {
+                        SETUPDI_EnumerateMatchingDevices(DeviceInfoSet,
+                                subKeyName, subKey, class, flags);
+                        RegCloseKey(subKey);
+                    }
+                    /* Allow enumeration to continue */
+                    l = ERROR_SUCCESS;
+                }
+            }
+        }
+        RegCloseKey(enumKey);
+    }
+}
+
 /***********************************************************************
  *             SetupDiGetClassDevsW (SETUPAPI.@)
  */
@@ -1392,11 +2332,8 @@ HDEVINFO WINAPI SetupDiGetClassDevsW(
        HWND parent,
        DWORD flags)
 {
-    TRACE("%s %s %p 0x%08x\n", debugstr_guid(class), debugstr_w(enumstr), parent, flags);
-
-    /* WinXP always succeeds, returns empty list for unknown classes */
-    FIXME("returning empty list\n");
-    return SetupDiCreateDeviceInfoList(class, parent);
+    return SetupDiGetClassDevsExW(class, enumstr, parent, flags, NULL, NULL,
+            NULL);
 }
 
 /***********************************************************************
@@ -1411,35 +2348,527 @@ HDEVINFO WINAPI SetupDiGetClassDevsExW(
         PCWSTR machine,
         PVOID reserved)
 {
-    FIXME("stub\n");
-    return NULL;
+    static const DWORD unsupportedFlags = DIGCF_DEFAULT | DIGCF_PRESENT |
+        DIGCF_PROFILE;
+    HDEVINFO set;
+
+    TRACE("%s %s %p 0x%08x %p %s %p\n", debugstr_guid(class),
+            debugstr_w(enumstr), parent, flags, deviceset, debugstr_w(machine),
+            reserved);
+
+    if (!(flags & DIGCF_ALLCLASSES) && !class)
+    {
+        SetLastError(ERROR_INVALID_PARAMETER);
+        return NULL;
+    }
+    if (flags & unsupportedFlags)
+        WARN("unsupported flags %08x\n", flags & unsupportedFlags);
+    if (deviceset)
+        set = deviceset;
+    else
+        set = SetupDiCreateDeviceInfoListExW(class, parent, machine, reserved);
+    if (set)
+    {
+        if (machine)
+            FIXME("%s: unimplemented for remote machines\n",
+                    debugstr_w(machine));
+        else if (flags & DIGCF_DEVICEINTERFACE)
+            SETUPDI_EnumerateInterfaces(set, class, enumstr, flags);
+        else
+            SETUPDI_EnumerateDevices(set, class, enumstr, flags);
+    }
+    return set;
+}
+
+/***********************************************************************
+ *             SetupDiGetDeviceInfoListDetailA  (SETUPAPI.@)
+ */
+BOOL WINAPI SetupDiGetDeviceInfoListDetailA(
+        HDEVINFO DeviceInfoSet,
+        PSP_DEVINFO_LIST_DETAIL_DATA_A DevInfoData )
+{
+    struct DeviceInfoSet *set = DeviceInfoSet;
+
+    TRACE("%p %p\n", DeviceInfoSet, DevInfoData);
+
+    if (!DeviceInfoSet || DeviceInfoSet == INVALID_HANDLE_VALUE)
+    {
+        SetLastError(ERROR_INVALID_HANDLE);
+        return FALSE;
+    }
+    if (set->magic != SETUP_DEVICE_INFO_SET_MAGIC)
+    {
+        SetLastError(ERROR_INVALID_HANDLE);
+        return FALSE;
+    }
+    if (!DevInfoData ||
+            DevInfoData->cbSize != sizeof(SP_DEVINFO_LIST_DETAIL_DATA_A))
+    {
+        SetLastError(ERROR_INVALID_PARAMETER);
+        return FALSE;
+    }
+    DevInfoData->ClassGuid = set->ClassGuid;
+    DevInfoData->RemoteMachineHandle = NULL;
+    DevInfoData->RemoteMachineName[0] = '\0';
+    return TRUE;
+}
+
+/***********************************************************************
+ *             SetupDiGetDeviceInfoListDetailW  (SETUPAPI.@)
+ */
+BOOL WINAPI SetupDiGetDeviceInfoListDetailW(
+        HDEVINFO DeviceInfoSet,
+        PSP_DEVINFO_LIST_DETAIL_DATA_W DevInfoData )
+{
+    struct DeviceInfoSet *set = DeviceInfoSet;
+
+    TRACE("%p %p\n", DeviceInfoSet, DevInfoData);
+
+    if (!DeviceInfoSet || DeviceInfoSet == INVALID_HANDLE_VALUE)
+    {
+        SetLastError(ERROR_INVALID_HANDLE);
+        return FALSE;
+    }
+    if (set->magic != SETUP_DEVICE_INFO_SET_MAGIC)
+    {
+        SetLastError(ERROR_INVALID_HANDLE);
+        return FALSE;
+    }
+    if (!DevInfoData ||
+            DevInfoData->cbSize != sizeof(SP_DEVINFO_LIST_DETAIL_DATA_W))
+    {
+        SetLastError(ERROR_INVALID_PARAMETER);
+        return FALSE;
+    }
+    DevInfoData->ClassGuid = set->ClassGuid;
+    DevInfoData->RemoteMachineHandle = NULL;
+    DevInfoData->RemoteMachineName[0] = '\0';
+    return TRUE;
+}
+
+/***********************************************************************
+ *             SetupDiCreateDeviceInterfaceA (SETUPAPI.@)
+ */
+BOOL WINAPI SetupDiCreateDeviceInterfaceA(
+        HDEVINFO DeviceInfoSet,
+        PSP_DEVINFO_DATA DeviceInfoData,
+        const GUID *InterfaceClassGuid,
+        PCSTR ReferenceString,
+        DWORD CreationFlags,
+        PSP_DEVICE_INTERFACE_DATA DeviceInterfaceData)
+{
+    BOOL ret;
+    LPWSTR ReferenceStringW = NULL;
+
+    TRACE("%p %p %s %s %08x %p\n", DeviceInfoSet, DeviceInfoData,
+            debugstr_guid(InterfaceClassGuid), debugstr_a(ReferenceString),
+            CreationFlags, DeviceInterfaceData);
+
+    if (ReferenceString)
+    {
+        ReferenceStringW = MultiByteToUnicode(ReferenceString, CP_ACP);
+        if (ReferenceStringW == NULL) return FALSE;
+    }
+
+    ret = SetupDiCreateDeviceInterfaceW(DeviceInfoSet, DeviceInfoData,
+            InterfaceClassGuid, ReferenceStringW, CreationFlags,
+            DeviceInterfaceData);
+
+    MyFree(ReferenceStringW);
+
+    return ret;
+}
+
+/***********************************************************************
+ *             SetupDiCreateDeviceInterfaceW (SETUPAPI.@)
+ */
+BOOL WINAPI SetupDiCreateDeviceInterfaceW(
+        HDEVINFO DeviceInfoSet,
+        PSP_DEVINFO_DATA DeviceInfoData,
+        const GUID *InterfaceClassGuid,
+        PCWSTR ReferenceString,
+        DWORD CreationFlags,
+        PSP_DEVICE_INTERFACE_DATA DeviceInterfaceData)
+{
+    struct DeviceInfoSet *set = DeviceInfoSet;
+    struct DeviceInfo *devInfo;
+    SP_DEVICE_INTERFACE_DATA *iface = NULL;
+    BOOL ret;
+
+    TRACE("%p %p %s %s %08x %p\n", DeviceInfoSet, DeviceInfoData,
+            debugstr_guid(InterfaceClassGuid), debugstr_w(ReferenceString),
+            CreationFlags, DeviceInterfaceData);
+
+    if (!DeviceInfoSet || DeviceInfoSet == INVALID_HANDLE_VALUE)
+    {
+        SetLastError(ERROR_INVALID_HANDLE);
+        return FALSE;
+    }
+    if (set->magic != SETUP_DEVICE_INFO_SET_MAGIC)
+    {
+        SetLastError(ERROR_INVALID_HANDLE);
+        return FALSE;
+    }
+    if (!DeviceInfoData || DeviceInfoData->cbSize != sizeof(SP_DEVINFO_DATA)
+            || !DeviceInfoData->Reserved)
+    {
+        SetLastError(ERROR_INVALID_PARAMETER);
+        return FALSE;
+    }
+    devInfo = (struct DeviceInfo *)DeviceInfoData->Reserved;
+    if (devInfo->set != set)
+    {
+        SetLastError(ERROR_INVALID_PARAMETER);
+        return FALSE;
+    }
+    if (!InterfaceClassGuid)
+    {
+        SetLastError(ERROR_INVALID_USER_BUFFER);
+        return FALSE;
+    }
+    if ((ret = SETUPDI_AddInterfaceInstance(DeviceInfoData, InterfaceClassGuid,
+                    ReferenceString, &iface)))
+    {
+        if (DeviceInterfaceData)
+        {
+            if (DeviceInterfaceData->cbSize != sizeof(SP_DEVICE_INTERFACE_DATA))
+            {
+                SetLastError(ERROR_INVALID_USER_BUFFER);
+                ret = FALSE;
+            }
+            else
+                *DeviceInterfaceData = *iface;
+        }
+    }
+    return ret;
+}
+
+/***********************************************************************
+ *             SetupDiCreateDeviceInterfaceRegKeyA (SETUPAPI.@)
+ */
+HKEY WINAPI SetupDiCreateDeviceInterfaceRegKeyA(
+        HDEVINFO DeviceInfoSet,
+        PSP_DEVICE_INTERFACE_DATA DeviceInterfaceData,
+        DWORD Reserved,
+        REGSAM samDesired,
+        HINF InfHandle,
+        PCSTR InfSectionName)
+{
+    HKEY key;
+    PWSTR InfSectionNameW = NULL;
+
+    TRACE("%p %p %d %08x %p %p\n", DeviceInfoSet, DeviceInterfaceData, Reserved,
+            samDesired, InfHandle, InfSectionName);
+    if (InfHandle)
+    {
+        if (!InfSectionName)
+        {
+            SetLastError(ERROR_INVALID_PARAMETER);
+            return INVALID_HANDLE_VALUE;
+        }
+        InfSectionNameW = MultiByteToUnicode(InfSectionName, CP_ACP);
+        if (!InfSectionNameW)
+            return INVALID_HANDLE_VALUE;
+    }
+    key = SetupDiCreateDeviceInterfaceRegKeyW(DeviceInfoSet,
+            DeviceInterfaceData, Reserved, samDesired, InfHandle,
+            InfSectionNameW);
+    MyFree(InfSectionNameW);
+    return key;
+}
+
+static PWSTR SETUPDI_GetInstancePath(struct InterfaceInfo *ifaceInfo)
+{
+    static const WCHAR hash[] = {'#',0};
+    PWSTR instancePath = NULL;
+
+    if (ifaceInfo->referenceString)
+    {
+        instancePath = HeapAlloc(GetProcessHeap(), 0,
+                (lstrlenW(ifaceInfo->referenceString) + 2) * sizeof(WCHAR));
+        if (instancePath)
+        {
+            lstrcpyW(instancePath, hash);
+            lstrcatW(instancePath, ifaceInfo->referenceString);
+        }
+        else
+            SetLastError(ERROR_OUTOFMEMORY);
+    }
+    else
+    {
+        instancePath = HeapAlloc(GetProcessHeap(), 0,
+                (lstrlenW(hash) + 1) * sizeof(WCHAR));
+        if (instancePath)
+            lstrcpyW(instancePath, hash);
+    }
+    return instancePath;
+}
+
+/***********************************************************************
+ *             SetupDiCreateDeviceInterfaceRegKeyW (SETUPAPI.@)
+ */
+HKEY WINAPI SetupDiCreateDeviceInterfaceRegKeyW(
+        HDEVINFO DeviceInfoSet,
+        PSP_DEVICE_INTERFACE_DATA DeviceInterfaceData,
+        DWORD Reserved,
+        REGSAM samDesired,
+        HINF InfHandle,
+        PCWSTR InfSectionName)
+{
+    struct DeviceInfoSet *set = DeviceInfoSet;
+    HKEY key = INVALID_HANDLE_VALUE, interfacesKey;
+    LONG l;
+
+    TRACE("%p %p %d %08x %p %p\n", DeviceInfoSet, DeviceInterfaceData, Reserved,
+            samDesired, InfHandle, InfSectionName);
+
+    if (!DeviceInfoSet || DeviceInfoSet == INVALID_HANDLE_VALUE ||
+            set->magic != SETUP_DEVICE_INFO_SET_MAGIC)
+    {
+        SetLastError(ERROR_INVALID_HANDLE);
+        return INVALID_HANDLE_VALUE;
+    }
+    if (!DeviceInterfaceData ||
+            DeviceInterfaceData->cbSize != sizeof(SP_DEVICE_INTERFACE_DATA) ||
+            !DeviceInterfaceData->Reserved)
+    {
+        SetLastError(ERROR_INVALID_PARAMETER);
+        return INVALID_HANDLE_VALUE;
+    }
+    if (InfHandle && !InfSectionName)
+    {
+        SetLastError(ERROR_INVALID_PARAMETER);
+        return INVALID_HANDLE_VALUE;
+    }
+    if (!(l = RegCreateKeyExW(HKEY_LOCAL_MACHINE, DeviceClasses, 0, NULL, 0,
+                    samDesired, NULL, &interfacesKey, NULL)))
+    {
+        HKEY parent;
+        WCHAR bracedGuidString[39];
+
+        SETUPDI_GuidToString(&DeviceInterfaceData->InterfaceClassGuid,
+                bracedGuidString);
+        if (!(l = RegCreateKeyExW(interfacesKey, bracedGuidString, 0, NULL, 0,
+                        samDesired, NULL, &parent, NULL)))
+        {
+            struct InterfaceInfo *ifaceInfo =
+                (struct InterfaceInfo *)DeviceInterfaceData->Reserved;
+            PWSTR instancePath = SETUPDI_GetInstancePath(ifaceInfo);
+            PWSTR interfKeyName = HeapAlloc(GetProcessHeap(), 0,
+                    (lstrlenW(ifaceInfo->symbolicLink) + 1) * sizeof(WCHAR));
+            HKEY interfKey;
+            WCHAR *ptr;
+
+            lstrcpyW(interfKeyName, ifaceInfo->symbolicLink);
+            if (lstrlenW(ifaceInfo->symbolicLink) > 3)
+            {
+                interfKeyName[0] = '#';
+                interfKeyName[1] = '#';
+                interfKeyName[3] = '#';
+            }
+            ptr = strchrW(interfKeyName, '\\');
+            if (ptr)
+                *ptr = 0;
+            l = RegCreateKeyExW(parent, interfKeyName, 0, NULL, 0,
+                    samDesired, NULL, &interfKey, NULL);
+            if (!l)
+            {
+                struct DeviceInfo *devInfo =
+                        (struct DeviceInfo *)ifaceInfo->device->Reserved;
+
+                l = RegSetValueExW(interfKey, DeviceInstance, 0, REG_SZ,
+                        (BYTE *)devInfo->instanceId,
+                        (lstrlenW(devInfo->instanceId) + 1) * sizeof(WCHAR));
+                if (!l)
+                {
+                    if (instancePath)
+                    {
+                        LONG l;
+
+                        l = RegCreateKeyExW(interfKey, instancePath, 0, NULL, 0,
+                                samDesired, NULL, &key, NULL);
+                        if (l)
+                        {
+                            SetLastError(l);
+                            key = INVALID_HANDLE_VALUE;
+                        }
+                        else if (InfHandle)
+                            FIXME("INF section installation unsupported\n");
+                    }
+                }
+                else
+                    SetLastError(l);
+                RegCloseKey(interfKey);
+            }
+            else
+                SetLastError(l);
+            HeapFree(GetProcessHeap(), 0, interfKeyName);
+            HeapFree(GetProcessHeap(), 0, instancePath);
+            RegCloseKey(parent);
+        }
+        else
+            SetLastError(l);
+        RegCloseKey(interfacesKey);
+    }
+    else
+        SetLastError(l);
+    return key;
+}
+
+/***********************************************************************
+ *             SetupDiDeleteDeviceInterfaceRegKey (SETUPAPI.@)
+ */
+BOOL WINAPI SetupDiDeleteDeviceInterfaceRegKey(
+        HDEVINFO DeviceInfoSet,
+        PSP_DEVICE_INTERFACE_DATA DeviceInterfaceData,
+        DWORD Reserved)
+{
+    struct DeviceInfoSet *set = DeviceInfoSet;
+    HKEY parent;
+    BOOL ret = FALSE;
+
+    TRACE("%p %p %d\n", DeviceInfoSet, DeviceInterfaceData, Reserved);
+
+    if (!DeviceInfoSet || DeviceInfoSet == INVALID_HANDLE_VALUE ||
+            set->magic != SETUP_DEVICE_INFO_SET_MAGIC)
+    {
+        SetLastError(ERROR_INVALID_HANDLE);
+        return FALSE;
+    }
+    if (!DeviceInterfaceData ||
+            DeviceInterfaceData->cbSize != sizeof(SP_DEVICE_INTERFACE_DATA) ||
+            !DeviceInterfaceData->Reserved)
+    {
+        SetLastError(ERROR_INVALID_PARAMETER);
+        return FALSE;
+    }
+    parent = SetupDiOpenClassRegKeyExW(&DeviceInterfaceData->InterfaceClassGuid,
+            KEY_ALL_ACCESS, DIOCR_INTERFACE, NULL, NULL);
+    if (parent != INVALID_HANDLE_VALUE)
+    {
+        struct InterfaceInfo *ifaceInfo =
+            (struct InterfaceInfo *)DeviceInterfaceData->Reserved;
+        PWSTR instancePath = SETUPDI_GetInstancePath(ifaceInfo);
+
+        if (instancePath)
+        {
+            LONG l = RegDeleteKeyW(parent, instancePath);
+
+            if (l)
+                SetLastError(l);
+            else
+                ret = TRUE;
+            HeapFree(GetProcessHeap(), 0, instancePath);
+        }
+        RegCloseKey(parent);
+    }
+    return ret;
 }
 
 /***********************************************************************
  *             SetupDiEnumDeviceInterfaces (SETUPAPI.@)
+ *
+ * PARAMS
+ *   DeviceInfoSet      [I]    Set of devices from which to enumerate
+ *                             interfaces
+ *   DeviceInfoData     [I]    (Optional) If specified, a specific device
+ *                             instance from which to enumerate interfaces.
+ *                             If it isn't specified, all interfaces for all
+ *                             devices in the set are enumerated.
+ *   InterfaceClassGuid [I]    The interface class to enumerate.
+ *   MemberIndex        [I]    An index of the interface instance to enumerate.
+ *                             A caller should start with MemberIndex set to 0,
+ *                             and continue until the function fails with
+ *                             ERROR_NO_MORE_ITEMS.
+ *   DeviceInterfaceData [I/O] Returns an enumerated interface.  Its cbSize
+ *                             member must be set to
+ *                             sizeof(SP_DEVICE_INTERFACE_DATA).
+ *
+ * RETURNS
+ *   Success: non-zero value.
+ *   Failure: FALSE.  Call GetLastError() for more info.
  */
 BOOL WINAPI SetupDiEnumDeviceInterfaces(
-       HDEVINFO devinfo,
+       HDEVINFO DeviceInfoSet,
        PSP_DEVINFO_DATA DeviceInfoData,
        CONST GUID * InterfaceClassGuid,
        DWORD MemberIndex,
        PSP_DEVICE_INTERFACE_DATA DeviceInterfaceData)
 {
+    struct DeviceInfoSet *set = DeviceInfoSet;
     BOOL ret = FALSE;
 
-    FIXME("%p, %p, %s, 0x%08x, %p\n", devinfo, DeviceInfoData,
+    TRACE("%p, %p, %s, %d, %p\n", DeviceInfoSet, DeviceInfoData,
      debugstr_guid(InterfaceClassGuid), MemberIndex, DeviceInterfaceData);
 
-    if (devinfo && devinfo != (HDEVINFO)INVALID_HANDLE_VALUE)
-    {
-        struct DeviceInfoSet *list = (struct DeviceInfoSet *)devinfo;
-        if (list->magic == SETUP_DEVICE_INFO_SET_MAGIC)
+    if (!DeviceInfoSet || DeviceInfoSet == INVALID_HANDLE_VALUE ||
+            set->magic != SETUP_DEVICE_INFO_SET_MAGIC)
+    {
+        SetLastError(ERROR_INVALID_HANDLE);
+        return FALSE;
+    }
+    if (DeviceInfoData && (DeviceInfoData->cbSize != sizeof(SP_DEVINFO_DATA) ||
+                !DeviceInfoData->Reserved))
+    {
+        SetLastError(ERROR_INVALID_PARAMETER);
+        return FALSE;
+    }
+    if (!DeviceInterfaceData ||
+            DeviceInterfaceData->cbSize != sizeof(SP_DEVICE_INTERFACE_DATA))
+    {
+        SetLastError(ERROR_INVALID_PARAMETER);
+        return FALSE;
+    }
+    if (DeviceInfoData)
+    {
+        struct DeviceInfo *devInfo =
+            (struct DeviceInfo *)DeviceInfoData->Reserved;
+        struct InterfaceInstances *iface;
+
+        if ((ret = SETUPDI_FindInterface(devInfo, InterfaceClassGuid, &iface)))
+        {
+            if (MemberIndex < iface->cInstances)
+                *DeviceInterfaceData = iface->instances[MemberIndex];
+            else
+            {
+                SetLastError(ERROR_NO_MORE_ITEMS);
+                ret = FALSE;
+            }
+        }
+        else
+            SetLastError(ERROR_NO_MORE_ITEMS);
+    }
+    else
+    {
+        struct DeviceInstance *devInst;
+        DWORD cEnumerated = 0;
+        BOOL found = FALSE;
+
+        LIST_FOR_EACH_ENTRY(devInst, &set->devices, struct DeviceInstance, entry)
+        {
+            struct DeviceInfo *devInfo = (struct DeviceInfo *)devInst->data.Reserved;
+            struct InterfaceInstances *iface;
+
+            if (found || cEnumerated >= MemberIndex + 1)
+                break;
+            if (SETUPDI_FindInterface(devInfo, InterfaceClassGuid, &iface))
+            {
+                if (cEnumerated + iface->cInstances < MemberIndex + 1)
+                    cEnumerated += iface->cInstances;
+                else
+                {
+                    DWORD instanceIndex = MemberIndex - cEnumerated;
+
+                    *DeviceInterfaceData = iface->instances[instanceIndex];
+                    cEnumerated += instanceIndex + 1;
+                    found = TRUE;
+                    ret = TRUE;
+                }
+            }
+        }
+        if (!found)
             SetLastError(ERROR_NO_MORE_ITEMS);
-        else
-            SetLastError(ERROR_INVALID_HANDLE);
     }
-    else
-        SetLastError(ERROR_INVALID_HANDLE);
     return ret;
 }
 
@@ -1460,18 +2889,21 @@ BOOL WINAPI SetupDiDestroyDeviceInfoList(HDEVINFO devinfo)
     BOOL ret = FALSE;
 
     TRACE("%p\n", devinfo);
-    if (devinfo && devinfo != (HDEVINFO)INVALID_HANDLE_VALUE)
+    if (devinfo && devinfo != INVALID_HANDLE_VALUE)
     {
-        struct DeviceInfoSet *list = (struct DeviceInfoSet *)devinfo;
+        struct DeviceInfoSet *list = devinfo;
 
         if (list->magic == SETUP_DEVICE_INFO_SET_MAGIC)
         {
-            DWORD i;
+            struct DeviceInstance *devInst, *devInst2;
 
-            for (i = 0; i < list->cDevices; i++)
-                SETUPDI_FreeDeviceInfo(
-                        (struct DeviceInfo *)list->devices[i].Reserved);
-            HeapFree(GetProcessHeap(), 0, list->devices);
+            LIST_FOR_EACH_ENTRY_SAFE(devInst, devInst2, &list->devices,
+                    struct DeviceInstance, entry)
+            {
+                SETUPDI_FreeDeviceInfo( (struct DeviceInfo *)devInst->data.Reserved );
+                list_remove(&devInst->entry);
+                HeapFree(GetProcessHeap(), 0, devInst);
+            }
             HeapFree(GetProcessHeap(), 0, list);
             ret = TRUE;
         }
@@ -1494,13 +2926,63 @@ BOOL WINAPI SetupDiGetDeviceInterfaceDetailA(
       PDWORD RequiredSize,
       PSP_DEVINFO_DATA DeviceInfoData)
 {
+    struct DeviceInfoSet *set = DeviceInfoSet;
+    struct InterfaceInfo *info;
+    DWORD bytesNeeded = FIELD_OFFSET(SP_DEVICE_INTERFACE_DETAIL_DATA_A, DevicePath[1]);
     BOOL ret = FALSE;
 
-    FIXME("(%p, %p, %p, %d, %p, %p)\n", DeviceInfoSet,
+    TRACE("(%p, %p, %p, %d, %p, %p)\n", DeviceInfoSet,
      DeviceInterfaceData, DeviceInterfaceDetailData,
      DeviceInterfaceDetailDataSize, RequiredSize, DeviceInfoData);
 
-    SetLastError(ERROR_INVALID_HANDLE);
+    if (!DeviceInfoSet || DeviceInfoSet == INVALID_HANDLE_VALUE ||
+            set->magic != SETUP_DEVICE_INFO_SET_MAGIC)
+    {
+        SetLastError(ERROR_INVALID_HANDLE);
+        return FALSE;
+    }
+    if (!DeviceInterfaceData ||
+            DeviceInterfaceData->cbSize != sizeof(SP_DEVICE_INTERFACE_DATA) ||
+            !DeviceInterfaceData->Reserved)
+    {
+        SetLastError(ERROR_INVALID_PARAMETER);
+        return FALSE;
+    }
+    if (DeviceInterfaceDetailData &&
+        DeviceInterfaceDetailData->cbSize != sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA_A))
+    {
+        SetLastError(ERROR_INVALID_USER_BUFFER);
+        return FALSE;
+    }
+    if (!DeviceInterfaceDetailData && DeviceInterfaceDetailDataSize)
+    {
+        SetLastError(ERROR_INVALID_USER_BUFFER);
+        return FALSE;
+    }
+    info = (struct InterfaceInfo *)DeviceInterfaceData->Reserved;
+    if (info->symbolicLink)
+        bytesNeeded += WideCharToMultiByte(CP_ACP, 0, info->symbolicLink, -1,
+                NULL, 0, NULL, NULL);
+    if (DeviceInterfaceDetailDataSize >= bytesNeeded)
+    {
+        if (info->symbolicLink)
+            WideCharToMultiByte(CP_ACP, 0, info->symbolicLink, -1,
+                    DeviceInterfaceDetailData->DevicePath,
+                    DeviceInterfaceDetailDataSize -
+                    offsetof(SP_DEVICE_INTERFACE_DETAIL_DATA_A, DevicePath),
+                    NULL, NULL);
+        else
+            DeviceInterfaceDetailData->DevicePath[0] = '\0';
+        if (DeviceInfoData && DeviceInfoData->cbSize == sizeof(SP_DEVINFO_DATA))
+            *DeviceInfoData = *info->device;
+        ret = TRUE;
+    }
+    else
+    {
+        if (RequiredSize)
+            *RequiredSize = bytesNeeded;
+        SetLastError(ERROR_INSUFFICIENT_BUFFER);
+    }
     return ret;
 }
 
@@ -1515,10 +2997,61 @@ BOOL WINAPI SetupDiGetDeviceInterfaceDetailW(
       PDWORD RequiredSize,
       PSP_DEVINFO_DATA DeviceInfoData)
 {
-    FIXME("(%p, %p, %p, %d, %p, %p): stub\n", DeviceInfoSet,
+    struct DeviceInfoSet *set = DeviceInfoSet;
+    struct InterfaceInfo *info;
+    DWORD bytesNeeded = offsetof(SP_DEVICE_INTERFACE_DETAIL_DATA_W, DevicePath)
+        + sizeof(WCHAR); /* include NULL terminator */
+    BOOL ret = FALSE;
+
+    TRACE("(%p, %p, %p, %d, %p, %p)\n", DeviceInfoSet,
      DeviceInterfaceData, DeviceInterfaceDetailData,
      DeviceInterfaceDetailDataSize, RequiredSize, DeviceInfoData);
-    return FALSE;
+
+    if (!DeviceInfoSet || DeviceInfoSet == INVALID_HANDLE_VALUE ||
+            set->magic != SETUP_DEVICE_INFO_SET_MAGIC)
+    {
+        SetLastError(ERROR_INVALID_HANDLE);
+        return FALSE;
+    }
+    if (!DeviceInterfaceData ||
+            DeviceInterfaceData->cbSize != sizeof(SP_DEVICE_INTERFACE_DATA) ||
+            !DeviceInterfaceData->Reserved)
+    {
+        SetLastError(ERROR_INVALID_PARAMETER);
+        return FALSE;
+    }
+    if (DeviceInterfaceDetailData && (DeviceInterfaceDetailData->cbSize <
+            offsetof(SP_DEVICE_INTERFACE_DETAIL_DATA_W, DevicePath) + sizeof(WCHAR) ||
+            DeviceInterfaceDetailData->cbSize > sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA_W)))
+    {
+        SetLastError(ERROR_INVALID_USER_BUFFER);
+        return FALSE;
+    }
+    if (!DeviceInterfaceDetailData && DeviceInterfaceDetailDataSize)
+    {
+        SetLastError(ERROR_INVALID_USER_BUFFER);
+        return FALSE;
+    }
+    info = (struct InterfaceInfo *)DeviceInterfaceData->Reserved;
+    if (info->symbolicLink)
+        bytesNeeded += sizeof(WCHAR)*lstrlenW(info->symbolicLink);
+    if (DeviceInterfaceDetailDataSize >= bytesNeeded)
+    {
+        if (info->symbolicLink)
+            lstrcpyW(DeviceInterfaceDetailData->DevicePath, info->symbolicLink);
+        else
+            DeviceInterfaceDetailData->DevicePath[0] = '\0';
+        if (DeviceInfoData && DeviceInfoData->cbSize == sizeof(SP_DEVINFO_DATA))
+            *DeviceInfoData = *info->device;
+        ret = TRUE;
+    }
+    else
+    {
+        if (RequiredSize)
+            *RequiredSize = bytesNeeded;
+        SetLastError(ERROR_INSUFFICIENT_BUFFER);
+    }
+    return ret;
 }
 
 struct PropertyMapEntry
@@ -1528,7 +3061,7 @@ struct PropertyMapEntry
     LPCWSTR nameW;
 };
 
-static struct PropertyMapEntry PropertyMap[] = {
+static const struct PropertyMapEntry PropertyMap[] = {
     { REG_SZ, "DeviceDesc", DeviceDesc },
     { REG_MULTI_SZ, "HardwareId", HardwareId },
     { REG_MULTI_SZ, "CompatibleIDs", CompatibleIDs },
@@ -1563,7 +3096,7 @@ BOOL WINAPI SetupDiGetDeviceRegistryPropertyA(
         PDWORD  RequiredSize)
 {
     BOOL ret = FALSE;
-    struct DeviceInfoSet *set = (struct DeviceInfoSet *)DeviceInfoSet;
+    struct DeviceInfoSet *set = DeviceInfoSet;
     struct DeviceInfo *devInfo;
 
     TRACE("%04x %p %d %p %p %d %p\n", (DWORD)DeviceInfoSet, DeviceInfoData,
@@ -1586,6 +3119,11 @@ BOOL WINAPI SetupDiGetDeviceRegistryPropertyA(
         SetLastError(ERROR_INVALID_PARAMETER);
         return FALSE;
     }
+    if (PropertyBufferSize && PropertyBuffer == NULL)
+    {
+        SetLastError(ERROR_INVALID_DATA);
+        return FALSE;
+    }
     devInfo = (struct DeviceInfo *)DeviceInfoData->Reserved;
     if (Property < sizeof(PropertyMap) / sizeof(PropertyMap[0])
         && PropertyMap[Property].nameA)
@@ -1594,12 +3132,14 @@ BOOL WINAPI SetupDiGetDeviceRegistryPropertyA(
         LONG l = RegQueryValueExA(devInfo->key, PropertyMap[Property].nameA,
                 NULL, PropertyRegDataType, PropertyBuffer, &size);
 
-        if (RequiredSize)
-            *RequiredSize = size;
-        if (!l)
+        if (l == ERROR_MORE_DATA || !PropertyBufferSize)
+            SetLastError(ERROR_INSUFFICIENT_BUFFER);
+        else if (!l)
             ret = TRUE;
         else
             SetLastError(l);
+        if (RequiredSize)
+            *RequiredSize = size;
     }
     return ret;
 }
@@ -1617,7 +3157,7 @@ BOOL WINAPI SetupDiGetDeviceRegistryPropertyW(
         PDWORD  RequiredSize)
 {
     BOOL ret = FALSE;
-    struct DeviceInfoSet *set = (struct DeviceInfoSet *)DeviceInfoSet;
+    struct DeviceInfoSet *set = DeviceInfoSet;
     struct DeviceInfo *devInfo;
 
     TRACE("%04x %p %d %p %p %d %p\n", (DWORD)DeviceInfoSet, DeviceInfoData,
@@ -1640,6 +3180,11 @@ BOOL WINAPI SetupDiGetDeviceRegistryPropertyW(
         SetLastError(ERROR_INVALID_PARAMETER);
         return FALSE;
     }
+    if (PropertyBufferSize && PropertyBuffer == NULL)
+    {
+        SetLastError(ERROR_INVALID_DATA);
+        return FALSE;
+    }
     devInfo = (struct DeviceInfo *)DeviceInfoData->Reserved;
     if (Property < sizeof(PropertyMap) / sizeof(PropertyMap[0])
         && PropertyMap[Property].nameW)
@@ -1648,12 +3193,14 @@ BOOL WINAPI SetupDiGetDeviceRegistryPropertyW(
         LONG l = RegQueryValueExW(devInfo->key, PropertyMap[Property].nameW,
                 NULL, PropertyRegDataType, PropertyBuffer, &size);
 
-        if (RequiredSize)
-            *RequiredSize = size;
-        if (!l)
+        if (l == ERROR_MORE_DATA || !PropertyBufferSize)
+            SetLastError(ERROR_INSUFFICIENT_BUFFER);
+        else if (!l)
             ret = TRUE;
         else
             SetLastError(l);
+        if (RequiredSize)
+            *RequiredSize = size;
     }
     return ret;
 }
@@ -1669,7 +3216,7 @@ BOOL WINAPI SetupDiSetDeviceRegistryPropertyA(
        DWORD PropertyBufferSize)
 {
     BOOL ret = FALSE;
-    struct DeviceInfoSet *set = (struct DeviceInfoSet *)DeviceInfoSet;
+    struct DeviceInfoSet *set = DeviceInfoSet;
     struct DeviceInfo *devInfo;
 
     TRACE("%p %p %d %p %d\n", DeviceInfoSet, DeviceInfoData, Property,
@@ -1717,7 +3264,7 @@ BOOL WINAPI SetupDiSetDeviceRegistryPropertyW(
        DWORD PropertyBufferSize)
 {
     BOOL ret = FALSE;
-    struct DeviceInfoSet *set = (struct DeviceInfoSet *)DeviceInfoSet;
+    struct DeviceInfoSet *set = DeviceInfoSet;
     struct DeviceInfo *devInfo;
 
     TRACE("%p %p %d %p %d\n", DeviceInfoSet, DeviceInfoData, Property,
@@ -1766,6 +3313,11 @@ BOOL WINAPI SetupDiInstallClassA(
     UNICODE_STRING FileNameW;
     BOOL Result;
 
+    if (!InfFileName)
+    {
+        SetLastError(ERROR_INVALID_PARAMETER);
+        return FALSE;
+    }
     if (!RtlCreateUnicodeStringFromAsciiz(&FileNameW, InfFileName))
     {
         SetLastError(ERROR_NOT_ENOUGH_MEMORY);
@@ -1781,6 +3333,7 @@ BOOL WINAPI SetupDiInstallClassA(
 
 static HKEY CreateClassKey(HINF hInf)
 {
+    static const WCHAR slash[] = { '\\',0 };
     WCHAR FullBuffer[MAX_PATH];
     WCHAR Buffer[MAX_PATH];
     DWORD RequiredSize;
@@ -1798,6 +3351,7 @@ static HKEY CreateClassKey(HINF hInf)
     }
 
     lstrcpyW(FullBuffer, ControlClass);
+    lstrcatW(FullBuffer, slash);
     lstrcatW(FullBuffer, Buffer);
 
     if (RegOpenKeyExW(HKEY_LOCAL_MACHINE,
@@ -1866,6 +3420,11 @@ BOOL WINAPI SetupDiInstallClassW(
 
     FIXME("\n");
 
+    if (!InfFileName)
+    {
+        SetLastError(ERROR_INVALID_PARAMETER);
+        return FALSE;
+    }
     if ((Flags & DI_NOVCP) && (FileQueue == NULL || FileQueue == INVALID_HANDLE_VALUE))
     {
        SetLastError(ERROR_INVALID_PARAMETER);
@@ -1893,9 +3452,7 @@ BOOL WINAPI SetupDiInstallClassW(
 
 
     /* Try to append a layout file */
-#if 0
     SetupOpenAppendInfFileW(NULL, hInf, NULL);
-#endif
 
     /* Retrieve the actual section name */
     SetupDiGetActualSectionToInstallW(hInf,
@@ -1923,7 +3480,7 @@ BOOL WINAPI SetupDiInstallClassW(
     SetupInstallFromInfSectionW(NULL,
                                hInf,
                                SectionName,
-                               SPINST_REGISTRY,
+                               SPINST_COPYINF | SPINST_FILES | SPINST_REGISTRY,
                                hClassKey,
                                NULL,
                                0,
@@ -1996,10 +3553,10 @@ HKEY WINAPI SetupDiOpenClassRegKeyExW(
         PCWSTR MachineName,
         PVOID Reserved)
 {
-    WCHAR bracedGuidString[39];
     HKEY hClassesKey;
-    HKEY hClassKey;
+    HKEY key;
     LPCWSTR lpKeyName;
+    LONG l;
 
     if (MachineName != NULL)
     {
@@ -2022,33 +3579,49 @@ HKEY WINAPI SetupDiOpenClassRegKeyExW(
         return INVALID_HANDLE_VALUE;
     }
 
-    if (RegOpenKeyExW(HKEY_LOCAL_MACHINE,
-                     lpKeyName,
-                     0,
-                     KEY_ALL_ACCESS,
-                     &hClassesKey))
+    if (!ClassGuid)
     {
-       return INVALID_HANDLE_VALUE;
+        if ((l = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
+                          lpKeyName,
+                          0,
+                          samDesired,
+                          &hClassesKey)))
+        {
+            SetLastError(l);
+            hClassesKey = INVALID_HANDLE_VALUE;
+        }
+        key = hClassesKey;
     }
-
-    if (ClassGuid == NULL)
-        return hClassesKey;
-
-    SETUPDI_GuidToString(ClassGuid, bracedGuidString);
-
-    if (RegOpenKeyExW(hClassesKey,
-                     bracedGuidString,
-                     0,
-                     KEY_ALL_ACCESS,
-                     &hClassKey))
+    else
     {
-       RegCloseKey(hClassesKey);
-       return INVALID_HANDLE_VALUE;
-    }
+        WCHAR bracedGuidString[39];
 
-    RegCloseKey(hClassesKey);
+        SETUPDI_GuidToString(ClassGuid, bracedGuidString);
 
-    return hClassKey;
+        if (!(l = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
+                          lpKeyName,
+                          0,
+                          samDesired,
+                          &hClassesKey)))
+        {
+            if ((l = RegOpenKeyExW(hClassesKey,
+                              bracedGuidString,
+                              0,
+                              samDesired,
+                              &key)))
+            {
+                SetLastError(l);
+                key = INVALID_HANDLE_VALUE;
+            }
+            RegCloseKey(hClassesKey);
+        }
+        else
+        {
+            SetLastError(l);
+            key = INVALID_HANDLE_VALUE;
+        }
+    }
+    return key;
 }
 
 /***********************************************************************
@@ -2117,6 +3690,46 @@ BOOL WINAPI SetupDiGetDeviceInstallParamsA(
     return FALSE;
 }
 
+static HKEY SETUPDI_OpenDevKey(struct DeviceInfo *devInfo, REGSAM samDesired)
+{
+    HKEY enumKey, key = INVALID_HANDLE_VALUE;
+    LONG l;
+
+    l = RegCreateKeyExW(HKEY_LOCAL_MACHINE, Enum, 0, NULL, 0, KEY_ALL_ACCESS,
+            NULL, &enumKey, NULL);
+    if (!l)
+    {
+        RegOpenKeyExW(enumKey, devInfo->instanceId, 0, samDesired, &key);
+        RegCloseKey(enumKey);
+    }
+    return key;
+}
+
+static HKEY SETUPDI_OpenDrvKey(struct DeviceInfo *devInfo, REGSAM samDesired)
+{
+    static const WCHAR slash[] = { '\\',0 };
+    WCHAR classKeyPath[MAX_PATH];
+    HKEY classKey, key = INVALID_HANDLE_VALUE;
+    LONG l;
+
+    lstrcpyW(classKeyPath, ControlClass);
+    lstrcatW(classKeyPath, slash);
+    SETUPDI_GuidToString(&devInfo->set->ClassGuid,
+            classKeyPath + lstrlenW(classKeyPath));
+    l = RegCreateKeyExW(HKEY_LOCAL_MACHINE, classKeyPath, 0, NULL, 0,
+            KEY_ALL_ACCESS, NULL, &classKey, NULL);
+    if (!l)
+    {
+        static const WCHAR fmt[] = { '%','0','4','u',0 };
+        WCHAR devId[10];
+
+        sprintfW(devId, fmt, devInfo->devId);
+        RegOpenKeyExW(classKey, devId, 0, samDesired, &key);
+        RegCloseKey(classKey);
+    }
+    return key;
+}
+
 /***********************************************************************
  *             SetupDiOpenDevRegKey (SETUPAPI.@)
  */
@@ -2128,7 +3741,246 @@ HKEY WINAPI SetupDiOpenDevRegKey(
        DWORD KeyType,
        REGSAM samDesired)
 {
-    FIXME("%p %p %d %d %d %x\n", DeviceInfoSet, DeviceInfoData,
+    struct DeviceInfoSet *set = DeviceInfoSet;
+    struct DeviceInfo *devInfo;
+    HKEY key = INVALID_HANDLE_VALUE;
+
+    TRACE("%p %p %d %d %d %x\n", DeviceInfoSet, DeviceInfoData,
           Scope, HwProfile, KeyType, samDesired);
-    return INVALID_HANDLE_VALUE;
+
+    if (!DeviceInfoSet || DeviceInfoSet == INVALID_HANDLE_VALUE)
+    {
+        SetLastError(ERROR_INVALID_HANDLE);
+        return INVALID_HANDLE_VALUE;
+    }
+    if (set->magic != SETUP_DEVICE_INFO_SET_MAGIC)
+    {
+        SetLastError(ERROR_INVALID_HANDLE);
+        return INVALID_HANDLE_VALUE;
+    }
+    if (!DeviceInfoData || DeviceInfoData->cbSize != sizeof(SP_DEVINFO_DATA)
+            || !DeviceInfoData->Reserved)
+    {
+        SetLastError(ERROR_INVALID_PARAMETER);
+        return INVALID_HANDLE_VALUE;
+    }
+    if (Scope != DICS_FLAG_GLOBAL && Scope != DICS_FLAG_CONFIGSPECIFIC)
+    {
+        SetLastError(ERROR_INVALID_FLAGS);
+        return INVALID_HANDLE_VALUE;
+    }
+    if (KeyType != DIREG_DEV && KeyType != DIREG_DRV)
+    {
+        SetLastError(ERROR_INVALID_FLAGS);
+        return INVALID_HANDLE_VALUE;
+    }
+    devInfo = (struct DeviceInfo *)DeviceInfoData->Reserved;
+    if (devInfo->set != set)
+    {
+        SetLastError(ERROR_INVALID_PARAMETER);
+        return INVALID_HANDLE_VALUE;
+    }
+    if (devInfo->phantom)
+    {
+        SetLastError(ERROR_DEVINFO_NOT_REGISTERED);
+        return INVALID_HANDLE_VALUE;
+    }
+    if (Scope != DICS_FLAG_GLOBAL)
+        FIXME("unimplemented for scope %d\n", Scope);
+    switch (KeyType)
+    {
+        case DIREG_DEV:
+            key = SETUPDI_OpenDevKey(devInfo, samDesired);
+            break;
+        case DIREG_DRV:
+            key = SETUPDI_OpenDrvKey(devInfo, samDesired);
+            break;
+        default:
+            WARN("unknown KeyType %d\n", KeyType);
+    }
+    return key;
+}
+
+static BOOL SETUPDI_DeleteDevKey(struct DeviceInfo *devInfo)
+{
+    HKEY enumKey;
+    BOOL ret = FALSE;
+    LONG l;
+
+    l = RegCreateKeyExW(HKEY_LOCAL_MACHINE, Enum, 0, NULL, 0, KEY_ALL_ACCESS,
+            NULL, &enumKey, NULL);
+    if (!l)
+    {
+        ret = RegDeleteTreeW(enumKey, devInfo->instanceId);
+        RegCloseKey(enumKey);
+    }
+    else
+        SetLastError(l);
+    return ret;
+}
+
+static BOOL SETUPDI_DeleteDrvKey(struct DeviceInfo *devInfo)
+{
+    static const WCHAR slash[] = { '\\',0 };
+    WCHAR classKeyPath[MAX_PATH];
+    HKEY classKey;
+    LONG l;
+    BOOL ret = FALSE;
+
+    lstrcpyW(classKeyPath, ControlClass);
+    lstrcatW(classKeyPath, slash);
+    SETUPDI_GuidToString(&devInfo->set->ClassGuid,
+            classKeyPath + lstrlenW(classKeyPath));
+    l = RegCreateKeyExW(HKEY_LOCAL_MACHINE, classKeyPath, 0, NULL, 0,
+            KEY_ALL_ACCESS, NULL, &classKey, NULL);
+    if (!l)
+    {
+        static const WCHAR fmt[] = { '%','0','4','u',0 };
+        WCHAR devId[10];
+
+        sprintfW(devId, fmt, devInfo->devId);
+        ret = RegDeleteTreeW(classKey, devId);
+        RegCloseKey(classKey);
+    }
+    else
+        SetLastError(l);
+    return ret;
+}
+
+/***********************************************************************
+ *             SetupDiDeleteDevRegKey (SETUPAPI.@)
+ */
+BOOL WINAPI SetupDiDeleteDevRegKey(
+       HDEVINFO DeviceInfoSet,
+       PSP_DEVINFO_DATA DeviceInfoData,
+       DWORD Scope,
+       DWORD HwProfile,
+       DWORD KeyType)
+{
+    struct DeviceInfoSet *set = DeviceInfoSet;
+    struct DeviceInfo *devInfo;
+    BOOL ret = FALSE;
+
+    TRACE("%p %p %d %d %d\n", DeviceInfoSet, DeviceInfoData, Scope, HwProfile,
+            KeyType);
+
+    if (!DeviceInfoSet || DeviceInfoSet == INVALID_HANDLE_VALUE)
+    {
+        SetLastError(ERROR_INVALID_HANDLE);
+        return FALSE;
+    }
+    if (set->magic != SETUP_DEVICE_INFO_SET_MAGIC)
+    {
+        SetLastError(ERROR_INVALID_HANDLE);
+        return FALSE;
+    }
+    if (!DeviceInfoData || DeviceInfoData->cbSize != sizeof(SP_DEVINFO_DATA)
+            || !DeviceInfoData->Reserved)
+    {
+        SetLastError(ERROR_INVALID_PARAMETER);
+        return FALSE;
+    }
+    if (Scope != DICS_FLAG_GLOBAL && Scope != DICS_FLAG_CONFIGSPECIFIC)
+    {
+        SetLastError(ERROR_INVALID_FLAGS);
+        return FALSE;
+    }
+    if (KeyType != DIREG_DEV && KeyType != DIREG_DRV && KeyType != DIREG_BOTH)
+    {
+        SetLastError(ERROR_INVALID_FLAGS);
+        return FALSE;
+    }
+    devInfo = (struct DeviceInfo *)DeviceInfoData->Reserved;
+    if (devInfo->set != set)
+    {
+        SetLastError(ERROR_INVALID_PARAMETER);
+        return FALSE;
+    }
+    if (devInfo->phantom)
+    {
+        SetLastError(ERROR_DEVINFO_NOT_REGISTERED);
+        return FALSE;
+    }
+    if (Scope != DICS_FLAG_GLOBAL)
+        FIXME("unimplemented for scope %d\n", Scope);
+    switch (KeyType)
+    {
+        case DIREG_DEV:
+            ret = SETUPDI_DeleteDevKey(devInfo);
+            break;
+        case DIREG_DRV:
+            ret = SETUPDI_DeleteDrvKey(devInfo);
+            break;
+        case DIREG_BOTH:
+            ret = SETUPDI_DeleteDevKey(devInfo);
+            if (ret)
+                ret = SETUPDI_DeleteDrvKey(devInfo);
+            break;
+        default:
+            WARN("unknown KeyType %d\n", KeyType);
+    }
+    return ret;
+}
+
+/***********************************************************************
+ *              CM_Get_Device_IDA  (SETUPAPI.@)
+ */
+CONFIGRET WINAPI CM_Get_Device_IDA( DEVINST dnDevInst, PSTR Buffer,
+                                   ULONG  BufferLen, ULONG  ulFlags)
+{
+    struct DeviceInfo *devInfo = GlobalLock((HANDLE)dnDevInst);
+
+    TRACE("%x->%p, %p, %u %u\n", dnDevInst, devInfo, Buffer, BufferLen, ulFlags);
+
+    if (!devInfo)
+        return CR_NO_SUCH_DEVINST;
+
+    WideCharToMultiByte(CP_ACP, 0, devInfo->instanceId, -1, Buffer, BufferLen, 0, 0);
+    TRACE("Returning %s\n", debugstr_a(Buffer));
+    return CR_SUCCESS;
+}
+
+/***********************************************************************
+ *              CM_Get_Device_IDW  (SETUPAPI.@)
+ */
+CONFIGRET WINAPI CM_Get_Device_IDW( DEVINST dnDevInst, LPWSTR Buffer,
+                                   ULONG  BufferLen, ULONG  ulFlags)
+{
+    struct DeviceInfo *devInfo = GlobalLock((HANDLE)dnDevInst);
+
+    TRACE("%x->%p, %p, %u %u\n", dnDevInst, devInfo, Buffer, BufferLen, ulFlags);
+
+    if (!devInfo)
+    {
+        WARN("dev instance %d not found!\n", dnDevInst);
+        return CR_NO_SUCH_DEVINST;
+    }
+
+    lstrcpynW(Buffer, devInfo->instanceId, BufferLen);
+    TRACE("Returning %s\n", debugstr_w(Buffer));
+    GlobalUnlock((HANDLE)dnDevInst);
+    return CR_SUCCESS;
+}
+
+
+
+/***********************************************************************
+ *              CM_Get_Device_ID_Size  (SETUPAPI.@)
+ */
+CONFIGRET WINAPI CM_Get_Device_ID_Size( PULONG  pulLen, DEVINST dnDevInst,
+                                        ULONG  ulFlags)
+{
+    struct DeviceInfo *ppdevInfo = GlobalLock((HANDLE)dnDevInst);
+
+    TRACE("%x->%p, %p, %u\n", dnDevInst, ppdevInfo, pulLen, ulFlags);
+
+    if (!ppdevInfo)
+    {
+        WARN("dev instance %d not found!\n", dnDevInst);
+        return CR_NO_SUCH_DEVINST;
+    }
+
+    *pulLen = lstrlenW(ppdevInfo->instanceId);
+    GlobalUnlock((HANDLE)dnDevInst);
+    return CR_SUCCESS;
 }