[PATCH 3/6] setupapi: Implement custom class installers in SetupDiCallClassInstaller().

Zebediah Figura z.figura12 at gmail.com
Wed May 22 00:10:07 CDT 2019


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




More information about the wine-devel mailing list