wine/dlls/setupapi/devinst.c
Rémi Bernon cfddd2c1e2 setupapi: Rank drivers according to their id match.
And as described on MSDN "Driver Rank Example".

In order to safely remove the hardcoded XBox gamepad ID list from
winebus.sys, and list their hardware IDs in the (future) xinput.sys
driver INF.

Signed-off-by: Rémi Bernon <rbernon@codeweavers.com>
Signed-off-by: Zebediah Figura <zfigura@codeweavers.com>
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
2021-08-18 20:07:08 +02:00

5278 lines
160 KiB
C

/*
* 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 <stdarg.h>
#include <stdlib.h>
#include "windef.h"
#include "winbase.h"
#include "winnt.h"
#include "winreg.h"
#include "winternl.h"
#include "wingdi.h"
#include "winuser.h"
#include "winnls.h"
#include "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 %u 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);
}
}
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 %u.\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 %u.\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 %u.\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 %u.\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 %u.\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 %u.\n", ret);
break;
}
if ((ret = RegOpenKeyExW(classes_key, class_name, 0, KEY_READ, &class_key)))
{
ERR("Failed to open class %s, error %u.\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 %u.\n", ret);
break;
}
if ((ret = RegOpenKeyExW(class_key, iface_name, 0, KEY_ALL_ACCESS, &iface_key)))
{
ERR("Failed to open interface %s, error %u.\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 %u.\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 %u.\n", debugstr_w(iface_name), ret);
if ((ret = RegDeleteKeyW(iface_key, L"")))
ERR("Failed to delete interface %s, error %u.\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 %d\n", lError);
if (lError == ERROR_SUCCESS || lError == ERROR_MORE_DATA)
{
TRACE("Key name: %p\n", szKeyName);
if (RegOpenKeyExW(hClassesKey,
szKeyName,
0,
KEY_ALL_ACCESS,
&hClassKey))
{
RegCloseKey(hClassesKey);
return FALSE;
}
if (!RegQueryValueExW(hClassKey,
NoUseClass,
NULL,
NULL,
NULL,
NULL))
{
TRACE("'NoUseClass' value found!\n");
RegCloseKey(hClassKey);
continue;
}
if ((Flags & DIBCI_NOINSTALLCLASS) &&
(!RegQueryValueExW(hClassKey,
NoInstallClass,
NULL,
NULL,
NULL,
NULL)))
{
TRACE("'NoInstallClass' value found!\n");
RegCloseKey(hClassKey);
continue;
}
if ((Flags & DIBCI_NODISPLAYCLASS) &&
(!RegQueryValueExW(hClassKey,
NoDisplayClass,
NULL,
NULL,
NULL,
NULL)))
{
TRACE("'NoDisplayClass' value found!\n");
RegCloseKey(hClassKey);
continue;
}
RegCloseKey(hClassKey);
TRACE("Guid: %p\n", szKeyName);
if (dwGuidListIndex < ClassGuidListSize)
{
if (szKeyName[0] == '{' && szKeyName[37] == '}')
{
szKeyName[37] = 0;
}
TRACE("Guid: %p\n", &szKeyName[1]);
UuidFromStringW(&szKeyName[1],
&ClassGuidList[dwGuidListIndex]);
}
dwGuidListIndex++;
}
if (lError != ERROR_SUCCESS)
break;
}
RegCloseKey(hClassesKey);
if (RequiredSize != NULL)
*RequiredSize = dwGuidListIndex;
if (ClassGuidListSize < dwGuidListIndex)
{
SetLastError(ERROR_INSUFFICIENT_BUFFER);
return FALSE;
}
return TRUE;
}
/***********************************************************************
* SetupDiClassGuidsFromNameA (SETUPAPI.@)
*/
BOOL WINAPI SetupDiClassGuidsFromNameA(
LPCSTR ClassName,
LPGUID ClassGuidList,
DWORD ClassGuidListSize,
PDWORD RequiredSize)
{
return SetupDiClassGuidsFromNameExA(ClassName, ClassGuidList,
ClassGuidListSize, RequiredSize,
NULL, NULL);
}
/***********************************************************************
* SetupDiClassGuidsFromNameW (SETUPAPI.@)
*/
BOOL WINAPI SetupDiClassGuidsFromNameW(
LPCWSTR ClassName,
LPGUID ClassGuidList,
DWORD ClassGuidListSize,
PDWORD RequiredSize)
{
return SetupDiClassGuidsFromNameExW(ClassName, ClassGuidList,
ClassGuidListSize, RequiredSize,
NULL, NULL);
}
/***********************************************************************
* SetupDiClassGuidsFromNameExA (SETUPAPI.@)
*/
BOOL WINAPI SetupDiClassGuidsFromNameExA(
LPCSTR ClassName,
LPGUID ClassGuidList,
DWORD ClassGuidListSize,
PDWORD RequiredSize,
LPCSTR MachineName,
PVOID Reserved)
{
LPWSTR ClassNameW = NULL;
LPWSTR MachineNameW = NULL;
BOOL bResult;
ClassNameW = MultiByteToUnicode(ClassName, CP_ACP);
if (ClassNameW == NULL)
return FALSE;
if (MachineName)
{
MachineNameW = MultiByteToUnicode(MachineName, CP_ACP);
if (MachineNameW == NULL)
{
MyFree(ClassNameW);
return FALSE;
}
}
bResult = SetupDiClassGuidsFromNameExW(ClassNameW, ClassGuidList,
ClassGuidListSize, RequiredSize,
MachineNameW, Reserved);
MyFree(MachineNameW);
MyFree(ClassNameW);
return bResult;
}
/***********************************************************************
* SetupDiClassGuidsFromNameExW (SETUPAPI.@)
*/
BOOL WINAPI SetupDiClassGuidsFromNameExW(
LPCWSTR ClassName,
LPGUID ClassGuidList,
DWORD ClassGuidListSize,
PDWORD RequiredSize,
LPCWSTR MachineName,
PVOID Reserved)
{
WCHAR szKeyName[40];
WCHAR szClassName[256];
HKEY hClassesKey;
HKEY hClassKey;
DWORD dwLength;
DWORD dwIndex;
LONG lError;
DWORD dwGuidListIndex = 0;
if (RequiredSize != NULL)
*RequiredSize = 0;
hClassesKey = SetupDiOpenClassRegKeyExW(NULL,
KEY_ALL_ACCESS,
DIOCR_INSTALLER,
MachineName,
Reserved);
if (hClassesKey == INVALID_HANDLE_VALUE)
{
return FALSE;
}
for (dwIndex = 0; ; dwIndex++)
{
dwLength = ARRAY_SIZE(szKeyName);
lError = RegEnumKeyExW(hClassesKey,
dwIndex,
szKeyName,
&dwLength,
NULL,
NULL,
NULL,
NULL);
TRACE("RegEnumKeyExW() returns %d\n", lError);
if (lError == ERROR_SUCCESS || lError == ERROR_MORE_DATA)
{
TRACE("Key name: %p\n", szKeyName);
if (RegOpenKeyExW(hClassesKey,
szKeyName,
0,
KEY_ALL_ACCESS,
&hClassKey))
{
RegCloseKey(hClassesKey);
return FALSE;
}
dwLength = 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 %d %d %d %p %s\n", DeviceInfoSet, DeviceInfoData, Scope,
HwProfile, KeyType, InfHandle, debugstr_a(InfSectionName));
if (InfHandle)
{
if (!InfSectionName)
{
SetLastError(ERROR_INVALID_PARAMETER);
return INVALID_HANDLE_VALUE;
}
else
{
InfSectionNameW = MultiByteToUnicode(InfSectionName, CP_ACP);
if (InfSectionNameW == NULL) return INVALID_HANDLE_VALUE;
}
}
key = SetupDiCreateDevRegKeyW(DeviceInfoSet, DeviceInfoData, Scope,
HwProfile, KeyType, InfHandle, InfSectionNameW);
MyFree(InfSectionNameW);
return key;
}
/***********************************************************************
* SetupDiCreateDevRegKeyW (SETUPAPI.@)
*/
HKEY WINAPI SetupDiCreateDevRegKeyW(HDEVINFO 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 %d, profile %d, type %d, 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 %d\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 %#x.\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 %#x, 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 %#x, 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 %u.\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 %d, 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 %d, 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 %d, 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 %d, 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 : &section_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 %u)\n", GetLastError());
return FALSE;
}
dwLength = ClassDescriptionSize;
ret = !RegQueryValueExA( hKey, NULL, NULL, NULL,
(LPBYTE)ClassDescription, &dwLength );
if (RequiredSize) *RequiredSize = dwLength;
RegCloseKey(hKey);
return ret;
}
/***********************************************************************
* SetupDiGetClassDescriptionExW (SETUPAPI.@)
*/
BOOL WINAPI SetupDiGetClassDescriptionExW(
const GUID* ClassGuid,
PWSTR ClassDescription,
DWORD ClassDescriptionSize,
PDWORD RequiredSize,
PCWSTR MachineName,
PVOID Reserved)
{
HKEY hKey;
DWORD dwLength;
BOOL ret;
hKey = SetupDiOpenClassRegKeyExW(ClassGuid,
KEY_ALL_ACCESS,
DIOCR_INSTALLER,
MachineName,
Reserved);
if (hKey == INVALID_HANDLE_VALUE)
{
WARN("SetupDiOpenClassRegKeyExW() failed (Error %u)\n", GetLastError());
return FALSE;
}
dwLength = ClassDescriptionSize * sizeof(WCHAR);
ret = !RegQueryValueExW( hKey, NULL, NULL, NULL,
(LPBYTE)ClassDescription, &dwLength );
if (RequiredSize) *RequiredSize = dwLength / sizeof(WCHAR);
RegCloseKey(hKey);
return ret;
}
/***********************************************************************
* SetupDiGetClassDevsA (SETUPAPI.@)
*/
HDEVINFO WINAPI SetupDiGetClassDevsA(const GUID *class, LPCSTR enumstr, HWND parent, DWORD flags)
{
HDEVINFO ret;
LPWSTR enumstrW = NULL;
if (enumstr)
{
int len = MultiByteToWideChar(CP_ACP, 0, enumstr, -1, NULL, 0);
enumstrW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
if (!enumstrW)
{
ret = INVALID_HANDLE_VALUE;
goto end;
}
MultiByteToWideChar(CP_ACP, 0, enumstr, -1, enumstrW, len);
}
ret = SetupDiGetClassDevsExW(class, enumstrW, parent, flags, NULL, NULL,
NULL);
HeapFree(GetProcessHeap(), 0, enumstrW);
end:
return ret;
}
/***********************************************************************
* SetupDiGetClassDevsExA (SETUPAPI.@)
*/
HDEVINFO WINAPI SetupDiGetClassDevsExA(
const GUID *class,
PCSTR enumstr,
HWND parent,
DWORD flags,
HDEVINFO deviceset,
PCSTR machine,
PVOID reserved)
{
HDEVINFO ret;
LPWSTR enumstrW = NULL, machineW = NULL;
if (enumstr)
{
int len = MultiByteToWideChar(CP_ACP, 0, enumstr, -1, NULL, 0);
enumstrW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
if (!enumstrW)
{
ret = INVALID_HANDLE_VALUE;
goto end;
}
MultiByteToWideChar(CP_ACP, 0, enumstr, -1, enumstrW, len);
}
if (machine)
{
int len = MultiByteToWideChar(CP_ACP, 0, machine, -1, NULL, 0);
machineW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
if (!machineW)
{
HeapFree(GetProcessHeap(), 0, enumstrW);
ret = INVALID_HANDLE_VALUE;
goto end;
}
MultiByteToWideChar(CP_ACP, 0, machine, -1, machineW, len);
}
ret = SetupDiGetClassDevsExW(class, enumstrW, parent, flags, deviceset,
machineW, reserved);
HeapFree(GetProcessHeap(), 0, enumstrW);
HeapFree(GetProcessHeap(), 0, machineW);
end:
return ret;
}
static void SETUPDI_AddDeviceInterfaces(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, %08x\n", DeviceInfoSet, debugstr_guid(guid),
debugstr_w(enumstr), flags);
if (interfacesKey != INVALID_HANDLE_VALUE)
{
if (flags & DIGCF_ALLCLASSES)
{
DWORD i, len;
WCHAR interfaceGuidStr[40];
LONG l = ERROR_SUCCESS;
for (i = 0; !l; i++)
{
len = 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, %08x\n", DeviceInfoSet, debugstr_guid(class),
debugstr_w(enumstr), flags);
l = RegCreateKeyExW(HKEY_LOCAL_MACHINE, Enum, 0, NULL, 0, KEY_READ, NULL,
&enumKey, NULL);
if (enumKey != INVALID_HANDLE_VALUE)
{
if (enumstr)
{
HKEY enumStrKey;
l = RegOpenKeyExW(enumKey, enumstr, 0, KEY_READ,
&enumStrKey);
if (!l)
{
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%08x %p %s %p\n", debugstr_guid(class),
debugstr_w(enumstr), parent, flags, deviceset, debugstr_w(machine),
reserved);
if (!(flags & DIGCF_ALLCLASSES) && !class)
{
SetLastError(ERROR_INVALID_PARAMETER);
return INVALID_HANDLE_VALUE;
}
if (flags & DIGCF_ALLCLASSES)
class = NULL;
if (flags & unsupportedFlags)
WARN("unsupported flags %08x\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 %08x %p\n", DeviceInfoSet, DeviceInfoData,
debugstr_guid(InterfaceClassGuid), debugstr_a(ReferenceString),
CreationFlags, DeviceInterfaceData);
if (ReferenceString)
{
ReferenceStringW = MultiByteToUnicode(ReferenceString, CP_ACP);
if (ReferenceStringW == NULL) return FALSE;
}
ret = SetupDiCreateDeviceInterfaceW(DeviceInfoSet, DeviceInfoData,
InterfaceClassGuid, ReferenceStringW, CreationFlags,
DeviceInterfaceData);
MyFree(ReferenceStringW);
return ret;
}
/***********************************************************************
* SetupDiCreateDeviceInterfaceW (SETUPAPI.@)
*/
BOOL WINAPI SetupDiCreateDeviceInterfaceW(HDEVINFO 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 %#x, 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 %d %08x %p %p\n", DeviceInfoSet, DeviceInterfaceData, Reserved,
samDesired, InfHandle, InfSectionName);
if (InfHandle)
{
if (!InfSectionName)
{
SetLastError(ERROR_INVALID_PARAMETER);
return INVALID_HANDLE_VALUE;
}
InfSectionNameW = MultiByteToUnicode(InfSectionName, CP_ACP);
if (!InfSectionNameW)
return INVALID_HANDLE_VALUE;
}
key = SetupDiCreateDeviceInterfaceRegKeyW(DeviceInfoSet,
DeviceInterfaceData, Reserved, samDesired, InfHandle,
InfSectionNameW);
MyFree(InfSectionNameW);
return key;
}
static 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 %d, access %#x, 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, &params_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 %d.\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 %u, 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 %d, 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 %d, 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 %d, type %p, buffer %p, size %d, 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 %d, type %p, buffer %p, size %d, 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 %d, buffer %p, size %d.\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 %d, buffer %p, size %d.\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%08x %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%08x %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%08x\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 %08x %p\n",
DeviceInfoSet, debugstr_w(DevicePath), OpenFlags, DeviceInterfaceData);
return FALSE;
}
/***********************************************************************
* SetupDiOpenDeviceInterfaceA (SETUPAPI.@)
*/
BOOL WINAPI SetupDiOpenDeviceInterfaceA(
HDEVINFO DeviceInfoSet,
PCSTR DevicePath,
DWORD OpenFlags,
PSP_DEVICE_INTERFACE_DATA DeviceInterfaceData)
{
FIXME("%p %s %08x %p\n", DeviceInfoSet,
debugstr_a(DevicePath), OpenFlags, DeviceInterfaceData);
return FALSE;
}
/***********************************************************************
* 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 %d, access %#x.\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 %u\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 %u\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 %#x.\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 %#x.\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 %#x.\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, &paramsW);
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, &paramsW);
}
/***********************************************************************
* 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 %#x %p %d %#x\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 %d, profile %d, type %d, access %#x.\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 %d\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 %#x.\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 %d, profile %d, type %d.\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 %d\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 %#x.\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("%u, %p, %u, %#x\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("%u, %p, %u, %#x\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, %u, %#x\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 %#x\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, %d, %p, %#x\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("%u, %p, %p, %p, %p, %#x, %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 %#x.\n", GetLastError());
continue;
}
if ((l = create_iface_key(iface, KEY_ALL_ACCESS, &iface_key)))
{
ERR("Failed to create interface key, error %u.\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 %#x 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 %#x.\n", devinfo, device_data, type);
if (type != SPDIT_COMPATDRIVER)
{
FIXME("Unhandled type %#x.\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 %#x, index %u, driver_data %p.\n",
devinfo, device_data, type, index, driver_data);
if (type != SPDIT_COMPATDRIVER)
{
FIXME("Unhandled type %#x.\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 %#x 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 %u, 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 %u, 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 %u.\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 %u.\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;
}