Signed-off-by: Zhiyi Zhang <zzhang(a)codeweavers.com>
---
dlls/setupapi/devinst.c | 101 +++++++++++++++++++++
dlls/setupapi/setupapi.spec | 2 +-
dlls/setupapi/tests/devinst.c | 163 +++++++++++++++++++++++++++++++++-
include/setupapi.h | 2 +
4 files changed, 266 insertions(+), 2 deletions(-)
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 eb2dab204d..fb44298292 100644
--- a/dlls/setupapi/setupapi.spec
+++ b/dlls/setupapi/setupapi.spec
@@ -475,7 +475,7 @@
@ stdcall SetupDiSetDeviceInstallParamsW(ptr ptr ptr)
@ stub SetupDiSetDeviceInterfaceDefault
@ stub SetupDiSetDeviceInterfacePropertyW
-@ stub SetupDiSetDevicePropertyW
+@ 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