|  | /* | 
|  | * SetupAPI device installer | 
|  | * | 
|  | * Copyright 2000 Andreas Mohr for CodeWeavers | 
|  | * | 
|  | * This library is free software; you can redistribute it and/or | 
|  | * modify it under the terms of the GNU Lesser General Public | 
|  | * License as published by the Free Software Foundation; either | 
|  | * version 2.1 of the License, or (at your option) any later version. | 
|  | * | 
|  | * This library is distributed in the hope that it will be useful, | 
|  | * but WITHOUT ANY WARRANTY; without even the implied warranty of | 
|  | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | 
|  | * Lesser General Public License for more details. | 
|  | * | 
|  | * You should have received a copy of the GNU Lesser General Public | 
|  | * License along with this library; if not, write to the Free Software | 
|  | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA | 
|  | */ | 
|  |  | 
|  | #include "config.h" | 
|  | #include "wine/port.h" | 
|  |  | 
|  | #include <stdarg.h> | 
|  |  | 
|  | #include "windef.h" | 
|  | #include "winbase.h" | 
|  | #include "winnt.h" | 
|  | #include "winreg.h" | 
|  | #include "winternl.h" | 
|  | #include "wingdi.h" | 
|  | #include "winuser.h" | 
|  | #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" | 
|  |  | 
|  | #include "setupapi_private.h" | 
|  |  | 
|  |  | 
|  | WINE_DEFAULT_DEBUG_CHANNEL(setupapi); | 
|  |  | 
|  | /* Unicode constants */ | 
|  | static const WCHAR ClassGUID[]  = {'C','l','a','s','s','G','U','I','D',0}; | 
|  | static const WCHAR Class[]  = {'C','l','a','s','s',0}; | 
|  | static const WCHAR ClassInstall32[]  = {'C','l','a','s','s','I','n','s','t','a','l','l','3','2',0}; | 
|  | static const WCHAR NoDisplayClass[]  = {'N','o','D','i','s','p','l','a','y','C','l','a','s','s',0}; | 
|  | static const WCHAR NoInstallClass[]  = {'N','o','I','n','s','t','a','l','l','C','l','a','s','s',0}; | 
|  | static const WCHAR NoUseClass[]  = {'N','o','U','s','e','C','l','a','s','s',0}; | 
|  | static const WCHAR NtExtension[]  = {'.','N','T',0}; | 
|  | static const WCHAR NtPlatformExtension[]  = {'.','N','T','x','8','6',0}; | 
|  | static const WCHAR Version[]  = {'V','e','r','s','i','o','n',0}; | 
|  | static const WCHAR WinExtension[]  = {'.','W','i','n',0}; | 
|  |  | 
|  | /* Registry key and value names */ | 
|  | static const WCHAR ControlClass[] = {'S','y','s','t','e','m','\\', | 
|  | 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\', | 
|  | 'C','o','n','t','r','o','l','\\', | 
|  | 'C','l','a','s','s',0}; | 
|  |  | 
|  | static const WCHAR DeviceClasses[] = {'S','y','s','t','e','m','\\', | 
|  | 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\', | 
|  | 'C','o','n','t','r','o','l','\\', | 
|  | 'D','e','v','i','c','e','C','l','a','s','s','e','s',0}; | 
|  | 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}; | 
|  | static const WCHAR Driver[] = {'D','r','i','v','e','r',0}; | 
|  | static const WCHAR ConfigFlags[] = {'C','o','n','f','i','g','F','l','a','g','s',0}; | 
|  | static const WCHAR Mfg[] = {'M','f','g',0}; | 
|  | static const WCHAR FriendlyName[] = {'F','r','i','e','n','d','l','y','N','a','m','e',0}; | 
|  | static const WCHAR LocationInformation[] = {'L','o','c','a','t','i','o','n','I','n','f','o','r','m','a','t','i','o','n',0}; | 
|  | static const WCHAR Capabilities[] = {'C','a','p','a','b','i','l','i','t','i','e','s',0}; | 
|  | 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 */ | 
|  | #define SETUP_DEVICE_INFO_SET_MAGIC 0xd00ff056 | 
|  |  | 
|  | struct DeviceInfoSet | 
|  | { | 
|  | DWORD magic;        /* if is equal to SETUP_DEVICE_INFO_SET_MAGIC struct is okay */ | 
|  | GUID ClassGuid; | 
|  | HWND hwndParent; | 
|  | DWORD cDevices; | 
|  | 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 | 
|  | { | 
|  | struct DeviceInfoSet *set; | 
|  | HKEY                  key; | 
|  | BOOL                  phantom; | 
|  | DWORD                 devId; | 
|  | LPWSTR                instanceId; | 
|  | struct list           interfaces; | 
|  | }; | 
|  |  | 
|  | 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 **interface) | 
|  | { | 
|  | 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)) | 
|  | { | 
|  | *interface = iface; | 
|  | found = TRUE; | 
|  | break; | 
|  | } | 
|  | } | 
|  | TRACE("returning %d (%p)\n", found, found ? *interface : 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) | 
|  | { | 
|  | 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) | 
|  | { | 
|  | devInfo->key = INVALID_HANDLE_VALUE; | 
|  | devInfo->phantom = phantom; | 
|  | lstrcpyW(devInfo->instanceId, instanceId); | 
|  | struprW(devInfo->instanceId); | 
|  | devInfo->key = SETUPDI_CreateDevKey(devInfo); | 
|  | if (devInfo->key != INVALID_HANDLE_VALUE) | 
|  | { | 
|  | if (phantom) | 
|  | RegSetValueExW(devInfo->key, Phantom, 0, REG_DWORD, | 
|  | (LPBYTE)&phantom, sizeof(phantom)); | 
|  | } | 
|  | list_init(&devInfo->interfaces); | 
|  | GlobalUnlock(devInst); | 
|  | } | 
|  | else | 
|  | { | 
|  | GlobalUnlock(devInst); | 
|  | GlobalFree(devInst); | 
|  | devInfo = NULL; | 
|  | } | 
|  | } | 
|  | return devInfo; | 
|  | } | 
|  |  | 
|  | static void SETUPDI_FreeDeviceInfo(struct DeviceInfo *devInfo) | 
|  | { | 
|  | struct InterfaceInstances *iface, *next; | 
|  |  | 
|  | if (devInfo->key != INVALID_HANDLE_VALUE) | 
|  | RegCloseKey(devInfo->key); | 
|  | if (devInfo->phantom) | 
|  | { | 
|  | HKEY enumKey; | 
|  | LONG l; | 
|  |  | 
|  | 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.  "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(set, set->cDevices, | 
|  | instanceId, phantom); | 
|  |  | 
|  | TRACE("%p, %s, %d, %s, %d\n", set, debugstr_guid(guid), devInst, | 
|  | debugstr_w(instanceId), phantom); | 
|  |  | 
|  | if (devInfo) | 
|  | { | 
|  | struct DeviceInstance *devInst = | 
|  | HeapAlloc(GetProcessHeap(), 0, sizeof(struct DeviceInstance)); | 
|  |  | 
|  | if (devInst) | 
|  | { | 
|  | 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 | 
|  | { | 
|  | HeapFree(GetProcessHeap(), 0, devInfo); | 
|  | SetLastError(ERROR_OUTOFMEMORY); | 
|  | } | 
|  | } | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | /*********************************************************************** | 
|  | *              SetupDiBuildClassInfoList  (SETUPAPI.@) | 
|  | * | 
|  | * Returns a list of setup class GUIDs that identify the classes | 
|  | * that are installed on a local machine. | 
|  | * | 
|  | * PARAMS | 
|  | *   Flags [I] control exclusion of classes from the list. | 
|  | *   ClassGuidList [O] pointer to a GUID-typed array that receives a list of setup class GUIDs. | 
|  | *   ClassGuidListSize [I] The number of GUIDs in the array (ClassGuidList). | 
|  | *   RequiredSize [O] pointer, which receives the number of GUIDs that are returned. | 
|  | * | 
|  | * RETURNS | 
|  | *   Success: TRUE. | 
|  | *   Failure: FALSE. | 
|  | */ | 
|  | BOOL WINAPI SetupDiBuildClassInfoList( | 
|  | DWORD Flags, | 
|  | LPGUID ClassGuidList, | 
|  | DWORD ClassGuidListSize, | 
|  | PDWORD RequiredSize) | 
|  | { | 
|  | TRACE("\n"); | 
|  | return SetupDiBuildClassInfoListExW(Flags, ClassGuidList, | 
|  | ClassGuidListSize, RequiredSize, | 
|  | NULL, NULL); | 
|  | } | 
|  |  | 
|  | /*********************************************************************** | 
|  | *              SetupDiBuildClassInfoListExA  (SETUPAPI.@) | 
|  | * | 
|  | * Returns a list of setup class GUIDs that identify the classes | 
|  | * that are installed on a local or remote macine. | 
|  | * | 
|  | * PARAMS | 
|  | *   Flags [I] control exclusion of classes from the list. | 
|  | *   ClassGuidList [O] pointer to a GUID-typed array that receives a list of setup class GUIDs. | 
|  | *   ClassGuidListSize [I] The number of GUIDs in the array (ClassGuidList). | 
|  | *   RequiredSize [O] pointer, which receives the number of GUIDs that are returned. | 
|  | *   MachineName [I] name of a remote machine. | 
|  | *   Reserved [I] must be NULL. | 
|  | * | 
|  | * RETURNS | 
|  | *   Success: TRUE. | 
|  | *   Failure: FALSE. | 
|  | */ | 
|  | BOOL WINAPI SetupDiBuildClassInfoListExA( | 
|  | DWORD Flags, | 
|  | LPGUID ClassGuidList, | 
|  | DWORD ClassGuidListSize, | 
|  | PDWORD RequiredSize, | 
|  | LPCSTR MachineName, | 
|  | PVOID Reserved) | 
|  | { | 
|  | LPWSTR MachineNameW = NULL; | 
|  | BOOL bResult; | 
|  |  | 
|  | TRACE("\n"); | 
|  |  | 
|  | if (MachineName) | 
|  | { | 
|  | MachineNameW = MultiByteToUnicode(MachineName, CP_ACP); | 
|  | if (MachineNameW == NULL) return FALSE; | 
|  | } | 
|  |  | 
|  | bResult = SetupDiBuildClassInfoListExW(Flags, ClassGuidList, | 
|  | ClassGuidListSize, RequiredSize, | 
|  | MachineNameW, Reserved); | 
|  |  | 
|  | MyFree(MachineNameW); | 
|  |  | 
|  | return bResult; | 
|  | } | 
|  |  | 
|  | /*********************************************************************** | 
|  | *              SetupDiBuildClassInfoListExW  (SETUPAPI.@) | 
|  | * | 
|  | * Returns a list of setup class GUIDs that identify the classes | 
|  | * that are installed on a local or remote macine. | 
|  | * | 
|  | * PARAMS | 
|  | *   Flags [I] control exclusion of classes from the list. | 
|  | *   ClassGuidList [O] pointer to a GUID-typed array that receives a list of setup class GUIDs. | 
|  | *   ClassGuidListSize [I] The number of GUIDs in the array (ClassGuidList). | 
|  | *   RequiredSize [O] pointer, which receives the number of GUIDs that are returned. | 
|  | *   MachineName [I] name of a remote machine. | 
|  | *   Reserved [I] must be NULL. | 
|  | * | 
|  | * RETURNS | 
|  | *   Success: TRUE. | 
|  | *   Failure: FALSE. | 
|  | */ | 
|  | BOOL WINAPI SetupDiBuildClassInfoListExW( | 
|  | DWORD Flags, | 
|  | LPGUID ClassGuidList, | 
|  | DWORD ClassGuidListSize, | 
|  | PDWORD RequiredSize, | 
|  | LPCWSTR MachineName, | 
|  | PVOID Reserved) | 
|  | { | 
|  | WCHAR szKeyName[40]; | 
|  | HKEY hClassesKey; | 
|  | HKEY hClassKey; | 
|  | DWORD dwLength; | 
|  | DWORD dwIndex; | 
|  | LONG lError; | 
|  | DWORD dwGuidListIndex = 0; | 
|  |  | 
|  | TRACE("\n"); | 
|  |  | 
|  | if (RequiredSize != NULL) | 
|  | *RequiredSize = 0; | 
|  |  | 
|  | hClassesKey = SetupDiOpenClassRegKeyExW(NULL, | 
|  | KEY_ALL_ACCESS, | 
|  | DIOCR_INSTALLER, | 
|  | MachineName, | 
|  | Reserved); | 
|  | if (hClassesKey == INVALID_HANDLE_VALUE) | 
|  | { | 
|  | return FALSE; | 
|  | } | 
|  |  | 
|  | for (dwIndex = 0; ; dwIndex++) | 
|  | { | 
|  | dwLength = 40; | 
|  | lError = RegEnumKeyExW(hClassesKey, | 
|  | dwIndex, | 
|  | szKeyName, | 
|  | &dwLength, | 
|  | NULL, | 
|  | NULL, | 
|  | NULL, | 
|  | NULL); | 
|  | TRACE("RegEnumKeyExW() returns %d\n", lError); | 
|  | if (lError == ERROR_SUCCESS || lError == ERROR_MORE_DATA) | 
|  | { | 
|  | TRACE("Key name: %p\n", szKeyName); | 
|  |  | 
|  | if (RegOpenKeyExW(hClassesKey, | 
|  | szKeyName, | 
|  | 0, | 
|  | KEY_ALL_ACCESS, | 
|  | &hClassKey)) | 
|  | { | 
|  | RegCloseKey(hClassesKey); | 
|  | return FALSE; | 
|  | } | 
|  |  | 
|  | if (!RegQueryValueExW(hClassKey, | 
|  | NoUseClass, | 
|  | NULL, | 
|  | NULL, | 
|  | NULL, | 
|  | NULL)) | 
|  | { | 
|  | TRACE("'NoUseClass' value found!\n"); | 
|  | RegCloseKey(hClassKey); | 
|  | continue; | 
|  | } | 
|  |  | 
|  | if ((Flags & DIBCI_NOINSTALLCLASS) && | 
|  | (!RegQueryValueExW(hClassKey, | 
|  | NoInstallClass, | 
|  | NULL, | 
|  | NULL, | 
|  | NULL, | 
|  | NULL))) | 
|  | { | 
|  | TRACE("'NoInstallClass' value found!\n"); | 
|  | RegCloseKey(hClassKey); | 
|  | continue; | 
|  | } | 
|  |  | 
|  | if ((Flags & DIBCI_NODISPLAYCLASS) && | 
|  | (!RegQueryValueExW(hClassKey, | 
|  | NoDisplayClass, | 
|  | NULL, | 
|  | NULL, | 
|  | NULL, | 
|  | NULL))) | 
|  | { | 
|  | TRACE("'NoDisplayClass' value found!\n"); | 
|  | RegCloseKey(hClassKey); | 
|  | continue; | 
|  | } | 
|  |  | 
|  | RegCloseKey(hClassKey); | 
|  |  | 
|  | TRACE("Guid: %p\n", szKeyName); | 
|  | if (dwGuidListIndex < ClassGuidListSize) | 
|  | { | 
|  | if (szKeyName[0] == '{' && szKeyName[37] == '}') | 
|  | { | 
|  | szKeyName[37] = 0; | 
|  | } | 
|  | TRACE("Guid: %p\n", &szKeyName[1]); | 
|  |  | 
|  | UuidFromStringW(&szKeyName[1], | 
|  | &ClassGuidList[dwGuidListIndex]); | 
|  | } | 
|  |  | 
|  | dwGuidListIndex++; | 
|  | } | 
|  |  | 
|  | if (lError != ERROR_SUCCESS) | 
|  | break; | 
|  | } | 
|  |  | 
|  | RegCloseKey(hClassesKey); | 
|  |  | 
|  | if (RequiredSize != NULL) | 
|  | *RequiredSize = dwGuidListIndex; | 
|  |  | 
|  | if (ClassGuidListSize < dwGuidListIndex) | 
|  | { | 
|  | SetLastError(ERROR_INSUFFICIENT_BUFFER); | 
|  | return FALSE; | 
|  | } | 
|  |  | 
|  | return TRUE; | 
|  | } | 
|  |  | 
|  | /*********************************************************************** | 
|  | *		SetupDiClassGuidsFromNameA  (SETUPAPI.@) | 
|  | */ | 
|  | BOOL WINAPI SetupDiClassGuidsFromNameA( | 
|  | LPCSTR ClassName, | 
|  | LPGUID ClassGuidList, | 
|  | DWORD ClassGuidListSize, | 
|  | PDWORD RequiredSize) | 
|  | { | 
|  | return SetupDiClassGuidsFromNameExA(ClassName, ClassGuidList, | 
|  | ClassGuidListSize, RequiredSize, | 
|  | NULL, NULL); | 
|  | } | 
|  |  | 
|  | /*********************************************************************** | 
|  | *		SetupDiClassGuidsFromNameW  (SETUPAPI.@) | 
|  | */ | 
|  | BOOL WINAPI SetupDiClassGuidsFromNameW( | 
|  | LPCWSTR ClassName, | 
|  | LPGUID ClassGuidList, | 
|  | DWORD ClassGuidListSize, | 
|  | PDWORD RequiredSize) | 
|  | { | 
|  | return SetupDiClassGuidsFromNameExW(ClassName, ClassGuidList, | 
|  | ClassGuidListSize, RequiredSize, | 
|  | NULL, NULL); | 
|  | } | 
|  |  | 
|  | /*********************************************************************** | 
|  | *		SetupDiClassGuidsFromNameExA  (SETUPAPI.@) | 
|  | */ | 
|  | BOOL WINAPI SetupDiClassGuidsFromNameExA( | 
|  | LPCSTR ClassName, | 
|  | LPGUID ClassGuidList, | 
|  | DWORD ClassGuidListSize, | 
|  | PDWORD RequiredSize, | 
|  | LPCSTR MachineName, | 
|  | PVOID Reserved) | 
|  | { | 
|  | LPWSTR ClassNameW = NULL; | 
|  | LPWSTR MachineNameW = NULL; | 
|  | BOOL bResult; | 
|  |  | 
|  | ClassNameW = MultiByteToUnicode(ClassName, CP_ACP); | 
|  | if (ClassNameW == NULL) | 
|  | return FALSE; | 
|  |  | 
|  | if (MachineName) | 
|  | { | 
|  | MachineNameW = MultiByteToUnicode(MachineName, CP_ACP); | 
|  | if (MachineNameW == NULL) | 
|  | { | 
|  | MyFree(ClassNameW); | 
|  | return FALSE; | 
|  | } | 
|  | } | 
|  |  | 
|  | bResult = SetupDiClassGuidsFromNameExW(ClassNameW, ClassGuidList, | 
|  | ClassGuidListSize, RequiredSize, | 
|  | MachineNameW, Reserved); | 
|  |  | 
|  | MyFree(MachineNameW); | 
|  | MyFree(ClassNameW); | 
|  |  | 
|  | return bResult; | 
|  | } | 
|  |  | 
|  | /*********************************************************************** | 
|  | *		SetupDiClassGuidsFromNameExW  (SETUPAPI.@) | 
|  | */ | 
|  | BOOL WINAPI SetupDiClassGuidsFromNameExW( | 
|  | LPCWSTR ClassName, | 
|  | LPGUID ClassGuidList, | 
|  | DWORD ClassGuidListSize, | 
|  | PDWORD RequiredSize, | 
|  | LPCWSTR MachineName, | 
|  | PVOID Reserved) | 
|  | { | 
|  | WCHAR szKeyName[40]; | 
|  | WCHAR szClassName[256]; | 
|  | HKEY hClassesKey; | 
|  | HKEY hClassKey; | 
|  | DWORD dwLength; | 
|  | DWORD dwIndex; | 
|  | LONG lError; | 
|  | DWORD dwGuidListIndex = 0; | 
|  |  | 
|  | if (RequiredSize != NULL) | 
|  | *RequiredSize = 0; | 
|  |  | 
|  | hClassesKey = SetupDiOpenClassRegKeyExW(NULL, | 
|  | KEY_ALL_ACCESS, | 
|  | DIOCR_INSTALLER, | 
|  | MachineName, | 
|  | Reserved); | 
|  | if (hClassesKey == INVALID_HANDLE_VALUE) | 
|  | { | 
|  | return FALSE; | 
|  | } | 
|  |  | 
|  | for (dwIndex = 0; ; dwIndex++) | 
|  | { | 
|  | dwLength = 40; | 
|  | lError = RegEnumKeyExW(hClassesKey, | 
|  | dwIndex, | 
|  | szKeyName, | 
|  | &dwLength, | 
|  | NULL, | 
|  | NULL, | 
|  | NULL, | 
|  | NULL); | 
|  | TRACE("RegEnumKeyExW() returns %d\n", lError); | 
|  | if (lError == ERROR_SUCCESS || lError == ERROR_MORE_DATA) | 
|  | { | 
|  | TRACE("Key name: %p\n", szKeyName); | 
|  |  | 
|  | if (RegOpenKeyExW(hClassesKey, | 
|  | szKeyName, | 
|  | 0, | 
|  | KEY_ALL_ACCESS, | 
|  | &hClassKey)) | 
|  | { | 
|  | RegCloseKey(hClassesKey); | 
|  | return FALSE; | 
|  | } | 
|  |  | 
|  | dwLength = 256 * sizeof(WCHAR); | 
|  | if (!RegQueryValueExW(hClassKey, | 
|  | Class, | 
|  | NULL, | 
|  | NULL, | 
|  | (LPBYTE)szClassName, | 
|  | &dwLength)) | 
|  | { | 
|  | TRACE("Class name: %p\n", szClassName); | 
|  |  | 
|  | if (strcmpiW(szClassName, ClassName) == 0) | 
|  | { | 
|  | TRACE("Found matching class name\n"); | 
|  |  | 
|  | TRACE("Guid: %p\n", szKeyName); | 
|  | if (dwGuidListIndex < ClassGuidListSize) | 
|  | { | 
|  | if (szKeyName[0] == '{' && szKeyName[37] == '}') | 
|  | { | 
|  | szKeyName[37] = 0; | 
|  | } | 
|  | TRACE("Guid: %p\n", &szKeyName[1]); | 
|  |  | 
|  | UuidFromStringW(&szKeyName[1], | 
|  | &ClassGuidList[dwGuidListIndex]); | 
|  | } | 
|  |  | 
|  | dwGuidListIndex++; | 
|  | } | 
|  | } | 
|  |  | 
|  | RegCloseKey(hClassKey); | 
|  | } | 
|  |  | 
|  | if (lError != ERROR_SUCCESS) | 
|  | break; | 
|  | } | 
|  |  | 
|  | RegCloseKey(hClassesKey); | 
|  |  | 
|  | if (RequiredSize != NULL) | 
|  | *RequiredSize = dwGuidListIndex; | 
|  |  | 
|  | if (ClassGuidListSize < dwGuidListIndex) | 
|  | { | 
|  | SetLastError(ERROR_INSUFFICIENT_BUFFER); | 
|  | return FALSE; | 
|  | } | 
|  |  | 
|  | return TRUE; | 
|  | } | 
|  |  | 
|  | /*********************************************************************** | 
|  | *              SetupDiClassNameFromGuidA  (SETUPAPI.@) | 
|  | */ | 
|  | BOOL WINAPI SetupDiClassNameFromGuidA( | 
|  | const GUID* ClassGuid, | 
|  | PSTR ClassName, | 
|  | DWORD ClassNameSize, | 
|  | PDWORD RequiredSize) | 
|  | { | 
|  | return SetupDiClassNameFromGuidExA(ClassGuid, ClassName, | 
|  | ClassNameSize, RequiredSize, | 
|  | NULL, NULL); | 
|  | } | 
|  |  | 
|  | /*********************************************************************** | 
|  | *              SetupDiClassNameFromGuidW  (SETUPAPI.@) | 
|  | */ | 
|  | BOOL WINAPI SetupDiClassNameFromGuidW( | 
|  | const GUID* ClassGuid, | 
|  | PWSTR ClassName, | 
|  | DWORD ClassNameSize, | 
|  | PDWORD RequiredSize) | 
|  | { | 
|  | return SetupDiClassNameFromGuidExW(ClassGuid, ClassName, | 
|  | ClassNameSize, RequiredSize, | 
|  | NULL, NULL); | 
|  | } | 
|  |  | 
|  | /*********************************************************************** | 
|  | *              SetupDiClassNameFromGuidExA  (SETUPAPI.@) | 
|  | */ | 
|  | BOOL WINAPI SetupDiClassNameFromGuidExA( | 
|  | const GUID* ClassGuid, | 
|  | PSTR ClassName, | 
|  | DWORD ClassNameSize, | 
|  | PDWORD RequiredSize, | 
|  | PCSTR MachineName, | 
|  | PVOID Reserved) | 
|  | { | 
|  | WCHAR ClassNameW[MAX_CLASS_NAME_LEN]; | 
|  | LPWSTR MachineNameW = NULL; | 
|  | BOOL ret; | 
|  |  | 
|  | if (MachineName) | 
|  | MachineNameW = MultiByteToUnicode(MachineName, CP_ACP); | 
|  | ret = SetupDiClassNameFromGuidExW(ClassGuid, ClassNameW, MAX_CLASS_NAME_LEN, | 
|  | NULL, MachineNameW, Reserved); | 
|  | if (ret) | 
|  | { | 
|  | int len = WideCharToMultiByte(CP_ACP, 0, ClassNameW, -1, ClassName, | 
|  | ClassNameSize, NULL, NULL); | 
|  |  | 
|  | if (!ClassNameSize && RequiredSize) | 
|  | *RequiredSize = len; | 
|  | } | 
|  | MyFree(MachineNameW); | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | /*********************************************************************** | 
|  | *		SetupDiClassNameFromGuidExW  (SETUPAPI.@) | 
|  | */ | 
|  | BOOL WINAPI SetupDiClassNameFromGuidExW( | 
|  | const GUID* ClassGuid, | 
|  | PWSTR ClassName, | 
|  | DWORD ClassNameSize, | 
|  | PDWORD RequiredSize, | 
|  | PCWSTR MachineName, | 
|  | PVOID Reserved) | 
|  | { | 
|  | HKEY hKey; | 
|  | DWORD dwLength; | 
|  |  | 
|  | hKey = SetupDiOpenClassRegKeyExW(ClassGuid, | 
|  | KEY_ALL_ACCESS, | 
|  | DIOCR_INSTALLER, | 
|  | MachineName, | 
|  | Reserved); | 
|  | if (hKey == INVALID_HANDLE_VALUE) | 
|  | { | 
|  | return FALSE; | 
|  | } | 
|  |  | 
|  | if (RequiredSize != NULL) | 
|  | { | 
|  | dwLength = 0; | 
|  | if (RegQueryValueExW(hKey, | 
|  | Class, | 
|  | NULL, | 
|  | NULL, | 
|  | NULL, | 
|  | &dwLength)) | 
|  | { | 
|  | RegCloseKey(hKey); | 
|  | return FALSE; | 
|  | } | 
|  |  | 
|  | *RequiredSize = dwLength / sizeof(WCHAR); | 
|  | } | 
|  |  | 
|  | dwLength = ClassNameSize * sizeof(WCHAR); | 
|  | if (RegQueryValueExW(hKey, | 
|  | Class, | 
|  | NULL, | 
|  | NULL, | 
|  | (LPBYTE)ClassName, | 
|  | &dwLength)) | 
|  | { | 
|  | RegCloseKey(hKey); | 
|  | return FALSE; | 
|  | } | 
|  |  | 
|  | RegCloseKey(hKey); | 
|  |  | 
|  | return TRUE; | 
|  | } | 
|  |  | 
|  | /*********************************************************************** | 
|  | *		SetupDiCreateDeviceInfoList (SETUPAPI.@) | 
|  | */ | 
|  | HDEVINFO WINAPI | 
|  | SetupDiCreateDeviceInfoList(const GUID *ClassGuid, | 
|  | HWND hwndParent) | 
|  | { | 
|  | return SetupDiCreateDeviceInfoListExW(ClassGuid, hwndParent, NULL, NULL); | 
|  | } | 
|  |  | 
|  | /*********************************************************************** | 
|  | *		SetupDiCreateDeviceInfoListExA (SETUPAPI.@) | 
|  | */ | 
|  | HDEVINFO WINAPI | 
|  | SetupDiCreateDeviceInfoListExA(const GUID *ClassGuid, | 
|  | HWND hwndParent, | 
|  | PCSTR MachineName, | 
|  | PVOID Reserved) | 
|  | { | 
|  | LPWSTR MachineNameW = NULL; | 
|  | HDEVINFO hDevInfo; | 
|  |  | 
|  | TRACE("\n"); | 
|  |  | 
|  | if (MachineName) | 
|  | { | 
|  | MachineNameW = MultiByteToUnicode(MachineName, CP_ACP); | 
|  | if (MachineNameW == NULL) | 
|  | return INVALID_HANDLE_VALUE; | 
|  | } | 
|  |  | 
|  | hDevInfo = SetupDiCreateDeviceInfoListExW(ClassGuid, hwndParent, | 
|  | MachineNameW, Reserved); | 
|  |  | 
|  | MyFree(MachineNameW); | 
|  |  | 
|  | return hDevInfo; | 
|  | } | 
|  |  | 
|  | /*********************************************************************** | 
|  | *		SetupDiCreateDeviceInfoListExW (SETUPAPI.@) | 
|  | * | 
|  | * Create an empty DeviceInfoSet list. | 
|  | * | 
|  | * PARAMS | 
|  | *   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 | 
|  | *                   local registry will be used. | 
|  | *   Reserved [I] must be NULL | 
|  | * | 
|  | * RETURNS | 
|  | *   Success: empty list. | 
|  | *   Failure: INVALID_HANDLE_VALUE. | 
|  | */ | 
|  | HDEVINFO WINAPI | 
|  | SetupDiCreateDeviceInfoListExW(const GUID *ClassGuid, | 
|  | HWND hwndParent, | 
|  | PCWSTR MachineName, | 
|  | PVOID Reserved) | 
|  | { | 
|  | struct DeviceInfoSet *list = NULL; | 
|  | DWORD size = sizeof(struct DeviceInfoSet); | 
|  |  | 
|  | TRACE("%s %p %s %p\n", debugstr_guid(ClassGuid), hwndParent, | 
|  | debugstr_w(MachineName), Reserved); | 
|  |  | 
|  | if (MachineName != NULL) | 
|  | { | 
|  | FIXME("remote support is not implemented\n"); | 
|  | SetLastError(ERROR_INVALID_MACHINENAME); | 
|  | return INVALID_HANDLE_VALUE; | 
|  | } | 
|  |  | 
|  | if (Reserved != NULL) | 
|  | { | 
|  | SetLastError(ERROR_INVALID_PARAMETER); | 
|  | return INVALID_HANDLE_VALUE; | 
|  | } | 
|  |  | 
|  | list = HeapAlloc(GetProcessHeap(), 0, size); | 
|  | if (!list) | 
|  | { | 
|  | SetLastError(ERROR_NOT_ENOUGH_MEMORY); | 
|  | return INVALID_HANDLE_VALUE; | 
|  | } | 
|  |  | 
|  | list->magic = SETUP_DEVICE_INFO_SET_MAGIC; | 
|  | list->hwndParent = hwndParent; | 
|  | memcpy(&list->ClassGuid, | 
|  | ClassGuid ? ClassGuid : &GUID_NULL, | 
|  | sizeof(list->ClassGuid)); | 
|  | list->cDevices = 0; | 
|  | 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 = (struct DeviceInfoSet *)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)); | 
|  |  | 
|  | 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; | 
|  | } | 
|  |  | 
|  | /*********************************************************************** | 
|  | *              SetupDiCreateDeviceInfoA (SETUPAPI.@) | 
|  | */ | 
|  | BOOL WINAPI SetupDiCreateDeviceInfoA( | 
|  | HDEVINFO DeviceInfoSet, | 
|  | PCSTR DeviceName, | 
|  | CONST GUID *ClassGuid, | 
|  | PCSTR DeviceDescription, | 
|  | HWND hwndParent, | 
|  | DWORD CreationFlags, | 
|  | PSP_DEVINFO_DATA DeviceInfoData) | 
|  | { | 
|  | BOOL ret = FALSE; | 
|  | LPWSTR DeviceNameW = NULL; | 
|  | LPWSTR DeviceDescriptionW = NULL; | 
|  |  | 
|  | if (DeviceName) | 
|  | { | 
|  | DeviceNameW = MultiByteToUnicode(DeviceName, CP_ACP); | 
|  | if (DeviceNameW == NULL) return FALSE; | 
|  | } | 
|  | if (DeviceDescription) | 
|  | { | 
|  | DeviceDescriptionW = MultiByteToUnicode(DeviceDescription, CP_ACP); | 
|  | if (DeviceDescriptionW == NULL) | 
|  | { | 
|  | MyFree(DeviceNameW); | 
|  | return FALSE; | 
|  | } | 
|  | } | 
|  |  | 
|  | ret = SetupDiCreateDeviceInfoW(DeviceInfoSet, DeviceNameW, ClassGuid, DeviceDescriptionW, | 
|  | hwndParent, CreationFlags, DeviceInfoData); | 
|  |  | 
|  | MyFree(DeviceNameW); | 
|  | MyFree(DeviceDescriptionW); | 
|  |  | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | static DWORD SETUPDI_DevNameToDevID(LPCWSTR devName) | 
|  | { | 
|  | LPCWSTR ptr; | 
|  | int devNameLen = lstrlenW(devName); | 
|  | DWORD devInst = 0; | 
|  | BOOL valid = TRUE; | 
|  |  | 
|  | TRACE("%s\n", debugstr_w(devName)); | 
|  | for (ptr = devName; valid && *ptr && ptr - devName < devNameLen; ) | 
|  | { | 
|  | if (isdigitW(*ptr)) | 
|  | { | 
|  | devInst *= 10; | 
|  | devInst |= *ptr - '0'; | 
|  | ptr++; | 
|  | } | 
|  | else | 
|  | valid = FALSE; | 
|  | } | 
|  | TRACE("%d\n", valid ? devInst : 0xffffffff); | 
|  | return valid ? devInst : 0xffffffff; | 
|  | } | 
|  |  | 
|  | /*********************************************************************** | 
|  | *              SetupDiCreateDeviceInfoW (SETUPAPI.@) | 
|  | */ | 
|  | BOOL WINAPI SetupDiCreateDeviceInfoW( | 
|  | HDEVINFO DeviceInfoSet, | 
|  | PCWSTR DeviceName, | 
|  | CONST GUID *ClassGuid, | 
|  | PCWSTR DeviceDescription, | 
|  | HWND hwndParent, | 
|  | DWORD CreationFlags, | 
|  | PSP_DEVINFO_DATA DeviceInfoData) | 
|  | { | 
|  | struct DeviceInfoSet *set = (struct DeviceInfoSet *)DeviceInfoSet; | 
|  | BOOL ret = FALSE, allocatedInstanceId = FALSE; | 
|  | LPCWSTR instanceId = NULL; | 
|  |  | 
|  | TRACE("%p %s %s %s %p %x %p\n", DeviceInfoSet, debugstr_w(DeviceName), | 
|  | debugstr_guid(ClassGuid), debugstr_w(DeviceDescription), | 
|  | hwndParent, CreationFlags, DeviceInfoData); | 
|  |  | 
|  | if (!DeviceName) | 
|  | { | 
|  | SetLastError(ERROR_INVALID_DEVINST_NAME); | 
|  | return FALSE; | 
|  | } | 
|  | if (!DeviceInfoSet || DeviceInfoSet == INVALID_HANDLE_VALUE) | 
|  | { | 
|  | SetLastError(ERROR_INVALID_HANDLE); | 
|  | return FALSE; | 
|  | } | 
|  | if (!ClassGuid) | 
|  | { | 
|  | SetLastError(ERROR_INVALID_PARAMETER); | 
|  | return FALSE; | 
|  | } | 
|  | if (set->magic != SETUP_DEVICE_INFO_SET_MAGIC) | 
|  | { | 
|  | SetLastError(ERROR_INVALID_HANDLE); | 
|  | return FALSE; | 
|  | } | 
|  | if (!IsEqualGUID(&set->ClassGuid, &GUID_NULL) && | 
|  | !IsEqualGUID(ClassGuid, &set->ClassGuid)) | 
|  | { | 
|  | SetLastError(ERROR_CLASS_MISMATCH); | 
|  | return FALSE; | 
|  | } | 
|  | if ((CreationFlags & DICD_GENERATE_ID)) | 
|  | { | 
|  | if (strchrW(DeviceName, '\\')) | 
|  | SetLastError(ERROR_INVALID_DEVINST_NAME); | 
|  | else | 
|  | { | 
|  | static const WCHAR newDeviceFmt[] = {'R','O','O','T','\\','%','s', | 
|  | '\\','%','0','4','d',0}; | 
|  | DWORD devId; | 
|  |  | 
|  | if (set->cDevices) | 
|  | { | 
|  | DWORD highestDevID = 0; | 
|  | struct DeviceInstance *devInst; | 
|  |  | 
|  | LIST_FOR_EACH_ENTRY(devInst, &set->devices, struct DeviceInstance, entry) | 
|  | { | 
|  | struct DeviceInfo *devInfo = (struct DeviceInfo *)devInst->data.Reserved; | 
|  | LPCWSTR devName = strrchrW(devInfo->instanceId, '\\'); | 
|  | DWORD id; | 
|  |  | 
|  | if (devName) | 
|  | devName++; | 
|  | else | 
|  | devName = devInfo->instanceId; | 
|  | id = SETUPDI_DevNameToDevID(devName); | 
|  | if (id != 0xffffffff && id > highestDevID) | 
|  | highestDevID = id; | 
|  | } | 
|  | devId = highestDevID + 1; | 
|  | } | 
|  | else | 
|  | devId = 0; | 
|  | /* 17 == lstrlenW(L"Root\\") + lstrlenW("\\") + 1 + %d max size */ | 
|  | instanceId = HeapAlloc(GetProcessHeap(), 0, | 
|  | (17 + lstrlenW(DeviceName)) * sizeof(WCHAR)); | 
|  | if (instanceId) | 
|  | { | 
|  | sprintfW((LPWSTR)instanceId, newDeviceFmt, DeviceName, | 
|  | devId); | 
|  | allocatedInstanceId = TRUE; | 
|  | ret = TRUE; | 
|  | } | 
|  | else | 
|  | ret = FALSE; | 
|  | } | 
|  | } | 
|  | else | 
|  | { | 
|  | struct DeviceInstance *devInst; | 
|  |  | 
|  | ret = TRUE; | 
|  | instanceId = DeviceName; | 
|  | LIST_FOR_EACH_ENTRY(devInst, &set->devices, struct DeviceInstance, entry) | 
|  | { | 
|  | struct DeviceInfo *devInfo = (struct DeviceInfo *)devInst->data.Reserved; | 
|  |  | 
|  | if (!lstrcmpiW(DeviceName, devInfo->instanceId)) | 
|  | { | 
|  | SetLastError(ERROR_DEVINST_ALREADY_EXISTS); | 
|  | ret = FALSE; | 
|  | } | 
|  | } | 
|  | } | 
|  | if (ret) | 
|  | { | 
|  | SP_DEVINFO_DATA *dev = NULL; | 
|  |  | 
|  | ret = SETUPDI_AddDeviceToSet(set, ClassGuid, 0 /* FIXME: DevInst */, | 
|  | instanceId, TRUE, &dev); | 
|  | if (ret) | 
|  | { | 
|  | if (DeviceDescription) | 
|  | SetupDiSetDeviceRegistryPropertyW(DeviceInfoSet, | 
|  | dev, SPDRP_DEVICEDESC, (const BYTE *)DeviceDescription, | 
|  | lstrlenW(DeviceDescription) * sizeof(WCHAR)); | 
|  | if (DeviceInfoData) | 
|  | { | 
|  | if (DeviceInfoData->cbSize != sizeof(SP_DEVINFO_DATA)) | 
|  | { | 
|  | SetLastError(ERROR_INVALID_USER_BUFFER); | 
|  | ret = FALSE; | 
|  | } | 
|  | else | 
|  | *DeviceInfoData = *dev; | 
|  | } | 
|  | } | 
|  | } | 
|  | if (allocatedInstanceId) | 
|  | HeapFree(GetProcessHeap(), 0, (LPWSTR)instanceId); | 
|  |  | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | /*********************************************************************** | 
|  | *		SetupDiRegisterDeviceInfo (SETUPAPI.@) | 
|  | */ | 
|  | BOOL WINAPI SetupDiRegisterDeviceInfo( | 
|  | HDEVINFO DeviceInfoSet, | 
|  | PSP_DEVINFO_DATA DeviceInfoData, | 
|  | DWORD Flags, | 
|  | PSP_DETSIG_CMPPROC CompareProc, | 
|  | PVOID CompareContext, | 
|  | PSP_DEVINFO_DATA DupDeviceInfoData) | 
|  | { | 
|  | struct DeviceInfoSet *set = (struct DeviceInfoSet *)DeviceInfoSet; | 
|  | struct DeviceInfo *devInfo; | 
|  |  | 
|  | TRACE("%p %p %08x %p %p %p\n", DeviceInfoSet, DeviceInfoData, Flags, | 
|  | CompareProc, CompareContext, DupDeviceInfoData); | 
|  |  | 
|  | 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 (devInfo->phantom) | 
|  | { | 
|  | devInfo->phantom = FALSE; | 
|  | RegDeleteValueW(devInfo->key, Phantom); | 
|  | } | 
|  | return TRUE; | 
|  | } | 
|  |  | 
|  | /*********************************************************************** | 
|  | *		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 = (struct DeviceInfoSet *)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); | 
|  | } | 
|  | else | 
|  | SetLastError(ERROR_INVALID_HANDLE); | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | /*********************************************************************** | 
|  | *		SetupDiGetDeviceInstanceIdA (SETUPAPI.@) | 
|  | */ | 
|  | BOOL WINAPI SetupDiGetDeviceInstanceIdA( | 
|  | HDEVINFO DeviceInfoSet, | 
|  | PSP_DEVINFO_DATA DeviceInfoData, | 
|  | PSTR DeviceInstanceId, | 
|  | DWORD DeviceInstanceIdSize, | 
|  | PDWORD RequiredSize) | 
|  | { | 
|  | BOOL ret = FALSE; | 
|  | DWORD size; | 
|  | PWSTR instanceId; | 
|  |  | 
|  | TRACE("%p %p %p %d %p\n", DeviceInfoSet, DeviceInfoData, DeviceInstanceId, | 
|  | DeviceInstanceIdSize, RequiredSize); | 
|  |  | 
|  | SetupDiGetDeviceInstanceIdW(DeviceInfoSet, | 
|  | DeviceInfoData, | 
|  | NULL, | 
|  | 0, | 
|  | &size); | 
|  | if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) | 
|  | return FALSE; | 
|  | instanceId = HeapAlloc(GetProcessHeap(), 0, size * sizeof(WCHAR)); | 
|  | if (instanceId) | 
|  | { | 
|  | ret = SetupDiGetDeviceInstanceIdW(DeviceInfoSet, | 
|  | DeviceInfoData, | 
|  | instanceId, | 
|  | size, | 
|  | &size); | 
|  | if (ret) | 
|  | { | 
|  | int len = WideCharToMultiByte(CP_ACP, 0, instanceId, -1, | 
|  | DeviceInstanceId, | 
|  | DeviceInstanceIdSize, NULL, NULL); | 
|  |  | 
|  | if (!len) | 
|  | ret = FALSE; | 
|  | else | 
|  | { | 
|  | if (len > DeviceInstanceIdSize) | 
|  | { | 
|  | SetLastError(ERROR_INSUFFICIENT_BUFFER); | 
|  | ret = FALSE; | 
|  | } | 
|  | if (RequiredSize) | 
|  | *RequiredSize = len; | 
|  | } | 
|  | } | 
|  | HeapFree(GetProcessHeap(), 0, instanceId); | 
|  | } | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | /*********************************************************************** | 
|  | *		SetupDiGetDeviceInstanceIdW (SETUPAPI.@) | 
|  | */ | 
|  | BOOL WINAPI SetupDiGetDeviceInstanceIdW( | 
|  | HDEVINFO DeviceInfoSet, | 
|  | PSP_DEVINFO_DATA DeviceInfoData, | 
|  | PWSTR DeviceInstanceId, | 
|  | DWORD DeviceInstanceIdSize, | 
|  | PDWORD RequiredSize) | 
|  | { | 
|  | struct DeviceInfoSet *set = (struct DeviceInfoSet *)DeviceInfoSet; | 
|  | struct DeviceInfo *devInfo; | 
|  |  | 
|  | TRACE("%p %p %p %d %p\n", DeviceInfoSet, DeviceInfoData, DeviceInstanceId, | 
|  | DeviceInstanceIdSize, RequiredSize); | 
|  |  | 
|  | 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; | 
|  | } | 
|  | TRACE("instance ID: %s\n", debugstr_w(devInfo->instanceId)); | 
|  | if (DeviceInstanceIdSize < strlenW(devInfo->instanceId) + 1) | 
|  | { | 
|  | SetLastError(ERROR_INSUFFICIENT_BUFFER); | 
|  | if (RequiredSize) | 
|  | *RequiredSize = lstrlenW(devInfo->instanceId) + 1; | 
|  | return FALSE; | 
|  | } | 
|  | lstrcpyW(DeviceInstanceId, devInfo->instanceId); | 
|  | if (RequiredSize) | 
|  | *RequiredSize = lstrlenW(devInfo->instanceId) + 1; | 
|  | return TRUE; | 
|  | } | 
|  |  | 
|  | /*********************************************************************** | 
|  | *		SetupDiGetActualSectionToInstallA (SETUPAPI.@) | 
|  | */ | 
|  | BOOL WINAPI SetupDiGetActualSectionToInstallA( | 
|  | HINF InfHandle, | 
|  | PCSTR InfSectionName, | 
|  | PSTR InfSectionWithExt, | 
|  | DWORD InfSectionWithExtSize, | 
|  | PDWORD RequiredSize, | 
|  | PSTR *Extension) | 
|  | { | 
|  | FIXME("\n"); | 
|  | return FALSE; | 
|  | } | 
|  |  | 
|  | /*********************************************************************** | 
|  | *		SetupDiGetActualSectionToInstallW (SETUPAPI.@) | 
|  | */ | 
|  | BOOL WINAPI SetupDiGetActualSectionToInstallW( | 
|  | HINF InfHandle, | 
|  | PCWSTR InfSectionName, | 
|  | PWSTR InfSectionWithExt, | 
|  | DWORD InfSectionWithExtSize, | 
|  | PDWORD RequiredSize, | 
|  | PWSTR *Extension) | 
|  | { | 
|  | WCHAR szBuffer[MAX_PATH]; | 
|  | DWORD dwLength; | 
|  | DWORD dwFullLength; | 
|  | LONG lLineCount = -1; | 
|  |  | 
|  | lstrcpyW(szBuffer, InfSectionName); | 
|  | dwLength = lstrlenW(szBuffer); | 
|  |  | 
|  | if (OsVersionInfo.dwPlatformId == VER_PLATFORM_WIN32_NT) | 
|  | { | 
|  | /* Test section name with '.NTx86' extension */ | 
|  | lstrcpyW(&szBuffer[dwLength], NtPlatformExtension); | 
|  | lLineCount = SetupGetLineCountW(InfHandle, szBuffer); | 
|  |  | 
|  | if (lLineCount == -1) | 
|  | { | 
|  | /* Test section name with '.NT' extension */ | 
|  | lstrcpyW(&szBuffer[dwLength], NtExtension); | 
|  | lLineCount = SetupGetLineCountW(InfHandle, szBuffer); | 
|  | } | 
|  | } | 
|  | else | 
|  | { | 
|  | /* Test section name with '.Win' extension */ | 
|  | lstrcpyW(&szBuffer[dwLength], WinExtension); | 
|  | lLineCount = SetupGetLineCountW(InfHandle, szBuffer); | 
|  | } | 
|  |  | 
|  | if (lLineCount == -1) | 
|  | { | 
|  | /* Test section name without extension */ | 
|  | szBuffer[dwLength] = 0; | 
|  | lLineCount = SetupGetLineCountW(InfHandle, szBuffer); | 
|  | } | 
|  |  | 
|  | if (lLineCount == -1) | 
|  | { | 
|  | SetLastError(ERROR_INVALID_PARAMETER); | 
|  | return FALSE; | 
|  | } | 
|  |  | 
|  | dwFullLength = lstrlenW(szBuffer); | 
|  |  | 
|  | if (InfSectionWithExt != NULL && InfSectionWithExtSize != 0) | 
|  | { | 
|  | if (InfSectionWithExtSize < (dwFullLength + 1)) | 
|  | { | 
|  | SetLastError(ERROR_INSUFFICIENT_BUFFER); | 
|  | return FALSE; | 
|  | } | 
|  |  | 
|  | lstrcpyW(InfSectionWithExt, szBuffer); | 
|  | if (Extension != NULL) | 
|  | { | 
|  | *Extension = (dwLength == dwFullLength) ? NULL : &InfSectionWithExt[dwLength]; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (RequiredSize != NULL) | 
|  | { | 
|  | *RequiredSize = dwFullLength + 1; | 
|  | } | 
|  |  | 
|  | return TRUE; | 
|  | } | 
|  |  | 
|  | /*********************************************************************** | 
|  | *		SetupDiGetClassDescriptionA  (SETUPAPI.@) | 
|  | */ | 
|  | BOOL WINAPI SetupDiGetClassDescriptionA( | 
|  | const GUID* ClassGuid, | 
|  | PSTR ClassDescription, | 
|  | DWORD ClassDescriptionSize, | 
|  | PDWORD RequiredSize) | 
|  | { | 
|  | return SetupDiGetClassDescriptionExA(ClassGuid, ClassDescription, | 
|  | ClassDescriptionSize, | 
|  | RequiredSize, NULL, NULL); | 
|  | } | 
|  |  | 
|  | /*********************************************************************** | 
|  | *		SetupDiGetClassDescriptionW  (SETUPAPI.@) | 
|  | */ | 
|  | BOOL WINAPI SetupDiGetClassDescriptionW( | 
|  | const GUID* ClassGuid, | 
|  | PWSTR ClassDescription, | 
|  | DWORD ClassDescriptionSize, | 
|  | PDWORD RequiredSize) | 
|  | { | 
|  | return SetupDiGetClassDescriptionExW(ClassGuid, ClassDescription, | 
|  | ClassDescriptionSize, | 
|  | RequiredSize, NULL, NULL); | 
|  | } | 
|  |  | 
|  | /*********************************************************************** | 
|  | *		SetupDiGetClassDescriptionExA  (SETUPAPI.@) | 
|  | */ | 
|  | BOOL WINAPI SetupDiGetClassDescriptionExA( | 
|  | const GUID* ClassGuid, | 
|  | PSTR ClassDescription, | 
|  | DWORD ClassDescriptionSize, | 
|  | PDWORD RequiredSize, | 
|  | PCSTR MachineName, | 
|  | PVOID Reserved) | 
|  | { | 
|  | 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; | 
|  | } | 
|  |  | 
|  | /*********************************************************************** | 
|  | *		SetupDiGetClassDescriptionExW  (SETUPAPI.@) | 
|  | */ | 
|  | BOOL WINAPI SetupDiGetClassDescriptionExW( | 
|  | const GUID* ClassGuid, | 
|  | PWSTR ClassDescription, | 
|  | DWORD ClassDescriptionSize, | 
|  | PDWORD RequiredSize, | 
|  | PCWSTR MachineName, | 
|  | PVOID Reserved) | 
|  | { | 
|  | HKEY hKey; | 
|  | DWORD dwLength; | 
|  | BOOL ret; | 
|  |  | 
|  | hKey = SetupDiOpenClassRegKeyExW(ClassGuid, | 
|  | KEY_ALL_ACCESS, | 
|  | DIOCR_INSTALLER, | 
|  | MachineName, | 
|  | Reserved); | 
|  | if (hKey == INVALID_HANDLE_VALUE) | 
|  | { | 
|  | WARN("SetupDiOpenClassRegKeyExW() failed (Error %u)\n", GetLastError()); | 
|  | return FALSE; | 
|  | } | 
|  |  | 
|  | dwLength = ClassDescriptionSize * sizeof(WCHAR); | 
|  | ret = !RegQueryValueExW( hKey, NULL, NULL, NULL, | 
|  | (LPBYTE)ClassDescription, &dwLength ); | 
|  | if (RequiredSize) *RequiredSize = dwLength / sizeof(WCHAR); | 
|  | RegCloseKey(hKey); | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | /*********************************************************************** | 
|  | *		SetupDiGetClassDevsA (SETUPAPI.@) | 
|  | */ | 
|  | HDEVINFO WINAPI SetupDiGetClassDevsA( | 
|  | CONST GUID *class, | 
|  | LPCSTR enumstr, | 
|  | HWND parent, | 
|  | DWORD flags) | 
|  | { | 
|  | HDEVINFO ret; | 
|  | LPWSTR enumstrW = 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); | 
|  | } | 
|  | 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 *interface) | 
|  | { | 
|  | 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, interface, 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 *interface, LPCWSTR enumstr) | 
|  | { | 
|  | struct DeviceInfoSet *set = (struct DeviceInfoSet *)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, | 
|  | interface); | 
|  | } | 
|  | 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 *interface, LPCWSTR enumstr, DWORD flags) | 
|  | { | 
|  | HKEY interfacesKey = SetupDiOpenClassRegKeyExW(interface, KEY_READ, | 
|  | DIOCR_INTERFACE, NULL, NULL); | 
|  |  | 
|  | TRACE("%p, %s, %s, %08x\n", DeviceInfoSet, debugstr_guid(interface), | 
|  | 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, interface, 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 = (struct DeviceInfoSet *)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.@) | 
|  | */ | 
|  | HDEVINFO WINAPI SetupDiGetClassDevsW( | 
|  | CONST GUID *class, | 
|  | LPCWSTR enumstr, | 
|  | HWND parent, | 
|  | DWORD flags) | 
|  | { | 
|  | return SetupDiGetClassDevsExW(class, enumstr, parent, flags, NULL, NULL, | 
|  | NULL); | 
|  | } | 
|  |  | 
|  | /*********************************************************************** | 
|  | *              SetupDiGetClassDevsExW (SETUPAPI.@) | 
|  | */ | 
|  | HDEVINFO WINAPI SetupDiGetClassDevsExW( | 
|  | CONST GUID *class, | 
|  | PCWSTR enumstr, | 
|  | HWND parent, | 
|  | DWORD flags, | 
|  | HDEVINFO deviceset, | 
|  | PCWSTR machine, | 
|  | PVOID reserved) | 
|  | { | 
|  | 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 = (struct DeviceInfoSet *)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 = (struct DeviceInfoSet *)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 = (struct DeviceInfoSet *)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 = (struct DeviceInfoSet *)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 = (struct DeviceInfoSet *)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 DeviceInfoSet, | 
|  | PSP_DEVINFO_DATA DeviceInfoData, | 
|  | CONST GUID * InterfaceClassGuid, | 
|  | DWORD MemberIndex, | 
|  | PSP_DEVICE_INTERFACE_DATA DeviceInterfaceData) | 
|  | { | 
|  | struct DeviceInfoSet *set = (struct DeviceInfoSet *)DeviceInfoSet; | 
|  | BOOL ret = FALSE; | 
|  |  | 
|  | TRACE("%p, %p, %s, %d, %p\n", DeviceInfoSet, DeviceInfoData, | 
|  | debugstr_guid(InterfaceClassGuid), MemberIndex, DeviceInterfaceData); | 
|  |  | 
|  | 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); | 
|  | } | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | /*********************************************************************** | 
|  | *		SetupDiDestroyDeviceInfoList (SETUPAPI.@) | 
|  | * | 
|  | * Destroy a DeviceInfoList and free all used memory of the list. | 
|  | * | 
|  | * PARAMS | 
|  | *   devinfo [I] DeviceInfoList pointer to list to destroy | 
|  | * | 
|  | * RETURNS | 
|  | *   Success: non zero value. | 
|  | *   Failure: zero value. | 
|  | */ | 
|  | BOOL WINAPI SetupDiDestroyDeviceInfoList(HDEVINFO devinfo) | 
|  | { | 
|  | BOOL ret = FALSE; | 
|  |  | 
|  | TRACE("%p\n", devinfo); | 
|  | if (devinfo && devinfo != INVALID_HANDLE_VALUE) | 
|  | { | 
|  | struct DeviceInfoSet *list = (struct DeviceInfoSet *)devinfo; | 
|  |  | 
|  | if (list->magic == SETUP_DEVICE_INFO_SET_MAGIC) | 
|  | { | 
|  | struct DeviceInstance *devInst, *devInst2; | 
|  |  | 
|  | 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; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (ret == FALSE) | 
|  | SetLastError(ERROR_INVALID_HANDLE); | 
|  |  | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | /*********************************************************************** | 
|  | *		SetupDiGetDeviceInterfaceDetailA (SETUPAPI.@) | 
|  | */ | 
|  | BOOL WINAPI SetupDiGetDeviceInterfaceDetailA( | 
|  | HDEVINFO DeviceInfoSet, | 
|  | PSP_DEVICE_INTERFACE_DATA DeviceInterfaceData, | 
|  | PSP_DEVICE_INTERFACE_DETAIL_DATA_A DeviceInterfaceDetailData, | 
|  | DWORD DeviceInterfaceDetailDataSize, | 
|  | PDWORD RequiredSize, | 
|  | PSP_DEVINFO_DATA DeviceInfoData) | 
|  | { | 
|  | struct DeviceInfoSet *set = (struct DeviceInfoSet *)DeviceInfoSet; | 
|  | struct InterfaceInfo *info; | 
|  | DWORD bytesNeeded = offsetof(SP_DEVICE_INTERFACE_DETAIL_DATA_A, DevicePath) | 
|  | + 1; | 
|  | BOOL ret = FALSE; | 
|  |  | 
|  | TRACE("(%p, %p, %p, %d, %p, %p)\n", DeviceInfoSet, | 
|  | DeviceInterfaceData, DeviceInterfaceDetailData, | 
|  | DeviceInterfaceDetailDataSize, RequiredSize, DeviceInfoData); | 
|  |  | 
|  | 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_A, DevicePath) + sizeof(char))) | 
|  | { | 
|  | 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; | 
|  | } | 
|  |  | 
|  | /*********************************************************************** | 
|  | *		SetupDiGetDeviceInterfaceDetailW (SETUPAPI.@) | 
|  | */ | 
|  | BOOL WINAPI SetupDiGetDeviceInterfaceDetailW( | 
|  | HDEVINFO DeviceInfoSet, | 
|  | PSP_DEVICE_INTERFACE_DATA DeviceInterfaceData, | 
|  | PSP_DEVICE_INTERFACE_DETAIL_DATA_W DeviceInterfaceDetailData, | 
|  | DWORD DeviceInterfaceDetailDataSize, | 
|  | PDWORD RequiredSize, | 
|  | PSP_DEVINFO_DATA DeviceInfoData) | 
|  | { | 
|  | struct DeviceInfoSet *set = (struct DeviceInfoSet *)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); | 
|  |  | 
|  | 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 | 
|  | { | 
|  | DWORD   regType; | 
|  | LPCSTR  nameA; | 
|  | LPCWSTR nameW; | 
|  | }; | 
|  |  | 
|  | static const struct PropertyMapEntry PropertyMap[] = { | 
|  | { REG_SZ, "DeviceDesc", DeviceDesc }, | 
|  | { REG_MULTI_SZ, "HardwareId", HardwareId }, | 
|  | { REG_MULTI_SZ, "CompatibleIDs", CompatibleIDs }, | 
|  | { 0, NULL, NULL }, /* SPDRP_UNUSED0 */ | 
|  | { REG_SZ, "Service", Service }, | 
|  | { 0, NULL, NULL }, /* SPDRP_UNUSED1 */ | 
|  | { 0, NULL, NULL }, /* SPDRP_UNUSED2 */ | 
|  | { REG_SZ, "Class", Class }, | 
|  | { REG_SZ, "ClassGUID", ClassGUID }, | 
|  | { REG_SZ, "Driver", Driver }, | 
|  | { REG_DWORD, "ConfigFlags", ConfigFlags }, | 
|  | { REG_SZ, "Mfg", Mfg }, | 
|  | { REG_SZ, "FriendlyName", FriendlyName }, | 
|  | { REG_SZ, "LocationInformation", LocationInformation }, | 
|  | { 0, NULL, NULL }, /* SPDRP_PHYSICAL_DEVICE_OBJECT_NAME */ | 
|  | { REG_DWORD, "Capabilities", Capabilities }, | 
|  | { REG_DWORD, "UINumber", UINumber }, | 
|  | { REG_MULTI_SZ, "UpperFilters", UpperFilters }, | 
|  | { REG_MULTI_SZ, "LowerFilters", LowerFilters }, | 
|  | }; | 
|  |  | 
|  | /*********************************************************************** | 
|  | *		SetupDiGetDeviceRegistryPropertyA (SETUPAPI.@) | 
|  | */ | 
|  | BOOL WINAPI SetupDiGetDeviceRegistryPropertyA( | 
|  | HDEVINFO  DeviceInfoSet, | 
|  | PSP_DEVINFO_DATA  DeviceInfoData, | 
|  | DWORD   Property, | 
|  | PDWORD  PropertyRegDataType, | 
|  | PBYTE   PropertyBuffer, | 
|  | DWORD   PropertyBufferSize, | 
|  | PDWORD  RequiredSize) | 
|  | { | 
|  | BOOL ret = FALSE; | 
|  | struct DeviceInfoSet *set = (struct DeviceInfoSet *)DeviceInfoSet; | 
|  | struct DeviceInfo *devInfo; | 
|  |  | 
|  | TRACE("%04x %p %d %p %p %d %p\n", (DWORD)DeviceInfoSet, DeviceInfoData, | 
|  | Property, PropertyRegDataType, PropertyBuffer, PropertyBufferSize, | 
|  | RequiredSize); | 
|  |  | 
|  | 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 (PropertyBufferSize && PropertyBuffer == NULL) | 
|  | { | 
|  | SetLastError(ERROR_INVALID_DATA); | 
|  | return FALSE; | 
|  | } | 
|  | devInfo = (struct DeviceInfo *)DeviceInfoData->Reserved; | 
|  | if (Property < sizeof(PropertyMap) / sizeof(PropertyMap[0]) | 
|  | && PropertyMap[Property].nameA) | 
|  | { | 
|  | DWORD size = PropertyBufferSize; | 
|  | LONG l = RegQueryValueExA(devInfo->key, PropertyMap[Property].nameA, | 
|  | NULL, PropertyRegDataType, PropertyBuffer, &size); | 
|  |  | 
|  | if (l == ERROR_MORE_DATA || !PropertyBufferSize) | 
|  | SetLastError(ERROR_INSUFFICIENT_BUFFER); | 
|  | else if (!l) | 
|  | ret = TRUE; | 
|  | else | 
|  | SetLastError(l); | 
|  | if (RequiredSize) | 
|  | *RequiredSize = size; | 
|  | } | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | /*********************************************************************** | 
|  | *		SetupDiGetDeviceRegistryPropertyW (SETUPAPI.@) | 
|  | */ | 
|  | BOOL WINAPI SetupDiGetDeviceRegistryPropertyW( | 
|  | HDEVINFO  DeviceInfoSet, | 
|  | PSP_DEVINFO_DATA  DeviceInfoData, | 
|  | DWORD   Property, | 
|  | PDWORD  PropertyRegDataType, | 
|  | PBYTE   PropertyBuffer, | 
|  | DWORD   PropertyBufferSize, | 
|  | PDWORD  RequiredSize) | 
|  | { | 
|  | BOOL ret = FALSE; | 
|  | struct DeviceInfoSet *set = (struct DeviceInfoSet *)DeviceInfoSet; | 
|  | struct DeviceInfo *devInfo; | 
|  |  | 
|  | TRACE("%04x %p %d %p %p %d %p\n", (DWORD)DeviceInfoSet, DeviceInfoData, | 
|  | Property, PropertyRegDataType, PropertyBuffer, PropertyBufferSize, | 
|  | RequiredSize); | 
|  |  | 
|  | 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 (PropertyBufferSize && PropertyBuffer == NULL) | 
|  | { | 
|  | SetLastError(ERROR_INVALID_DATA); | 
|  | return FALSE; | 
|  | } | 
|  | devInfo = (struct DeviceInfo *)DeviceInfoData->Reserved; | 
|  | if (Property < sizeof(PropertyMap) / sizeof(PropertyMap[0]) | 
|  | && PropertyMap[Property].nameW) | 
|  | { | 
|  | DWORD size = PropertyBufferSize; | 
|  | LONG l = RegQueryValueExW(devInfo->key, PropertyMap[Property].nameW, | 
|  | NULL, PropertyRegDataType, PropertyBuffer, &size); | 
|  |  | 
|  | if (l == ERROR_MORE_DATA || !PropertyBufferSize) | 
|  | SetLastError(ERROR_INSUFFICIENT_BUFFER); | 
|  | else if (!l) | 
|  | ret = TRUE; | 
|  | else | 
|  | SetLastError(l); | 
|  | if (RequiredSize) | 
|  | *RequiredSize = size; | 
|  | } | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | /*********************************************************************** | 
|  | *		SetupDiSetDeviceRegistryPropertyA (SETUPAPI.@) | 
|  | */ | 
|  | BOOL WINAPI SetupDiSetDeviceRegistryPropertyA( | 
|  | HDEVINFO DeviceInfoSet, | 
|  | PSP_DEVINFO_DATA DeviceInfoData, | 
|  | DWORD Property, | 
|  | const BYTE *PropertyBuffer, | 
|  | DWORD PropertyBufferSize) | 
|  | { | 
|  | BOOL ret = FALSE; | 
|  | struct DeviceInfoSet *set = (struct DeviceInfoSet *)DeviceInfoSet; | 
|  | struct DeviceInfo *devInfo; | 
|  |  | 
|  | TRACE("%p %p %d %p %d\n", DeviceInfoSet, DeviceInfoData, Property, | 
|  | PropertyBuffer, PropertyBufferSize); | 
|  |  | 
|  | 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 (Property < sizeof(PropertyMap) / sizeof(PropertyMap[0]) | 
|  | && PropertyMap[Property].nameA) | 
|  | { | 
|  | LONG l = RegSetValueExA(devInfo->key, PropertyMap[Property].nameA, 0, | 
|  | PropertyMap[Property].regType, PropertyBuffer, | 
|  | PropertyBufferSize); | 
|  | if (!l) | 
|  | ret = TRUE; | 
|  | else | 
|  | SetLastError(l); | 
|  | } | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | /*********************************************************************** | 
|  | *		SetupDiSetDeviceRegistryPropertyW (SETUPAPI.@) | 
|  | */ | 
|  | BOOL WINAPI SetupDiSetDeviceRegistryPropertyW( | 
|  | HDEVINFO DeviceInfoSet, | 
|  | PSP_DEVINFO_DATA DeviceInfoData, | 
|  | DWORD Property, | 
|  | const BYTE *PropertyBuffer, | 
|  | DWORD PropertyBufferSize) | 
|  | { | 
|  | BOOL ret = FALSE; | 
|  | struct DeviceInfoSet *set = (struct DeviceInfoSet *)DeviceInfoSet; | 
|  | struct DeviceInfo *devInfo; | 
|  |  | 
|  | TRACE("%p %p %d %p %d\n", DeviceInfoSet, DeviceInfoData, Property, | 
|  | PropertyBuffer, PropertyBufferSize); | 
|  |  | 
|  | 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 (Property < sizeof(PropertyMap) / sizeof(PropertyMap[0]) | 
|  | && PropertyMap[Property].nameW) | 
|  | { | 
|  | LONG l = RegSetValueExW(devInfo->key, PropertyMap[Property].nameW, 0, | 
|  | PropertyMap[Property].regType, PropertyBuffer, | 
|  | PropertyBufferSize); | 
|  | if (!l) | 
|  | ret = TRUE; | 
|  | else | 
|  | SetLastError(l); | 
|  | } | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | /*********************************************************************** | 
|  | *		SetupDiInstallClassA (SETUPAPI.@) | 
|  | */ | 
|  | BOOL WINAPI SetupDiInstallClassA( | 
|  | HWND hwndParent, | 
|  | PCSTR InfFileName, | 
|  | DWORD Flags, | 
|  | HSPFILEQ FileQueue) | 
|  | { | 
|  | UNICODE_STRING FileNameW; | 
|  | BOOL Result; | 
|  |  | 
|  | if (!InfFileName) | 
|  | { | 
|  | SetLastError(ERROR_INVALID_PARAMETER); | 
|  | return FALSE; | 
|  | } | 
|  | if (!RtlCreateUnicodeStringFromAsciiz(&FileNameW, InfFileName)) | 
|  | { | 
|  | SetLastError(ERROR_NOT_ENOUGH_MEMORY); | 
|  | return FALSE; | 
|  | } | 
|  |  | 
|  | Result = SetupDiInstallClassW(hwndParent, FileNameW.Buffer, Flags, FileQueue); | 
|  |  | 
|  | RtlFreeUnicodeString(&FileNameW); | 
|  |  | 
|  | return Result; | 
|  | } | 
|  |  | 
|  | static HKEY CreateClassKey(HINF hInf) | 
|  | { | 
|  | static const WCHAR slash[] = { '\\',0 }; | 
|  | WCHAR FullBuffer[MAX_PATH]; | 
|  | WCHAR Buffer[MAX_PATH]; | 
|  | DWORD RequiredSize; | 
|  | HKEY hClassKey; | 
|  |  | 
|  | if (!SetupGetLineTextW(NULL, | 
|  | hInf, | 
|  | Version, | 
|  | ClassGUID, | 
|  | Buffer, | 
|  | MAX_PATH, | 
|  | &RequiredSize)) | 
|  | { | 
|  | return INVALID_HANDLE_VALUE; | 
|  | } | 
|  |  | 
|  | lstrcpyW(FullBuffer, ControlClass); | 
|  | lstrcatW(FullBuffer, slash); | 
|  | lstrcatW(FullBuffer, Buffer); | 
|  |  | 
|  | if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, | 
|  | FullBuffer, | 
|  | 0, | 
|  | KEY_ALL_ACCESS, | 
|  | &hClassKey)) | 
|  | { | 
|  | if (!SetupGetLineTextW(NULL, | 
|  | hInf, | 
|  | Version, | 
|  | Class, | 
|  | Buffer, | 
|  | MAX_PATH, | 
|  | &RequiredSize)) | 
|  | { | 
|  | return INVALID_HANDLE_VALUE; | 
|  | } | 
|  |  | 
|  | if (RegCreateKeyExW(HKEY_LOCAL_MACHINE, | 
|  | FullBuffer, | 
|  | 0, | 
|  | NULL, | 
|  | REG_OPTION_NON_VOLATILE, | 
|  | KEY_ALL_ACCESS, | 
|  | NULL, | 
|  | &hClassKey, | 
|  | NULL)) | 
|  | { | 
|  | return INVALID_HANDLE_VALUE; | 
|  | } | 
|  |  | 
|  | } | 
|  |  | 
|  | if (RegSetValueExW(hClassKey, | 
|  | Class, | 
|  | 0, | 
|  | REG_SZ, | 
|  | (LPBYTE)Buffer, | 
|  | RequiredSize * sizeof(WCHAR))) | 
|  | { | 
|  | RegCloseKey(hClassKey); | 
|  | RegDeleteKeyW(HKEY_LOCAL_MACHINE, | 
|  | FullBuffer); | 
|  | return INVALID_HANDLE_VALUE; | 
|  | } | 
|  |  | 
|  | return hClassKey; | 
|  | } | 
|  |  | 
|  | /*********************************************************************** | 
|  | *		SetupDiInstallClassW (SETUPAPI.@) | 
|  | */ | 
|  | BOOL WINAPI SetupDiInstallClassW( | 
|  | HWND hwndParent, | 
|  | PCWSTR InfFileName, | 
|  | DWORD Flags, | 
|  | HSPFILEQ FileQueue) | 
|  | { | 
|  | WCHAR SectionName[MAX_PATH]; | 
|  | DWORD SectionNameLength = 0; | 
|  | HINF hInf; | 
|  | BOOL bFileQueueCreated = FALSE; | 
|  | HKEY hClassKey; | 
|  |  | 
|  |  | 
|  | FIXME("\n"); | 
|  |  | 
|  | if (!InfFileName) | 
|  | { | 
|  | SetLastError(ERROR_INVALID_PARAMETER); | 
|  | return FALSE; | 
|  | } | 
|  | if ((Flags & DI_NOVCP) && (FileQueue == NULL || FileQueue == INVALID_HANDLE_VALUE)) | 
|  | { | 
|  | SetLastError(ERROR_INVALID_PARAMETER); | 
|  | return FALSE; | 
|  | } | 
|  |  | 
|  | /* Open the .inf file */ | 
|  | hInf = SetupOpenInfFileW(InfFileName, | 
|  | NULL, | 
|  | INF_STYLE_WIN4, | 
|  | NULL); | 
|  | if (hInf == INVALID_HANDLE_VALUE) | 
|  | { | 
|  |  | 
|  | return FALSE; | 
|  | } | 
|  |  | 
|  | /* Create or open the class registry key 'HKLM\\CurrentControlSet\\Class\\{GUID}' */ | 
|  | hClassKey = CreateClassKey(hInf); | 
|  | if (hClassKey == INVALID_HANDLE_VALUE) | 
|  | { | 
|  | SetupCloseInfFile(hInf); | 
|  | return FALSE; | 
|  | } | 
|  |  | 
|  |  | 
|  | /* Try to append a layout file */ | 
|  | SetupOpenAppendInfFileW(NULL, hInf, NULL); | 
|  |  | 
|  | /* Retrieve the actual section name */ | 
|  | SetupDiGetActualSectionToInstallW(hInf, | 
|  | ClassInstall32, | 
|  | SectionName, | 
|  | MAX_PATH, | 
|  | &SectionNameLength, | 
|  | NULL); | 
|  |  | 
|  | #if 0 | 
|  | if (!(Flags & DI_NOVCP)) | 
|  | { | 
|  | FileQueue = SetupOpenFileQueue(); | 
|  | if (FileQueue == INVALID_HANDLE_VALUE) | 
|  | { | 
|  | SetupCloseInfFile(hInf); | 
|  | return FALSE; | 
|  | } | 
|  |  | 
|  | bFileQueueCreated = TRUE; | 
|  |  | 
|  | } | 
|  | #endif | 
|  |  | 
|  | SetupInstallFromInfSectionW(NULL, | 
|  | hInf, | 
|  | SectionName, | 
|  | SPINST_COPYINF | SPINST_FILES | SPINST_REGISTRY, | 
|  | hClassKey, | 
|  | NULL, | 
|  | 0, | 
|  | NULL, | 
|  | NULL, | 
|  | INVALID_HANDLE_VALUE, | 
|  | NULL); | 
|  |  | 
|  | /* FIXME: More code! */ | 
|  |  | 
|  | if (bFileQueueCreated) | 
|  | SetupCloseFileQueue(FileQueue); | 
|  |  | 
|  | SetupCloseInfFile(hInf); | 
|  |  | 
|  | return TRUE; | 
|  | } | 
|  |  | 
|  |  | 
|  | /*********************************************************************** | 
|  | *		SetupDiOpenClassRegKey  (SETUPAPI.@) | 
|  | */ | 
|  | HKEY WINAPI SetupDiOpenClassRegKey( | 
|  | const GUID* ClassGuid, | 
|  | REGSAM samDesired) | 
|  | { | 
|  | return SetupDiOpenClassRegKeyExW(ClassGuid, samDesired, | 
|  | DIOCR_INSTALLER, NULL, NULL); | 
|  | } | 
|  |  | 
|  |  | 
|  | /*********************************************************************** | 
|  | *		SetupDiOpenClassRegKeyExA  (SETUPAPI.@) | 
|  | */ | 
|  | HKEY WINAPI SetupDiOpenClassRegKeyExA( | 
|  | const GUID* ClassGuid, | 
|  | REGSAM samDesired, | 
|  | DWORD Flags, | 
|  | PCSTR MachineName, | 
|  | PVOID Reserved) | 
|  | { | 
|  | PWSTR MachineNameW = NULL; | 
|  | HKEY hKey; | 
|  |  | 
|  | TRACE("\n"); | 
|  |  | 
|  | if (MachineName) | 
|  | { | 
|  | MachineNameW = MultiByteToUnicode(MachineName, CP_ACP); | 
|  | if (MachineNameW == NULL) | 
|  | return INVALID_HANDLE_VALUE; | 
|  | } | 
|  |  | 
|  | hKey = SetupDiOpenClassRegKeyExW(ClassGuid, samDesired, | 
|  | Flags, MachineNameW, Reserved); | 
|  |  | 
|  | MyFree(MachineNameW); | 
|  |  | 
|  | return hKey; | 
|  | } | 
|  |  | 
|  |  | 
|  | /*********************************************************************** | 
|  | *		SetupDiOpenClassRegKeyExW  (SETUPAPI.@) | 
|  | */ | 
|  | HKEY WINAPI SetupDiOpenClassRegKeyExW( | 
|  | const GUID* ClassGuid, | 
|  | REGSAM samDesired, | 
|  | DWORD Flags, | 
|  | PCWSTR MachineName, | 
|  | PVOID Reserved) | 
|  | { | 
|  | HKEY hClassesKey; | 
|  | HKEY key; | 
|  | LPCWSTR lpKeyName; | 
|  | LONG l; | 
|  |  | 
|  | if (MachineName != NULL) | 
|  | { | 
|  | FIXME("Remote access not supported yet!\n"); | 
|  | return INVALID_HANDLE_VALUE; | 
|  | } | 
|  |  | 
|  | if (Flags == DIOCR_INSTALLER) | 
|  | { | 
|  | lpKeyName = ControlClass; | 
|  | } | 
|  | else if (Flags == DIOCR_INTERFACE) | 
|  | { | 
|  | lpKeyName = DeviceClasses; | 
|  | } | 
|  | else | 
|  | { | 
|  | ERR("Invalid Flags parameter!\n"); | 
|  | SetLastError(ERROR_INVALID_PARAMETER); | 
|  | return INVALID_HANDLE_VALUE; | 
|  | } | 
|  |  | 
|  | if (!ClassGuid) | 
|  | { | 
|  | if ((l = RegOpenKeyExW(HKEY_LOCAL_MACHINE, | 
|  | lpKeyName, | 
|  | 0, | 
|  | samDesired, | 
|  | &hClassesKey))) | 
|  | { | 
|  | SetLastError(l); | 
|  | hClassesKey = INVALID_HANDLE_VALUE; | 
|  | } | 
|  | key = hClassesKey; | 
|  | } | 
|  | else | 
|  | { | 
|  | WCHAR bracedGuidString[39]; | 
|  |  | 
|  | SETUPDI_GuidToString(ClassGuid, bracedGuidString); | 
|  |  | 
|  | 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; | 
|  | } | 
|  |  | 
|  | /*********************************************************************** | 
|  | *		SetupDiOpenDeviceInterfaceW (SETUPAPI.@) | 
|  | */ | 
|  | BOOL WINAPI SetupDiOpenDeviceInterfaceW( | 
|  | HDEVINFO DeviceInfoSet, | 
|  | PCWSTR DevicePath, | 
|  | DWORD OpenFlags, | 
|  | PSP_DEVICE_INTERFACE_DATA DeviceInterfaceData) | 
|  | { | 
|  | FIXME("%p %s %08x %p\n", | 
|  | DeviceInfoSet, debugstr_w(DevicePath), OpenFlags, DeviceInterfaceData); | 
|  | return FALSE; | 
|  | } | 
|  |  | 
|  | /*********************************************************************** | 
|  | *		SetupDiOpenDeviceInterfaceA (SETUPAPI.@) | 
|  | */ | 
|  | BOOL WINAPI SetupDiOpenDeviceInterfaceA( | 
|  | HDEVINFO DeviceInfoSet, | 
|  | PCSTR DevicePath, | 
|  | DWORD OpenFlags, | 
|  | PSP_DEVICE_INTERFACE_DATA DeviceInterfaceData) | 
|  | { | 
|  | FIXME("%p %s %08x %p\n", DeviceInfoSet, | 
|  | debugstr_a(DevicePath), OpenFlags, DeviceInterfaceData); | 
|  | return FALSE; | 
|  | } | 
|  |  | 
|  | /*********************************************************************** | 
|  | *		SetupDiSetClassInstallParamsA (SETUPAPI.@) | 
|  | */ | 
|  | BOOL WINAPI SetupDiSetClassInstallParamsA( | 
|  | HDEVINFO  DeviceInfoSet, | 
|  | PSP_DEVINFO_DATA DeviceInfoData, | 
|  | PSP_CLASSINSTALL_HEADER ClassInstallParams, | 
|  | DWORD ClassInstallParamsSize) | 
|  | { | 
|  | FIXME("%p %p %x %u\n",DeviceInfoSet, DeviceInfoData, | 
|  | ClassInstallParams->InstallFunction, ClassInstallParamsSize); | 
|  | return FALSE; | 
|  | } | 
|  |  | 
|  | /*********************************************************************** | 
|  | *		SetupDiCallClassInstaller (SETUPAPI.@) | 
|  | */ | 
|  | BOOL WINAPI SetupDiCallClassInstaller( | 
|  | DI_FUNCTION InstallFunction, | 
|  | HDEVINFO DeviceInfoSet, | 
|  | PSP_DEVINFO_DATA DeviceInfoData) | 
|  | { | 
|  | FIXME("%d %p %p\n", InstallFunction, DeviceInfoSet, DeviceInfoData); | 
|  | return FALSE; | 
|  | } | 
|  |  | 
|  | /*********************************************************************** | 
|  | *		SetupDiGetDeviceInstallParamsA (SETUPAPI.@) | 
|  | */ | 
|  | BOOL WINAPI SetupDiGetDeviceInstallParamsA( | 
|  | HDEVINFO DeviceInfoSet, | 
|  | PSP_DEVINFO_DATA DeviceInfoData, | 
|  | PSP_DEVINSTALL_PARAMS_A DeviceInstallParams) | 
|  | { | 
|  | FIXME("%p %p %p\n", DeviceInfoSet, DeviceInfoData, DeviceInstallParams); | 
|  | 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.@) | 
|  | */ | 
|  | HKEY WINAPI SetupDiOpenDevRegKey( | 
|  | HDEVINFO DeviceInfoSet, | 
|  | PSP_DEVINFO_DATA DeviceInfoData, | 
|  | DWORD Scope, | 
|  | DWORD HwProfile, | 
|  | DWORD KeyType, | 
|  | REGSAM samDesired) | 
|  | { | 
|  | struct DeviceInfoSet *set = (struct DeviceInfoSet *)DeviceInfoSet; | 
|  | struct DeviceInfo *devInfo; | 
|  | HKEY key = INVALID_HANDLE_VALUE; | 
|  |  | 
|  | TRACE("%p %p %d %d %d %x\n", DeviceInfoSet, DeviceInfoData, | 
|  | Scope, HwProfile, KeyType, samDesired); | 
|  |  | 
|  | 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; | 
|  | } | 
|  |  | 
|  | /*********************************************************************** | 
|  | *		SetupDiOpenDevRegKey (SETUPAPI.@) | 
|  | */ | 
|  | BOOL WINAPI SetupDiDeleteDevRegKey( | 
|  | HDEVINFO DeviceInfoSet, | 
|  | PSP_DEVINFO_DATA DeviceInfoData, | 
|  | DWORD Scope, | 
|  | DWORD HwProfile, | 
|  | DWORD KeyType) | 
|  | { | 
|  | struct DeviceInfoSet *set = (struct DeviceInfoSet *)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; | 
|  | } |