[PATCH 0/1] MR11189: setupapi: Implement SPDRP_ENUMERATOR_NAME and fix missing error codes in...
SPDRP_ENUMERATOR_NAME was unmapped in PropertyMap, causing the function to return FALSE without setting any error code. Extract the enumerator name from the device instance ID for both A/W variants. Also set ERROR_INVALID_DATA for unknown properties instead of returning with stale error codes, which fixes infinite retry loops in some driver installers (e.g., vivo pcsuite). Tests added for both A and W variants: * SPDRP_UNUSED0 returns ERROR_INVALID_DATA * SPDRP_ENUMERATOR_NAME returns correct enumerator name (e.g. "ROOT") * PropertyRegDataType set to REG_SZ * RequiredSize correctly reported when probing with NULL buffer * ERROR_INSUFFICIENT_BUFFER when buffer too small or NULL with size=0 * Failure when PropertyBuffer is NULL but PropertyBufferSize \> 0 -- https://gitlab.winehq.org/wine/wine/-/merge_requests/11189
From: zhangyue <test@.com> --- dlls/setupapi/devinst.c | 103 ++++++++++++++++++++++++++++++++++ dlls/setupapi/tests/devinst.c | 80 ++++++++++++++++++++++++++ 2 files changed, 183 insertions(+) diff --git a/dlls/setupapi/devinst.c b/dlls/setupapi/devinst.c index 8f36ead4ef3..121dda98a59 100644 --- a/dlls/setupapi/devinst.c +++ b/dlls/setupapi/devinst.c @@ -3132,6 +3132,63 @@ BOOL WINAPI SetupDiGetDeviceRegistryPropertyA(HDEVINFO devinfo, return FALSE; } + if (Property == SPDRP_ENUMERATOR_NAME) + { + const WCHAR *backslash; + DWORD len; + int size_needed, size_written; + + if (!device->instanceId || !*device->instanceId) + { + SetLastError(ERROR_INVALID_DATA); + return FALSE; + } + + backslash = wcschr(device->instanceId, L'\\'); + len = backslash ? (DWORD)(backslash - device->instanceId) : lstrlenW(device->instanceId); + if (len == 0) + { + SetLastError(ERROR_INVALID_DATA); + return FALSE; + } + + size_needed = WideCharToMultiByte(CP_ACP, 0, device->instanceId, len, NULL, 0, NULL, NULL); + if (size_needed <= 0) + { + SetLastError(ERROR_INVALID_DATA); + return FALSE; + } + + size_needed++; + + if (RequiredSize) + *RequiredSize = size_needed; + + if (PropertyRegDataType) + *PropertyRegDataType = REG_SZ; + + if (!PropertyBuffer || (int)PropertyBufferSize < size_needed) + { + SetLastError(ERROR_INSUFFICIENT_BUFFER); + return FALSE; + } + + size_written = WideCharToMultiByte(CP_ACP, 0, + device->instanceId, len, + (char *)PropertyBuffer, + PropertyBufferSize - 1, + NULL, NULL); + + if (size_written <= 0) + { + SetLastError(ERROR_INVALID_DATA); + return FALSE; + } + + ((char *)PropertyBuffer)[size_written] = 0; + return TRUE; + } + if (Property < ARRAY_SIZE(PropertyMap) && PropertyMap[Property].nameA) { DWORD size = PropertyBufferSize; @@ -3149,6 +3206,10 @@ BOOL WINAPI SetupDiGetDeviceRegistryPropertyA(HDEVINFO devinfo, if (RequiredSize) *RequiredSize = size; } + else + { + SetLastError(ERROR_INVALID_DATA); + } return ret; } @@ -3174,6 +3235,44 @@ BOOL WINAPI SetupDiGetDeviceRegistryPropertyW(HDEVINFO devinfo, return FALSE; } + if (Property == SPDRP_ENUMERATOR_NAME) + { + const WCHAR *backslash; + DWORD len, size_needed; + + if (!device->instanceId || !*device->instanceId) + { + SetLastError(ERROR_INVALID_DATA); + return FALSE; + } + + backslash = wcschr(device->instanceId, L'\\'); + len = backslash ? (DWORD)(backslash - device->instanceId) : lstrlenW(device->instanceId); + if (len == 0) + { + SetLastError(ERROR_INVALID_DATA); + return FALSE; + } + + size_needed = (len + 1) * sizeof(WCHAR); + + if (RequiredSize) + *RequiredSize = size_needed; + + if (PropertyRegDataType) + *PropertyRegDataType = REG_SZ; + + if (!PropertyBuffer || PropertyBufferSize < size_needed) + { + SetLastError(ERROR_INSUFFICIENT_BUFFER); + return FALSE; + } + + memcpy(PropertyBuffer, device->instanceId, len * sizeof(WCHAR)); + ((WCHAR *)PropertyBuffer)[len] = 0; + return TRUE; + } + if (Property < ARRAY_SIZE(PropertyMap) && PropertyMap[Property].nameW) { DWORD size = PropertyBufferSize; @@ -3191,6 +3290,10 @@ BOOL WINAPI SetupDiGetDeviceRegistryPropertyW(HDEVINFO devinfo, if (RequiredSize) *RequiredSize = size; } + else + { + SetLastError(ERROR_INVALID_DATA); + } return ret; } diff --git a/dlls/setupapi/tests/devinst.c b/dlls/setupapi/tests/devinst.c index d21a78b36ad..de29d93edd3 100644 --- a/dlls/setupapi/tests/devinst.c +++ b/dlls/setupapi/tests/devinst.c @@ -2438,6 +2438,46 @@ todo_wine { ok(ret, "Failed to get property, error %#lx.\n", GetLastError()); ok(!lstrcmpA(buf, "device_name"), "Got unexpected value %s.\n", buf); + /* SPDRP_UNUSED0: PropertyMap[3].nameW == NULL, fix returns ERROR_INVALID_DATA */ + SetLastError(0xdeadbeef); + ret = SetupDiGetDeviceRegistryPropertyA(set, &device, SPDRP_UNUSED0, NULL, (BYTE *)buf, sizeof(buf), NULL); + ok(!ret, "Expected failure for SPDRP_UNUSED0.\n"); + ok(GetLastError() == ERROR_INVALID_DATA, "Got unexpected error %#lx for SPDRP_UNUSED0.\n", GetLastError()); + + /* SPDRP_ENUMERATOR_NAME: enumerator is first segment of instanceId ("ROOT\...\0000" → "ROOT") */ + memset(buf, 0, sizeof(buf)); + ret = SetupDiGetDeviceRegistryPropertyA(set, &device, SPDRP_ENUMERATOR_NAME, NULL, (BYTE *)buf, sizeof(buf), NULL); + ok(ret, "Failed to get SPDRP_ENUMERATOR_NAME, error %#lx.\n", GetLastError()); + ok(!strcmp(buf, "ROOT"), "Got unexpected enumerator '%s', expected 'ROOT'.\n", buf); + + /* SPDRP_ENUMERATOR_NAME: test PropertyRegDataType output */ + memset(buf, 0, sizeof(buf)); + type = 0xdeadbeef; + ret = SetupDiGetDeviceRegistryPropertyA(set, &device, SPDRP_ENUMERATOR_NAME, &type, (BYTE *)buf, sizeof(buf), NULL); + ok(ret, "Failed to get SPDRP_ENUMERATOR_NAME, error %#lx.\n", GetLastError()); + ok(type == REG_SZ, "Got unexpected type %#lx, expected REG_SZ.\n", type); + + /* SPDRP_ENUMERATOR_NAME: test RequiredSize output (PropertyBuffer=NULL, PropertyBufferSize=0) */ + size = 0xdeadbeef; + SetLastError(0xdeadbeef); + ret = SetupDiGetDeviceRegistryPropertyA(set, &device, SPDRP_ENUMERATOR_NAME, NULL, NULL, 0, &size); + ok(!ret, "Expected failure for NULL buffer.\n"); + ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER, "Got unexpected error %#lx.\n", GetLastError()); + ok(size > 0, "Got unexpected size %lu.\n", size); + + /* SPDRP_ENUMERATOR_NAME: test with buffer too small */ + SetLastError(0xdeadbeef); + ret = SetupDiGetDeviceRegistryPropertyA(set, &device, SPDRP_ENUMERATOR_NAME, NULL, (BYTE *)buf, 3, &size); + ok(!ret, "Expected failure for small buffer.\n"); + ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER, "Got unexpected error %#lx.\n", GetLastError()); + + /* SPDRP_ENUMERATOR_NAME: PropertyBuffer is NULL but PropertyBufferSize > 0 */ + /* Windows returns ERROR_INVALID_PARAMETER (A) or ERROR_PARTIAL_COPY (W) here, + * Wine returns ERROR_INVALID_DATA. Just check for failure. */ + SetLastError(0xdeadbeef); + ret = SetupDiGetDeviceRegistryPropertyA(set, &device, SPDRP_ENUMERATOR_NAME, NULL, NULL, sizeof(buf), NULL); + ok(!ret, "Expected failure for NULL buffer with size.\n"); + SetupDiDestroyDeviceInfoList(set); /* Create device from a registered class */ @@ -2598,6 +2638,46 @@ todo_wine { ok(ret, "Failed to get property, error %#lx.\n", GetLastError()); ok(!lstrcmpW(buf, L"device_name"), "Got unexpected value %s.\n", wine_dbgstr_w(buf)); + /* SPDRP_UNUSED0: PropertyMap[3].nameW == NULL, fix returns ERROR_INVALID_DATA */ + SetLastError(0xdeadbeef); + ret = SetupDiGetDeviceRegistryPropertyW(set, &device, SPDRP_UNUSED0, NULL, (BYTE *)buf, sizeof(buf), NULL); + ok(!ret, "Expected failure for SPDRP_UNUSED0.\n"); + ok(GetLastError() == ERROR_INVALID_DATA, "Got unexpected error %#lx for SPDRP_UNUSED0.\n", GetLastError()); + + /* SPDRP_ENUMERATOR_NAME: enumerator is first segment of instanceId ("ROOT\...\0000" → "ROOT") */ + memset(buf, 0, sizeof(buf)); + ret = SetupDiGetDeviceRegistryPropertyW(set, &device, SPDRP_ENUMERATOR_NAME, NULL, (BYTE *)buf, sizeof(buf), NULL); + ok(ret, "Failed to get SPDRP_ENUMERATOR_NAME, error %#lx.\n", GetLastError()); + ok(!lstrcmpW(buf, L"ROOT"), "Got unexpected enumerator %s.\n", wine_dbgstr_w(buf)); + + /* SPDRP_ENUMERATOR_NAME: test PropertyRegDataType output */ + memset(buf, 0, sizeof(buf)); + type = 0xdeadbeef; + ret = SetupDiGetDeviceRegistryPropertyW(set, &device, SPDRP_ENUMERATOR_NAME, &type, (BYTE *)buf, sizeof(buf), NULL); + ok(ret, "Failed to get SPDRP_ENUMERATOR_NAME, error %#lx.\n", GetLastError()); + ok(type == REG_SZ, "Got unexpected type %#lx, expected REG_SZ.\n", type); + + /* SPDRP_ENUMERATOR_NAME: test RequiredSize output (PropertyBuffer=NULL, PropertyBufferSize=0) */ + size = 0xdeadbeef; + SetLastError(0xdeadbeef); + ret = SetupDiGetDeviceRegistryPropertyW(set, &device, SPDRP_ENUMERATOR_NAME, NULL, NULL, 0, &size); + ok(!ret, "Expected failure for NULL buffer.\n"); + ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER, "Got unexpected error %#lx.\n", GetLastError()); + ok(size > 0, "Got unexpected size %lu.\n", size); + + /* SPDRP_ENUMERATOR_NAME: test with buffer too small */ + SetLastError(0xdeadbeef); + ret = SetupDiGetDeviceRegistryPropertyW(set, &device, SPDRP_ENUMERATOR_NAME, NULL, (BYTE *)buf, 3 * sizeof(WCHAR), &size); + ok(!ret, "Expected failure for small buffer.\n"); + ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER, "Got unexpected error %#lx.\n", GetLastError()); + + /* SPDRP_ENUMERATOR_NAME: PropertyBuffer is NULL but PropertyBufferSize > 0 */ + /* Windows returns ERROR_INVALID_PARAMETER (A) or ERROR_PARTIAL_COPY (W) here, + * Wine returns ERROR_INVALID_DATA. Just check for failure. */ + SetLastError(0xdeadbeef); + ret = SetupDiGetDeviceRegistryPropertyW(set, &device, SPDRP_ENUMERATOR_NAME, NULL, NULL, sizeof(buf), NULL); + ok(!ret, "Expected failure for NULL buffer with size.\n"); + SetupDiDestroyDeviceInfoList(set); /* Create device from a registered class */ -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/11189
@zfigura Friendly ping — could you take a look at this setupapi patch? It's been sitting for a while. Happy to adjust anything if needed. Thanks! -- https://gitlab.winehq.org/wine/wine/-/merge_requests/11189#note_144397
Sorry for missing that. I'm not sure if the authorship is formatted correctly; the name should match the formatting of your github name and the email is missing. The contents of the patch look fine though. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/11189#note_145408
This merge request was approved by Elizabeth Figura. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/11189
participants (3)
-
Elizabeth Figura (@zfigura) -
Yue Zhang (@YueZhang) -
zhangyue