[PATCH v2 5/6] setupapi: Implement class co-installers in SetupDiCallClassInstaller().

Zebediah Figura z.figura12 at gmail.com
Wed May 22 11:15:41 CDT 2019


Signed-off-by: Zebediah Figura <z.figura12 at 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 0bd9a5bf73..abf6fe9ed8 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);
 
-- 
2.21.0




More information about the wine-devel mailing list