[PATCH v4 1/2] setupapi: Implement SetupDiSetDevicePropertyW.

Zhiyi Zhang zzhang at codeweavers.com
Tue Jan 29 21:21:18 CST 2019


Signed-off-by: Zhiyi Zhang <zzhang at codeweavers.com>
---
v2: Simplify registry handling. Remove inconsistent tests.
v3: Add a FIXME for unhandled errors in SetupDiGetDevicePropertyW. Thanks to Zeb.
v4: Don't update the whole spec file.

 dlls/setupapi/devinst.c       | 101 +++++++++++++++++++++
 dlls/setupapi/setupapi.spec   |   1 +
 dlls/setupapi/tests/devinst.c | 163 +++++++++++++++++++++++++++++++++-
 include/setupapi.h            |   2 +
 4 files changed, 266 insertions(+), 1 deletion(-)

diff --git a/dlls/setupapi/devinst.c b/dlls/setupapi/devinst.c
index 36c1854654..13f7853290 100644
--- a/dlls/setupapi/devinst.c
+++ b/dlls/setupapi/devinst.c
@@ -327,6 +327,28 @@ static WCHAR *get_refstr_key_path(struct device_iface *iface)
     return path;
 }
 
+static BOOL is_valid_property_type(DEVPROPTYPE prop_type)
+{
+    DWORD type = prop_type & DEVPROP_MASK_TYPE;
+    DWORD typemod = prop_type & DEVPROP_MASK_TYPEMOD;
+
+    if (type > MAX_DEVPROP_TYPE)
+        return FALSE;
+    if (typemod > MAX_DEVPROP_TYPEMOD)
+        return FALSE;
+
+    if (typemod == DEVPROP_TYPEMOD_ARRAY
+        && (type == DEVPROP_TYPE_EMPTY || type == DEVPROP_TYPE_NULL || type == DEVPROP_TYPE_STRING
+            || type == DEVPROP_TYPE_SECURITY_DESCRIPTOR_STRING))
+        return FALSE;
+
+    if (typemod == DEVPROP_TYPEMOD_LIST
+        && !(type == DEVPROP_TYPE_STRING || type == DEVPROP_TYPE_SECURITY_DESCRIPTOR_STRING))
+        return FALSE;
+
+    return TRUE;
+}
+
 static LPWSTR SETUPDI_CreateSymbolicLinkPath(LPCWSTR instanceId,
         const GUID *InterfaceClassGuid, LPCWSTR ReferenceString)
 {
@@ -3410,6 +3432,85 @@ BOOL WINAPI SetupDiSetDeviceInstallParamsW(
     return TRUE;
 }
 
+BOOL WINAPI SetupDiSetDevicePropertyW(HDEVINFO devinfo, PSP_DEVINFO_DATA device_data, const DEVPROPKEY *key,
+                                      DEVPROPTYPE type, const BYTE *buffer, DWORD size, DWORD flags)
+{
+    static const WCHAR propertiesW[] = {'P', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's', 0};
+    static const WCHAR formatW[] = {'\\', '%', '0', '4', 'X', 0};
+    struct device *device;
+    HKEY properties_hkey, property_hkey;
+    WCHAR property_hkey_path[44];
+    LSTATUS ls;
+
+    TRACE("%p %p %p %#x %p %d %#x\n", devinfo, device_data, key, type, buffer, size, flags);
+
+    if (!(device = get_device(devinfo, device_data)))
+        return FALSE;
+
+    if (!key || !is_valid_property_type(type)
+        || (buffer && !size && !(type == DEVPROP_TYPE_EMPTY || type == DEVPROP_TYPE_NULL))
+        || (buffer && size && (type == DEVPROP_TYPE_EMPTY || type == DEVPROP_TYPE_NULL)))
+    {
+        SetLastError(ERROR_INVALID_DATA);
+        return FALSE;
+    }
+
+    if (size && !buffer)
+    {
+        SetLastError(ERROR_INVALID_USER_BUFFER);
+        return FALSE;
+    }
+
+    if (flags)
+    {
+        SetLastError(ERROR_INVALID_FLAGS);
+        return FALSE;
+    }
+
+    ls = RegCreateKeyExW(device->key, propertiesW, 0, NULL, 0, KEY_READ | KEY_WRITE, NULL, &properties_hkey, NULL);
+    if (ls)
+    {
+        SetLastError(ls);
+        return FALSE;
+    }
+
+    SETUPDI_GuidToString(&key->fmtid, property_hkey_path);
+    sprintfW(property_hkey_path + 38, formatW, key->pid);
+
+    if (type == DEVPROP_TYPE_EMPTY)
+    {
+        ls = RegDeleteKeyW(properties_hkey, property_hkey_path);
+        RegCloseKey(properties_hkey);
+        SetLastError(ls == ERROR_FILE_NOT_FOUND ? ERROR_NOT_FOUND : ls);
+        return !ls;
+    }
+    else if (type == DEVPROP_TYPE_NULL)
+    {
+        if (!(ls = RegOpenKeyW(properties_hkey, property_hkey_path, &property_hkey)))
+        {
+            ls = RegDeleteValueW(property_hkey, NULL);
+            RegCloseKey(property_hkey);
+        }
+
+        RegCloseKey(properties_hkey);
+        SetLastError(ls == ERROR_FILE_NOT_FOUND ? ERROR_NOT_FOUND : ls);
+        return !ls;
+    }
+    else
+    {
+        if (!(ls = RegCreateKeyExW(properties_hkey, property_hkey_path, 0, NULL, 0, KEY_READ | KEY_WRITE, NULL,
+                                  &property_hkey, NULL)))
+        {
+            ls = RegSetValueExW(property_hkey, NULL, 0, 0xffff0000 | (0xffff & type), buffer, size);
+            RegCloseKey(property_hkey);
+        }
+
+        RegCloseKey(properties_hkey);
+        SetLastError(ls);
+        return !ls;
+    }
+}
+
 static HKEY SETUPDI_OpenDevKey(struct device *device, REGSAM samDesired)
 {
     HKEY enumKey, key = INVALID_HANDLE_VALUE;
diff --git a/dlls/setupapi/setupapi.spec b/dlls/setupapi/setupapi.spec
index 0b44e184fc..844bb21ecc 100644
--- a/dlls/setupapi/setupapi.spec
+++ b/dlls/setupapi/setupapi.spec
@@ -389,6 +389,7 @@
 @ stdcall SetupDiSetClassInstallParamsW(ptr ptr ptr long)
 @ stdcall SetupDiSetDeviceInstallParamsA(ptr ptr ptr)
 @ stdcall SetupDiSetDeviceInstallParamsW(ptr ptr ptr)
+@ stdcall SetupDiSetDevicePropertyW(ptr ptr ptr long ptr long long)
 @ stdcall SetupDiSetDeviceRegistryPropertyA(ptr ptr long ptr long)
 @ stdcall SetupDiSetDeviceRegistryPropertyW(ptr ptr long ptr long)
 @ stub SetupDiSetDriverInstallParamsA
diff --git a/dlls/setupapi/tests/devinst.c b/dlls/setupapi/tests/devinst.c
index 74fa545029..0a77fca9c2 100644
--- a/dlls/setupapi/tests/devinst.c
+++ b/dlls/setupapi/tests/devinst.c
@@ -26,7 +26,8 @@
 #include "wingdi.h"
 #include "winuser.h"
 #include "winreg.h"
-#include "guiddef.h"
+#include "initguid.h"
+#include "devpkey.h"
 #include "setupapi.h"
 #include "cfgmgr32.h"
 
@@ -37,6 +38,8 @@
 static GUID guid = {0x6a55b5a4, 0x3f65, 0x11db, {0xb7,0x04,0x00,0x11,0x95,0x5c,0x2b,0xdb}};
 static GUID guid2 = {0x6a55b5a5, 0x3f65, 0x11db, {0xb7,0x04,0x00,0x11,0x95,0x5c,0x2b,0xdb}};
 
+BOOL (WINAPI *pSetupDiSetDevicePropertyW)(HDEVINFO, PSP_DEVINFO_DATA, const DEVPROPKEY *, DEVPROPTYPE, const BYTE *, DWORD, DWORD);
+
 static void test_create_device_list_ex(void)
 {
     static const WCHAR machine[] = { 'd','u','m','m','y',0 };
@@ -342,6 +345,163 @@ static void test_device_info(void)
     SetupDiDestroyDeviceInfoList(set);
 }
 
+static void test_device_property(void)
+{
+    static const WCHAR valueW[] = {'d', 'e', 'a', 'd', 'b', 'e', 'e', 'f', 0};
+    SP_DEVINFO_DATA device_data = {sizeof(device_data)};
+    HMODULE hmod;
+    HDEVINFO set;
+    DWORD err;
+    BOOL ret;
+
+    hmod = LoadLibraryA("setupapi.dll");
+    pSetupDiSetDevicePropertyW = (void *)GetProcAddress(hmod, "SetupDiSetDevicePropertyW");
+
+    if (!pSetupDiSetDevicePropertyW)
+    {
+        win_skip("SetupDiSetDevicePropertyW() are only available on vista+, skipping tests.\n");
+        FreeLibrary(hmod);
+        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_data);
+    ok(ret, "Failed to create device, error %#x.\n", GetLastError());
+
+    /* SetupDiSetDevicePropertyW */
+    /* #1 Null device info list */
+    SetLastError(0xdeadbeef);
+    ret = pSetupDiSetDevicePropertyW(NULL, &device_data, &DEVPKEY_Device_FriendlyName, DEVPROP_TYPE_STRING, (const BYTE *)valueW, sizeof(valueW), 0);
+    err = GetLastError();
+    ok(!ret, "Expect failure\n");
+    ok(err == ERROR_INVALID_HANDLE, "Expect last error %#x, got %#x\n", ERROR_INVALID_HANDLE, err);
+
+    /* #2 Null device */
+    SetLastError(0xdeadbeef);
+    ret = pSetupDiSetDevicePropertyW(set, NULL, &DEVPKEY_Device_FriendlyName, DEVPROP_TYPE_STRING, (const BYTE *)valueW, sizeof(valueW), 0);
+    err = GetLastError();
+    ok(!ret, "Expect failure\n");
+    ok(err == ERROR_INVALID_PARAMETER, "Expect last error %#x, got %#x\n", ERROR_INVALID_PARAMETER, err);
+
+    /* #3 Null property key pointer */
+    SetLastError(0xdeadbeef);
+    ret = pSetupDiSetDevicePropertyW(set, &device_data, NULL, DEVPROP_TYPE_STRING, (const BYTE *)valueW, sizeof(valueW), 0);
+    err = GetLastError();
+    ok(!ret, "Expect failure\n");
+    ok(err == ERROR_INVALID_DATA, "Expect last error %#x, got %#x\n", ERROR_INVALID_DATA, err);
+
+    /* #4 Invalid property key type */
+    SetLastError(0xdeadbeef);
+    ret = pSetupDiSetDevicePropertyW(set, &device_data, &DEVPKEY_Device_FriendlyName, 0xffff, (const BYTE *)valueW, sizeof(valueW), 0);
+    err = GetLastError();
+    ok(!ret, "Expect failure\n");
+    ok(err == ERROR_INVALID_DATA, "Expect last error %#x, got %#x\n", ERROR_INVALID_DATA, err);
+
+    /* #5 Null buffer pointer */
+    SetLastError(0xdeadbeef);
+    ret = pSetupDiSetDevicePropertyW(set, &device_data, &DEVPKEY_Device_FriendlyName, DEVPROP_TYPE_STRING, NULL, sizeof(valueW), 0);
+    err = GetLastError();
+    ok(!ret, "Expect failure\n");
+    ok(err == ERROR_INVALID_USER_BUFFER, "Expect last error %#x, got %#x\n", ERROR_INVALID_USER_BUFFER, err);
+
+    /* #6 Zero buffer size */
+    SetLastError(0xdeadbeef);
+    ret = pSetupDiSetDevicePropertyW(set, &device_data, &DEVPKEY_Device_FriendlyName, DEVPROP_TYPE_STRING, (const BYTE *)valueW, 0, 0);
+    err = GetLastError();
+    ok(!ret, "Expect failure\n");
+    ok(err == ERROR_INVALID_DATA, "Expect last error %#x, got %#x\n", ERROR_INVALID_DATA, err);
+
+    /* #7 Flags not zero */
+    SetLastError(0xdeadbeef);
+    ret = pSetupDiSetDevicePropertyW(set, &device_data, &DEVPKEY_Device_FriendlyName, DEVPROP_TYPE_STRING, (const BYTE *)valueW, sizeof(valueW), 1);
+    err = GetLastError();
+    ok(!ret, "Expect failure\n");
+    ok(err == ERROR_INVALID_FLAGS, "Expect last error %#x, got %#x\n", ERROR_INVALID_FLAGS, err);
+
+    /* #8 Normal */
+    SetLastError(0xdeadbeef);
+    ret = pSetupDiSetDevicePropertyW(set, &device_data, &DEVPKEY_Device_FriendlyName, DEVPROP_TYPE_STRING, (const BYTE *)valueW, sizeof(valueW), 0);
+    err = GetLastError();
+    ok(ret, "Expect success\n");
+    ok(err == NO_ERROR, "Expect last error %#x, got %#x\n", NO_ERROR, err);
+
+    /* #9 Delete property with buffer not null */
+    ret = pSetupDiSetDevicePropertyW(set, &device_data, &DEVPKEY_Device_FriendlyName, DEVPROP_TYPE_STRING, (const BYTE *)valueW, sizeof(valueW), 0);
+    ok(ret, "Expect success\n");
+    SetLastError(0xdeadbeef);
+    ret = pSetupDiSetDevicePropertyW(set, &device_data, &DEVPKEY_Device_FriendlyName, DEVPROP_TYPE_EMPTY, (const BYTE *)valueW, 0, 0);
+    err = GetLastError();
+    ok(ret, "Expect success\n");
+    ok(err == NO_ERROR, "Expect last error %#x, got %#x\n", NO_ERROR, err);
+
+    /* #10 Delete property with size not zero */
+    ret = pSetupDiSetDevicePropertyW(set, &device_data, &DEVPKEY_Device_FriendlyName, DEVPROP_TYPE_STRING, (const BYTE *)valueW, sizeof(valueW), 0);
+    ok(ret, "Expect success\n");
+    SetLastError(0xdeadbeef);
+    ret = pSetupDiSetDevicePropertyW(set, &device_data, &DEVPKEY_Device_FriendlyName, DEVPROP_TYPE_EMPTY, NULL, sizeof(valueW), 0);
+    err = GetLastError();
+    ok(!ret, "Expect failure\n");
+    ok(err == ERROR_INVALID_USER_BUFFER, "Expect last error %#x, got %#x\n", ERROR_INVALID_USER_BUFFER, err);
+
+    /* #11 Delete property */
+    ret = pSetupDiSetDevicePropertyW(set, &device_data, &DEVPKEY_Device_FriendlyName, DEVPROP_TYPE_STRING, (const BYTE *)valueW, sizeof(valueW), 0);
+    ok(ret, "Expect success\n");
+    SetLastError(0xdeadbeef);
+    ret = pSetupDiSetDevicePropertyW(set, &device_data, &DEVPKEY_Device_FriendlyName, DEVPROP_TYPE_EMPTY, NULL, 0, 0);
+    err = GetLastError();
+    ok(ret, "Expect success\n");
+    ok(err == NO_ERROR, "Expect last error %#x, got %#x\n", NO_ERROR, err);
+
+    /* #12 Delete non-existent property */
+    SetLastError(0xdeadbeef);
+    ret = pSetupDiSetDevicePropertyW(set, &device_data, &DEVPKEY_Device_FriendlyName, DEVPROP_TYPE_EMPTY, NULL, 0, 0);
+    err = GetLastError();
+    ok(!ret, "Expect failure\n");
+    ok(err == ERROR_NOT_FOUND, "Expect last error %#x, got %#x\n", ERROR_NOT_FOUND, err);
+
+    /* #13 Delete property value with buffer not null */
+    ret = pSetupDiSetDevicePropertyW(set, &device_data, &DEVPKEY_Device_FriendlyName, DEVPROP_TYPE_STRING, (const BYTE *)valueW, sizeof(valueW), 0);
+    ok(ret, "Expect success\n");
+    SetLastError(0xdeadbeef);
+    ret = pSetupDiSetDevicePropertyW(set, &device_data, &DEVPKEY_Device_FriendlyName, DEVPROP_TYPE_NULL, (const BYTE *)valueW, 0, 0);
+    err = GetLastError();
+    ok(ret, "Expect success\n");
+    ok(err == NO_ERROR, "Expect last error %#x, got %#x\n", NO_ERROR, err);
+
+    /* #14 Delete property value with size not zero */
+    ret = pSetupDiSetDevicePropertyW(set, &device_data, &DEVPKEY_Device_FriendlyName, DEVPROP_TYPE_STRING, (const BYTE *)valueW, sizeof(valueW), 0);
+    ok(ret, "Expect success\n");
+    SetLastError(0xdeadbeef);
+    ret = pSetupDiSetDevicePropertyW(set, &device_data, &DEVPKEY_Device_FriendlyName, DEVPROP_TYPE_NULL, NULL, sizeof(valueW), 0);
+    err = GetLastError();
+    ok(!ret, "Expect failure\n");
+    ok(err == ERROR_INVALID_USER_BUFFER, "Expect last error %#x, got %#x\n", ERROR_INVALID_USER_BUFFER, err);
+
+    /* #15 Delete property value */
+    ret = pSetupDiSetDevicePropertyW(set, &device_data, &DEVPKEY_Device_FriendlyName, DEVPROP_TYPE_STRING, (const BYTE *)valueW, sizeof(valueW), 0);
+    ok(ret, "Expect success\n");
+    SetLastError(0xdeadbeef);
+    ret = pSetupDiSetDevicePropertyW(set, &device_data, &DEVPKEY_Device_FriendlyName, DEVPROP_TYPE_NULL, NULL, 0, 0);
+    err = GetLastError();
+    ok(ret, "Expect success\n");
+    ok(err == NO_ERROR, "Expect last error %#x, got %#x\n", NO_ERROR, err);
+
+    /* #16 Delete non-existent property value */
+    SetLastError(0xdeadbeef);
+    ret = pSetupDiSetDevicePropertyW(set, &device_data, &DEVPKEY_Device_FriendlyName, DEVPROP_TYPE_NULL, NULL, 0, 0);
+    err = GetLastError();
+    ok(!ret, "Expect failure\n");
+    ok(err == ERROR_NOT_FOUND, "Expect last error %#x, got %#x\n", ERROR_NOT_FOUND, err);
+
+    ret = SetupDiRemoveDevice(set, &device_data);
+    ok(ret, "Got unexpected error %#x.\n", GetLastError());
+
+    SetupDiDestroyDeviceInfoList(set);
+    FreeLibrary(hmod);
+}
+
 static void test_get_device_instance_id(void)
 {
     BOOL ret;
@@ -1339,6 +1499,7 @@ START_TEST(devinst)
     test_open_class_key();
     test_install_class();
     test_device_info();
+    test_device_property();
     test_get_device_instance_id();
     test_register_device_info();
     test_device_iface();
diff --git a/include/setupapi.h b/include/setupapi.h
index 76e255cc44..4491617d8a 100644
--- a/include/setupapi.h
+++ b/include/setupapi.h
@@ -1634,6 +1634,8 @@ BOOL     WINAPI SetupDiSetDeviceInterfaceDefault(HDEVINFO, PSP_DEVICE_INTERFACE_
 BOOL     WINAPI SetupDiSetDeviceInstallParamsA(HDEVINFO, PSP_DEVINFO_DATA, PSP_DEVINSTALL_PARAMS_A);
 BOOL     WINAPI SetupDiSetDeviceInstallParamsW(HDEVINFO, PSP_DEVINFO_DATA, PSP_DEVINSTALL_PARAMS_W);
 #define         SetupDiSetDeviceInstallParams WINELIB_NAME_AW(SetupDiSetDeviceInstallParams)
+BOOL     WINAPI SetupDiSetDevicePropertyW(HDEVINFO, PSP_DEVINFO_DATA, const DEVPROPKEY *, DEVPROPTYPE, const BYTE *, DWORD, DWORD);
+#define         SetupDiSetDeviceProperty WINELIB_NAME_AW(SetupDiSetDeviceProperty) /* note: A function doesn't exist */
 BOOL     WINAPI SetupDiSetDeviceRegistryPropertyA(HDEVINFO, PSP_DEVINFO_DATA, DWORD, const BYTE *, DWORD);
 BOOL     WINAPI SetupDiSetDeviceRegistryPropertyW(HDEVINFO, PSP_DEVINFO_DATA, DWORD, const BYTE *, DWORD);
 #define         SetupDiSetDeviceRegistryProperty WINELIB_NAME_AW(SetupDiSetDeviceRegistryProperty)
-- 
2.20.1





More information about the wine-devel mailing list