Signed-off-by: Zebediah Figura z.figura12@gmail.com --- dlls/setupapi/devinst.c | 91 ++++++++++++---- dlls/setupapi/tests/Makefile.in | 5 +- dlls/setupapi/tests/coinst.c | 51 +++++++++ dlls/setupapi/tests/coinst.spec | 6 ++ dlls/setupapi/tests/devinst.c | 177 ++++++++++++++++++++++++++++++++ 5 files changed, 309 insertions(+), 21 deletions(-) create mode 100644 dlls/setupapi/tests/coinst.c create mode 100644 dlls/setupapi/tests/coinst.spec
diff --git a/dlls/setupapi/devinst.c b/dlls/setupapi/devinst.c index a3d7336067..8e3e5615c4 100644 --- a/dlls/setupapi/devinst.c +++ b/dlls/setupapi/devinst.c @@ -3565,30 +3565,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; }
/*********************************************************************** diff --git a/dlls/setupapi/tests/Makefile.in b/dlls/setupapi/tests/Makefile.in index 5b9bf8190c..bbfdf590d5 100644 --- a/dlls/setupapi/tests/Makefile.in +++ b/dlls/setupapi/tests/Makefile.in @@ -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
diff --git a/dlls/setupapi/tests/coinst.c b/dlls/setupapi/tests/coinst.c new file mode 100644 index 0000000000..dd64b15cca --- /dev/null +++ b/dlls/setupapi/tests/coinst.c @@ -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; +} diff --git a/dlls/setupapi/tests/coinst.spec b/dlls/setupapi/tests/coinst.spec new file mode 100644 index 0000000000..34a5fdde6d --- /dev/null +++ b/dlls/setupapi/tests/coinst.spec @@ -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 diff --git a/dlls/setupapi/tests/devinst.c b/dlls/setupapi/tests/devinst.c index 880129756c..5d9c03fa36 100644 --- a/dlls/setupapi/tests/devinst.c +++ b/dlls/setupapi/tests/devinst.c @@ -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 }; @@ -2352,9 +2370,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;
@@ -2389,6 +2553,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)