I also changed the default return value of GetUserGeoID() to make it more compliant with the new APIs.
Signed-off-by: João Diogo Craveiro Ferreira devilj@outlook.pt --- dlls/kernel32/kernel32.spec | 1 + dlls/kernel32/locale.c | 137 ++++++++++++++++++++++++++++++++++-- include/winnls.h | 1 + 3 files changed, 132 insertions(+), 7 deletions(-)
diff --git a/dlls/kernel32/kernel32.spec b/dlls/kernel32/kernel32.spec index beebea9c1c..9a8e33eb9a 100644 --- a/dlls/kernel32/kernel32.spec +++ b/dlls/kernel32/kernel32.spec @@ -869,6 +869,7 @@ # @ stub GetUILanguageInfo @ stdcall -arch=x86_64 GetUmsCompletionListEvent(ptr ptr) # @ stub -arch=x86_64 GetUmsSystemThreadInformation +@ stdcall GetUserDefaultGeoName(ptr long) @ stdcall -import GetUserDefaultLCID() @ stdcall -import GetUserDefaultLangID() @ stdcall -import GetUserDefaultLocaleName(ptr long) diff --git a/dlls/kernel32/locale.c b/dlls/kernel32/locale.c index 4957e8df5e..87cf9b402e 100644 --- a/dlls/kernel32/locale.c +++ b/dlls/kernel32/locale.c @@ -4090,10 +4090,23 @@ static inline const struct geoinfo_t *get_geoinfoptr_by_name(const WCHAR *name)
/****************************************************************************** * GetUserGeoID (KERNEL32.@) + * + * Retrieves the ID of the user's geographic nation or region. + * + * PARAMS + * GeoClass [I] One of GEOCLASS_NATION or GEOCLASS_REGION (SYSGEOCLASS enum from "winnls.h"). + * + * RETURNS + * SUCCESS: The ID of the specified geographic class. + * FAILURE: GEOID_NOT_AVAILABLE. + * + * NOTES + * This function only fails if the geoclass specified is invalid; + * otherwise, a valid GeoID is always returned. */ -GEOID WINAPI GetUserGeoID( GEOCLASS GeoClass ) +GEOID WINAPI GetUserGeoID(GEOCLASS GeoClass) { - GEOID ret = GEOID_NOT_AVAILABLE; + GEOID ret = 39070; /* World */ static const WCHAR nationW[] = {'N','a','t','i','o','n',0}; static const WCHAR regionW[] = {'R','e','g','i','o','n',0}; WCHAR bufferW[40], *end; @@ -4104,11 +4117,20 @@ GEOID WINAPI GetUserGeoID( GEOCLASS GeoClass ) count = sizeof(bufferW);
if (GeoClass == GEOCLASS_NATION) + { + TRACE("(GEOCLASS_NATION)\n"); RtlInitUnicodeString( &keyW, nationW ); + } else if (GeoClass == GEOCLASS_REGION) + { + TRACE("(GEOCLASS_REGION)\n"); RtlInitUnicodeString( &keyW, regionW ); + } else - return ret; + { + WARN("Unknown geoclass %d\n", GeoClass); + return GEOID_NOT_AVAILABLE; + }
if (!(hkey = create_geo_regkey())) return ret;
@@ -4121,13 +4143,114 @@ GEOID WINAPI GetUserGeoID( GEOCLASS GeoClass ) }
/****************************************************************************** - * set_geo_reg + * GetUserDefaultGeoName (KERNEL32.@) * - * Does the heavy lifting for SetUserGeoName() and SetUserGeoID(). + * Retrieves the name of the user's geographic location. * - * The return value and last error set here can (and probably should) - * be returned to the callers of those two functions. + * This name is a two-letter ISO 3166 country code + * or a three-digit UN M.49 code for anything other than countries (e.g. continents). + * + * If geoNameCount is 0, this function will return the minimum length + * required for a buffer to hold the name. + * + * PARAMS + * geoName [O] Pointer to a buffer to hold the name. + * geoNameCount [I] Length of the buffer, including null, measured in WCHARs. + * + * RETURNS + * SUCCESS: Number of WCHARs written to the buffer -OR- + * minimum length for the buffer, if geoNameCount was 0. + * FAILURE: Zero. Call GetLastError() to determine the cause. + * + * NOTES: + * On failure, GetLastError() will return one of the following values: + * - ERROR_INVALID_PARAMETER: one of the parameters was invalid. + * - ERROR_INSUFFICIENT_BUFFER: the specified buffer was too small to hold the name. + * - ERROR_BADDB: an internal error caused Wine to be unable to read the name. */ +int WINAPI GetUserDefaultGeoName(LPWSTR geoName, int geoNameCount) +{ + int ret = 0, size = 0; + DWORD count; + static const WCHAR nameW[] = {'N','a','m','e',0}; + KEY_VALUE_PARTIAL_INFORMATION kvi; + UNICODE_STRING keyname; + HANDLE hkey = create_geo_regkey(); + RtlInitUnicodeString(&keyname, nameW); + + TRACE("(%p, %d)\n", geoName, geoNameCount); + + if (!hkey) + { + SetLastError(ERROR_BADDB); + return 0; + } + + if (geoNameCount < 0 || (!geoName && geoNameCount)) + { + NtClose(hkey); + SetLastError(ERROR_INVALID_PARAMETER); + return 0; + } + + if (NtQueryValueKey(hkey, &keyname, KeyValuePartialInformation, &kvi, sizeof(kvi), &count) + == STATUS_BUFFER_OVERFLOW && kvi.DataLength) + size = kvi.DataLength / sizeof(WCHAR); + else + SetLastError(ERROR_BADDB); + + + if (size) + { + if (!geoNameCount) + ret = size; + else if (geoNameCount < size) + SetLastError(ERROR_INSUFFICIENT_BUFFER); + else if (geoNameCount >= size) + { + size_t kvi_size = 0; + KEY_VALUE_PARTIAL_INFORMATION *kvi = NULL; + + TRACE("==== geoNameCount: %d ===\n", geoNameCount); + + kvi_size = sizeof(KEY_VALUE_PARTIAL_INFORMATION) + (geoNameCount * sizeof(WCHAR)); + if (!(kvi = HeapAlloc(GetProcessHeap(), 0, kvi_size))) + { + ERR("Couldn't allocate memory for KeyValuePartialInformation. Bailing.\n"); + SetLastError(ERROR_NOT_ENOUGH_MEMORY); + } + else + { + NTSTATUS status = NtQueryValueKey(hkey, &keyname, KeyValuePartialInformation, + kvi, kvi_size, &count); + if (status == STATUS_SUCCESS) + { + ret = size; + memcpy(geoName, kvi->Data, kvi->DataLength); + } + else + { + ERR("Unexpected error retrieving geoname: status=0x%x, kvi->DataLength=%u, kvi->Data=%s\n", + status, kvi->DataLength, wine_dbgstr_w((WCHAR*)kvi->Data)); + SetLastError(ERROR_BADDB); + } + HeapFree(GetProcessHeap(), 0, kvi); + } + } + } + + NtClose(hkey); + return ret; +} + +/****************************************************************************** +* set_geo_reg +* +* Does the heavy lifting for SetUserGeoName() and SetUserGeoID(). +* +* The return value and last error set here can (and probably should) +* be returned to the callers of those two functions. +*/ static int set_geo_reg(const struct geoinfo_t *geoinfo) { static const WCHAR nationW[] = {'N','a','t','i','o','n',0}; diff --git a/include/winnls.h b/include/winnls.h index 3aeb47c9e4..059a459153 100644 --- a/include/winnls.h +++ b/include/winnls.h @@ -935,6 +935,7 @@ WINBASEAPI INT WINAPI GetTimeFormatA(LCID,DWORD,const SYSTEMTIME*,LPCSTR WINBASEAPI INT WINAPI GetTimeFormatEx(LPCWSTR,DWORD,const SYSTEMTIME*,LPCWSTR,LPWSTR,INT); WINBASEAPI INT WINAPI GetTimeFormatW(LCID,DWORD,const SYSTEMTIME*,LPCWSTR,LPWSTR,INT); #define GetTimeFormat WINELIB_NAME_AW(GetTimeFormat) +WINBASEAPI int WINAPI GetUserDefaultGeoName(LPWSTR,int); WINBASEAPI LANGID WINAPI GetUserDefaultLangID(void); WINBASEAPI LCID WINAPI GetUserDefaultLCID(void); WINBASEAPI INT WINAPI GetUserDefaultLocaleName(LPWSTR,int);
Hi,
While running your changed tests, I think I found new failures. Being a bot and all I'm not very good at pattern recognition, so I might be wrong, but could you please double-check?
Full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=58230
Your paranoid android.
=== debian10 (32 bit Chinese:China report) ===
kernel32: debugger.c:320: Test failed: GetThreadContext failed: 5
=== debian10 (32 bit WoW report) ===
kernel32: comm.c:919: Test failed: OutQueue should not be empty
=== debian10 (64 bit WoW report) ===
kernel32: debugger.c:320: Test failed: GetThreadContext failed: 5