[PATCH] kernel32: Handle GEOCLASS_REGION in Get/SetUserGeoID.
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(a)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.@) */ -- 2.23.0
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.
participants (2)
-
João Diogo Ferreira -
Marvin