setupapi: Implement class co-installers in SetupDiCallClassInstaller().
Signed-off-by: Zebediah Figura <z.figura12@gmail.com> Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
parent
afba7d9d93
commit
8b1050c7f4
|
@ -3524,19 +3524,73 @@ BOOL WINAPI SetupDiSetClassInstallParamsW(
|
|||
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 += strlenW(p) + 1)
|
||||
{
|
||||
TRACE("Found co-installer %s.\n", debugstr_w(p));
|
||||
if ((procnameW = strchrW(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 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;
|
||||
HKEY class_key;
|
||||
DWORD size;
|
||||
|
||||
TRACE("function %#x, devinfo %p, device_data %p.\n", function, devinfo, device_data);
|
||||
|
@ -3544,6 +3598,22 @@ BOOL WINAPI SetupDiCallClassInstaller(DI_FUNCTION function, HDEVINFO devinfo, SP
|
|||
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 ((class_key = SetupDiOpenClassRegKey(&device->class, KEY_READ)) != INVALID_HANDLE_VALUE)
|
||||
{
|
||||
if (!RegGetValueW(class_key, NULL, installer32W, RRF_RT_REG_SZ, NULL, NULL, &size))
|
||||
|
|
|
@ -49,3 +49,23 @@ DWORD WINAPI class_error(DI_FUNCTION function, HDEVINFO set, SP_DEVINFO_DATA *de
|
|||
{
|
||||
return 0xdeadbeef;
|
||||
}
|
||||
|
||||
DWORD WINAPI co_success(DI_FUNCTION function, HDEVINFO set, SP_DEVINFO_DATA *device,
|
||||
COINSTALLER_CONTEXT_DATA *context)
|
||||
{
|
||||
callback_count++;
|
||||
last_message = function;
|
||||
return ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
DWORD WINAPI CoDeviceInstall(DI_FUNCTION function, HDEVINFO set, SP_DEVINFO_DATA *device,
|
||||
COINSTALLER_CONTEXT_DATA *context)
|
||||
{
|
||||
return co_success(function, set, device, context);
|
||||
}
|
||||
|
||||
DWORD WINAPI co_error(DI_FUNCTION function, HDEVINFO set, SP_DEVINFO_DATA *device,
|
||||
COINSTALLER_CONTEXT_DATA *context)
|
||||
{
|
||||
return 0xdeadbeef;
|
||||
}
|
||||
|
|
|
@ -2,5 +2,8 @@
|
|||
@ stdcall ClassInstall(long ptr ptr)
|
||||
@ stdcall class_default(long ptr ptr)
|
||||
@ stdcall class_error(long ptr ptr)
|
||||
@ stdcall co_success(long ptr ptr ptr)
|
||||
@ stdcall CoDeviceInstall(long ptr ptr ptr)
|
||||
@ stdcall co_error(long ptr ptr ptr)
|
||||
@ extern callback_count
|
||||
@ extern last_message
|
||||
|
|
|
@ -2507,6 +2507,126 @@ static void test_class_installer(void)
|
|||
RegCloseKey(class_key);
|
||||
}
|
||||
|
||||
static void test_class_coinstaller(void)
|
||||
{
|
||||
SP_DEVINFO_DATA device = {sizeof(device)};
|
||||
char regdata[200];
|
||||
HKEY coinst_key;
|
||||
HDEVINFO set;
|
||||
BOOL ret;
|
||||
LONG res;
|
||||
|
||||
res = RegCreateKeyA(HKEY_LOCAL_MACHINE, "System\\CurrentControlSet\\Control\\CoDeviceInstallers", &coinst_key);
|
||||
ok(!res, "Failed to open CoDeviceInstallers key, error %u.\n", res);
|
||||
strcpy(regdata, "winetest_coinst.dll,co_success");
|
||||
regdata[strlen(regdata) + 1] = 0;
|
||||
res = RegSetValueExA(coinst_key, "{6a55b5a4-3f65-11db-b704-0011955c2bdb}", 0,
|
||||
REG_MULTI_SZ, (BYTE *)regdata, strlen(regdata) + 2);
|
||||
ok(!res, "Failed to set registry value, error %u.\n", res);
|
||||
|
||||
/* We must recreate the device list, or Windows will not recognize that the
|
||||
* class co-installer exists. */
|
||||
set = SetupDiCreateDeviceInfoList(&guid, NULL);
|
||||
ok(set != INVALID_HANDLE_VALUE, "Failed to create device list, error %#x.\n", GetLastError());
|
||||
ret = SetupDiCreateDeviceInfoA(set, "Root\\LEGACY_BOGUS\\0000", &guid, NULL, NULL, 0, &device);
|
||||
ok(ret, "Failed to create device, error %#x.\n", GetLastError());
|
||||
|
||||
ret = SetupDiCallClassInstaller(DIF_ALLOW_INSTALL, set, &device);
|
||||
ok(!ret, "Expected failure.\n");
|
||||
ok(GetLastError() == ERROR_DI_DO_DEFAULT, "Got unexpected error %#x.\n", GetLastError());
|
||||
|
||||
ok(*coinst_callback_count == 1, "Got %d callbacks.\n", *coinst_callback_count);
|
||||
ok(*coinst_last_message == DIF_ALLOW_INSTALL, "Got unexpected message %#x.\n", *coinst_last_message);
|
||||
*coinst_callback_count = 0;
|
||||
|
||||
ret = SetupDiCallClassInstaller(0xdeadbeef, set, &device);
|
||||
ok(!ret, "Expected failure.\n");
|
||||
ok(GetLastError() == ERROR_DI_DO_DEFAULT, "Got unexpected error %#x.\n", GetLastError());
|
||||
|
||||
ok(*coinst_callback_count == 1, "Got %d callbacks.\n", *coinst_callback_count);
|
||||
ok(*coinst_last_message == 0xdeadbeef, "Got unexpected message %#x.\n", *coinst_last_message);
|
||||
*coinst_callback_count = 0;
|
||||
|
||||
ok(!device_is_registered(set, &device), "Expected device not to be registered.\n");
|
||||
ret = SetupDiCallClassInstaller(DIF_REGISTERDEVICE, set, &device);
|
||||
ok(ret, "Failed to call class installer, error %#x.\n", GetLastError());
|
||||
ok(device_is_registered(set, &device), "Expected device to be registered.\n");
|
||||
|
||||
ok(*coinst_callback_count == 1, "Got %d callbacks.\n", *coinst_callback_count);
|
||||
ok(*coinst_last_message == DIF_REGISTERDEVICE, "Got unexpected message %#x.\n", *coinst_last_message);
|
||||
*coinst_callback_count = 0;
|
||||
|
||||
ret = SetupDiCallClassInstaller(DIF_REMOVE, set, &device);
|
||||
ok(ret, "Failed to call class installer, error %#x.\n", GetLastError());
|
||||
ok(!device_is_registered(set, &device), "Expected device not to be registered.\n");
|
||||
|
||||
ok(*coinst_callback_count == 1, "Got %d callbacks.\n", *coinst_callback_count);
|
||||
ok(*coinst_last_message == DIF_REMOVE, "Got unexpected message %#x.\n", *coinst_last_message);
|
||||
*coinst_callback_count = 0;
|
||||
|
||||
SetupDiDestroyDeviceInfoList(set);
|
||||
|
||||
todo_wine ok(*coinst_callback_count == 1, "Got %d callbacks.\n", *coinst_callback_count);
|
||||
todo_wine ok(*coinst_last_message == DIF_DESTROYPRIVATEDATA, "Got unexpected message %#x.\n", *coinst_last_message);
|
||||
*coinst_callback_count = 0;
|
||||
|
||||
/* Test returning an error from the co-installer. */
|
||||
|
||||
strcpy(regdata, "winetest_coinst.dll,co_error");
|
||||
regdata[strlen(regdata) + 1] = 0;
|
||||
res = RegSetValueExA(coinst_key, "{6a55b5a4-3f65-11db-b704-0011955c2bdb}", 0,
|
||||
REG_MULTI_SZ, (BYTE *)regdata, strlen(regdata) + 2);
|
||||
ok(!res, "Failed to set registry value, error %u.\n", res);
|
||||
|
||||
set = SetupDiCreateDeviceInfoList(&guid, NULL);
|
||||
ok(set != INVALID_HANDLE_VALUE, "Failed to create device list, error %#x.\n", GetLastError());
|
||||
ret = SetupDiCreateDeviceInfoA(set, "Root\\LEGACY_BOGUS\\0000", &guid, NULL, NULL, 0, &device);
|
||||
ok(ret, "Failed to create device, error %#x.\n", GetLastError());
|
||||
|
||||
ret = SetupDiCallClassInstaller(DIF_ALLOW_INSTALL, set, &device);
|
||||
ok(!ret, "Expected failure.\n");
|
||||
ok(GetLastError() == 0xdeadbeef, "Got unexpected error %#x.\n", GetLastError());
|
||||
|
||||
ok(!device_is_registered(set, &device), "Expected device not to be registered.\n");
|
||||
ret = SetupDiCallClassInstaller(DIF_REGISTERDEVICE, set, &device);
|
||||
ok(!ret, "Expected failure.\n");
|
||||
ok(GetLastError() == 0xdeadbeef, "Got unexpected error %#x.\n", GetLastError());
|
||||
ok(!device_is_registered(set, &device), "Expected device not to be registered.\n");
|
||||
|
||||
SetupDiDestroyDeviceInfoList(set);
|
||||
|
||||
/* The default entry point is CoDeviceInstall(). */
|
||||
|
||||
strcpy(regdata, "winetest_coinst.dll");
|
||||
regdata[strlen(regdata) + 1] = 0;
|
||||
res = RegSetValueExA(coinst_key, "{6a55b5a4-3f65-11db-b704-0011955c2bdb}", 0,
|
||||
REG_MULTI_SZ, (BYTE *)regdata, strlen(regdata) + 2);
|
||||
ok(!res, "Failed to set registry value, error %u.\n", res);
|
||||
|
||||
set = SetupDiCreateDeviceInfoList(&guid, NULL);
|
||||
ok(set != INVALID_HANDLE_VALUE, "Failed to create device list, error %#x.\n", GetLastError());
|
||||
ret = SetupDiCreateDeviceInfoA(set, "Root\\LEGACY_BOGUS\\0000", &guid, NULL, NULL, 0, &device);
|
||||
ok(ret, "Failed to create device, error %#x.\n", GetLastError());
|
||||
|
||||
ret = SetupDiCallClassInstaller(DIF_ALLOW_INSTALL, set, &device);
|
||||
ok(!ret, "Expected failure.\n");
|
||||
ok(GetLastError() == ERROR_DI_DO_DEFAULT, "Got unexpected error %#x.\n", GetLastError());
|
||||
|
||||
ok(*coinst_callback_count == 1, "Got %d callbacks.\n", *coinst_callback_count);
|
||||
ok(*coinst_last_message == DIF_ALLOW_INSTALL, "Got unexpected message %#x.\n", *coinst_last_message);
|
||||
*coinst_callback_count = 0;
|
||||
|
||||
SetupDiDestroyDeviceInfoList(set);
|
||||
|
||||
ok(*coinst_callback_count == 1, "Got %d callbacks.\n", *coinst_callback_count);
|
||||
ok(*coinst_last_message == DIF_DESTROYPRIVATEDATA, "Got unexpected message %#x.\n", *coinst_last_message);
|
||||
*coinst_callback_count = 0;
|
||||
|
||||
res = RegDeleteValueA(coinst_key, "{6a55b5a4-3f65-11db-b704-0011955c2bdb}");
|
||||
ok(!res, "Failed to delete value, error %u.\n", res);
|
||||
RegCloseKey(coinst_key);
|
||||
}
|
||||
|
||||
static void test_call_class_installer(void)
|
||||
{
|
||||
SP_DEVINFO_DATA device = {sizeof(device)};
|
||||
|
@ -2553,6 +2673,7 @@ static void test_call_class_installer(void)
|
|||
coinst_last_message = (void *)GetProcAddress(coinst, "last_message");
|
||||
|
||||
test_class_installer();
|
||||
test_class_coinstaller();
|
||||
|
||||
FreeLibrary(coinst);
|
||||
|
||||
|
|
Loading…
Reference in New Issue