Signed-off-by: Zhiyi Zhang zzhang@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)