This patch lays the groundwork for autodetecting the user's GeoID in the future, preventing many headaches with programs crashing. See bug #46196: https://bugs.winehq.org/show_bug.cgi?id=46196
Also better error reporting brings these functions to (almost) full conformity with Windows, which is also just nice :)
Signed-off-by: João Diogo Craveiro Ferreira devilj@outlook.pt --- dlls/kernel32/locale.c | 191 +++++++++++++++++++++++++---------------- 1 file changed, 116 insertions(+), 75 deletions(-)
diff --git a/dlls/kernel32/locale.c b/dlls/kernel32/locale.c index d44a2b0916..653cea000d 100644 --- a/dlls/kernel32/locale.c +++ b/dlls/kernel32/locale.c @@ -4347,81 +4347,6 @@ BOOL WINAPI InvalidateNLSCache(void) return FALSE; }
-/****************************************************************************** - * GetUserGeoID (KERNEL32.@) - */ -GEOID WINAPI GetUserGeoID( GEOCLASS GeoClass ) -{ - GEOID ret = GEOID_NOT_AVAILABLE; - static const WCHAR geoW[] = {'G','e','o',0}; - static const WCHAR nationW[] = {'N','a','t','i','o','n',0}; - WCHAR bufferW[40], *end; - DWORD count; - HANDLE hkey, hSubkey = 0; - UNICODE_STRING keyW; - const KEY_VALUE_PARTIAL_INFORMATION *info = (KEY_VALUE_PARTIAL_INFORMATION *)bufferW; - RtlInitUnicodeString( &keyW, nationW ); - count = sizeof(bufferW); - - if(!(hkey = create_registry_key())) return ret; - - switch( GeoClass ){ - case GEOCLASS_NATION: - if ((hSubkey = NLS_RegOpenKey(hkey, geoW))) - { - if((NtQueryValueKey(hSubkey, &keyW, KeyValuePartialInformation, - bufferW, count, &count) == STATUS_SUCCESS ) && info->DataLength) - ret = strtolW((LPCWSTR)info->Data, &end, 10); - } - break; - case GEOCLASS_REGION: - FIXME("GEOCLASS_REGION not handled yet\n"); - break; - } - - NtClose(hkey); - if (hSubkey) NtClose(hSubkey); - return ret; -} - -/****************************************************************************** - * SetUserGeoID (KERNEL32.@) - */ -BOOL WINAPI SetUserGeoID( GEOID GeoID ) -{ - static const WCHAR geoW[] = {'G','e','o',0}; - static const WCHAR nationW[] = {'N','a','t','i','o','n',0}; - static const WCHAR formatW[] = {'%','i',0}; - UNICODE_STRING nameW,keyW; - WCHAR bufferW[10]; - OBJECT_ATTRIBUTES attr; - HANDLE hkey; - - if(!(hkey = create_registry_key())) return FALSE; - - attr.Length = sizeof(attr); - attr.RootDirectory = hkey; - attr.ObjectName = &nameW; - attr.Attributes = 0; - attr.SecurityDescriptor = NULL; - attr.SecurityQualityOfService = NULL; - RtlInitUnicodeString( &nameW, geoW ); - RtlInitUnicodeString( &keyW, nationW ); - - if (NtCreateKey( &hkey, KEY_ALL_ACCESS, &attr, 0, NULL, 0, NULL ) != STATUS_SUCCESS) - - { - NtClose(attr.RootDirectory); - return FALSE; - } - - sprintfW(bufferW, formatW, GeoID); - NtSetValueKey(hkey, &keyW, 0, REG_SZ, bufferW, (strlenW(bufferW) + 1) * sizeof(WCHAR)); - NtClose(attr.RootDirectory); - NtClose(hkey); - return TRUE; -} - typedef struct { union @@ -4853,6 +4778,122 @@ static const struct geoinfo_t *get_geoinfo_dataptr(GEOID geoid) return NULL; }
+/****************************************************************************** + * GetUserGeoID (KERNEL32.@) + * Fetches a user's geographic location ID. + * + * PARAMS + * GeoClass [I] One of GEOCLASS_NATION or GEOCLASS_REGION. + * + * RETURNS + * Success: The GeoID for the requested class. + * Failure: GEOID_NOT_AVAILABLE, likely because the GeoID for that class had not been set yet. + * + */ +GEOID WINAPI GetUserGeoID(GEOCLASS GeoClass) +{ + GEOID ret = GEOID_NOT_AVAILABLE; + static const WCHAR geoW[] = {'G','e','o',0}; + 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; + DWORD count; + HANDLE hkey, hSubkey = 0; + UNICODE_STRING keyW; + const KEY_VALUE_PARTIAL_INFORMATION *info = (KEY_VALUE_PARTIAL_INFORMATION *)bufferW; + count = sizeof(bufferW); + + if (GeoClass == GEOCLASS_NATION) + RtlInitUnicodeString( &keyW, nationW ); + else if(GeoClass == GEOCLASS_REGION) + RtlInitUnicodeString( &keyW, regionW ); + else + return ret; + + if(!(hkey = create_registry_key())) return ret; + + if ((hSubkey = NLS_RegOpenKey(hkey, geoW))) + { + if((NtQueryValueKey(hSubkey, &keyW, KeyValuePartialInformation, + bufferW, count, &count) == STATUS_SUCCESS ) && info->DataLength) + ret = strtolW((LPCWSTR)info->Data, &end, 10); + } + + NtClose(hkey); + if (hSubkey) NtClose(hSubkey); + return ret; +} + +/****************************************************************************** + * SetUserGeoID (KERNEL32.@) + * Sets the user's geographic location ID. + * + * PARAMS + * GeoID [I] The new GeoID. + * + * RETURNS + * Success: TRUE. + * Failure: FALSE, if the GeoID was invalid or an unexpected error occured. Call GetLastError() to learn the reason. + * + * NOTES + * GetLastError() will report the following possible error conditions: + * - ERROR_INVALID_PARAMETER: The GeoID was invalid. + * - ERROR_INTERNAL_ERROR: There was an unexpected error. + * + * GeoIDs may be internally classified as LOCATION_BOTH; these are functionally identical to GEOCLASS_REGION, + * and will be set as region ID only, not as a nation. + * + */ +BOOL WINAPI SetUserGeoID(GEOID GeoID) +{ + const struct geoinfo_t *geoinfo = get_geoinfo_dataptr(GeoID); + static const WCHAR geoW[] = {'G','e','o',0}; + static const WCHAR nationW[] = {'N','a','t','i','o','n',0}; + static const WCHAR regionW[] = {'R','e','g','i','o','n',0}; + static const WCHAR formatW[] = {'%','i',0}; + UNICODE_STRING nameW,keyW; + WCHAR bufferW[10]; + OBJECT_ATTRIBUTES attr; + HANDLE hkey; + + if(!geoinfo) + { + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + if(!(hkey = create_registry_key())) + { + SetLastError(ERROR_INTERNAL_ERROR); + return FALSE; + } + + attr.Length = sizeof(attr); + attr.RootDirectory = hkey; + attr.ObjectName = &nameW; + attr.Attributes = 0; + attr.SecurityDescriptor = NULL; + attr.SecurityQualityOfService = NULL; + RtlInitUnicodeString( &nameW, geoW ); + + if (geoinfo->kind == LOCATION_NATION) + RtlInitUnicodeString( &keyW, nationW ); + else + RtlInitUnicodeString( &keyW, regionW ); + + if (NtCreateKey( &hkey, KEY_ALL_ACCESS, &attr, 0, NULL, 0, NULL ) != STATUS_SUCCESS) + { + NtClose(attr.RootDirectory); + SetLastError(ERROR_INTERNAL_ERROR); + return FALSE; + } + + sprintfW(bufferW, formatW, geoinfo->id); + NtSetValueKey(hkey, &keyW, 0, REG_SZ, bufferW, (strlenW(bufferW) + 1) * sizeof(WCHAR)); + NtClose(attr.RootDirectory); + NtClose(hkey); + return TRUE; +} + /****************************************************************************** * GetGeoInfoW (KERNEL32.@) */
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=57026
Your paranoid android.
=== debian10 (64 bit WoW report) ===
kernel32: debugger.c:320: Test failed: GetThreadContext failed: 5
Sorry for spamming this. All of those were sent hours ago, not sure why they're only showing up now. Thought they had been sent to /dev/null since I wasn't subscribed to the list yet.
And if this wasn't embarrassing enough, now I'll have the Marvin the Testbot spamming all my mistakes right back at me.
Sorry once again.