wine-wine/dlls/setupapi/devinst.c

5141 lines
156 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 "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};
struct driver
{
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;
}
ERR("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);
}
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);
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)
{
struct device *device;
TRACE("devinfo %p, device_data %p.\n", devinfo, device_data);
if (!(device = get_device(devinfo, device_data)))
return FALSE;
remove_device(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);
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)
{
WCHAR *device_ids;
const WCHAR *p;
DWORD 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; *p; p += lstrlenW(p) + 1)
{
if (!wcsicmp(p, id))
{
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_name[LINE_LEN], mfg_key[LINE_LEN], mfg_key_ext[LINE_LEN], id[MAX_DEVICE_ID_LEN], version[MAX_DEVICE_ID_LEN];
INFCONTEXT ctx;
DWORD i, j, k;
HINF hinf;
TRACE("Enumerating drivers from %s.\n", debugstr_w(path));
if ((hinf = SetupOpenInfFileW(path, NULL, INF_STYLE_WIN4, NULL)) == INVALID_HANDLE_VALUE)
return;
for (i = 0; SetupGetLineByIndexW(hinf, manufacturerW, i, &ctx); ++i)
{
SetupGetStringFieldW(&ctx, 0, mfg_name, ARRAY_SIZE(mfg_name), NULL);
if (!SetupGetStringFieldW(&ctx, 1, mfg_key, ARRAY_SIZE(mfg_key), NULL))
lstrcpyW(mfg_key, mfg_name);
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, mfg_key_ext, ARRAY_SIZE(mfg_key_ext), NULL, NULL))
{
WARN("Failed to find section for %s, skipping.\n", debugstr_w(mfg_key));
continue;
}
for (j = 0; SetupGetLineByIndexW(hinf, mfg_key_ext, j, &ctx); ++j)
{
for (k = 2; SetupGetStringFieldW(&ctx, k, id, ARRAY_SIZE(id), NULL); ++k)
{
if (device_matches_id(device, HardwareId, id) || device_matches_id(device, CompatibleIDs, id))
{
unsigned int count = ++device->driver_count;
device->drivers = heap_realloc(device->drivers, count * sizeof(*device->drivers));
lstrcpyW(device->drivers[count - 1].inf_path, path);
lstrcpyW(device->drivers[count - 1].manufacturer, mfg_name);
lstrcpyW(device->drivers[count - 1].mfg_key, mfg_key_ext);
SetupGetStringFieldW(&ctx, 0, device->drivers[count - 1].description,
ARRAY_SIZE(device->drivers[count - 1].description), NULL);
SetupGetStringFieldW(&ctx, 1, device->drivers[count - 1].section,
ARRAY_SIZE(device->drivers[count - 1].section), NULL);
TRACE("Found compatible driver: manufacturer %s, desc %s.\n",
debugstr_w(mfg_name), debugstr_w(device->drivers[count - 1].description));
}
}
}
}
SetupCloseInfFile(hinf);
}
/***********************************************************************
* 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;
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;
}
WARN("Semi-stub, selecting the first available driver.\n");
device->selected_driver = &device->drivers[0];
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)))
{
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());
}
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;
}