/* * 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 #include #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 "winsvc.h" #include "setupapi.h" #include "wine/debug.h" #include "wine/heap.h" #include "wine/list.h" #include "cfgmgr32.h" #include "winioctl.h" #include "rpc.h" #include "rpcdce.h" #include "cguid.h" #include "setupapi_private.h" WINE_DEFAULT_DEBUG_CHANNEL(setupapi); /* Unicode constants */ static const WCHAR Chicago[] = {'$','C','h','i','c','a','g','o','$',0}; 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}; #ifdef __i386__ static const WCHAR NtPlatformExtension[] = {'.','N','T','x','8','6',0}; #elif defined(__x86_64__) static const WCHAR NtPlatformExtension[] = {'.','N','T','a','m','d','6','4',0}; #elif defined(__arm__) static const WCHAR NtPlatformExtension[] = {'.','N','T','a','r','m',0}; #elif defined(__aarch64__) static const WCHAR NtPlatformExtension[] = {'.','N','T','a','r','m','6','4',0}; #endif static const WCHAR Signature[] = {'S','i','g','n','a','t','u','r','e',0}; static const WCHAR Version[] = {'V','e','r','s','i','o','n',0}; static const WCHAR WinExtension[] = {'.','W','i','n',0}; static const WCHAR WindowsNT[] = {'$','W','i','n','d','o','w','s',' ','N','T','$',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 DeviceParameters[] = {'D','e','v','i','c','e',' ','P','a','r','a','m','e','t','e','r','s',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}; static const WCHAR Control[] = {'C','o','n','t','r','o','l',0}; static const WCHAR Linked[] = {'L','i','n','k','e','d',0}; static const WCHAR dotInterfaces[] = {'.','I','n','t','e','r','f','a','c','e','s',0}; static const WCHAR AddInterface[] = {'A','d','d','I','n','t','e','r','f','a','c','e',0}; static const WCHAR backslashW[] = {'\\',0}; static const WCHAR emptyW[] = {0}; #define SERVICE_CONTROL_REENUMERATE_ROOT_DEVICES 128 struct driver { DWORD rank; WCHAR inf_path[MAX_PATH]; WCHAR manufacturer[LINE_LEN]; WCHAR mfg_key[LINE_LEN]; WCHAR description[LINE_LEN]; WCHAR section[LINE_LEN]; }; /* 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; struct list devices; }; struct device { struct DeviceInfoSet *set; HKEY key; BOOL phantom; WCHAR *instanceId; struct list interfaces; GUID class; DEVINST devnode; struct list entry; BOOL removed; SP_DEVINSTALL_PARAMS_W params; struct driver *drivers; unsigned int driver_count; struct driver *selected_driver; }; struct device_iface { WCHAR *refstr; WCHAR *symlink; struct device *device; GUID class; DWORD flags; HKEY class_key; HKEY refstr_key; struct list entry; }; static struct DeviceInfoSet *get_device_set(HDEVINFO devinfo) { struct DeviceInfoSet *set = devinfo; if (!devinfo || devinfo == INVALID_HANDLE_VALUE || set->magic != SETUP_DEVICE_INFO_SET_MAGIC) { SetLastError(ERROR_INVALID_HANDLE); return NULL; } return set; } static struct device *get_device(HDEVINFO devinfo, const SP_DEVINFO_DATA *data) { struct DeviceInfoSet *set; struct device *device; if (!(set = get_device_set(devinfo))) return FALSE; if (!data || data->cbSize != sizeof(*data) || !data->Reserved) { SetLastError(ERROR_INVALID_PARAMETER); return NULL; } device = (struct device *)data->Reserved; if (device->set != set) { SetLastError(ERROR_INVALID_PARAMETER); return NULL; } if (device->removed) { SetLastError(ERROR_NO_SUCH_DEVINST); return NULL; } return device; } static struct device_iface *get_device_iface(HDEVINFO devinfo, const SP_DEVICE_INTERFACE_DATA *data) { if (!get_device_set(devinfo)) return FALSE; if (!data || data->cbSize != sizeof(*data) || !data->Reserved) { SetLastError(ERROR_INVALID_PARAMETER); return NULL; } return (struct device_iface *)data->Reserved; } static inline void copy_device_data(SP_DEVINFO_DATA *data, const struct device *device) { data->ClassGuid = device->class; data->DevInst = device->devnode; data->Reserved = (ULONG_PTR)device; } static inline void copy_device_iface_data(SP_DEVICE_INTERFACE_DATA *data, const struct device_iface *iface) { data->InterfaceClassGuid = iface->class; data->Flags = iface->flags; data->Reserved = (ULONG_PTR)iface; } static struct device **devnode_table; static unsigned int devnode_table_size; static DEVINST alloc_devnode(struct device *device) { unsigned int i; for (i = 0; i < devnode_table_size; ++i) { if (!devnode_table[i]) break; } if (i == devnode_table_size) { if (devnode_table) { devnode_table_size *= 2; devnode_table = heap_realloc_zero(devnode_table, devnode_table_size * sizeof(*devnode_table)); } else { devnode_table_size = 256; devnode_table = heap_alloc_zero(devnode_table_size * sizeof(*devnode_table)); } } devnode_table[i] = device; return i; } static void free_devnode(DEVINST devnode) { devnode_table[devnode] = NULL; } static struct device *get_devnode_device(DEVINST devnode) { if (devnode < devnode_table_size) return devnode_table[devnode]; WARN("device node %lu not found\n", devnode); return NULL; } 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}; swprintf(guidStr, 39, 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 WCHAR *get_iface_key_path(struct device_iface *iface) { static const WCHAR slashW[] = {'\\',0}; WCHAR *path, *ptr; size_t len = lstrlenW(DeviceClasses) + 1 + 38 + 1 + lstrlenW(iface->symlink); if (!(path = heap_alloc((len + 1) * sizeof(WCHAR)))) { SetLastError(ERROR_OUTOFMEMORY); return NULL; } lstrcpyW(path, DeviceClasses); lstrcatW(path, slashW); SETUPDI_GuidToString(&iface->class, path + lstrlenW(path)); lstrcatW(path, slashW); ptr = path + lstrlenW(path); lstrcatW(path, iface->symlink); if (lstrlenW(iface->symlink) > 3) ptr[0] = ptr[1] = ptr[3] = '#'; ptr = wcschr(ptr, '\\'); if (ptr) *ptr = 0; return path; } static WCHAR *get_refstr_key_path(struct device_iface *iface) { static const WCHAR hashW[] = {'#',0}; static const WCHAR slashW[] = {'\\',0}; WCHAR *path, *ptr; size_t len = lstrlenW(DeviceClasses) + 1 + 38 + 1 + lstrlenW(iface->symlink) + 1 + 1; if (iface->refstr) len += lstrlenW(iface->refstr); if (!(path = heap_alloc((len + 1) * sizeof(WCHAR)))) { SetLastError(ERROR_OUTOFMEMORY); return NULL; } lstrcpyW(path, DeviceClasses); lstrcatW(path, slashW); SETUPDI_GuidToString(&iface->class, path + lstrlenW(path)); lstrcatW(path, slashW); ptr = path + lstrlenW(path); lstrcatW(path, iface->symlink); if (lstrlenW(iface->symlink) > 3) ptr[0] = ptr[1] = ptr[3] = '#'; ptr = wcschr(ptr, '\\'); if (ptr) *ptr = 0; lstrcatW(path, slashW); lstrcatW(path, hashW); if (iface->refstr) lstrcatW(path, iface->refstr); return path; } static BOOL is_valid_property_type(DEVPROPTYPE prop_type) { DWORD type = prop_type & DEVPROP_MASK_TYPE; DWORD typemod = prop_type & DEVPROP_MASK_TYPEMOD; if (type > MAX_DEVPROP_TYPE) return FALSE; if (typemod > MAX_DEVPROP_TYPEMOD) return FALSE; if (typemod == DEVPROP_TYPEMOD_ARRAY && (type == DEVPROP_TYPE_EMPTY || type == DEVPROP_TYPE_NULL || type == DEVPROP_TYPE_STRING || type == DEVPROP_TYPE_SECURITY_DESCRIPTOR_STRING)) return FALSE; if (typemod == DEVPROP_TYPEMOD_LIST && !(type == DEVPROP_TYPE_STRING || type == DEVPROP_TYPE_SECURITY_DESCRIPTOR_STRING)) return FALSE; return TRUE; } 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 = swprintf(ret, len, fmt, instanceId, guidStr); LPWSTR ptr; /* replace '\\' with '#' after the "\\\\?\\" beginning */ for (ptr = wcschr(ret + 4, '\\'); ptr; ptr = wcschr(ptr + 1, '\\')) *ptr = '#'; if (ReferenceString && *ReferenceString) { ret[printed] = '\\'; lstrcpyW(ret + printed + 1, ReferenceString); } } CharLowerW(ret); return ret; } static BOOL is_linked(HKEY key) { DWORD linked, type, size; HKEY control_key; BOOL ret = FALSE; if (!RegOpenKeyW(key, Control, &control_key)) { size = sizeof(DWORD); if (!RegQueryValueExW(control_key, Linked, NULL, &type, (BYTE *)&linked, &size) && type == REG_DWORD && linked) ret = TRUE; RegCloseKey(control_key); } return ret; } static struct device_iface *SETUPDI_CreateDeviceInterface(struct device *device, const GUID *class, const WCHAR *refstr) { struct device_iface *iface = NULL; WCHAR *refstr2 = NULL, *symlink = NULL, *path = NULL; HKEY key; LONG ret; TRACE("%p %s %s\n", device, debugstr_guid(class), debugstr_w(refstr)); /* check if it already exists */ LIST_FOR_EACH_ENTRY(iface, &device->interfaces, struct device_iface, entry) { if (IsEqualGUID(&iface->class, class) && !lstrcmpiW(iface->refstr, refstr)) return iface; } iface = heap_alloc(sizeof(*iface)); symlink = SETUPDI_CreateSymbolicLinkPath(device->instanceId, class, refstr); if (!iface || !symlink) { SetLastError(ERROR_OUTOFMEMORY); goto err; } if (refstr && !(refstr2 = strdupW(refstr))) { SetLastError(ERROR_OUTOFMEMORY); goto err; } iface->refstr = refstr2; iface->symlink = symlink; iface->device = device; iface->class = *class; iface->flags = 0; if (!(path = get_iface_key_path(iface))) { SetLastError(ERROR_OUTOFMEMORY); goto err; } if ((ret = RegCreateKeyW(HKEY_LOCAL_MACHINE, path, &key))) { SetLastError(ret); goto err; } RegSetValueExW(key, DeviceInstance, 0, REG_SZ, (BYTE *)device->instanceId, lstrlenW(device->instanceId) * sizeof(WCHAR)); heap_free(path); iface->class_key = key; if (!(path = get_refstr_key_path(iface))) { SetLastError(ERROR_OUTOFMEMORY); goto err; } if ((ret = RegCreateKeyW(HKEY_LOCAL_MACHINE, path, &key))) { SetLastError(ret); goto err; } RegSetValueExW(key, SymbolicLink, 0, REG_SZ, (BYTE *)iface->symlink, lstrlenW(iface->symlink) * sizeof(WCHAR)); if (is_linked(key)) iface->flags |= SPINT_ACTIVE; heap_free(path); iface->refstr_key = key; list_add_tail(&device->interfaces, &iface->entry); return iface; err: heap_free(iface); heap_free(refstr2); heap_free(symlink); heap_free(path); return NULL; } static BOOL SETUPDI_SetInterfaceSymbolicLink(struct device_iface *iface, const WCHAR *symlink) { heap_free(iface->symlink); if ((iface->symlink = strdupW(symlink))) return TRUE; return FALSE; } static HKEY SETUPDI_CreateDevKey(struct device *device) { 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, device->instanceId, 0, NULL, 0, KEY_READ | KEY_WRITE, NULL, &key, NULL); RegCloseKey(enumKey); } return key; } static LONG open_driver_key(struct device *device, REGSAM access, HKEY *key) { HKEY class_key; WCHAR path[50]; DWORD size = sizeof(path); LONG l; if ((l = RegCreateKeyExW(HKEY_LOCAL_MACHINE, ControlClass, 0, NULL, 0, KEY_CREATE_SUB_KEY, NULL, &class_key, NULL))) { ERR("Failed to open driver class root key, error %lu.\n", l); return l; } if (!(l = RegGetValueW(device->key, NULL, Driver, RRF_RT_REG_SZ, NULL, path, &size))) { if (!(l = RegOpenKeyExW(class_key, path, 0, access, key))) { RegCloseKey(class_key); return l; } TRACE("Failed to open driver key, error %lu.\n", l); } RegCloseKey(class_key); return l; } static LONG create_driver_key(struct device *device, HKEY *key) { static const WCHAR formatW[] = {'%','0','4','u',0}; static const WCHAR slash[] = { '\\',0 }; unsigned int i = 0; WCHAR path[50]; HKEY class_key; DWORD dispos; LONG l; if (!open_driver_key(device, KEY_READ | KEY_WRITE, key)) return ERROR_SUCCESS; if ((l = RegCreateKeyExW(HKEY_LOCAL_MACHINE, ControlClass, 0, NULL, 0, KEY_CREATE_SUB_KEY, NULL, &class_key, NULL))) { ERR("Failed to open driver class root key, error %lu.\n", l); return l; } SETUPDI_GuidToString(&device->class, path); lstrcatW(path, slash); /* Allocate a new driver key, by finding the first integer value that's not * already taken. */ for (;;) { swprintf(path + 39, ARRAY_SIZE(path) - 39, formatW, i++); if ((l = RegCreateKeyExW(class_key, path, 0, NULL, 0, KEY_READ | KEY_WRITE, NULL, key, &dispos))) break; else if (dispos == REG_CREATED_NEW_KEY) { RegSetValueExW(device->key, Driver, 0, REG_SZ, (BYTE *)path, lstrlenW(path) * sizeof(WCHAR)); RegCloseKey(class_key); return ERROR_SUCCESS; } RegCloseKey(*key); } ERR("Failed to create driver key, error %lu.\n", l); RegCloseKey(class_key); return l; } static LONG delete_driver_key(struct device *device) { HKEY key; LONG l; if (!(l = open_driver_key(device, KEY_READ | KEY_WRITE, &key))) { l = RegDeleteKeyW(key, emptyW); RegCloseKey(key); } return l; } 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 }, }; static BOOL SETUPDI_SetDeviceRegistryPropertyW(struct device *device, DWORD prop, const BYTE *buffer, DWORD size) { if (prop < ARRAY_SIZE(PropertyMap) && PropertyMap[prop].nameW) { LONG ret = RegSetValueExW(device->key, PropertyMap[prop].nameW, 0, PropertyMap[prop].regType, buffer, size); if (!ret) return TRUE; SetLastError(ret); } return FALSE; } static void remove_device_iface(struct device_iface *iface) { RegDeleteTreeW(iface->refstr_key, NULL); RegDeleteKeyW(iface->refstr_key, emptyW); RegCloseKey(iface->refstr_key); iface->refstr_key = NULL; /* Also remove the class key if it's empty. */ RegDeleteKeyW(iface->class_key, emptyW); RegCloseKey(iface->class_key); iface->class_key = NULL; iface->flags |= SPINT_REMOVED; } static void delete_device_iface(struct device_iface *iface) { list_remove(&iface->entry); RegCloseKey(iface->refstr_key); RegCloseKey(iface->class_key); heap_free(iface->refstr); heap_free(iface->symlink); heap_free(iface); } /* remove all interfaces associated with the device, including those not * enumerated in the set */ static void remove_all_device_ifaces(struct device *device) { HKEY classes_key; DWORD i, len; LONG ret; if ((ret = RegOpenKeyExW(HKEY_LOCAL_MACHINE, DeviceClasses, 0, KEY_READ, &classes_key))) { WARN("Failed to open classes key, error %lu.\n", ret); return; } for (i = 0; ; ++i) { WCHAR class_name[40]; HKEY class_key; DWORD j; len = ARRAY_SIZE(class_name); if ((ret = RegEnumKeyExW(classes_key, i, class_name, &len, NULL, NULL, NULL, NULL))) { if (ret != ERROR_NO_MORE_ITEMS) ERR("Failed to enumerate classes, error %lu.\n", ret); break; } if ((ret = RegOpenKeyExW(classes_key, class_name, 0, KEY_READ, &class_key))) { ERR("Failed to open class %s, error %lu.\n", debugstr_w(class_name), ret); continue; } for (j = 0; ; ++j) { WCHAR iface_name[MAX_DEVICE_ID_LEN + 39], device_name[MAX_DEVICE_ID_LEN]; HKEY iface_key; len = ARRAY_SIZE(iface_name); if ((ret = RegEnumKeyExW(class_key, j, iface_name, &len, NULL, NULL, NULL, NULL))) { if (ret != ERROR_NO_MORE_ITEMS) ERR("Failed to enumerate interfaces, error %lu.\n", ret); break; } if ((ret = RegOpenKeyExW(class_key, iface_name, 0, KEY_ALL_ACCESS, &iface_key))) { ERR("Failed to open interface %s, error %lu.\n", debugstr_w(iface_name), ret); continue; } len = sizeof(device_name); if ((ret = RegQueryValueExW(iface_key, L"DeviceInstance", NULL, NULL, (BYTE *)device_name, &len))) { ERR("Failed to query device instance, error %lu.\n", ret); RegCloseKey(iface_key); continue; } if (!wcsicmp(device_name, device->instanceId)) { if ((ret = RegDeleteTreeW(iface_key, NULL))) ERR("Failed to delete interface %s subkeys, error %lu.\n", debugstr_w(iface_name), ret); if ((ret = RegDeleteKeyW(iface_key, L""))) ERR("Failed to delete interface %s, error %lu.\n", debugstr_w(iface_name), ret); } RegCloseKey(iface_key); } RegCloseKey(class_key); } RegCloseKey(classes_key); } static void remove_device(struct device *device) { WCHAR id[MAX_DEVICE_ID_LEN], *p; struct device_iface *iface; HKEY enum_key; delete_driver_key(device); LIST_FOR_EACH_ENTRY(iface, &device->interfaces, struct device_iface, entry) { remove_device_iface(iface); } RegDeleteTreeW(device->key, NULL); RegDeleteKeyW(device->key, emptyW); /* delete all empty parents of the key */ if (!RegOpenKeyExW(HKEY_LOCAL_MACHINE, Enum, 0, 0, &enum_key)) { lstrcpyW(id, device->instanceId); while ((p = wcsrchr(id, '\\'))) { *p = 0; RegDeleteKeyW(enum_key, id); } RegCloseKey(enum_key); } RegCloseKey(device->key); device->key = NULL; device->removed = TRUE; } static void delete_device(struct device *device) { struct device_iface *iface, *next; SP_DEVINFO_DATA device_data; device_data.cbSize = sizeof(device_data); copy_device_data(&device_data, device); SetupDiCallClassInstaller(DIF_DESTROYPRIVATEDATA, device->set, &device_data); if (device->phantom) { remove_device(device); remove_all_device_ifaces(device); } RegCloseKey(device->key); heap_free(device->instanceId); heap_free(device->drivers); LIST_FOR_EACH_ENTRY_SAFE(iface, next, &device->interfaces, struct device_iface, entry) { delete_device_iface(iface); } free_devnode(device->devnode); list_remove(&device->entry); heap_free(device); } /* Create a new device, or return a device already in the set. */ static struct device *create_device(struct DeviceInfoSet *set, const GUID *class, const WCHAR *instanceid, BOOL phantom) { const DWORD one = 1; struct device *device; WCHAR guidstr[MAX_GUID_STRING_LEN]; WCHAR class_name[MAX_CLASS_NAME_LEN]; DWORD size; TRACE("%p, %s, %s, %d\n", set, debugstr_guid(class), debugstr_w(instanceid), phantom); LIST_FOR_EACH_ENTRY(device, &set->devices, struct device, entry) { if (!wcsicmp(instanceid, device->instanceId)) { TRACE("Found device %p already in set.\n", device); return device; } } if (!(device = heap_alloc_zero(sizeof(*device)))) { SetLastError(ERROR_OUTOFMEMORY); return NULL; } if (!(device->instanceId = strdupW(instanceid))) { SetLastError(ERROR_OUTOFMEMORY); heap_free(device); return NULL; } wcsupr(device->instanceId); device->set = set; device->key = SETUPDI_CreateDevKey(device); device->phantom = phantom; list_init(&device->interfaces); device->class = *class; device->devnode = alloc_devnode(device); device->removed = FALSE; list_add_tail(&set->devices, &device->entry); device->params.cbSize = sizeof(SP_DEVINSTALL_PARAMS_W); if (phantom) RegSetValueExW(device->key, Phantom, 0, REG_DWORD, (const BYTE *)&one, sizeof(one)); SETUPDI_GuidToString(class, guidstr); SETUPDI_SetDeviceRegistryPropertyW(device, SPDRP_CLASSGUID, (const BYTE *)guidstr, sizeof(guidstr)); if (SetupDiClassNameFromGuidW(class, class_name, ARRAY_SIZE(class_name), NULL)) { size = (lstrlenW(class_name) + 1) * sizeof(WCHAR); SETUPDI_SetDeviceRegistryPropertyW(device, SPDRP_CLASS, (const BYTE *)class_name, size); } TRACE("Created new device %p.\n", device); return device; } /*********************************************************************** * 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 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. * 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 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. * 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 %ld\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 = ARRAY_SIZE(szKeyName); lError = RegEnumKeyExW(hClassesKey, dwIndex, szKeyName, &dwLength, NULL, NULL, NULL, NULL); TRACE("RegEnumKeyExW() returns %ld\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 = sizeof(szClassName); if (!RegQueryValueExW(hClassKey, Class, NULL, NULL, (LPBYTE)szClassName, &dwLength)) { TRACE("Class name: %p\n", szClassName); if (wcsicmp(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 empty 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 && *MachineName) { 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_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 %ld %ld %ld %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 devinfo, SP_DEVINFO_DATA *device_data, DWORD Scope, DWORD HwProfile, DWORD KeyType, HINF InfHandle, const WCHAR *InfSectionName) { struct device *device; HKEY key = INVALID_HANDLE_VALUE; LONG l; TRACE("devinfo %p, device_data %p, scope %ld, profile %ld, type %ld, inf_handle %p, inf_section %s.\n", devinfo, device_data, Scope, HwProfile, KeyType, InfHandle, debugstr_w(InfSectionName)); if (!(device = get_device(devinfo, device_data))) 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 (device->phantom) { SetLastError(ERROR_DEVINFO_NOT_REGISTERED); return INVALID_HANDLE_VALUE; } if (Scope != DICS_FLAG_GLOBAL) FIXME("unimplemented for scope %ld\n", Scope); switch (KeyType) { case DIREG_DEV: l = RegCreateKeyExW(device->key, DeviceParameters, 0, NULL, 0, KEY_READ | KEY_WRITE, NULL, &key, NULL); break; case DIREG_DRV: l = create_driver_key(device, &key); break; default: FIXME("Unhandled type %#lx.\n", KeyType); l = ERROR_CALL_NOT_IMPLEMENTED; } if (InfHandle) SetupInstallFromInfSectionW(NULL, InfHandle, InfSectionName, SPINST_ALL, NULL, NULL, SP_COPY_NEWER_ONLY, NULL, NULL, devinfo, device_data); SetLastError(l); return l ? INVALID_HANDLE_VALUE : key; } /*********************************************************************** * SetupDiCreateDeviceInfoA (SETUPAPI.@) */ BOOL WINAPI SetupDiCreateDeviceInfoA(HDEVINFO DeviceInfoSet, const char *name, const GUID *ClassGuid, PCSTR DeviceDescription, HWND hwndParent, DWORD CreationFlags, PSP_DEVINFO_DATA DeviceInfoData) { WCHAR nameW[MAX_DEVICE_ID_LEN]; BOOL ret = FALSE; LPWSTR DeviceDescriptionW = NULL; if (!name || strlen(name) >= MAX_DEVICE_ID_LEN) { SetLastError(ERROR_INVALID_DEVINST_NAME); return FALSE; } MultiByteToWideChar(CP_ACP, 0, name, -1, nameW, ARRAY_SIZE(nameW)); if (DeviceDescription) { DeviceDescriptionW = MultiByteToUnicode(DeviceDescription, CP_ACP); if (DeviceDescriptionW == NULL) return FALSE; } ret = SetupDiCreateDeviceInfoW(DeviceInfoSet, nameW, ClassGuid, DeviceDescriptionW, hwndParent, CreationFlags, DeviceInfoData); MyFree(DeviceDescriptionW); return ret; } /*********************************************************************** * SetupDiCreateDeviceInfoW (SETUPAPI.@) */ BOOL WINAPI SetupDiCreateDeviceInfoW(HDEVINFO devinfo, const WCHAR *name, const GUID *class, const WCHAR *description, HWND parent, DWORD flags, SP_DEVINFO_DATA *device_data) { WCHAR id[MAX_DEVICE_ID_LEN]; struct DeviceInfoSet *set; HKEY enum_hkey; HKEY instance_hkey; struct device *device; LONG l; TRACE("devinfo %p, name %s, class %s, description %s, hwnd %p, flags %#lx, device_data %p.\n", devinfo, debugstr_w(name), debugstr_guid(class), debugstr_w(description), parent, flags, device_data); if (!name || lstrlenW(name) >= MAX_DEVICE_ID_LEN) { SetLastError(ERROR_INVALID_DEVINST_NAME); return FALSE; } if (!(set = get_device_set(devinfo))) return FALSE; if (!class) { SetLastError(ERROR_INVALID_PARAMETER); return FALSE; } if (!IsEqualGUID(&set->ClassGuid, &GUID_NULL) && !IsEqualGUID(class, &set->ClassGuid)) { SetLastError(ERROR_CLASS_MISMATCH); return FALSE; } if ((flags & DICD_GENERATE_ID)) { static const WCHAR formatW[] = {'R','O','O','T','\\','%','s','\\','%','0','4','u',0}; unsigned int instance_id; if (wcschr(name, '\\')) { SetLastError(ERROR_INVALID_DEVINST_NAME); return FALSE; } for (instance_id = 0; ; ++instance_id) { if (swprintf(id, ARRAY_SIZE(id), formatW, name, instance_id) == -1) { SetLastError(ERROR_INVALID_DEVINST_NAME); return FALSE; } RegCreateKeyExW(HKEY_LOCAL_MACHINE, Enum, 0, NULL, 0, KEY_READ, NULL, &enum_hkey, NULL); if (!(l = RegOpenKeyExW(enum_hkey, id, 0, KEY_READ, &instance_hkey))) RegCloseKey(instance_hkey); if (l == ERROR_FILE_NOT_FOUND) break; RegCloseKey(enum_hkey); } } else { /* Check if instance is already in registry */ RegCreateKeyExW(HKEY_LOCAL_MACHINE, Enum, 0, NULL, 0, KEY_READ, NULL, &enum_hkey, NULL); if (!RegOpenKeyExW(enum_hkey, name, 0, KEY_READ, &instance_hkey)) { RegCloseKey(instance_hkey); RegCloseKey(enum_hkey); SetLastError(ERROR_DEVINST_ALREADY_EXISTS); return FALSE; } RegCloseKey(enum_hkey); /* Check if instance is already in set */ lstrcpyW(id, name); LIST_FOR_EACH_ENTRY(device, &set->devices, struct device, entry) { if (!lstrcmpiW(name, device->instanceId)) { SetLastError(ERROR_DEVINST_ALREADY_EXISTS); return FALSE; } } } if (!(device = create_device(set, class, id, TRUE))) return FALSE; if (description) { SETUPDI_SetDeviceRegistryPropertyW(device, SPDRP_DEVICEDESC, (const BYTE *)description, lstrlenW(description) * sizeof(WCHAR)); } if (device_data) { if (device_data->cbSize != sizeof(SP_DEVINFO_DATA)) { SetLastError(ERROR_INVALID_USER_BUFFER); return FALSE; } else copy_device_data(device_data, device); } return TRUE; } /*********************************************************************** * SetupDiRegisterDeviceInfo (SETUPAPI.@) */ BOOL WINAPI SetupDiRegisterDeviceInfo(HDEVINFO devinfo, SP_DEVINFO_DATA *device_data, DWORD flags, PSP_DETSIG_CMPPROC compare_proc, void *context, SP_DEVINFO_DATA *duplicate_data) { struct device *device; TRACE("devinfo %p, data %p, flags %#lx, compare_proc %p, context %p, duplicate_data %p.\n", devinfo, device_data, flags, compare_proc, context, duplicate_data); if (!(device = get_device(devinfo, device_data))) return FALSE; if (device->phantom) { device->phantom = FALSE; RegDeleteValueW(device->key, Phantom); } return TRUE; } /*********************************************************************** * SetupDiRemoveDevice (SETUPAPI.@) */ BOOL WINAPI SetupDiRemoveDevice(HDEVINFO devinfo, SP_DEVINFO_DATA *device_data) { SC_HANDLE manager = NULL, service = NULL; struct device *device; WCHAR *service_name = NULL; DWORD size; TRACE("devinfo %p, device_data %p.\n", devinfo, device_data); if (!(device = get_device(devinfo, device_data))) return FALSE; if (!(manager = OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT))) return FALSE; if (!RegGetValueW(device->key, NULL, L"Service", RRF_RT_REG_SZ, NULL, NULL, &size)) { service_name = malloc(size); if (!RegGetValueW(device->key, NULL, L"Service", RRF_RT_REG_SZ, NULL, service_name, &size)) service = OpenServiceW(manager, service_name, SERVICE_USER_DEFINED_CONTROL); } remove_device(device); if (service) { SERVICE_STATUS status; if (!ControlService(service, SERVICE_CONTROL_REENUMERATE_ROOT_DEVICES, &status)) ERR("Failed to control service %s, error %lu.\n", debugstr_w(service_name), GetLastError()); CloseServiceHandle(service); } CloseServiceHandle(manager); free(service_name); remove_all_device_ifaces(device); return TRUE; } /*********************************************************************** * SetupDiDeleteDeviceInfo (SETUPAPI.@) */ BOOL WINAPI SetupDiDeleteDeviceInfo(HDEVINFO devinfo, SP_DEVINFO_DATA *device_data) { struct device *device; TRACE("devinfo %p, device_data %p.\n", devinfo, device_data); if (!(device = get_device(devinfo, device_data))) return FALSE; delete_device(device); return TRUE; } /*********************************************************************** * SetupDiRemoveDeviceInterface (SETUPAPI.@) */ BOOL WINAPI SetupDiRemoveDeviceInterface(HDEVINFO devinfo, SP_DEVICE_INTERFACE_DATA *iface_data) { struct device_iface *iface; TRACE("devinfo %p, iface_data %p.\n", devinfo, iface_data); if (!(iface = get_device_iface(devinfo, iface_data))) return FALSE; remove_device_iface(iface); return TRUE; } /*********************************************************************** * SetupDiDeleteDeviceInterfaceData (SETUPAPI.@) */ BOOL WINAPI SetupDiDeleteDeviceInterfaceData(HDEVINFO devinfo, SP_DEVICE_INTERFACE_DATA *iface_data) { struct device_iface *iface; TRACE("devinfo %p, iface_data %p.\n", devinfo, iface_data); if (!(iface = get_device_iface(devinfo, iface_data))) return FALSE; delete_device_iface(iface); return TRUE; } /*********************************************************************** * SetupDiEnumDeviceInfo (SETUPAPI.@) */ BOOL WINAPI DECLSPEC_HOTPATCH SetupDiEnumDeviceInfo(HDEVINFO devinfo, DWORD index, SP_DEVINFO_DATA *device_data) { struct DeviceInfoSet *set; struct device *device; DWORD i = 0; TRACE("devinfo %p, index %ld, device_data %p\n", devinfo, index, device_data); if (!(set = get_device_set(devinfo))) return FALSE; if (!device_data) { SetLastError(ERROR_INVALID_PARAMETER); return FALSE; } if (device_data->cbSize != sizeof(SP_DEVINFO_DATA)) { SetLastError(ERROR_INVALID_USER_BUFFER); return FALSE; } LIST_FOR_EACH_ENTRY(device, &set->devices, struct device, entry) { if (i++ == index) { copy_device_data(device_data, device); return TRUE; } } SetLastError(ERROR_NO_MORE_ITEMS); return FALSE; } /*********************************************************************** * SetupDiGetDeviceInstanceIdA (SETUPAPI.@) */ BOOL WINAPI SetupDiGetDeviceInstanceIdA(HDEVINFO devinfo, SP_DEVINFO_DATA *device_data, char *id, DWORD size, DWORD *needed) { WCHAR idW[MAX_DEVICE_ID_LEN]; TRACE("devinfo %p, device_data %p, id %p, size %ld, needed %p.\n", devinfo, device_data, id, size, needed); if (!SetupDiGetDeviceInstanceIdW(devinfo, device_data, idW, ARRAY_SIZE(idW), NULL)) return FALSE; if (needed) *needed = WideCharToMultiByte(CP_ACP, 0, idW, -1, NULL, 0, NULL, NULL); if (size && WideCharToMultiByte(CP_ACP, 0, idW, -1, id, size, NULL, NULL)) return TRUE; SetLastError(ERROR_INSUFFICIENT_BUFFER); return FALSE; } /*********************************************************************** * SetupDiGetDeviceInstanceIdW (SETUPAPI.@) */ BOOL WINAPI SetupDiGetDeviceInstanceIdW(HDEVINFO devinfo, SP_DEVINFO_DATA *device_data, WCHAR *DeviceInstanceId, DWORD DeviceInstanceIdSize, DWORD *RequiredSize) { struct device *device; TRACE("devinfo %p, device_data %p, DeviceInstanceId %p, DeviceInstanceIdSize %ld, RequiredSize %p.\n", devinfo, device_data, DeviceInstanceId, DeviceInstanceIdSize, RequiredSize); if (!(device = get_device(devinfo, device_data))) return FALSE; TRACE("instance ID: %s\n", debugstr_w(device->instanceId)); if (DeviceInstanceIdSize < lstrlenW(device->instanceId) + 1) { SetLastError(ERROR_INSUFFICIENT_BUFFER); if (RequiredSize) *RequiredSize = lstrlenW(device->instanceId) + 1; return FALSE; } lstrcpyW(DeviceInstanceId, device->instanceId); if (RequiredSize) *RequiredSize = lstrlenW(device->instanceId) + 1; return TRUE; } /*********************************************************************** * SetupDiGetActualSectionToInstallExA (SETUPAPI.@) */ BOOL WINAPI SetupDiGetActualSectionToInstallExA(HINF hinf, const char *section, SP_ALTPLATFORM_INFO *altplatform, char *section_ext, DWORD size, DWORD *needed, char **extptr, void *reserved) { WCHAR sectionW[LINE_LEN], section_extW[LINE_LEN], *extptrW; BOOL ret; MultiByteToWideChar(CP_ACP, 0, section, -1, sectionW, ARRAY_SIZE(sectionW)); ret = SetupDiGetActualSectionToInstallExW(hinf, sectionW, altplatform, section_extW, ARRAY_SIZE(section_extW), NULL, &extptrW, reserved); if (ret) { if (needed) *needed = WideCharToMultiByte(CP_ACP, 0, section_extW, -1, NULL, 0, NULL, NULL); if (section_ext) ret = !!WideCharToMultiByte(CP_ACP, 0, section_extW, -1, section_ext, size, NULL, NULL); if (extptr) { if (extptrW) *extptr = section_ext + WideCharToMultiByte(CP_ACP, 0, section_extW, extptrW - section_extW, NULL, 0, NULL, NULL); else *extptr = NULL; } } return ret; } /*********************************************************************** * SetupDiGetActualSectionToInstallA (SETUPAPI.@) */ BOOL WINAPI SetupDiGetActualSectionToInstallA(HINF hinf, const char *section, char *section_ext, DWORD size, DWORD *needed, char **extptr) { return SetupDiGetActualSectionToInstallExA(hinf, section, NULL, section_ext, size, needed, extptr, NULL); } /*********************************************************************** * SetupDiGetActualSectionToInstallExW (SETUPAPI.@) */ BOOL WINAPI SetupDiGetActualSectionToInstallExW(HINF hinf, const WCHAR *section, SP_ALTPLATFORM_INFO *altplatform, WCHAR *section_ext, DWORD size, DWORD *needed, WCHAR **extptr, void *reserved) { WCHAR buffer[MAX_PATH]; DWORD len; DWORD full_len; LONG line_count = -1; TRACE("hinf %p, section %s, altplatform %p, ext %p, size %ld, needed %p, extptr %p, reserved %p.\n", hinf, debugstr_w(section), altplatform, section_ext, size, needed, extptr, reserved); if (altplatform) FIXME("SP_ALTPLATFORM_INFO unsupported\n"); lstrcpyW(buffer, section); len = lstrlenW(buffer); if (OsVersionInfo.dwPlatformId == VER_PLATFORM_WIN32_NT) { /* Test section name with '.NTx86' extension */ lstrcpyW(&buffer[len], NtPlatformExtension); line_count = SetupGetLineCountW(hinf, buffer); if (line_count == -1) { /* Test section name with '.NT' extension */ lstrcpyW(&buffer[len], NtExtension); line_count = SetupGetLineCountW(hinf, buffer); } } else { /* Test section name with '.Win' extension */ lstrcpyW(&buffer[len], WinExtension); line_count = SetupGetLineCountW(hinf, buffer); } if (line_count == -1) buffer[len] = 0; full_len = lstrlenW(buffer); if (section_ext != NULL && size != 0) { if (size < (full_len + 1)) { SetLastError(ERROR_INSUFFICIENT_BUFFER); return FALSE; } lstrcpyW(section_ext, buffer); if (extptr != NULL) { *extptr = (len == full_len) ? NULL : §ion_ext[len]; } } if (needed != NULL) { *needed = full_len + 1; } return TRUE; } /*********************************************************************** * SetupDiGetActualSectionToInstallW (SETUPAPI.@) */ BOOL WINAPI SetupDiGetActualSectionToInstallW(HINF hinf, const WCHAR *section, WCHAR *section_ext, DWORD size, DWORD *needed, WCHAR **extptr) { return SetupDiGetActualSectionToInstallExW(hinf, section, NULL, section_ext, size, needed, extptr, NULL); } /*********************************************************************** * 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 %lu)\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 %lu)\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(struct device *device, HKEY key, const GUID *guid, DWORD flags) { DWORD i, len; WCHAR subKeyName[MAX_PATH]; LONG l = ERROR_SUCCESS; for (i = 0; !l; i++) { len = ARRAY_SIZE(subKeyName); l = RegEnumKeyExW(key, i, subKeyName, &len, NULL, NULL, NULL, NULL); if (!l) { HKEY subKey; struct device_iface *iface; if (*subKeyName == '#') { /* The subkey name is the reference string, with a '#' prepended */ l = RegOpenKeyExW(key, subKeyName, 0, KEY_READ, &subKey); if (!l) { WCHAR symbolicLink[MAX_PATH]; DWORD dataType; if (!(flags & DIGCF_PRESENT) || is_linked(subKey)) { iface = SETUPDI_CreateDeviceInterface(device, guid, subKeyName + 1); len = sizeof(symbolicLink); l = RegQueryValueExW(subKey, SymbolicLink, NULL, &dataType, (BYTE *)symbolicLink, &len); if (!l && dataType == REG_SZ) SETUPDI_SetInterfaceSymbolicLink(iface, symbolicLink); } RegCloseKey(subKey); } } /* Allow enumeration to continue */ l = ERROR_SUCCESS; } } /* FIXME: find and add all the device's interfaces to the device */ } static void SETUPDI_EnumerateMatchingInterfaces(HDEVINFO DeviceInfoSet, HKEY key, const GUID *guid, const WCHAR *enumstr, DWORD flags) { struct DeviceInfoSet *set = DeviceInfoSet; DWORD i, len; WCHAR subKeyName[MAX_PATH]; LONG l; HKEY enumKey = INVALID_HANDLE_VALUE; TRACE("%s\n", debugstr_w(enumstr)); l = RegCreateKeyExW(HKEY_LOCAL_MACHINE, Enum, 0, NULL, 0, KEY_READ, NULL, &enumKey, NULL); for (i = 0; !l; i++) { len = ARRAY_SIZE(subKeyName); 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; struct device *device; deviceClassStr[37] = 0; UuidFromStringW(&deviceClassStr[1], &deviceClass); if ((device = create_device(set, &deviceClass, deviceInst, FALSE))) SETUPDI_AddDeviceInterfaces(device, subKey, guid, flags); } RegCloseKey(deviceKey); } } } RegCloseKey(subKey); } /* Allow enumeration to continue */ l = ERROR_SUCCESS; } } if (enumKey != INVALID_HANDLE_VALUE) RegCloseKey(enumKey); } static void SETUPDI_EnumerateInterfaces(HDEVINFO DeviceInfoSet, const GUID *guid, LPCWSTR enumstr, DWORD flags) { HKEY interfacesKey = SetupDiOpenClassRegKeyExW(guid, KEY_READ, DIOCR_INTERFACE, NULL, NULL); TRACE("%p, %s, %s, %08lx\n", DeviceInfoSet, debugstr_guid(guid), debugstr_w(enumstr), flags); if (interfacesKey != INVALID_HANDLE_VALUE) { if (flags & DIGCF_ALLCLASSES) { DWORD i, len; WCHAR interfaceGuidStr[40]; LONG l = ERROR_SUCCESS; for (i = 0; !l; i++) { len = ARRAY_SIZE(interfaceGuidStr); 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); interfaceGuidStr[37] = '}'; interfaceGuidStr[38] = 0; l = RegOpenKeyExW(interfacesKey, interfaceGuidStr, 0, KEY_READ, &interfaceKey); if (!l) { SETUPDI_EnumerateMatchingInterfaces(DeviceInfoSet, interfaceKey, &interfaceGuid, enumstr, flags); RegCloseKey(interfaceKey); } } } } } else { /* In this case, SetupDiOpenClassRegKeyExW opened the specific * interface's key, so just pass that long */ SETUPDI_EnumerateMatchingInterfaces(DeviceInfoSet, interfacesKey, guid, enumstr, flags); } RegCloseKey(interfacesKey); } } static void SETUPDI_EnumerateMatchingDeviceInstances(struct DeviceInfoSet *set, LPCWSTR enumerator, LPCWSTR deviceName, HKEY deviceKey, const GUID *class, DWORD flags) { WCHAR id[MAX_DEVICE_ID_LEN]; 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 = ARRAY_SIZE(deviceInstance); 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}; if (swprintf(id, ARRAY_SIZE(id), fmt, enumerator, deviceName, deviceInstance) != -1) { create_device(set, &deviceClass, id, FALSE); } } } } RegCloseKey(subKey); } /* Allow enumeration to continue */ l = ERROR_SUCCESS; } } } static void SETUPDI_EnumerateMatchingDevices(HDEVINFO DeviceInfoSet, LPCWSTR parent, HKEY key, const GUID *class, DWORD flags) { struct DeviceInfoSet *set = DeviceInfoSet; DWORD i, len; WCHAR subKeyName[MAX_PATH]; LONG l = ERROR_SUCCESS; TRACE("%s\n", debugstr_w(parent)); for (i = 0; !l; i++) { len = ARRAY_SIZE(subKeyName); 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, %08lx\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) { WCHAR *bus, *device; if (!wcschr(enumstr, '\\')) { SETUPDI_EnumerateMatchingDevices(DeviceInfoSet, enumstr, enumStrKey, class, flags); } else if ((bus = strdupW(enumstr))) { device = wcschr(bus, '\\'); *device++ = 0; SETUPDI_EnumerateMatchingDeviceInstances(DeviceInfoSet, bus, device, enumStrKey, class, flags); HeapFree(GetProcessHeap(), 0, bus); } RegCloseKey(enumStrKey); } } else { DWORD i, len; WCHAR subKeyName[MAX_PATH]; l = ERROR_SUCCESS; for (i = 0; !l; i++) { len = ARRAY_SIZE(subKeyName); 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, void *reserved) { static const DWORD unsupportedFlags = DIGCF_DEFAULT | DIGCF_PROFILE; HDEVINFO set; TRACE("%s %s %p 0x%08lx %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 INVALID_HANDLE_VALUE; } if (flags & DIGCF_ALLCLASSES) class = NULL; if (flags & unsupportedFlags) WARN("unsupported flags %08lx\n", flags & unsupportedFlags); if (deviceset) set = deviceset; else set = SetupDiCreateDeviceInfoListExW((flags & DIGCF_DEVICEINTERFACE) ? NULL : class, parent, machine, reserved); if (set != INVALID_HANDLE_VALUE) { if (machine && *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 devinfo, SP_DEVINFO_LIST_DETAIL_DATA_A *DevInfoData) { struct DeviceInfoSet *set; TRACE("devinfo %p, detail_data %p.\n", devinfo, DevInfoData); if (!(set = get_device_set(devinfo))) 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 devinfo, SP_DEVINFO_LIST_DETAIL_DATA_W *DevInfoData) { struct DeviceInfoSet *set; TRACE("devinfo %p, detail_data %p.\n", devinfo, DevInfoData); if (!(set = get_device_set(devinfo))) 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 %08lx %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 devinfo, SP_DEVINFO_DATA *device_data, const GUID *class, const WCHAR *refstr, DWORD flags, SP_DEVICE_INTERFACE_DATA *iface_data) { struct device *device; struct device_iface *iface; TRACE("devinfo %p, device_data %p, class %s, refstr %s, flags %#lx, iface_data %p.\n", devinfo, device_data, debugstr_guid(class), debugstr_w(refstr), flags, iface_data); if (!(device = get_device(devinfo, device_data))) return FALSE; if (!class) { SetLastError(ERROR_INVALID_USER_BUFFER); return FALSE; } if (!(iface = SETUPDI_CreateDeviceInterface(device, class, refstr))) return FALSE; if (iface_data) { if (iface_data->cbSize != sizeof(SP_DEVICE_INTERFACE_DATA)) { SetLastError(ERROR_INVALID_USER_BUFFER); return FALSE; } copy_device_iface_data(iface_data, iface); } return TRUE; } /*********************************************************************** * 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 %ld %08lx %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 LONG create_iface_key(const struct device_iface *iface, REGSAM access, HKEY *key) { return RegCreateKeyExW(iface->refstr_key, DeviceParameters, 0, NULL, 0, access, NULL, key, NULL); } /*********************************************************************** * SetupDiCreateDeviceInterfaceRegKeyW (SETUPAPI.@) */ HKEY WINAPI SetupDiCreateDeviceInterfaceRegKeyW(HDEVINFO devinfo, SP_DEVICE_INTERFACE_DATA *iface_data, DWORD reserved, REGSAM access, HINF hinf, const WCHAR *section) { struct device_iface *iface; HKEY params_key; LONG ret; TRACE("devinfo %p, iface_data %p, reserved %ld, access %#lx, hinf %p, section %s.\n", devinfo, iface_data, reserved, access, hinf, debugstr_w(section)); if (!(iface = get_device_iface(devinfo, iface_data))) return INVALID_HANDLE_VALUE; if (hinf && !section) { SetLastError(ERROR_INVALID_PARAMETER); return INVALID_HANDLE_VALUE; } ret = create_iface_key(iface, access, ¶ms_key); if (ret) { SetLastError(ret); return INVALID_HANDLE_VALUE; } return params_key; } /*********************************************************************** * SetupDiDeleteDeviceInterfaceRegKey (SETUPAPI.@) */ BOOL WINAPI SetupDiDeleteDeviceInterfaceRegKey(HDEVINFO devinfo, SP_DEVICE_INTERFACE_DATA *iface_data, DWORD reserved) { struct device_iface *iface; LONG ret; TRACE("devinfo %p, iface_data %p, reserved %ld.\n", devinfo, iface_data, reserved); if (!(iface = get_device_iface(devinfo, iface_data))) return FALSE; ret = RegDeleteKeyW(iface->refstr_key, DeviceParameters); if (ret) { SetLastError(ret); return FALSE; } return TRUE; } /*********************************************************************** * SetupDiEnumDeviceInterfaces (SETUPAPI.@) * * PARAMS * DeviceInfoSet [I] Set of devices from which to enumerate * interfaces * DeviceInfoData [I] (Optional) If specified, a specific device * instance from which to enumerate interfaces. * If it isn't specified, all interfaces for all * devices in the set are enumerated. * InterfaceClassGuid [I] The interface class to enumerate. * MemberIndex [I] An index of the interface instance to enumerate. * A caller should start with MemberIndex set to 0, * and continue until the function fails with * ERROR_NO_MORE_ITEMS. * DeviceInterfaceData [I/O] Returns an enumerated interface. Its cbSize * member must be set to * sizeof(SP_DEVICE_INTERFACE_DATA). * * RETURNS * Success: non-zero value. * Failure: FALSE. Call GetLastError() for more info. */ BOOL WINAPI SetupDiEnumDeviceInterfaces(HDEVINFO devinfo, SP_DEVINFO_DATA *device_data, const GUID *class, DWORD index, SP_DEVICE_INTERFACE_DATA *iface_data) { struct DeviceInfoSet *set; struct device *device; struct device_iface *iface; DWORD i = 0; TRACE("devinfo %p, device_data %p, class %s, index %lu, iface_data %p.\n", devinfo, device_data, debugstr_guid(class), index, iface_data); if (!iface_data || iface_data->cbSize != sizeof(SP_DEVICE_INTERFACE_DATA)) { SetLastError(ERROR_INVALID_PARAMETER); return FALSE; } /* In case application fails to check return value, clear output */ memset(iface_data, 0, sizeof(*iface_data)); iface_data->cbSize = sizeof(SP_DEVICE_INTERFACE_DATA); if (device_data) { if (!(device = get_device(devinfo, device_data))) return FALSE; LIST_FOR_EACH_ENTRY(iface, &device->interfaces, struct device_iface, entry) { if (IsEqualGUID(&iface->class, class)) { if (i == index) { copy_device_iface_data(iface_data, iface); return TRUE; } i++; } } } else { if (!(set = get_device_set(devinfo))) return FALSE; LIST_FOR_EACH_ENTRY(device, &set->devices, struct device, entry) { LIST_FOR_EACH_ENTRY(iface, &device->interfaces, struct device_iface, entry) { if (IsEqualGUID(&iface->class, class)) { if (i == index) { copy_device_iface_data(iface_data, iface); return TRUE; } i++; } } } } SetLastError(ERROR_NO_MORE_ITEMS); return FALSE; } /*********************************************************************** * 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) { struct DeviceInfoSet *set; struct device *device, *device2; TRACE("devinfo %p.\n", devinfo); if (!(set = get_device_set(devinfo))) return FALSE; LIST_FOR_EACH_ENTRY_SAFE(device, device2, &set->devices, struct device, entry) { delete_device(device); } heap_free(set); SetLastError(ERROR_SUCCESS); return TRUE; } /*********************************************************************** * SetupDiGetDeviceInterfaceDetailA (SETUPAPI.@) */ BOOL WINAPI SetupDiGetDeviceInterfaceDetailA(HDEVINFO devinfo, SP_DEVICE_INTERFACE_DATA *iface_data, SP_DEVICE_INTERFACE_DETAIL_DATA_A *DeviceInterfaceDetailData, DWORD DeviceInterfaceDetailDataSize, DWORD *RequiredSize, SP_DEVINFO_DATA *device_data) { struct device_iface *iface; DWORD bytesNeeded = FIELD_OFFSET(SP_DEVICE_INTERFACE_DETAIL_DATA_A, DevicePath[1]); BOOL ret = FALSE; TRACE("devinfo %p, iface_data %p, detail_data %p, size %ld, needed %p, device_data %p.\n", devinfo, iface_data, DeviceInterfaceDetailData, DeviceInterfaceDetailDataSize, RequiredSize, device_data); if (!(iface = get_device_iface(devinfo, iface_data))) return FALSE; if (DeviceInterfaceDetailData && DeviceInterfaceDetailData->cbSize != sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA_A)) { SetLastError(ERROR_INVALID_USER_BUFFER); return FALSE; } if (!DeviceInterfaceDetailData && DeviceInterfaceDetailDataSize) { SetLastError(ERROR_INVALID_USER_BUFFER); return FALSE; } if (iface->symlink) bytesNeeded += WideCharToMultiByte(CP_ACP, 0, iface->symlink, -1, NULL, 0, NULL, NULL); if (DeviceInterfaceDetailDataSize >= bytesNeeded) { if (iface->symlink) WideCharToMultiByte(CP_ACP, 0, iface->symlink, -1, DeviceInterfaceDetailData->DevicePath, DeviceInterfaceDetailDataSize - offsetof(SP_DEVICE_INTERFACE_DETAIL_DATA_A, DevicePath), NULL, NULL); else DeviceInterfaceDetailData->DevicePath[0] = '\0'; ret = TRUE; } else { if (RequiredSize) *RequiredSize = bytesNeeded; SetLastError(ERROR_INSUFFICIENT_BUFFER); } if (device_data && device_data->cbSize == sizeof(SP_DEVINFO_DATA)) copy_device_data(device_data, iface->device); return ret; } /*********************************************************************** * SetupDiGetDeviceInterfaceDetailW (SETUPAPI.@) */ BOOL WINAPI SetupDiGetDeviceInterfaceDetailW(HDEVINFO devinfo, SP_DEVICE_INTERFACE_DATA *iface_data, SP_DEVICE_INTERFACE_DETAIL_DATA_W *DeviceInterfaceDetailData, DWORD DeviceInterfaceDetailDataSize, DWORD *RequiredSize, SP_DEVINFO_DATA *device_data) { struct device_iface *iface; DWORD bytesNeeded = offsetof(SP_DEVICE_INTERFACE_DETAIL_DATA_W, DevicePath) + sizeof(WCHAR); /* include NULL terminator */ BOOL ret = FALSE; TRACE("devinfo %p, iface_data %p, detail_data %p, size %ld, needed %p, device_data %p.\n", devinfo, iface_data, DeviceInterfaceDetailData, DeviceInterfaceDetailDataSize, RequiredSize, device_data); if (!(iface = get_device_iface(devinfo, iface_data))) 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; } if (iface->symlink) bytesNeeded += sizeof(WCHAR) * lstrlenW(iface->symlink); if (DeviceInterfaceDetailDataSize >= bytesNeeded) { if (iface->symlink) lstrcpyW(DeviceInterfaceDetailData->DevicePath, iface->symlink); else DeviceInterfaceDetailData->DevicePath[0] = '\0'; ret = TRUE; } else { if (RequiredSize) *RequiredSize = bytesNeeded; SetLastError(ERROR_INSUFFICIENT_BUFFER); } if (device_data && device_data->cbSize == sizeof(SP_DEVINFO_DATA)) copy_device_data(device_data, iface->device); return ret; } /*********************************************************************** * SetupDiGetDeviceRegistryPropertyA (SETUPAPI.@) */ BOOL WINAPI SetupDiGetDeviceRegistryPropertyA(HDEVINFO devinfo, SP_DEVINFO_DATA *device_data, DWORD Property, DWORD *PropertyRegDataType, BYTE *PropertyBuffer, DWORD PropertyBufferSize, DWORD *RequiredSize) { BOOL ret = FALSE; struct device *device; TRACE("devinfo %p, device_data %p, property %ld, type %p, buffer %p, size %ld, required %p\n", devinfo, device_data, Property, PropertyRegDataType, PropertyBuffer, PropertyBufferSize, RequiredSize); if (!(device = get_device(devinfo, device_data))) return FALSE; if (PropertyBufferSize && PropertyBuffer == NULL) { SetLastError(ERROR_INVALID_DATA); return FALSE; } if (Property < ARRAY_SIZE(PropertyMap) && PropertyMap[Property].nameA) { DWORD size = PropertyBufferSize; LONG l = RegQueryValueExA(device->key, PropertyMap[Property].nameA, NULL, PropertyRegDataType, PropertyBuffer, &size); if (l == ERROR_FILE_NOT_FOUND) SetLastError(ERROR_INVALID_DATA); else 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 devinfo, SP_DEVINFO_DATA *device_data, DWORD Property, DWORD *PropertyRegDataType, BYTE *PropertyBuffer, DWORD PropertyBufferSize, DWORD *RequiredSize) { BOOL ret = FALSE; struct device *device; TRACE("devinfo %p, device_data %p, prop %ld, type %p, buffer %p, size %ld, required %p\n", devinfo, device_data, Property, PropertyRegDataType, PropertyBuffer, PropertyBufferSize, RequiredSize); if (!(device = get_device(devinfo, device_data))) return FALSE; if (PropertyBufferSize && PropertyBuffer == NULL) { SetLastError(ERROR_INVALID_DATA); return FALSE; } if (Property < ARRAY_SIZE(PropertyMap) && PropertyMap[Property].nameW) { DWORD size = PropertyBufferSize; LONG l = RegQueryValueExW(device->key, PropertyMap[Property].nameW, NULL, PropertyRegDataType, PropertyBuffer, &size); if (l == ERROR_FILE_NOT_FOUND) SetLastError(ERROR_INVALID_DATA); else 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 devinfo, SP_DEVINFO_DATA *device_data, DWORD Property, const BYTE *PropertyBuffer, DWORD PropertyBufferSize) { BOOL ret = FALSE; struct device *device; TRACE("devinfo %p, device_data %p, prop %ld, buffer %p, size %ld.\n", devinfo, device_data, Property, PropertyBuffer, PropertyBufferSize); if (!(device = get_device(devinfo, device_data))) return FALSE; if (Property < ARRAY_SIZE(PropertyMap) && PropertyMap[Property].nameA) { LONG l = RegSetValueExA(device->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 devinfo, SP_DEVINFO_DATA *device_data, DWORD prop, const BYTE *buffer, DWORD size) { struct device *device; TRACE("devinfo %p, device_data %p, prop %ld, buffer %p, size %ld.\n", devinfo, device_data, prop, buffer, size); if (!(device = get_device(devinfo, device_data))) return FALSE; return SETUPDI_SetDeviceRegistryPropertyW(device, prop, buffer, size); } /*********************************************************************** * 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 && *MachineName) { 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; } /*********************************************************************** * SetupDiOpenDeviceInfoA (SETUPAPI.@) */ BOOL WINAPI SetupDiOpenDeviceInfoA(HDEVINFO devinfo, PCSTR instance_id, HWND hwnd_parent, DWORD flags, PSP_DEVINFO_DATA device_data) { WCHAR instance_idW[MAX_DEVICE_ID_LEN]; TRACE("%p %s %p 0x%08lx %p\n", devinfo, debugstr_a(instance_id), hwnd_parent, flags, device_data); if (!instance_id || strlen(instance_id) >= MAX_DEVICE_ID_LEN) { SetLastError(ERROR_INVALID_PARAMETER); return FALSE; } MultiByteToWideChar(CP_ACP, 0, instance_id, -1, instance_idW, ARRAY_SIZE(instance_idW)); return SetupDiOpenDeviceInfoW(devinfo, instance_idW, hwnd_parent, flags, device_data); } /*********************************************************************** * SetupDiOpenDeviceInfoW (SETUPAPI.@) */ BOOL WINAPI SetupDiOpenDeviceInfoW(HDEVINFO devinfo, PCWSTR instance_id, HWND hwnd_parent, DWORD flags, PSP_DEVINFO_DATA device_data) { struct DeviceInfoSet *set; struct device *device; WCHAR classW[40]; GUID guid; HKEY enumKey = NULL; HKEY instanceKey = NULL; DWORD phantom; DWORD size; DWORD error = ERROR_NO_SUCH_DEVINST; TRACE("%p %s %p 0x%08lx %p\n", devinfo, debugstr_w(instance_id), hwnd_parent, flags, device_data); if (!(set = get_device_set(devinfo))) return FALSE; if (!instance_id) { SetLastError(ERROR_INVALID_PARAMETER); return FALSE; } if (hwnd_parent) FIXME("hwnd_parent unsupported\n"); if (flags) FIXME("flags unsupported: 0x%08lx\n", flags); RegCreateKeyExW(HKEY_LOCAL_MACHINE, Enum, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &enumKey, NULL); /* Instance needs to be already existent in registry, if not, report ERROR_NO_SUCH_DEVINST */ if (RegOpenKeyExW(enumKey, instance_id, 0, KEY_READ, &instanceKey)) goto done; /* If it's an unregistered instance, aka phantom instance, report ERROR_NO_SUCH_DEVINST */ size = sizeof(phantom); if (!RegQueryValueExW(instanceKey, Phantom, NULL, NULL, (BYTE *)&phantom, &size)) goto done; /* Check class GUID */ size = sizeof(classW); if (RegQueryValueExW(instanceKey, ClassGUID, NULL, NULL, (BYTE *)classW, &size)) goto done; classW[37] = 0; UuidFromStringW(&classW[1], &guid); if (!IsEqualGUID(&set->ClassGuid, &GUID_NULL) && !IsEqualGUID(&guid, &set->ClassGuid)) { error = ERROR_CLASS_MISMATCH; goto done; } if (!(device = create_device(set, &guid, instance_id, FALSE))) goto done; if (!device_data || device_data->cbSize == sizeof(SP_DEVINFO_DATA)) { if (device_data) copy_device_data(device_data, device); error = NO_ERROR; } else error = ERROR_INVALID_USER_BUFFER; done: RegCloseKey(instanceKey); RegCloseKey(enumKey); SetLastError(error); return !error; } /*********************************************************************** * SetupDiOpenDeviceInterfaceW (SETUPAPI.@) */ BOOL WINAPI SetupDiOpenDeviceInterfaceW( HDEVINFO DeviceInfoSet, PCWSTR DevicePath, DWORD OpenFlags, PSP_DEVICE_INTERFACE_DATA DeviceInterfaceData) { FIXME("%p %s %08lx %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 %08lx %p\n", DeviceInfoSet, debugstr_a(DevicePath), OpenFlags, DeviceInterfaceData); return FALSE; } /*********************************************************************** * SetupDiOpenDeviceInterfaceRegKey (SETUPAPI.@) */ HKEY WINAPI SetupDiOpenDeviceInterfaceRegKey(HDEVINFO devinfo, PSP_DEVICE_INTERFACE_DATA iface_data, DWORD reserved, REGSAM access) { struct device_iface *iface; LSTATUS lr; HKEY key; TRACE("devinfo %p, iface_data %p, reserved %ld, access %#lx.\n", devinfo, iface_data, reserved, access); if (!(iface = get_device_iface(devinfo, iface_data))) return INVALID_HANDLE_VALUE; lr = RegOpenKeyExW(iface->refstr_key, DeviceParameters, 0, access, &key); if (lr) { SetLastError(lr); return INVALID_HANDLE_VALUE; } return key; } /*********************************************************************** * SetupDiSetClassInstallParamsA (SETUPAPI.@) */ BOOL WINAPI SetupDiSetClassInstallParamsA( HDEVINFO DeviceInfoSet, PSP_DEVINFO_DATA DeviceInfoData, PSP_CLASSINSTALL_HEADER ClassInstallParams, DWORD ClassInstallParamsSize) { FIXME("%p %p %x %lu\n",DeviceInfoSet, DeviceInfoData, ClassInstallParams->InstallFunction, ClassInstallParamsSize); return FALSE; } /*********************************************************************** * SetupDiSetClassInstallParamsW (SETUPAPI.@) */ BOOL WINAPI SetupDiSetClassInstallParamsW( HDEVINFO DeviceInfoSet, PSP_DEVINFO_DATA DeviceInfoData, PSP_CLASSINSTALL_HEADER ClassInstallParams, DWORD ClassInstallParamsSize) { FIXME("%p %p %x %lu\n",DeviceInfoSet, DeviceInfoData, ClassInstallParams->InstallFunction, ClassInstallParamsSize); return FALSE; } static BOOL call_coinstallers(WCHAR *list, DI_FUNCTION function, HDEVINFO devinfo, SP_DEVINFO_DATA *device_data) { DWORD (CALLBACK *coinst_proc)(DI_FUNCTION, HDEVINFO, SP_DEVINFO_DATA *, COINSTALLER_CONTEXT_DATA *); COINSTALLER_CONTEXT_DATA coinst_ctx; WCHAR *p, *procnameW; HMODULE module; char *procname; DWORD ret; for (p = list; *p; p += lstrlenW(p) + 1) { TRACE("Found co-installer %s.\n", debugstr_w(p)); if ((procnameW = wcschr(p, ','))) *procnameW = 0; if ((module = LoadLibraryExW(p, NULL, LOAD_LIBRARY_SEARCH_SYSTEM32))) { if (procnameW) { procname = strdupWtoA(procnameW + 1); coinst_proc = (void *)GetProcAddress(module, procname); heap_free(procname); } else coinst_proc = (void *)GetProcAddress(module, "CoDeviceInstall"); if (coinst_proc) { memset(&coinst_ctx, 0, sizeof(coinst_ctx)); TRACE("Calling co-installer %p.\n", coinst_proc); ret = coinst_proc(function, devinfo, device_data, &coinst_ctx); TRACE("Co-installer %p returned %#lx.\n", coinst_proc, ret); if (ret == ERROR_DI_POSTPROCESSING_REQUIRED) FIXME("Co-installer postprocessing not implemented.\n"); else if (ret) { ERR("Co-installer returned error %#lx.\n", ret); FreeLibrary(module); SetLastError(ret); return FALSE; } } FreeLibrary(module); } } return TRUE; } /*********************************************************************** * SetupDiCallClassInstaller (SETUPAPI.@) */ BOOL WINAPI SetupDiCallClassInstaller(DI_FUNCTION function, HDEVINFO devinfo, SP_DEVINFO_DATA *device_data) { static const WCHAR class_coinst_pathW[] = {'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','o','D','e','v','i','c','e','I','n','s','t','a','l','l','e','r','s',0}; static const WCHAR coinstallers32W[] = {'C','o','I','n','s','t','a','l','l','e','r','s','3','2',0}; static const WCHAR installer32W[] = {'I','n','s','t','a','l','l','e','r','3','2',0}; DWORD (CALLBACK *classinst_proc)(DI_FUNCTION, HDEVINFO, SP_DEVINFO_DATA *); DWORD ret = ERROR_DI_DO_DEFAULT; HKEY class_key, coinst_key; WCHAR *path, *procnameW; struct device *device; WCHAR guidstr[39]; BOOL coret = TRUE; HMODULE module; char *procname; DWORD size; TRACE("function %#x, devinfo %p, device_data %p.\n", function, devinfo, device_data); if (!(device = get_device(devinfo, device_data))) return FALSE; if (!RegOpenKeyExW(HKEY_LOCAL_MACHINE, class_coinst_pathW, 0, KEY_READ, &coinst_key)) { SETUPDI_GuidToString(&device->class, guidstr); if (!RegGetValueW(coinst_key, NULL, guidstr, RRF_RT_REG_MULTI_SZ, NULL, NULL, &size)) { path = heap_alloc(size); if (!RegGetValueW(coinst_key, NULL, guidstr, RRF_RT_REG_MULTI_SZ, NULL, path, &size)) coret = call_coinstallers(path, function, devinfo, device_data); heap_free(path); } RegCloseKey(coinst_key); } if (!coret) return FALSE; if (!open_driver_key(device, KEY_READ, &coinst_key)) { if (!RegGetValueW(coinst_key, NULL, coinstallers32W, RRF_RT_REG_MULTI_SZ, NULL, NULL, &size)) { path = heap_alloc(size); if (!RegGetValueW(coinst_key, NULL, coinstallers32W, RRF_RT_REG_MULTI_SZ, NULL, path, &size)) coret = call_coinstallers(path, function, devinfo, device_data); heap_free(path); } RegCloseKey(coinst_key); } if ((class_key = SetupDiOpenClassRegKey(&device->class, KEY_READ)) != INVALID_HANDLE_VALUE) { if (!RegGetValueW(class_key, NULL, installer32W, RRF_RT_REG_SZ, NULL, NULL, &size)) { path = heap_alloc(size); if (!RegGetValueW(class_key, NULL, installer32W, RRF_RT_REG_SZ, NULL, path, &size)) { TRACE("Found class installer %s.\n", debugstr_w(path)); if ((procnameW = wcschr(path, ','))) *procnameW = 0; if ((module = LoadLibraryExW(path, NULL, LOAD_LIBRARY_SEARCH_SYSTEM32))) { if (procnameW) { procname = strdupWtoA(procnameW + 1); classinst_proc = (void *)GetProcAddress(module, procname); heap_free(procname); } else classinst_proc = (void *)GetProcAddress(module, "ClassInstall"); if (classinst_proc) { TRACE("Calling class installer %p.\n", classinst_proc); ret = classinst_proc(function, devinfo, device_data); TRACE("Class installer %p returned %#lx.\n", classinst_proc, ret); } FreeLibrary(module); } } heap_free(path); } RegCloseKey(class_key); } if (ret == ERROR_DI_DO_DEFAULT) { switch (function) { case DIF_REGISTERDEVICE: return SetupDiRegisterDeviceInfo(devinfo, device_data, 0, NULL, NULL, NULL); case DIF_REMOVE: return SetupDiRemoveDevice(devinfo, device_data); case DIF_SELECTBESTCOMPATDRV: return SetupDiSelectBestCompatDrv(devinfo, device_data); case DIF_REGISTER_COINSTALLERS: return SetupDiRegisterCoDeviceInstallers(devinfo, device_data); case DIF_INSTALLDEVICEFILES: return SetupDiInstallDriverFiles(devinfo, device_data); case DIF_INSTALLINTERFACES: return SetupDiInstallDeviceInterfaces(devinfo, device_data); case DIF_INSTALLDEVICE: return SetupDiInstallDevice(devinfo, device_data); case DIF_FINISHINSTALL_ACTION: case DIF_PROPERTYCHANGE: case DIF_SELECTDEVICE: case DIF_UNREMOVE: FIXME("Unhandled function %#x.\n", function); } } if (ret) SetLastError(ret); return !ret; } /*********************************************************************** * SetupDiGetDeviceInstallParamsW (SETUPAPI.@) */ BOOL WINAPI SetupDiGetDeviceInstallParamsW(HDEVINFO devinfo, SP_DEVINFO_DATA *device_data, SP_DEVINSTALL_PARAMS_W *params) { struct device *device; TRACE("devinfo %p, device_data %p, params %p.\n", devinfo, device_data, params); if (params->cbSize != sizeof(SP_DEVINSTALL_PARAMS_W)) { SetLastError(ERROR_INVALID_USER_BUFFER); return FALSE; } if (!(device = get_device(devinfo, device_data))) return FALSE; *params = device->params; return TRUE; } /*********************************************************************** * SetupDiGetDeviceInstallParamsA (SETUPAPI.@) */ BOOL WINAPI SetupDiGetDeviceInstallParamsA(HDEVINFO devinfo, SP_DEVINFO_DATA *device_data, SP_DEVINSTALL_PARAMS_A *params) { SP_DEVINSTALL_PARAMS_W paramsW; BOOL ret; if (params->cbSize != sizeof(SP_DEVINSTALL_PARAMS_A)) { SetLastError(ERROR_INVALID_USER_BUFFER); return FALSE; } paramsW.cbSize = sizeof(paramsW); ret = SetupDiGetDeviceInstallParamsW(devinfo, device_data, ¶msW); params->Flags = paramsW.Flags; params->FlagsEx = paramsW.FlagsEx; params->hwndParent = paramsW.hwndParent; params->InstallMsgHandler = paramsW.InstallMsgHandler; params->InstallMsgHandlerContext = paramsW.InstallMsgHandlerContext; params->FileQueue = paramsW.FileQueue; params->ClassInstallReserved = paramsW.ClassInstallReserved; params->Reserved = paramsW.Reserved; WideCharToMultiByte(CP_ACP, 0, paramsW.DriverPath, -1, params->DriverPath, sizeof(params->DriverPath), NULL, NULL); return ret; } /*********************************************************************** * SetupDiSetDeviceInstallParamsA (SETUPAPI.@) */ BOOL WINAPI SetupDiSetDeviceInstallParamsA(HDEVINFO devinfo, SP_DEVINFO_DATA *device_data, SP_DEVINSTALL_PARAMS_A *params) { SP_DEVINSTALL_PARAMS_W paramsW; if (params->cbSize != sizeof(SP_DEVINSTALL_PARAMS_A)) { SetLastError(ERROR_INVALID_USER_BUFFER); return FALSE; } paramsW.cbSize = sizeof(paramsW); paramsW.Flags = params->Flags; paramsW.FlagsEx = params->FlagsEx; paramsW.hwndParent = params->hwndParent; paramsW.InstallMsgHandler = params->InstallMsgHandler; paramsW.InstallMsgHandlerContext = params->InstallMsgHandlerContext; paramsW.FileQueue = params->FileQueue; paramsW.ClassInstallReserved = params->ClassInstallReserved; paramsW.Reserved = params->Reserved; MultiByteToWideChar(CP_ACP, 0, params->DriverPath, -1, paramsW.DriverPath, ARRAY_SIZE(paramsW.DriverPath)); return SetupDiSetDeviceInstallParamsW(devinfo, device_data, ¶msW); } /*********************************************************************** * SetupDiSetDeviceInstallParamsW (SETUPAPI.@) */ BOOL WINAPI SetupDiSetDeviceInstallParamsW(HDEVINFO devinfo, SP_DEVINFO_DATA *device_data, SP_DEVINSTALL_PARAMS_W *params) { struct device *device; TRACE("devinfo %p, device_data %p, params %p.\n", devinfo, device_data, params); if (params->cbSize != sizeof(SP_DEVINSTALL_PARAMS_W)) { SetLastError(ERROR_INVALID_USER_BUFFER); return FALSE; } if (!(device = get_device(devinfo, device_data))) return FALSE; device->params = *params; return TRUE; } BOOL WINAPI SetupDiSetDevicePropertyW(HDEVINFO devinfo, PSP_DEVINFO_DATA device_data, const DEVPROPKEY *key, DEVPROPTYPE type, const BYTE *buffer, DWORD size, DWORD flags) { static const WCHAR propertiesW[] = {'P', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's', 0}; static const WCHAR formatW[] = {'\\', '%', '0', '4', 'X', 0}; struct device *device; HKEY properties_hkey, property_hkey; WCHAR property_hkey_path[44]; LSTATUS ls; TRACE("%p %p %p %#lx %p %ld %#lx\n", devinfo, device_data, key, type, buffer, size, flags); if (!(device = get_device(devinfo, device_data))) return FALSE; if (!key || !is_valid_property_type(type) || (buffer && !size && !(type == DEVPROP_TYPE_EMPTY || type == DEVPROP_TYPE_NULL)) || (buffer && size && (type == DEVPROP_TYPE_EMPTY || type == DEVPROP_TYPE_NULL))) { SetLastError(ERROR_INVALID_DATA); return FALSE; } if (size && !buffer) { SetLastError(ERROR_INVALID_USER_BUFFER); return FALSE; } if (flags) { SetLastError(ERROR_INVALID_FLAGS); return FALSE; } ls = RegCreateKeyExW(device->key, propertiesW, 0, NULL, 0, KEY_READ | KEY_WRITE, NULL, &properties_hkey, NULL); if (ls) { SetLastError(ls); return FALSE; } SETUPDI_GuidToString(&key->fmtid, property_hkey_path); swprintf(property_hkey_path + 38, ARRAY_SIZE(property_hkey_path) - 38, formatW, key->pid); if (type == DEVPROP_TYPE_EMPTY) { ls = RegDeleteKeyW(properties_hkey, property_hkey_path); RegCloseKey(properties_hkey); SetLastError(ls == ERROR_FILE_NOT_FOUND ? ERROR_NOT_FOUND : ls); return !ls; } else if (type == DEVPROP_TYPE_NULL) { if (!(ls = RegOpenKeyW(properties_hkey, property_hkey_path, &property_hkey))) { ls = RegDeleteValueW(property_hkey, NULL); RegCloseKey(property_hkey); } RegCloseKey(properties_hkey); SetLastError(ls == ERROR_FILE_NOT_FOUND ? ERROR_NOT_FOUND : ls); return !ls; } else { if (!(ls = RegCreateKeyExW(properties_hkey, property_hkey_path, 0, NULL, 0, KEY_READ | KEY_WRITE, NULL, &property_hkey, NULL))) { ls = RegSetValueExW(property_hkey, NULL, 0, 0xffff0000 | (0xffff & type), buffer, size); RegCloseKey(property_hkey); } RegCloseKey(properties_hkey); SetLastError(ls); return !ls; } } /*********************************************************************** * SetupDiOpenDevRegKey (SETUPAPI.@) */ HKEY WINAPI SetupDiOpenDevRegKey(HDEVINFO devinfo, SP_DEVINFO_DATA *device_data, DWORD Scope, DWORD HwProfile, DWORD KeyType, REGSAM samDesired) { struct device *device; HKEY key = INVALID_HANDLE_VALUE; LONG l; TRACE("devinfo %p, device_data %p, scope %ld, profile %ld, type %ld, access %#lx.\n", devinfo, device_data, Scope, HwProfile, KeyType, samDesired); if (!(device = get_device(devinfo, device_data))) 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 (device->phantom) { SetLastError(ERROR_DEVINFO_NOT_REGISTERED); return INVALID_HANDLE_VALUE; } if (Scope != DICS_FLAG_GLOBAL) FIXME("unimplemented for scope %ld\n", Scope); switch (KeyType) { case DIREG_DEV: l = RegOpenKeyExW(device->key, DeviceParameters, 0, samDesired, &key); break; case DIREG_DRV: l = open_driver_key(device, samDesired, &key); break; default: FIXME("Unhandled type %#lx.\n", KeyType); l = ERROR_CALL_NOT_IMPLEMENTED; } SetLastError(l == ERROR_FILE_NOT_FOUND ? ERROR_KEY_DOES_NOT_EXIST : l); return l ? INVALID_HANDLE_VALUE : key; } /*********************************************************************** * SetupDiDeleteDevRegKey (SETUPAPI.@) */ BOOL WINAPI SetupDiDeleteDevRegKey(HDEVINFO devinfo, SP_DEVINFO_DATA *device_data, DWORD Scope, DWORD HwProfile, DWORD KeyType) { struct device *device; LONG l; TRACE("devinfo %p, device_data %p, scope %ld, profile %ld, type %ld.\n", devinfo, device_data, Scope, HwProfile, KeyType); if (!(device = get_device(devinfo, device_data))) 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; } if (device->phantom) { SetLastError(ERROR_DEVINFO_NOT_REGISTERED); return FALSE; } if (Scope != DICS_FLAG_GLOBAL) FIXME("unimplemented for scope %ld\n", Scope); switch (KeyType) { case DIREG_DRV: l = delete_driver_key(device); break; case DIREG_BOTH: if ((l = delete_driver_key(device))) break; /* fall through */ case DIREG_DEV: l = RegDeleteKeyW(device->key, DeviceParameters); break; default: FIXME("Unhandled type %#lx.\n", KeyType); l = ERROR_CALL_NOT_IMPLEMENTED; } SetLastError(l); return !l; } /*********************************************************************** * CM_Get_Device_IDA (SETUPAPI.@) */ CONFIGRET WINAPI CM_Get_Device_IDA(DEVINST devnode, char *buffer, ULONG len, ULONG flags) { struct device *device = get_devnode_device(devnode); TRACE("%lu, %p, %lu, %#lx\n", devnode, buffer, len, flags); if (!device) return CR_NO_SUCH_DEVINST; WideCharToMultiByte(CP_ACP, 0, device->instanceId, -1, buffer, len, 0, 0); TRACE("Returning %s\n", debugstr_a(buffer)); return CR_SUCCESS; } /*********************************************************************** * CM_Get_Device_IDW (SETUPAPI.@) */ CONFIGRET WINAPI CM_Get_Device_IDW(DEVINST devnode, WCHAR *buffer, ULONG len, ULONG flags) { struct device *device = get_devnode_device(devnode); TRACE("%lu, %p, %lu, %#lx\n", devnode, buffer, len, flags); if (!device) return CR_NO_SUCH_DEVINST; lstrcpynW(buffer, device->instanceId, len); TRACE("Returning %s\n", debugstr_w(buffer)); return CR_SUCCESS; } /*********************************************************************** * CM_Get_Device_ID_Size (SETUPAPI.@) */ CONFIGRET WINAPI CM_Get_Device_ID_Size(ULONG *len, DEVINST devnode, ULONG flags) { struct device *device = get_devnode_device(devnode); TRACE("%p, %lu, %#lx\n", len, devnode, flags); if (!device) return CR_NO_SUCH_DEVINST; *len = lstrlenW(device->instanceId); return CR_SUCCESS; } /*********************************************************************** * SetupDiGetINFClassA (SETUPAPI.@) */ BOOL WINAPI SetupDiGetINFClassA(PCSTR inf, LPGUID class_guid, PSTR class_name, DWORD size, PDWORD required_size) { BOOL retval; DWORD required_sizeA, required_sizeW; PWSTR class_nameW = NULL; UNICODE_STRING infW; if (inf) { if (!RtlCreateUnicodeStringFromAsciiz(&infW, inf)) { SetLastError(ERROR_NOT_ENOUGH_MEMORY); return FALSE; } } else infW.Buffer = NULL; if (class_name && size) { if (!(class_nameW = HeapAlloc(GetProcessHeap(), 0, size * sizeof(WCHAR)))) { RtlFreeUnicodeString(&infW); SetLastError(ERROR_NOT_ENOUGH_MEMORY); return FALSE; } } retval = SetupDiGetINFClassW(infW.Buffer, class_guid, class_nameW, size, &required_sizeW); if (retval) { required_sizeA = WideCharToMultiByte( CP_ACP, 0, class_nameW, required_sizeW, class_name, size, NULL, NULL); if(required_size) *required_size = required_sizeA; } else if(required_size) *required_size = required_sizeW; HeapFree(GetProcessHeap(), 0, class_nameW); RtlFreeUnicodeString(&infW); return retval; } /*********************************************************************** * SetupDiGetINFClassW (SETUPAPI.@) */ BOOL WINAPI SetupDiGetINFClassW(PCWSTR inf, LPGUID class_guid, PWSTR class_name, DWORD size, PDWORD required_size) { BOOL have_guid, have_name; DWORD dret; WCHAR buffer[MAX_PATH]; if (!inf) { SetLastError(ERROR_INVALID_PARAMETER); return FALSE; } if (INVALID_FILE_ATTRIBUTES == GetFileAttributesW(inf)) { FIXME("%s not found. Searching via DevicePath not implemented\n", debugstr_w(inf)); SetLastError(ERROR_FILE_NOT_FOUND); return FALSE; } if (!class_guid || !class_name || !size) { SetLastError(ERROR_INVALID_PARAMETER); return FALSE; } if (!GetPrivateProfileStringW(Version, Signature, NULL, buffer, MAX_PATH, inf)) return FALSE; if (lstrcmpiW(buffer, Chicago) && lstrcmpiW(buffer, WindowsNT)) return FALSE; buffer[0] = '\0'; have_guid = 0 < GetPrivateProfileStringW(Version, ClassGUID, NULL, buffer, MAX_PATH, inf); if (have_guid) { buffer[lstrlenW(buffer)-1] = 0; if (RPC_S_OK != UuidFromStringW(buffer + 1, class_guid)) { FIXME("failed to convert \"%s\" into a guid\n", debugstr_w(buffer)); SetLastError(ERROR_INVALID_PARAMETER); return FALSE; } } buffer[0] = '\0'; dret = GetPrivateProfileStringW(Version, Class, NULL, buffer, MAX_PATH, inf); have_name = 0 < dret; if (dret >= MAX_PATH -1) FIXME("buffer might be too small\n"); if (have_guid && !have_name) { class_name[0] = '\0'; FIXME("class name lookup via guid not implemented\n"); } if (have_name) { if (dret < size) lstrcpyW(class_name, buffer); else { SetLastError(ERROR_INSUFFICIENT_BUFFER); have_name = FALSE; } } if (required_size) *required_size = dret + ((dret) ? 1 : 0); return (have_guid || have_name); } static LSTATUS get_device_property(struct device *device, const DEVPROPKEY *prop_key, DEVPROPTYPE *prop_type, BYTE *prop_buff, DWORD prop_buff_size, DWORD *required_size, DWORD flags) { WCHAR key_path[55] = L"Properties\\"; HKEY hkey; DWORD value_type; DWORD value_size = 0; LSTATUS ls; if (!prop_key) return ERROR_INVALID_DATA; if (!prop_type || (!prop_buff && prop_buff_size)) return ERROR_INVALID_USER_BUFFER; if (flags) return ERROR_INVALID_FLAGS; SETUPDI_GuidToString(&prop_key->fmtid, key_path + 11); swprintf(key_path + 49, ARRAY_SIZE(key_path) - 49, L"\\%04X", prop_key->pid); ls = RegOpenKeyExW(device->key, key_path, 0, KEY_QUERY_VALUE, &hkey); if (!ls) { value_size = prop_buff_size; ls = RegQueryValueExW(hkey, NULL, NULL, &value_type, prop_buff, &value_size); RegCloseKey(hkey); } switch (ls) { case NO_ERROR: case ERROR_MORE_DATA: *prop_type = 0xffff & value_type; ls = (ls == ERROR_MORE_DATA || !prop_buff) ? ERROR_INSUFFICIENT_BUFFER : NO_ERROR; break; case ERROR_FILE_NOT_FOUND: *prop_type = DEVPROP_TYPE_EMPTY; value_size = 0; ls = ERROR_NOT_FOUND; break; default: *prop_type = DEVPROP_TYPE_EMPTY; value_size = 0; FIXME("Unhandled error %#lx\n", ls); break; } if (required_size) *required_size = value_size; return ls; } /*********************************************************************** * SetupDiGetDevicePropertyW (SETUPAPI.@) */ BOOL WINAPI SetupDiGetDevicePropertyW(HDEVINFO devinfo, PSP_DEVINFO_DATA device_data, const DEVPROPKEY *prop_key, DEVPROPTYPE *prop_type, BYTE *prop_buff, DWORD prop_buff_size, DWORD *required_size, DWORD flags) { struct device *device; LSTATUS ls; TRACE("%p, %p, %p, %p, %p, %ld, %p, %#lx\n", devinfo, device_data, prop_key, prop_type, prop_buff, prop_buff_size, required_size, flags); if (!(device = get_device(devinfo, device_data))) return FALSE; ls = get_device_property(device, prop_key, prop_type, prop_buff, prop_buff_size, required_size, flags); SetLastError(ls); return !ls; } /*********************************************************************** * CM_Get_DevNode_Property_ExW (SETUPAPI.@) */ CONFIGRET WINAPI CM_Get_DevNode_Property_ExW(DEVINST devnode, const DEVPROPKEY *prop_key, DEVPROPTYPE *prop_type, BYTE *prop_buff, ULONG *prop_buff_size, ULONG flags, HMACHINE machine) { struct device *device = get_devnode_device(devnode); LSTATUS ls; TRACE("%lu, %p, %p, %p, %p, %#lx, %p\n", devnode, prop_key, prop_type, prop_buff, prop_buff_size, flags, machine); if (machine) return CR_MACHINE_UNAVAILABLE; if (!device) return CR_NO_SUCH_DEVINST; if (!prop_buff_size) return CR_INVALID_POINTER; ls = get_device_property(device, prop_key, prop_type, prop_buff, *prop_buff_size, prop_buff_size, flags); switch (ls) { case NO_ERROR: return CR_SUCCESS; case ERROR_INVALID_DATA: return CR_INVALID_DATA; case ERROR_INVALID_USER_BUFFER: return CR_INVALID_POINTER; case ERROR_INVALID_FLAGS: return CR_INVALID_FLAG; case ERROR_INSUFFICIENT_BUFFER: return CR_BUFFER_SMALL; case ERROR_NOT_FOUND: return CR_NO_SUCH_VALUE; } return CR_FAILURE; } /*********************************************************************** * CM_Get_DevNode_PropertyW (SETUPAPI.@) */ CONFIGRET WINAPI CM_Get_DevNode_PropertyW(DEVINST dev, const DEVPROPKEY *key, DEVPROPTYPE *type, PVOID buf, PULONG len, ULONG flags) { return CM_Get_DevNode_Property_ExW(dev, key, type, buf, len, flags, NULL); } /*********************************************************************** * SetupDiInstallDeviceInterfaces (SETUPAPI.@) */ BOOL WINAPI SetupDiInstallDeviceInterfaces(HDEVINFO devinfo, SP_DEVINFO_DATA *device_data) { WCHAR section_ext[LINE_LEN], iface_section[LINE_LEN], refstr[LINE_LEN], guidstr[39]; UINT install_flags = SPINST_ALL; struct device_iface *iface; struct device *device; struct driver *driver; void *callback_ctx; GUID iface_guid; INFCONTEXT ctx; HKEY iface_key; HINF hinf; LONG l; TRACE("devinfo %p, device_data %p.\n", devinfo, device_data); if (!(device = get_device(devinfo, device_data))) return FALSE; if (!(driver = device->selected_driver)) { ERR("No driver selected for device %p.\n", devinfo); SetLastError(ERROR_NO_DRIVER_SELECTED); return FALSE; } if ((hinf = SetupOpenInfFileW(driver->inf_path, NULL, INF_STYLE_WIN4, NULL)) == INVALID_HANDLE_VALUE) return FALSE; SetupDiGetActualSectionToInstallW(hinf, driver->section, section_ext, ARRAY_SIZE(section_ext), NULL, NULL); if (device->params.Flags & DI_NOFILECOPY) install_flags &= ~SPINST_FILES; callback_ctx = SetupInitDefaultQueueCallback(NULL); lstrcatW(section_ext, dotInterfaces); if (SetupFindFirstLineW(hinf, section_ext, AddInterface, &ctx)) { do { SetupGetStringFieldW(&ctx, 1, guidstr, ARRAY_SIZE(guidstr), NULL); SetupGetStringFieldW(&ctx, 2, refstr, ARRAY_SIZE(refstr), NULL); guidstr[37] = 0; UuidFromStringW(&guidstr[1], &iface_guid); if (!(iface = SETUPDI_CreateDeviceInterface(device, &iface_guid, refstr))) { ERR("Failed to create device interface, error %#lx.\n", GetLastError()); continue; } if ((l = create_iface_key(iface, KEY_ALL_ACCESS, &iface_key))) { ERR("Failed to create interface key, error %lu.\n", l); continue; } SetupGetStringFieldW(&ctx, 3, iface_section, ARRAY_SIZE(iface_section), NULL); SetupInstallFromInfSectionW(NULL, hinf, iface_section, install_flags, iface_key, NULL, SP_COPY_NEWER_ONLY, SetupDefaultQueueCallbackW, callback_ctx, NULL, NULL); RegCloseKey(iface_key); } while (SetupFindNextMatchLineW(&ctx, AddInterface, &ctx)); } SetupTermDefaultQueueCallback(callback_ctx); SetupCloseInfFile(hinf); return TRUE; } /*********************************************************************** * SetupDiRegisterCoDeviceInstallers (SETUPAPI.@) */ BOOL WINAPI SetupDiRegisterCoDeviceInstallers(HDEVINFO devinfo, SP_DEVINFO_DATA *device_data) { static const WCHAR coinstallersW[] = {'.','C','o','I','n','s','t','a','l','l','e','r','s',0}; WCHAR coinst_key_ext[LINE_LEN]; struct device *device; struct driver *driver; void *callback_ctx; HKEY driver_key; HINF hinf; LONG l; TRACE("devinfo %p, device_data %p.\n", devinfo, device_data); if (!(device = get_device(devinfo, device_data))) return FALSE; if (!(driver = device->selected_driver)) { ERR("No driver selected for device %p.\n", devinfo); SetLastError(ERROR_NO_DRIVER_SELECTED); return FALSE; } if ((hinf = SetupOpenInfFileW(driver->inf_path, NULL, INF_STYLE_WIN4, NULL)) == INVALID_HANDLE_VALUE) return FALSE; SetupDiGetActualSectionToInstallW(hinf, driver->section, coinst_key_ext, ARRAY_SIZE(coinst_key_ext), NULL, NULL); lstrcatW(coinst_key_ext, coinstallersW); if ((l = create_driver_key(device, &driver_key))) { SetLastError(l); SetupCloseInfFile(hinf); return FALSE; } callback_ctx = SetupInitDefaultQueueCallback(NULL); SetupInstallFromInfSectionW(NULL, hinf, coinst_key_ext, SPINST_ALL, driver_key, NULL, SP_COPY_NEWER_ONLY, SetupDefaultQueueCallbackW, callback_ctx, NULL, NULL); SetupTermDefaultQueueCallback(callback_ctx); RegCloseKey(driver_key); SetupCloseInfFile(hinf); return TRUE; } /* Check whether the given hardware or compatible ID matches any of the device's * own hardware or compatible IDs. */ static BOOL device_matches_id(const struct device *device, const WCHAR *id_type, const WCHAR *id, DWORD *driver_rank) { WCHAR *device_ids; const WCHAR *p; DWORD i, size; if (!RegGetValueW(device->key, NULL, id_type, RRF_RT_REG_MULTI_SZ, NULL, NULL, &size)) { device_ids = heap_alloc(size); if (!RegGetValueW(device->key, NULL, id_type, RRF_RT_REG_MULTI_SZ, NULL, device_ids, &size)) { for (p = device_ids, i = 0; *p; p += lstrlenW(p) + 1, i++) { if (!wcsicmp(p, id)) { *driver_rank += min(i, 0xff); heap_free(device_ids); return TRUE; } } } heap_free(device_ids); } return FALSE; } static BOOL version_is_compatible(const WCHAR *version) { const WCHAR *machine_ext = NtPlatformExtension + 1, *p; size_t len = lstrlenW(version); BOOL wow64; /* We are only concerned with architecture. */ if ((p = wcschr(version, '.'))) len = p - version; if (!wcsnicmp(version, NtExtension + 1, len)) return TRUE; if (IsWow64Process(GetCurrentProcess(), &wow64) && wow64) { #ifdef __i386__ static const WCHAR wow_ext[] = {'N','T','a','m','d','6','4',0}; machine_ext = wow_ext; #elif defined(__arm__) static const WCHAR wow_ext[] = {'N','T','a','r','m','6','4',0}; machine_ext = wow_ext; #endif } return !wcsnicmp(version, machine_ext, len); } static void enum_compat_drivers_from_file(struct device *device, const WCHAR *path) { static const WCHAR manufacturerW[] = {'M','a','n','u','f','a','c','t','u','r','e','r',0}; WCHAR mfg_key[LINE_LEN], id[MAX_DEVICE_ID_LEN], version[MAX_DEVICE_ID_LEN]; DWORD i, j, k, driver_count = device->driver_count; struct driver driver, *drivers = device->drivers; INFCONTEXT ctx; BOOL found; HINF hinf; TRACE("Enumerating drivers from %s.\n", debugstr_w(path)); if ((hinf = SetupOpenInfFileW(path, NULL, INF_STYLE_WIN4, NULL)) == INVALID_HANDLE_VALUE) return; lstrcpyW(driver.inf_path, path); for (i = 0; SetupGetLineByIndexW(hinf, manufacturerW, i, &ctx); ++i) { SetupGetStringFieldW(&ctx, 0, driver.manufacturer, ARRAY_SIZE(driver.manufacturer), NULL); if (!SetupGetStringFieldW(&ctx, 1, mfg_key, ARRAY_SIZE(mfg_key), NULL)) lstrcpyW(mfg_key, driver.manufacturer); if (SetupGetFieldCount(&ctx) >= 2) { BOOL compatible = FALSE; for (j = 2; SetupGetStringFieldW(&ctx, j, version, ARRAY_SIZE(version), NULL); ++j) { if (version_is_compatible(version)) { compatible = TRUE; break; } } if (!compatible) continue; } if (!SetupDiGetActualSectionToInstallW(hinf, mfg_key, driver.mfg_key, ARRAY_SIZE(driver.mfg_key), NULL, NULL)) { WARN("Failed to find section for %s, skipping.\n", debugstr_w(mfg_key)); continue; } for (j = 0; SetupGetLineByIndexW(hinf, driver.mfg_key, j, &ctx); ++j) { driver.rank = 0; for (k = 2, found = FALSE; SetupGetStringFieldW(&ctx, k, id, ARRAY_SIZE(id), NULL); ++k) { if ((found = device_matches_id(device, HardwareId, id, &driver.rank))) break; driver.rank += 0x2000; if ((found = device_matches_id(device, CompatibleIDs, id, &driver.rank))) break; driver.rank = 0x1000 + min(0x0100 * (k - 2), 0xf00); } if (found) { SetupGetStringFieldW(&ctx, 0, driver.description, ARRAY_SIZE(driver.description), NULL); SetupGetStringFieldW(&ctx, 1, driver.section, ARRAY_SIZE(driver.section), NULL); TRACE("Found compatible driver: rank %#lx manufacturer %s, desc %s.\n", driver.rank, debugstr_w(driver.manufacturer), debugstr_w(driver.description)); driver_count++; drivers = heap_realloc(drivers, driver_count * sizeof(*drivers)); drivers[driver_count - 1] = driver; } } } SetupCloseInfFile(hinf); device->drivers = drivers; device->driver_count = driver_count; } /*********************************************************************** * SetupDiBuildDriverInfoList (SETUPAPI.@) */ BOOL WINAPI SetupDiBuildDriverInfoList(HDEVINFO devinfo, SP_DEVINFO_DATA *device_data, DWORD type) { struct device *device; TRACE("devinfo %p, device_data %p, type %#lx.\n", devinfo, device_data, type); if (type != SPDIT_COMPATDRIVER) { FIXME("Unhandled type %#lx.\n", type); SetLastError(ERROR_CALL_NOT_IMPLEMENTED); return FALSE; } if (!(device = get_device(devinfo, device_data))) return FALSE; if (device->params.Flags & DI_ENUMSINGLEINF) { enum_compat_drivers_from_file(device, device->params.DriverPath); } else { static const WCHAR default_path[] = {'C',':','/','w','i','n','d','o','w','s','/','i','n','f',0}; static const WCHAR wildcardW[] = {'*',0}; WCHAR dir[MAX_PATH], file[MAX_PATH]; WIN32_FIND_DATAW find_data; HANDLE find_handle; if (device->params.DriverPath[0]) lstrcpyW(dir, device->params.DriverPath); else lstrcpyW(dir, default_path); lstrcatW(dir, backslashW); lstrcatW(dir, wildcardW); TRACE("Searching for drivers in %s.\n", debugstr_w(dir)); if ((find_handle = FindFirstFileW(dir, &find_data)) != INVALID_HANDLE_VALUE) { do { lstrcpyW(file, dir); lstrcpyW(file + lstrlenW(file) - 1, find_data.cFileName); enum_compat_drivers_from_file(device, file); } while (FindNextFileW(find_handle, &find_data)); FindClose(find_handle); } } if (device->driver_count) { WCHAR classname[MAX_CLASS_NAME_LEN], guidstr[39]; GUID class; if (SetupDiGetINFClassW(device->drivers[0].inf_path, &class, classname, ARRAY_SIZE(classname), NULL)) { device_data->ClassGuid = device->class = class; SETUPDI_GuidToString(&class, guidstr); RegSetValueExW(device->key, L"ClassGUID", 0, REG_SZ, (BYTE *)guidstr, sizeof(guidstr)); RegSetValueExW(device->key, L"Class", 0, REG_SZ, (BYTE *)classname, wcslen(classname) * sizeof(WCHAR)); } } return TRUE; } static BOOL copy_driver_data(SP_DRVINFO_DATA_W *data, const struct driver *driver) { INFCONTEXT ctx; HINF hinf; if ((hinf = SetupOpenInfFileW(driver->inf_path, NULL, INF_STYLE_WIN4, NULL)) == INVALID_HANDLE_VALUE) return FALSE; data->ProviderName[0] = 0; if (SetupFindFirstLineW(hinf, L"Version", L"Provider", &ctx)) SetupGetStringFieldW(&ctx, 1, data->ProviderName, ARRAY_SIZE(data->ProviderName), NULL); wcscpy(data->Description, driver->description); wcscpy(data->MfgName, driver->manufacturer); data->DriverType = SPDIT_COMPATDRIVER; data->Reserved = (ULONG_PTR)driver; SetupCloseInfFile(hinf); return TRUE; } static void driver_data_wtoa(SP_DRVINFO_DATA_A *a, const SP_DRVINFO_DATA_W *w) { a->DriverType = w->DriverType; a->Reserved = w->Reserved; WideCharToMultiByte(CP_ACP, 0, w->Description, -1, a->Description, sizeof(a->Description), NULL, NULL); WideCharToMultiByte(CP_ACP, 0, w->MfgName, -1, a->MfgName, sizeof(a->MfgName), NULL, NULL); WideCharToMultiByte(CP_ACP, 0, w->ProviderName, -1, a->ProviderName, sizeof(a->ProviderName), NULL, NULL); } /*********************************************************************** * SetupDiEnumDriverInfoW (SETUPAPI.@) */ BOOL WINAPI SetupDiEnumDriverInfoW(HDEVINFO devinfo, SP_DEVINFO_DATA *device_data, DWORD type, DWORD index, SP_DRVINFO_DATA_W *driver_data) { struct device *device; TRACE("devinfo %p, device_data %p, type %#lx, index %lu, driver_data %p.\n", devinfo, device_data, type, index, driver_data); if (type != SPDIT_COMPATDRIVER) { FIXME("Unhandled type %#lx.\n", type); SetLastError(ERROR_CALL_NOT_IMPLEMENTED); return FALSE; } if (!(device = get_device(devinfo, device_data))) return FALSE; if (index >= device->driver_count) { SetLastError(ERROR_NO_MORE_ITEMS); return FALSE; } return copy_driver_data(driver_data, &device->drivers[index]); } /*********************************************************************** * SetupDiEnumDriverInfoA (SETUPAPI.@) */ BOOL WINAPI SetupDiEnumDriverInfoA(HDEVINFO devinfo, SP_DEVINFO_DATA *device_data, DWORD type, DWORD index, SP_DRVINFO_DATA_A *driver_data) { SP_DRVINFO_DATA_W driver_dataW; BOOL ret; driver_dataW.cbSize = sizeof(driver_dataW); ret = SetupDiEnumDriverInfoW(devinfo, device_data, type, index, &driver_dataW); if (ret) driver_data_wtoa(driver_data, &driver_dataW); return ret; } /*********************************************************************** * SetupDiSelectBestCompatDrv (SETUPAPI.@) */ BOOL WINAPI SetupDiSelectBestCompatDrv(HDEVINFO devinfo, SP_DEVINFO_DATA *device_data) { struct device *device; struct driver *best; DWORD i; TRACE("devinfo %p, device_data %p.\n", devinfo, device_data); if (!(device = get_device(devinfo, device_data))) return FALSE; if (!device->driver_count) { WARN("No compatible drivers were enumerated for device %s.\n", debugstr_w(device->instanceId)); SetLastError(ERROR_NO_COMPAT_DRIVERS); return FALSE; } best = device->drivers; for (i = 1; i < device->driver_count; ++i) { if (device->drivers[i].rank >= best->rank) continue; best = device->drivers + i; } TRACE("selected driver: rank %#lx manufacturer %s, desc %s.\n", best->rank, debugstr_w(best->manufacturer), debugstr_w(best->description)); device->selected_driver = best; return TRUE; } /*********************************************************************** * SetupDiGetSelectedDriverW (SETUPAPI.@) */ BOOL WINAPI SetupDiGetSelectedDriverW(HDEVINFO devinfo, SP_DEVINFO_DATA *device_data, SP_DRVINFO_DATA_W *driver_data) { struct device *device; TRACE("devinfo %p, device_data %p, driver_data %p.\n", devinfo, device_data, driver_data); if (!(device = get_device(devinfo, device_data))) return FALSE; if (!device->selected_driver) { SetLastError(ERROR_NO_DRIVER_SELECTED); return FALSE; } return copy_driver_data(driver_data, device->selected_driver); } /*********************************************************************** * SetupDiGetSelectedDriverA (SETUPAPI.@) */ BOOL WINAPI SetupDiGetSelectedDriverA(HDEVINFO devinfo, SP_DEVINFO_DATA *device_data, SP_DRVINFO_DATA_A *driver_data) { SP_DRVINFO_DATA_W driver_dataW; BOOL ret; driver_dataW.cbSize = sizeof(driver_dataW); if ((ret = SetupDiGetSelectedDriverW(devinfo, device_data, &driver_dataW))) driver_data_wtoa(driver_data, &driver_dataW); return ret; } /*********************************************************************** * SetupDiGetDriverInfoDetailW (SETUPAPI.@) */ BOOL WINAPI SetupDiGetDriverInfoDetailW(HDEVINFO devinfo, SP_DEVINFO_DATA *device_data, SP_DRVINFO_DATA_W *driver_data, SP_DRVINFO_DETAIL_DATA_W *detail_data, const DWORD size, DWORD *ret_size) { struct driver *driver = (struct driver *)driver_data->Reserved; DWORD size_needed, i, id_size = 1; WCHAR id[MAX_DEVICE_ID_LEN]; INFCONTEXT ctx; HANDLE file; HINF hinf; TRACE("devinfo %p, device_data %p, driver_data %p, detail_data %p, size %lu, ret_size %p.\n", devinfo, device_data, driver_data, detail_data, size, ret_size); if ((detail_data || size) && size < sizeof(SP_DRVINFO_DETAIL_DATA_W)) { SetLastError(ERROR_INVALID_USER_BUFFER); return FALSE; } if ((hinf = SetupOpenInfFileW(driver->inf_path, NULL, INF_STYLE_WIN4, NULL)) == INVALID_HANDLE_VALUE) return FALSE; SetupFindFirstLineW(hinf, driver->mfg_key, driver->description, &ctx); for (i = 2; SetupGetStringFieldW(&ctx, i, id, ARRAY_SIZE(id), NULL); ++i) id_size += wcslen(id) + 1; size_needed = FIELD_OFFSET(SP_DRVINFO_DETAIL_DATA_W, HardwareID[id_size]); if (ret_size) *ret_size = size_needed; if (!detail_data) return TRUE; detail_data->CompatIDsLength = detail_data->CompatIDsOffset = 0; detail_data->HardwareID[0] = 0; if (size >= size_needed) { id_size = 0; for (i = 2; SetupGetStringFieldW(&ctx, i, id, ARRAY_SIZE(id), NULL); ++i) { wcscpy(&detail_data->HardwareID[id_size], id); if (i == 3) detail_data->CompatIDsOffset = id_size; id_size += wcslen(id) + 1; } detail_data->HardwareID[id_size++] = 0; if (i > 3) detail_data->CompatIDsLength = id_size - detail_data->CompatIDsOffset; } SetupCloseInfFile(hinf); if ((file = CreateFileW(driver->inf_path, 0, 0, NULL, OPEN_EXISTING, 0, NULL)) == INVALID_HANDLE_VALUE) return FALSE; GetFileTime(file, NULL, NULL, &detail_data->InfDate); CloseHandle(file); wcscpy(detail_data->SectionName, driver->section); wcscpy(detail_data->InfFileName, driver->inf_path); wcscpy(detail_data->DrvDescription, driver->description); if (size < size_needed) { SetLastError(ERROR_INSUFFICIENT_BUFFER); return FALSE; } return TRUE; } /*********************************************************************** * SetupDiGetDriverInfoDetailA (SETUPAPI.@) */ BOOL WINAPI SetupDiGetDriverInfoDetailA(HDEVINFO devinfo, SP_DEVINFO_DATA *device_data, SP_DRVINFO_DATA_A *driver_data, SP_DRVINFO_DETAIL_DATA_A *detail_data, const DWORD size, DWORD *ret_size) { struct driver *driver = (struct driver *)driver_data->Reserved; DWORD size_needed, i, id_size = 1; char id[MAX_DEVICE_ID_LEN]; INFCONTEXT ctx; HANDLE file; HINF hinf; TRACE("devinfo %p, device_data %p, driver_data %p, detail_data %p, size %lu, ret_size %p.\n", devinfo, device_data, driver_data, detail_data, size, ret_size); if ((detail_data || size) && size < sizeof(SP_DRVINFO_DETAIL_DATA_A)) { SetLastError(ERROR_INVALID_USER_BUFFER); return FALSE; } if ((hinf = SetupOpenInfFileW(driver->inf_path, NULL, INF_STYLE_WIN4, NULL)) == INVALID_HANDLE_VALUE) return FALSE; SetupFindFirstLineW(hinf, driver->mfg_key, driver->description, &ctx); for (i = 2; SetupGetStringFieldA(&ctx, i, id, ARRAY_SIZE(id), NULL); ++i) id_size += strlen(id) + 1; size_needed = FIELD_OFFSET(SP_DRVINFO_DETAIL_DATA_A, HardwareID[id_size]); if (ret_size) *ret_size = size_needed; if (!detail_data) { SetupCloseInfFile(hinf); return TRUE; } detail_data->CompatIDsLength = detail_data->CompatIDsOffset = 0; detail_data->HardwareID[0] = 0; if (size >= size_needed) { id_size = 0; for (i = 2; SetupGetStringFieldA(&ctx, i, id, ARRAY_SIZE(id), NULL); ++i) { strcpy(&detail_data->HardwareID[id_size], id); if (i == 3) detail_data->CompatIDsOffset = id_size; id_size += strlen(id) + 1; } detail_data->HardwareID[id_size++] = 0; if (i > 3) detail_data->CompatIDsLength = id_size - detail_data->CompatIDsOffset; } SetupCloseInfFile(hinf); if ((file = CreateFileW(driver->inf_path, 0, 0, NULL, OPEN_EXISTING, 0, NULL)) == INVALID_HANDLE_VALUE) return FALSE; GetFileTime(file, NULL, NULL, &detail_data->InfDate); CloseHandle(file); WideCharToMultiByte(CP_ACP, 0, driver->section, -1, detail_data->SectionName, sizeof(detail_data->SectionName), NULL, NULL); WideCharToMultiByte(CP_ACP, 0, driver->inf_path, -1, detail_data->InfFileName, sizeof(detail_data->InfFileName), NULL, NULL); WideCharToMultiByte(CP_ACP, 0, driver->description, -1, detail_data->DrvDescription, sizeof(detail_data->InfFileName), NULL, NULL); if (size < size_needed) { SetLastError(ERROR_INSUFFICIENT_BUFFER); return FALSE; } return TRUE; } /*********************************************************************** * SetupDiInstallDriverFiles (SETUPAPI.@) */ BOOL WINAPI SetupDiInstallDriverFiles(HDEVINFO devinfo, SP_DEVINFO_DATA *device_data) { WCHAR section[LINE_LEN], section_ext[LINE_LEN], iface_section[LINE_LEN]; struct device *device; struct driver *driver; void *callback_ctx; INFCONTEXT ctx; HINF hinf; TRACE("devinfo %p, device_data %p.\n", devinfo, device_data); if (!(device = get_device(devinfo, device_data))) return FALSE; if (!(driver = device->selected_driver)) { ERR("No driver selected for device %p.\n", devinfo); SetLastError(ERROR_NO_DRIVER_SELECTED); return FALSE; } if ((hinf = SetupOpenInfFileW(driver->inf_path, NULL, INF_STYLE_WIN4, NULL)) == INVALID_HANDLE_VALUE) return FALSE; SetupFindFirstLineW(hinf, driver->mfg_key, driver->description, &ctx); SetupGetStringFieldW(&ctx, 1, section, ARRAY_SIZE(section), NULL); SetupDiGetActualSectionToInstallW(hinf, section, section_ext, ARRAY_SIZE(section_ext), NULL, NULL); callback_ctx = SetupInitDefaultQueueCallback(NULL); SetupInstallFromInfSectionW(NULL, hinf, section_ext, SPINST_FILES, NULL, NULL, SP_COPY_NEWER_ONLY, SetupDefaultQueueCallbackW, callback_ctx, NULL, NULL); lstrcatW(section_ext, dotInterfaces); if (SetupFindFirstLineW(hinf, section_ext, AddInterface, &ctx)) { do { SetupGetStringFieldW(&ctx, 3, iface_section, ARRAY_SIZE(iface_section), NULL); SetupInstallFromInfSectionW(NULL, hinf, iface_section, SPINST_FILES, NULL, NULL, SP_COPY_NEWER_ONLY, SetupDefaultQueueCallbackW, callback_ctx, NULL, NULL); } while (SetupFindNextMatchLineW(&ctx, AddInterface, &ctx)); } SetupTermDefaultQueueCallback(callback_ctx); SetupCloseInfFile(hinf); return TRUE; } /*********************************************************************** * SetupDiInstallDevice (SETUPAPI.@) */ BOOL WINAPI SetupDiInstallDevice(HDEVINFO devinfo, SP_DEVINFO_DATA *device_data) { static const WCHAR infpathW[] = {'I','n','f','P','a','t','h',0}; static const WCHAR infsectionW[] = {'I','n','f','S','e','c','t','i','o','n',0}; static const WCHAR infsectionextW[] = {'I','n','f','S','e','c','t','i','o','n','E','x','t',0}; static const WCHAR dothwW[] = {'.','H','W',0}; static const WCHAR dotservicesW[] = {'.','S','e','r','v','i','c','e','s',0}; static const WCHAR addserviceW[] = {'A','d','d','S','e','r','v','i','c','e',0}; static const WCHAR rootW[] = {'r','o','o','t','\\',0}; WCHAR section_ext[LINE_LEN], subsection[LINE_LEN], inf_path[MAX_PATH], *extptr, *filepart; UINT install_flags = SPINST_ALL; HKEY driver_key, device_key; SC_HANDLE manager, service; WCHAR svc_name[LINE_LEN]; struct device *device; struct driver *driver; void *callback_ctx; INFCONTEXT ctx; HINF hinf; LONG l; TRACE("devinfo %p, device_data %p.\n", devinfo, device_data); if (!(device = get_device(devinfo, device_data))) return FALSE; if (!(driver = device->selected_driver)) { ERR("No driver selected for device %p.\n", devinfo); SetLastError(ERROR_NO_DRIVER_SELECTED); return FALSE; } if ((hinf = SetupOpenInfFileW(driver->inf_path, NULL, INF_STYLE_WIN4, NULL)) == INVALID_HANDLE_VALUE) return FALSE; RegSetValueExW(device->key, L"DeviceDesc", 0, REG_SZ, (BYTE *)driver->description, wcslen(driver->description) * sizeof(WCHAR)); SetupDiGetActualSectionToInstallW(hinf, driver->section, section_ext, ARRAY_SIZE(section_ext), NULL, &extptr); if ((l = create_driver_key(device, &driver_key))) { SetLastError(l); SetupCloseInfFile(hinf); return FALSE; } if ((l = RegCreateKeyExW(device->key, DeviceParameters, 0, NULL, 0, KEY_READ | KEY_WRITE, NULL, &device_key, NULL))) { SetLastError(l); RegCloseKey(driver_key); SetupCloseInfFile(hinf); return FALSE; } if (device->params.Flags & DI_NOFILECOPY) install_flags &= ~SPINST_FILES; callback_ctx = SetupInitDefaultQueueCallback(NULL); SetupInstallFromInfSectionW(NULL, hinf, section_ext, install_flags, driver_key, NULL, SP_COPY_NEWER_ONLY, SetupDefaultQueueCallbackW, callback_ctx, NULL, NULL); lstrcpyW(subsection, section_ext); lstrcatW(subsection, dothwW); SetupInstallFromInfSectionW(NULL, hinf, subsection, install_flags, device_key, NULL, SP_COPY_NEWER_ONLY, SetupDefaultQueueCallbackW, callback_ctx, NULL, NULL); lstrcpyW(subsection, section_ext); lstrcatW(subsection, dotservicesW); SetupInstallServicesFromInfSectionW(hinf, subsection, 0); svc_name[0] = 0; if (SetupFindFirstLineW(hinf, subsection, addserviceW, &ctx)) { do { INT flags; if (SetupGetIntField(&ctx, 2, &flags) && (flags & SPSVCINST_ASSOCSERVICE)) { if (SetupGetStringFieldW(&ctx, 1, svc_name, ARRAY_SIZE(svc_name), NULL) && svc_name[0]) RegSetValueExW(device->key, Service, 0, REG_SZ, (BYTE *)svc_name, lstrlenW(svc_name) * sizeof(WCHAR)); break; } } while (SetupFindNextMatchLineW(&ctx, addserviceW, &ctx)); } SetupTermDefaultQueueCallback(callback_ctx); SetupCloseInfFile(hinf); SetupCopyOEMInfW(driver->inf_path, NULL, SPOST_NONE, 0, inf_path, ARRAY_SIZE(inf_path), NULL, &filepart); TRACE("Copied INF file %s to %s.\n", debugstr_w(driver->inf_path), debugstr_w(inf_path)); RegSetValueExW(driver_key, infpathW, 0, REG_SZ, (BYTE *)filepart, lstrlenW(filepart) * sizeof(WCHAR)); RegSetValueExW(driver_key, infsectionW, 0, REG_SZ, (BYTE *)driver->section, lstrlenW(driver->section) * sizeof(WCHAR)); if (extptr) RegSetValueExW(driver_key, infsectionextW, 0, REG_SZ, (BYTE *)extptr, lstrlenW(extptr) * sizeof(WCHAR)); RegCloseKey(device_key); RegCloseKey(driver_key); if (!wcsnicmp(device->instanceId, rootW, lstrlenW(rootW)) && svc_name[0] && (manager = OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT))) { if ((service = OpenServiceW(manager, svc_name, SERVICE_START | SERVICE_USER_DEFINED_CONTROL))) { SERVICE_STATUS status; if (!StartServiceW(service, 0, NULL) && GetLastError() != ERROR_SERVICE_ALREADY_RUNNING) { ERR("Failed to start service %s for device %s, error %lu.\n", debugstr_w(svc_name), debugstr_w(device->instanceId), GetLastError()); } if (!ControlService(service, SERVICE_CONTROL_REENUMERATE_ROOT_DEVICES, &status)) { ERR("Failed to control service %s for device %s, error %lu.\n", debugstr_w(svc_name), debugstr_w(device->instanceId), GetLastError()); } CloseServiceHandle(service); } else ERR("Failed to open service %s for device %s.\n", debugstr_w(svc_name), debugstr_w(device->instanceId)); CloseServiceHandle(manager); } return TRUE; }