Signed-off-by: Zebediah Figura z.figura12@gmail.com --- dlls/setupapi/devinst.c | 51 +++++++++++++++++++++++++++++++++++++---- 1 file changed, 47 insertions(+), 4 deletions(-)
diff --git a/dlls/setupapi/devinst.c b/dlls/setupapi/devinst.c index 1db2e52399..d2921bcf44 100644 --- a/dlls/setupapi/devinst.c +++ b/dlls/setupapi/devinst.c @@ -108,6 +108,7 @@ struct driver { WCHAR inf_path[MAX_PATH]; WCHAR manufacturer[LINE_LEN]; + WCHAR mfg_key[LINE_LEN]; WCHAR description[LINE_LEN]; };
@@ -4117,12 +4118,53 @@ BOOL WINAPI SetupDiInstallDeviceInterfaces(HDEVINFO dev, PSP_DEVINFO_DATA info_d /*********************************************************************** * SetupDiRegisterCoDeviceInstallers (SETUPAPI.@) */ -BOOL WINAPI SetupDiRegisterCoDeviceInstallers(HDEVINFO dev, PSP_DEVINFO_DATA info_data) +BOOL WINAPI SetupDiRegisterCoDeviceInstallers(HDEVINFO devinfo, SP_DEVINFO_DATA *device_data) { - FIXME("%p, %p stub\n", dev, info_data); + static const WCHAR coinstallersW[] = {'.','C','o','I','n','s','t','a','l','l','e','r','s',0}; + WCHAR coinst_key[LINE_LEN], coinst_key_ext[LINE_LEN]; + struct device *device; + struct driver *driver; + void *callback_ctx; + HKEY driver_key; + INFCONTEXT ctx; + HINF hinf; + LONG l;
- SetLastError(ERROR_CALL_NOT_IMPLEMENTED); - return FALSE; + 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, coinst_key, ARRAY_SIZE(coinst_key), NULL); + SetupDiGetActualSectionToInstallW(hinf, coinst_key, coinst_key_ext, ARRAY_SIZE(coinst_key_ext), NULL, NULL); + strcatW(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 @@ -4231,6 +4273,7 @@ static void enum_compat_drivers_from_file(struct device *device, const WCHAR *pa device->drivers = heap_realloc(device->drivers, count * sizeof(*device->drivers)); strcpyW(device->drivers[count - 1].inf_path, path); strcpyW(device->drivers[count - 1].manufacturer, mfg_name); + strcpyW(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);
Signed-off-by: Zebediah Figura z.figura12@gmail.com --- dlls/setupapi/devinst.c | 33 +++++++++++++++++++----- dlls/setupapi/tests/devinst.c | 48 +++++++++++++++++++++++++++++++++++ include/setupapi.h | 1 + 3 files changed, 75 insertions(+), 7 deletions(-)
diff --git a/dlls/setupapi/devinst.c b/dlls/setupapi/devinst.c index d2921bcf44..a3d7336067 100644 --- a/dlls/setupapi/devinst.c +++ b/dlls/setupapi/devinst.c @@ -3561,15 +3561,34 @@ BOOL WINAPI SetupDiSetClassInstallParamsW( }
/*********************************************************************** - * SetupDiCallClassInstaller (SETUPAPI.@) + * SetupDiCallClassInstaller (SETUPAPI.@) */ -BOOL WINAPI SetupDiCallClassInstaller( - DI_FUNCTION InstallFunction, - HDEVINFO DeviceInfoSet, - PSP_DEVINFO_DATA DeviceInfoData) +BOOL WINAPI SetupDiCallClassInstaller(DI_FUNCTION function, HDEVINFO devinfo, SP_DEVINFO_DATA *device_data) { - FIXME("%d %p %p\n", InstallFunction, DeviceInfoSet, DeviceInfoData); - return FALSE; + 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); + return FALSE; + } }
/*********************************************************************** diff --git a/dlls/setupapi/tests/devinst.c b/dlls/setupapi/tests/devinst.c index a717bc08c6..880129756c 100644 --- a/dlls/setupapi/tests/devinst.c +++ b/dlls/setupapi/tests/devinst.c @@ -2344,6 +2344,53 @@ static void test_driver_list(void) DeleteFileA("C:/windows/inf/wine_test2.pnf"); }
+static BOOL device_is_registered(HDEVINFO set, SP_DEVINFO_DATA *device) +{ + HKEY key = SetupDiOpenDevRegKey(set, device, DICS_FLAG_GLOBAL, 0, DIREG_DRV, 0); + ok(key == INVALID_HANDLE_VALUE, "Expected failure.\n"); + RegCloseKey(key); + return GetLastError() == ERROR_KEY_DOES_NOT_EXIST; +} + +static void test_call_class_installer(void) +{ + SP_DEVINFO_DATA device = {sizeof(device)}; + HDEVINFO set; + BOOL ret; + + if (wow64) + { + win_skip("SetupDiCallClassInstaller() does not work on WoW64.\n"); + return; + } + + 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()); + + 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"); + + /* This is probably not failure per se, but rather an indication that no + * class installer was called and no default handler exists. */ + ret = SetupDiCallClassInstaller(DIF_ALLOW_INSTALL, set, &device); + ok(!ret, "Expected failure.\n"); + ok(GetLastError() == ERROR_DI_DO_DEFAULT, "Got unexpected error %#x.\n", GetLastError()); + + ret = SetupDiCallClassInstaller(0xdeadbeef, set, &device); + ok(!ret, "Expected failure.\n"); + ok(GetLastError() == ERROR_DI_DO_DEFAULT, "Got unexpected error %#x.\n", GetLastError()); + + 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); +} + START_TEST(devinst) { static BOOL (WINAPI *pIsWow64Process)(HANDLE, BOOL *); @@ -2380,4 +2427,5 @@ START_TEST(devinst) test_device_interface_key(); test_device_install_params(); test_driver_list(); + test_call_class_installer(); } diff --git a/include/setupapi.h b/include/setupapi.h index 45f830ef5a..49a696c2a8 100644 --- a/include/setupapi.h +++ b/include/setupapi.h @@ -1106,6 +1106,7 @@ DECL_WINELIB_SETUPAPI_TYPE_AW(PSP_INF_SIGNER_INFO) #define DIF_POWERMESSAGEWAKE 0x27 #define DIF_ADDREMOTEPROPERTYPAGE_ADVANCED 0x28 #define DIF_UPDATEDRIVER_UI 0x29 +#define DIF_FINISHINSTALL_ACTION 0x2a #define DIF_RESERVED2 0x30
/* Directory ids */
Hi,
While running your changed tests, I think I found new failures. Being a bot and all I'm not very good at pattern recognition, so I might be wrong, but could you please double-check?
Full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=52660
Your paranoid android.
=== w1064v1809 (32 bit report) ===
setupapi: devinst: Timeout
=== debian9 (32 bit WoW report) ===
setupapi: devinst.c:2355: Test failed: SetupDiCallClassInstaller() does not work on WoW64.
=== debian9 (64 bit WoW report) ===
setupapi: devinst.c:2355: Test failed: SetupDiCallClassInstaller() does not work on WoW64.
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)
Hi,
While running your changed tests, I think I found new failures. Being a bot and all I'm not very good at pattern recognition, so I might be wrong, but could you please double-check?
Full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=52661
Your paranoid android.
=== w1064v1809 (32 bit report) ===
setupapi: devinst: Timeout
=== debian9 (32 bit WoW report) ===
setupapi: devinst.c:2519: Test failed: SetupDiCallClassInstaller() does not work on WoW64.
=== debian9 (64 bit WoW report) ===
setupapi: devinst.c:2519: Test failed: SetupDiCallClassInstaller() does not work on WoW64.
Signed-off-by: Zebediah Figura z.figura12@gmail.com --- dlls/setupapi/devinst.c | 5 +++++ dlls/setupapi/tests/devinst.c | 8 ++++---- 2 files changed, 9 insertions(+), 4 deletions(-)
diff --git a/dlls/setupapi/devinst.c b/dlls/setupapi/devinst.c index 8e3e5615c4..281d7d3178 100644 --- a/dlls/setupapi/devinst.c +++ b/dlls/setupapi/devinst.c @@ -724,6 +724,11 @@ static void remove_device(struct device *device) 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); diff --git a/dlls/setupapi/tests/devinst.c b/dlls/setupapi/tests/devinst.c index 5d9c03fa36..2cd5637aed 100644 --- a/dlls/setupapi/tests/devinst.c +++ b/dlls/setupapi/tests/devinst.c @@ -2428,8 +2428,8 @@ static void test_class_installer(void)
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); + 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;
/* Test returning an error. */ @@ -2506,8 +2506,8 @@ static void test_class_installer(void)
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); + 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 = RegDeleteKeyA(class_key, "");
Hi,
While running your changed tests, I think I found new failures. Being a bot and all I'm not very good at pattern recognition, so I might be wrong, but could you please double-check?
Full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=52662
Your paranoid android.
=== w1064v1809 (32 bit report) ===
setupapi: devinst: Timeout
=== debian9 (32 bit WoW report) ===
setupapi: devinst.c:2519: Test failed: SetupDiCallClassInstaller() does not work on WoW64.
=== debian9 (64 bit WoW report) ===
setupapi: devinst.c:2519: Test failed: SetupDiCallClassInstaller() does not work on WoW64.
Signed-off-by: Zebediah Figura z.figura12@gmail.com --- dlls/setupapi/devinst.c | 72 ++++++++++++++++++- dlls/setupapi/tests/coinst.c | 20 ++++++ dlls/setupapi/tests/coinst.spec | 3 + dlls/setupapi/tests/devinst.c | 121 ++++++++++++++++++++++++++++++++ 4 files changed, 215 insertions(+), 1 deletion(-)
diff --git a/dlls/setupapi/devinst.c b/dlls/setupapi/devinst.c index 281d7d3178..4d44e0086c 100644 --- a/dlls/setupapi/devinst.c +++ b/dlls/setupapi/devinst.c @@ -3565,19 +3565,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); @@ -3585,6 +3639,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)) diff --git a/dlls/setupapi/tests/coinst.c b/dlls/setupapi/tests/coinst.c index dd64b15cca..5e8d890cfa 100644 --- a/dlls/setupapi/tests/coinst.c +++ b/dlls/setupapi/tests/coinst.c @@ -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; +} diff --git a/dlls/setupapi/tests/coinst.spec b/dlls/setupapi/tests/coinst.spec index 34a5fdde6d..bbfd316b4b 100644 --- a/dlls/setupapi/tests/coinst.spec +++ b/dlls/setupapi/tests/coinst.spec @@ -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 diff --git a/dlls/setupapi/tests/devinst.c b/dlls/setupapi/tests/devinst.c index 2cd5637aed..5b5cae0c3c 100644 --- a/dlls/setupapi/tests/devinst.c +++ b/dlls/setupapi/tests/devinst.c @@ -2515,6 +2515,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)}; @@ -2561,6 +2681,7 @@ static void test_call_class_installer(void) coinst_last_message = (void *)GetProcAddress(coinst, "last_message");
test_class_installer(); + test_class_coinstaller();
FreeLibrary(coinst);
Hi,
While running your changed tests, I think I found new failures. Being a bot and all I'm not very good at pattern recognition, so I might be wrong, but could you please double-check?
Full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=52663
Your paranoid android.
=== w1064v1809 (32 bit report) ===
setupapi: devinst: Timeout
=== debian9 (32 bit WoW report) ===
setupapi: devinst.c:2639: Test failed: SetupDiCallClassInstaller() does not work on WoW64.
=== debian9 (64 bit WoW report) ===
setupapi: devinst.c:2639: Test failed: SetupDiCallClassInstaller() does not work on WoW64.
Signed-off-by: Zebediah Figura z.figura12@gmail.com --- dlls/setupapi/devinst.c | 13 +++++++++++++ 1 file changed, 13 insertions(+)
diff --git a/dlls/setupapi/devinst.c b/dlls/setupapi/devinst.c index 4d44e0086c..71311238c3 100644 --- a/dlls/setupapi/devinst.c +++ b/dlls/setupapi/devinst.c @@ -3622,6 +3622,7 @@ BOOL WINAPI SetupDiCallClassInstaller(DI_FUNCTION function, HDEVINFO devinfo, SP '\','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; @@ -3655,6 +3656,18 @@ BOOL WINAPI SetupDiCallClassInstaller(DI_FUNCTION function, HDEVINFO devinfo, SP 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))
Hi,
While running your changed tests, I think I found new failures. Being a bot and all I'm not very good at pattern recognition, so I might be wrong, but could you please double-check?
Full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=52664
Your paranoid android.
=== debian9 (32 bit WoW report) ===
setupapi: devinst.c:2639: Test failed: SetupDiCallClassInstaller() does not work on WoW64.
=== debian9 (64 bit WoW report) ===
setupapi: devinst.c:2639: Test failed: SetupDiCallClassInstaller() does not work on WoW64.
These pass on every testbot platform except for Windows Server 2003. There they hang, because the call to SetupDiRegisterCoDeviceInstallers() triggers a dialog box warning the user that the driver is unsigned. I can find no way to prevent or anticipate this, not even by setting DI_QUIETINSTALL.
A similar problem appears for all following patches related to device installation, i.e. SetupDiInstallDriverFiles(), SetupDiInstallDeviceInterfaces(), SetupDiInstallDevice(), UpdateDriverForPlugAndPlayDevices(). I will continue to send tests to prove correctness, unless requested otherwise. --- dlls/setupapi/tests/devinst.c | 91 +++++++++++++++++++++++++++++++++++ 1 file changed, 91 insertions(+)
diff --git a/dlls/setupapi/tests/devinst.c b/dlls/setupapi/tests/devinst.c index 5b5cae0c3c..c3cad84744 100644 --- a/dlls/setupapi/tests/devinst.c +++ b/dlls/setupapi/tests/devinst.c @@ -2635,6 +2635,96 @@ static void test_class_coinstaller(void) RegCloseKey(coinst_key); }
+static void test_device_coinstaller(void) +{ + static const char hardware_id[] = "bogus_hardware_id\0"; + SP_DEVINSTALL_PARAMS_A params = {sizeof(params)}; + SP_DEVINFO_DATA device = {sizeof(device)}; + char inf_path[MAX_PATH]; + HKEY class_key; + HDEVINFO set; + BOOL ret; + LONG res; + + static const char inf_data[] = "[Version]\n" + "Signature="$Chicago$"\n" + "ClassGuid={6a55b5a4-3f65-11db-b704-0011955c2bdb}\n" + "[Manufacturer]\n" + "mfg1=mfg1_key,NT" MYEXT "\n" + "[mfg1_key.nt" MYEXT "]\n" + "desc1=dev1,bogus_hardware_id\n" + "[dev1.CoInstallers]\n" + "AddReg=dev1_AddReg\n" + "[dev1_AddReg]\n" + "HKR,,CoInstallers32,0x00010000,"winetest_coinst.dll,co_success"\n"; + + 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); + + GetTempPathA(sizeof(inf_path), inf_path); + strcat(inf_path, "setupapi_test.inf"); + create_file(inf_path, inf_data); + + 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 = SetupDiSetDeviceRegistryPropertyA(set, &device, SPDRP_HARDWAREID, + (const BYTE *)hardware_id, sizeof(hardware_id)); + ok(ret, "Failed to set hardware ID, error %#x.\n", GetLastError()); + ret = SetupDiRegisterDeviceInfo(set, &device, 0, NULL, NULL, NULL); + ok(ret, "Failed to register device, error %#x.\n", GetLastError()); + + ret = SetupDiGetDeviceInstallParamsA(set, &device, ¶ms); + ok(ret, "Failed to get device install params, error %#x.\n", GetLastError()); + strcpy(params.DriverPath, inf_path); + params.Flags = DI_ENUMSINGLEINF; + ret = SetupDiSetDeviceInstallParamsA(set, &device, ¶ms); + ok(ret, "Failed to set device install params, error %#x.\n", GetLastError()); + ret = SetupDiBuildDriverInfoList(set, &device, SPDIT_COMPATDRIVER); + ok(ret, "Failed to build driver list, error %#x.\n", GetLastError()); + ret = SetupDiSelectBestCompatDrv(set, &device); + ok(ret, "Failed to call class installer, 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, "Got %d callbacks.\n", *coinst_callback_count); + + ret = SetupDiRegisterCoDeviceInstallers(set, &device); + ok(ret, "Failed to register device co-installer, error %#x.\n", GetLastError()); + + ok(!*coinst_callback_count, "Got %d callbacks.\n", *coinst_callback_count); + + 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(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; + + 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)}; @@ -2682,6 +2772,7 @@ static void test_call_class_installer(void)
test_class_installer(); test_class_coinstaller(); + test_device_coinstaller();
FreeLibrary(coinst);