mirror of
git://source.winehq.org/git/wine.git
synced 2024-11-05 18:01:34 +00:00
setupapi: Implement custom class 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
43ee138d47
commit
fce26e60cc
5 changed files with 309 additions and 21 deletions
|
@ -3524,30 +3524,81 @@ BOOL WINAPI SetupDiSetClassInstallParamsW(
|
|||
*/
|
||||
BOOL WINAPI SetupDiCallClassInstaller(DI_FUNCTION function, HDEVINFO devinfo, SP_DEVINFO_DATA *device_data)
|
||||
{
|
||||
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;
|
||||
WCHAR *path, *procnameW;
|
||||
struct device *device;
|
||||
HMODULE module;
|
||||
char *procname;
|
||||
HKEY class_key;
|
||||
DWORD size;
|
||||
|
||||
TRACE("function %#x, devinfo %p, device_data %p.\n", function, devinfo, device_data);
|
||||
|
||||
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_FINISHINSTALL_ACTION:
|
||||
case DIF_INSTALLDEVICE:
|
||||
case DIF_INSTALLDEVICEFILES:
|
||||
case DIF_INSTALLINTERFACES:
|
||||
case DIF_PROPERTYCHANGE:
|
||||
case DIF_SELECTDEVICE:
|
||||
case DIF_UNREMOVE:
|
||||
FIXME("Unhandled function %#x.\n", function);
|
||||
default:
|
||||
SetLastError(ERROR_DI_DO_DEFAULT);
|
||||
if (!(device = get_device(devinfo, device_data)))
|
||||
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))
|
||||
{
|
||||
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 = strchrW(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_FINISHINSTALL_ACTION:
|
||||
case DIF_INSTALLDEVICE:
|
||||
case DIF_INSTALLDEVICEFILES:
|
||||
case DIF_INSTALLINTERFACES:
|
||||
case DIF_PROPERTYCHANGE:
|
||||
case DIF_SELECTDEVICE:
|
||||
case DIF_UNREMOVE:
|
||||
FIXME("Unhandled function %#x.\n", function);
|
||||
}
|
||||
}
|
||||
|
||||
if (ret) SetLastError(ret);
|
||||
return !ret;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
|
|
|
@ -1,13 +1,16 @@
|
|||
TESTDLL = setupapi.dll
|
||||
IMPORTS = advapi32 cabinet setupapi shell32 uuid user32
|
||||
|
||||
C_SRCS = \
|
||||
SOURCES = \
|
||||
coinst.c \
|
||||
coinst.spec \
|
||||
devinst.c \
|
||||
diskspace.c \
|
||||
install.c \
|
||||
misc.c \
|
||||
parser.c \
|
||||
query.c \
|
||||
setupapi.rc \
|
||||
setupcab.c \
|
||||
stringtable.c
|
||||
|
||||
|
|
51
dlls/setupapi/tests/coinst.c
Normal file
51
dlls/setupapi/tests/coinst.c
Normal file
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* Test co-installer and class installer DLL
|
||||
*
|
||||
* Copyright 2018 Zebediah Figura
|
||||
*
|
||||
* 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 "winuser.h"
|
||||
#include "winreg.h"
|
||||
#include "setupapi.h"
|
||||
|
||||
unsigned int callback_count;
|
||||
DI_FUNCTION last_message;
|
||||
|
||||
DWORD WINAPI class_success(DI_FUNCTION function, HDEVINFO set, SP_DEVINFO_DATA *device)
|
||||
{
|
||||
callback_count++;
|
||||
last_message = function;
|
||||
return ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
DWORD WINAPI ClassInstall(DI_FUNCTION function, HDEVINFO set, SP_DEVINFO_DATA *device)
|
||||
{
|
||||
return class_success(function, set, device);
|
||||
}
|
||||
|
||||
DWORD WINAPI class_default(DI_FUNCTION function, HDEVINFO set, SP_DEVINFO_DATA *device)
|
||||
{
|
||||
return ERROR_DI_DO_DEFAULT;
|
||||
}
|
||||
|
||||
DWORD WINAPI class_error(DI_FUNCTION function, HDEVINFO set, SP_DEVINFO_DATA *device)
|
||||
{
|
||||
return 0xdeadbeef;
|
||||
}
|
6
dlls/setupapi/tests/coinst.spec
Normal file
6
dlls/setupapi/tests/coinst.spec
Normal file
|
@ -0,0 +1,6 @@
|
|||
@ stdcall class_success(long ptr ptr)
|
||||
@ stdcall ClassInstall(long ptr ptr)
|
||||
@ stdcall class_default(long ptr ptr)
|
||||
@ stdcall class_error(long ptr ptr)
|
||||
@ extern callback_count
|
||||
@ extern last_message
|
|
@ -56,6 +56,24 @@ static void create_file(const char *name, const char *data)
|
|||
CloseHandle(file);
|
||||
}
|
||||
|
||||
static void load_resource(const char *name, const char *filename)
|
||||
{
|
||||
DWORD written;
|
||||
HANDLE file;
|
||||
HRSRC res;
|
||||
void *ptr;
|
||||
|
||||
file = CreateFileA(filename, GENERIC_READ|GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, 0);
|
||||
ok(file != INVALID_HANDLE_VALUE, "file creation failed, at %s, error %d\n", filename, GetLastError());
|
||||
|
||||
res = FindResourceA(NULL, name, "TESTDLL");
|
||||
ok( res != 0, "couldn't find resource\n" );
|
||||
ptr = LockResource( LoadResource( GetModuleHandleA(NULL), res ));
|
||||
WriteFile( file, ptr, SizeofResource( GetModuleHandleA(NULL), res ), &written, NULL );
|
||||
ok( written == SizeofResource( GetModuleHandleA(NULL), res ), "couldn't write resource\n" );
|
||||
CloseHandle( file );
|
||||
}
|
||||
|
||||
static void test_create_device_list_ex(void)
|
||||
{
|
||||
static const WCHAR machine[] = { 'd','u','m','m','y',0 };
|
||||
|
@ -2344,9 +2362,155 @@ static BOOL device_is_registered(HDEVINFO set, SP_DEVINFO_DATA *device)
|
|||
return GetLastError() == ERROR_KEY_DOES_NOT_EXIST;
|
||||
}
|
||||
|
||||
static unsigned int *coinst_callback_count;
|
||||
static DI_FUNCTION *coinst_last_message;
|
||||
|
||||
static void test_class_installer(void)
|
||||
{
|
||||
SP_DEVINFO_DATA device = {sizeof(device)};
|
||||
char regdata[200];
|
||||
HKEY class_key;
|
||||
HDEVINFO set;
|
||||
BOOL ret;
|
||||
LONG res;
|
||||
|
||||
res = RegCreateKeyA(HKEY_LOCAL_MACHINE, "System\\CurrentControlSet\\Control\\Class"
|
||||
"\\{6a55b5a4-3f65-11db-b704-0011955c2bdb}", &class_key);
|
||||
ok(!res, "Failed to create class key, error %u.\n", res);
|
||||
|
||||
strcpy(regdata, "winetest_coinst.dll,class_success");
|
||||
res = RegSetValueExA(class_key, "Installer32", 0, REG_SZ, (BYTE *)regdata, strlen(regdata)+1);
|
||||
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, "Failed to call class installer, 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, "Failed to call class installer, 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 not 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. */
|
||||
|
||||
strcpy(regdata, "winetest_coinst.dll,class_error");
|
||||
res = RegSetValueExA(class_key, "Installer32", 0, REG_SZ, (BYTE *)regdata, strlen(regdata)+1);
|
||||
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");
|
||||
|
||||
ret = SetupDiCallClassInstaller(DIF_REMOVE, 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);
|
||||
|
||||
/* Test returning ERROR_DI_DO_DEFAULT. */
|
||||
|
||||
strcpy(regdata, "winetest_coinst.dll,class_default");
|
||||
res = RegSetValueExA(class_key, "Installer32", 0, REG_SZ, (BYTE *)regdata, strlen(regdata)+1);
|
||||
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(!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");
|
||||
|
||||
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");
|
||||
|
||||
SetupDiDestroyDeviceInfoList(set);
|
||||
|
||||
/* The default entry point is ClassInstall(). */
|
||||
|
||||
strcpy(regdata, "winetest_coinst.dll");
|
||||
res = RegSetValueExA(class_key, "Installer32", 0, REG_SZ, (BYTE *)regdata, strlen(regdata)+1);
|
||||
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, "Failed to call class installer, 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);
|
||||
|
||||
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;
|
||||
|
||||
res = RegDeleteKeyA(class_key, "");
|
||||
ok(!res, "Failed to delete class key, error %u.\n", res);
|
||||
RegCloseKey(class_key);
|
||||
}
|
||||
|
||||
static void test_call_class_installer(void)
|
||||
{
|
||||
SP_DEVINFO_DATA device = {sizeof(device)};
|
||||
HMODULE coinst;
|
||||
HDEVINFO set;
|
||||
BOOL ret;
|
||||
|
||||
|
@ -2381,6 +2545,19 @@ static void test_call_class_installer(void)
|
|||
ok(!device_is_registered(set, &device), "Expected device not to be registered.\n");
|
||||
|
||||
SetupDiDestroyDeviceInfoList(set);
|
||||
|
||||
load_resource("coinst.dll", "C:\\windows\\system32\\winetest_coinst.dll");
|
||||
|
||||
coinst = LoadLibraryA("winetest_coinst.dll");
|
||||
coinst_callback_count = (void *)GetProcAddress(coinst, "callback_count");
|
||||
coinst_last_message = (void *)GetProcAddress(coinst, "last_message");
|
||||
|
||||
test_class_installer();
|
||||
|
||||
FreeLibrary(coinst);
|
||||
|
||||
ret = DeleteFileA("C:\\windows\\system32\\winetest_coinst.dll");
|
||||
ok(ret, "Failed to delete file, error %u.\n", GetLastError());
|
||||
}
|
||||
|
||||
START_TEST(devinst)
|
||||
|
|
Loading…
Reference in a new issue