The new UN codes (required for SetUserGeoName()) were tested
and confirmed to match with Windows 10 v1909.
Introduced new helpers to keep this file clean.
I wrote some documentation. This is my original work
and/or work copied from the Wine source itself
and does not include any other copyrighted material,
nor does it copy any content owned or written by Microsoft.
Signed-off-by: João Diogo Craveiro Ferreira <devilj(a)outlook.pt>
---
Supersedes: 172357
V3: Fix build error from patch #2.
V2: Squash UN codes and clean-up into this commit.
---
dlls/kernel32/kernel32.spec | 1 +
dlls/kernel32/locale.c | 336 ++++++++++++++++++++++++++----------
include/winnls.h | 1 +
3 files changed, 251 insertions(+), 87 deletions(-)
diff --git a/dlls/kernel32/kernel32.spec b/dlls/kernel32/kernel32.spec
index 23c25b7acd..9898bdec6e 100644
--- a/dlls/kernel32/kernel32.spec
+++ b/dlls/kernel32/kernel32.spec
@@ -1469,6 +1469,7 @@
@ stdcall -arch=x86_64 SetUmsThreadInformation(ptr long ptr long)
@ stdcall -import SetUnhandledExceptionFilter(ptr)
@ stdcall SetUserGeoID(long)
+@ stdcall SetUserGeoName(wstr)
@ stub SetVDMCurrentDirectories
@ stdcall SetVolumeLabelA(str str)
@ stdcall SetVolumeLabelW(wstr wstr)
diff --git a/dlls/kernel32/locale.c b/dlls/kernel32/locale.c
index 4bb0b16109..5bb314b595 100644
--- a/dlls/kernel32/locale.c
+++ b/dlls/kernel32/locale.c
@@ -645,11 +645,11 @@ static BOOL is_genitive_name_supported( LCTYPE lctype )
}
/***********************************************************************
- * create_registry_key
+ * create_intl_regkey
*
* Create the Control Panel\\International registry key.
*/
-static inline HANDLE create_registry_key(void)
+static inline HANDLE create_intl_regkey(void)
{
static const WCHAR cplW[] = {'C','o','n','t','r','o','l',' ','P','a','n','e','l',0};
static const WCHAR intlW[] = {'I','n','t','e','r','n','a','t','i','o','n','a','l',0};
@@ -678,6 +678,37 @@ static inline HANDLE create_registry_key(void)
return hkey;
}
+/***********************************************************************
+ * create_geo_regkey
+ *
+ * Create the Control Panel\\International\\Geo registry key.
+ */
+
+static inline HANDLE create_geo_regkey(void)
+{
+ static const WCHAR geoW[] = {'G','e','o',0};
+ UNICODE_STRING name;
+ OBJECT_ATTRIBUTES attr;
+ HANDLE intl_key, hkey;
+
+ if (!(intl_key = create_intl_regkey()))
+ return 0;
+
+ attr.Length = sizeof(attr);
+ attr.RootDirectory = intl_key;
+ attr.ObjectName = &name;
+ attr.Attributes = 0;
+ attr.SecurityDescriptor = NULL;
+ attr.SecurityQualityOfService = NULL;
+ RtlInitUnicodeString(&name, geoW);
+
+ if (NtCreateKey( &hkey, KEY_ALL_ACCESS, &attr, 0, NULL, 0, NULL ) != STATUS_SUCCESS)
+ hkey = 0;
+
+ NtClose(intl_key);
+ return hkey;
+}
+
/* update the registry settings for a given locale parameter */
/* return TRUE if an update was needed */
@@ -790,7 +821,7 @@ void LOCALE_InitRegistry(void)
HANDLE hkey;
LCID lcid = GetUserDefaultLCID();
- if (!(hkey = create_registry_key()))
+ if (!(hkey = create_intl_regkey()))
return; /* don't do anything if we can't create the registry key */
locale_update_registry( hkey, localeW, lcid_LC_MESSAGES, lc_messages_values,
@@ -1240,7 +1271,7 @@ static INT get_registry_locale_info( struct registry_value *registry_value, LPWS
if (!registry_value->cached_value)
{
- if (!(hkey = create_registry_key()))
+ if (!(hkey = create_intl_regkey()))
{
RtlLeaveCriticalSection( &cache_section );
return -1;
@@ -1686,7 +1717,7 @@ BOOL WINAPI SetLocaleInfoW( LCID lcid, LCTYPE lctype, LPCWSTR data )
/* FIXME: profile functions should map to registry */
WriteProfileStringW( intlW, value->name, data );
- if (!(hkey = create_registry_key())) return FALSE;
+ if (!(hkey = create_intl_regkey())) return FALSE;
RtlInitUnicodeString( &valueW, value->name );
status = NtSetValueKey( hkey, &valueW, 0, REG_SZ, data, (strlenW(data)+1)*sizeof(WCHAR) );
@@ -3593,21 +3624,6 @@ void LOCALE_Init(void)
setlocale(LC_NUMERIC, "C"); /* FIXME: oleaut32 depends on this */
}
-static HANDLE NLS_RegOpenKey(HANDLE hRootKey, LPCWSTR szKeyName)
-{
- UNICODE_STRING keyName;
- OBJECT_ATTRIBUTES attr;
- HANDLE hkey;
-
- RtlInitUnicodeString( &keyName, szKeyName );
- InitializeObjectAttributes(&attr, &keyName, 0, hRootKey, NULL);
-
- if (NtOpenKey( &hkey, KEY_READ, &attr ) != STATUS_SUCCESS)
- hkey = 0;
-
- return hkey;
-}
-
/******************************************************************************
* EnumSystemLanguageGroupsA (KERNEL32.@)
*/
@@ -3889,7 +3905,7 @@ static const struct geoinfo_t geoinfodata[] = {
{ 305, {'X','X',0}, {'X','X',0}, 161832256 }, /* Baker Island */
{ 306, {'B','V',0}, {'B','V','T',0}, 39070, 74 }, /* Bouvet Island */
{ 307, {'K','Y',0}, {'C','Y','M',0}, 10039880, 136 }, /* Cayman Islands */
- { 308, {'X','X',0}, {'X','X',0}, 10210824, 0, LOCATION_BOTH }, /* Channel Islands */
+ { 308, {'X','X',0}, {'X','X',0}, 10210824, 830, LOCATION_BOTH }, /* Channel Islands */
{ 309, {'C','X',0}, {'C','X','R',0}, 12, 162 }, /* Christmas Island */
{ 310, {'X','X',0}, {'X','X',0}, 27114 }, /* Clipperton Island */
{ 311, {'C','C',0}, {'C','C','K',0}, 10210825, 166 }, /* Cocos (Keeling) Islands */
@@ -3929,47 +3945,54 @@ static const struct geoinfo_t geoinfodata[] = {
{ 349, {'T','C',0}, {'T','C','A',0}, 10039880, 796 }, /* Turks and Caicos Islands */
{ 351, {'V','G',0}, {'V','G','B',0}, 10039880, 92 }, /* Virgin Islands, British */
{ 352, {'W','F',0}, {'W','L','F',0}, 26286, 876 }, /* Wallis and Futuna */
- { 742, {'X','X',0}, {'X','X',0}, 39070, 0, LOCATION_REGION }, /* Africa */
- { 2129, {'X','X',0}, {'X','X',0}, 39070, 0, LOCATION_REGION }, /* Asia */
- { 10541, {'X','X',0}, {'X','X',0}, 39070, 0, LOCATION_REGION }, /* Europe */
+ { 742, {'X','X',0}, {'X','X',0}, 39070, 2, LOCATION_REGION }, /* Africa */
+ { 2129, {'X','X',0}, {'X','X',0}, 39070, 142, LOCATION_REGION }, /* Asia */
+ { 10541, {'X','X',0}, {'X','X',0}, 39070, 150, LOCATION_REGION }, /* Europe */
{ 15126, {'I','M',0}, {'I','M','N',0}, 10039882, 833 }, /* Man, Isle of */
{ 19618, {'M','K',0}, {'M','K','D',0}, 47610, 807 }, /* Macedonia, Former Yugoslav Republic of */
- { 20900, {'X','X',0}, {'X','X',0}, 27114, 0, LOCATION_REGION }, /* Melanesia */
- { 21206, {'X','X',0}, {'X','X',0}, 27114, 0, LOCATION_REGION }, /* Micronesia */
+ { 20900, {'X','X',0}, {'X','X',0}, 27114, 54, LOCATION_REGION }, /* Melanesia */
+ { 21206, {'X','X',0}, {'X','X',0}, 27114, 57, LOCATION_REGION }, /* Micronesia */
{ 21242, {'X','X',0}, {'X','X',0}, 161832256 }, /* Midway Islands */
- { 23581, {'X','X',0}, {'X','X',0}, 10026358, 0, LOCATION_REGION }, /* Northern America */
- { 26286, {'X','X',0}, {'X','X',0}, 27114, 0, LOCATION_REGION }, /* Polynesia */
- { 27082, {'X','X',0}, {'X','X',0}, 161832257, 0, LOCATION_REGION }, /* Central America */
- { 27114, {'X','X',0}, {'X','X',0}, 39070, 0, LOCATION_REGION }, /* Oceania */
+ { 23581, {'X','X',0}, {'X','X',0}, 10026358, 21, LOCATION_REGION }, /* Northern America */
+ { 26286, {'X','X',0}, {'X','X',0}, 27114, 61, LOCATION_REGION }, /* Polynesia */
+ { 27082, {'X','X',0}, {'X','X',0}, 161832257, 13, LOCATION_REGION }, /* Central America */
+ { 27114, {'X','X',0}, {'X','X',0}, 39070, 9, LOCATION_REGION }, /* Oceania */
{ 30967, {'S','X',0}, {'S','X','M',0}, 10039880, 534 }, /* Sint Maarten (Dutch part) */
- { 31396, {'X','X',0}, {'X','X',0}, 161832257, 0, LOCATION_REGION }, /* South America */
+ { 31396, {'X','X',0}, {'X','X',0}, 161832257, 5, LOCATION_REGION }, /* South America */
{ 31706, {'M','F',0}, {'M','A','F',0}, 10039880, 663 }, /* Saint Martin (French part) */
- { 39070, {'X','X',0}, {'X','X',0}, 39070, 0, LOCATION_REGION }, /* World */
- { 42483, {'X','X',0}, {'X','X',0}, 742, 0, LOCATION_REGION }, /* Western Africa */
- { 42484, {'X','X',0}, {'X','X',0}, 742, 0, LOCATION_REGION }, /* Middle Africa */
- { 42487, {'X','X',0}, {'X','X',0}, 742, 0, LOCATION_REGION }, /* Northern Africa */
- { 47590, {'X','X',0}, {'X','X',0}, 2129, 0, LOCATION_REGION }, /* Central Asia */
- { 47599, {'X','X',0}, {'X','X',0}, 2129, 0, LOCATION_REGION }, /* South-Eastern Asia */
- { 47600, {'X','X',0}, {'X','X',0}, 2129, 0, LOCATION_REGION }, /* Eastern Asia */
- { 47603, {'X','X',0}, {'X','X',0}, 742, 0, LOCATION_REGION }, /* Eastern Africa */
- { 47609, {'X','X',0}, {'X','X',0}, 10541, 0, LOCATION_REGION }, /* Eastern Europe */
- { 47610, {'X','X',0}, {'X','X',0}, 10541, 0, LOCATION_REGION }, /* Southern Europe */
- { 47611, {'X','X',0}, {'X','X',0}, 2129, 0, LOCATION_REGION }, /* Middle East */
- { 47614, {'X','X',0}, {'X','X',0}, 2129, 0, LOCATION_REGION }, /* Southern Asia */
+ { 39070, {'X','X',0}, {'X','X',0}, 39070, 1, LOCATION_REGION }, /* World */
+ { 42483, {'X','X',0}, {'X','X',0}, 742, 11, LOCATION_REGION }, /* Western Africa */
+ { 42484, {'X','X',0}, {'X','X',0}, 742, 17, LOCATION_REGION }, /* Middle Africa */
+ { 42487, {'X','X',0}, {'X','X',0}, 742, 15, LOCATION_REGION }, /* Northern Africa */
+ { 47590, {'X','X',0}, {'X','X',0}, 2129, 143, LOCATION_REGION }, /* Central Asia */
+ { 47599, {'X','X',0}, {'X','X',0}, 2129, 35, LOCATION_REGION }, /* South-Eastern Asia */
+ { 47600, {'X','X',0}, {'X','X',0}, 2129, 30, LOCATION_REGION }, /* Eastern Asia */
+ { 47603, {'X','X',0}, {'X','X',0}, 742, 14, LOCATION_REGION }, /* Eastern Africa */
+ { 47609, {'X','X',0}, {'X','X',0}, 10541, 151, LOCATION_REGION }, /* Eastern Europe */
+ { 47610, {'X','X',0}, {'X','X',0}, 10541, 39, LOCATION_REGION }, /* Southern Europe */
+ { 47611, {'X','X',0}, {'X','X',0}, 2129, 145, LOCATION_REGION }, /* Middle East */
+ { 47614, {'X','X',0}, {'X','X',0}, 2129, 34, LOCATION_REGION }, /* Southern Asia */
{ 7299303, {'T','L',0}, {'T','L','S',0}, 47599, 626 }, /* Democratic Republic of Timor-Leste */
- { 10026358, {'X','X',0}, {'X','X',0}, 39070, 0, LOCATION_REGION }, /* Americas */
+ { 10026358, {'X','X',0}, {'X','X',0}, 39070, 19, LOCATION_REGION }, /* Americas */
{ 10028789, {'A','X',0}, {'A','L','A',0}, 10039882, 248 }, /* Åland Islands */
- { 10039880, {'X','X',0}, {'X','X',0}, 161832257, 0, LOCATION_REGION }, /* Caribbean */
- { 10039882, {'X','X',0}, {'X','X',0}, 10541, 0, LOCATION_REGION }, /* Northern Europe */
- { 10039883, {'X','X',0}, {'X','X',0}, 742, 0, LOCATION_REGION }, /* Southern Africa */
- { 10210824, {'X','X',0}, {'X','X',0}, 10541, 0, LOCATION_REGION }, /* Western Europe */
- { 10210825, {'X','X',0}, {'X','X',0}, 27114, 0, LOCATION_REGION }, /* Australia and New Zealand */
+ { 10039880, {'X','X',0}, {'X','X',0}, 161832257, 29, LOCATION_REGION }, /* Caribbean */
+ { 10039882, {'X','X',0}, {'X','X',0}, 10541, 154, LOCATION_REGION }, /* Northern Europe */
+ { 10039883, {'X','X',0}, {'X','X',0}, 742, 18, LOCATION_REGION }, /* Southern Africa */
+ { 10210824, {'X','X',0}, {'X','X',0}, 10541, 155, LOCATION_REGION }, /* Western Europe */
+ { 10210825, {'X','X',0}, {'X','X',0}, 27114, 53, LOCATION_REGION }, /* Australia and New Zealand */
{ 161832015, {'B','L',0}, {'B','L','M',0}, 10039880, 652 }, /* Saint Barthélemy */
{ 161832256, {'U','M',0}, {'U','M','I',0}, 27114, 581 }, /* U.S. Minor Outlying Islands */
- { 161832257, {'X','X',0}, {'X','X',0}, 10026358, 0, LOCATION_REGION }, /* Latin America and the Caribbean */
+ { 161832257, {'X','X',0}, {'X','X',0}, 10026358, 419, LOCATION_REGION }, /* Latin America and the Caribbean */
};
-static const struct geoinfo_t *get_geoinfo_dataptr(GEOID geoid)
+static const WCHAR geoname_uncode_fmtW[] = {'%','0','3','i',0};
+
+/******************************************************************************
+ * get_geoinfoptr_by_id
+ *
+ * Returns a pointer to a geoinfo struct by finding its GeoID.
+ */
+static const struct geoinfo_t *get_geoinfoptr_by_id(GEOID geoid)
{
int min, max;
@@ -3994,17 +4017,87 @@ static const struct geoinfo_t *get_geoinfo_dataptr(GEOID geoid)
return NULL;
}
+/******************************************************************************
+ * get_geoinfoptr_by_str
+ *
+ * Returns a pointer to a geoinfo struct by
+ * matching a string to the specified geotype.
+ */
+
+static const struct geoinfo_t *get_geoinfoptr_by_str(const WCHAR *str, GEOTYPE geotype)
+{
+ int num;
+
+ if (!str)
+ return NULL;
+
+ switch (geotype)
+ {
+ case GEO_ISO2:
+ for (int i = 0; i < ARRAY_SIZE(geoinfodata); i++)
+ if (!(strcmpW(geoinfodata[i].iso2W, str))) return &geoinfodata[i];
+ break;
+ case GEO_ISO_UN_NUMBER:
+ if (!(num = atoiW(str))) return NULL;
+ for (int i = 0; i < ARRAY_SIZE(geoinfodata); i++)
+ if (num == geoinfodata[i].uncode) return &geoinfodata[i];
+ break;
+ }
+ return NULL;
+}
+
+/******************************************************************************
+ * get_geoinfoptr_by_name
+ *
+ * Automatically parse and fix a geoname and
+ * return a pointer to the matching geoinfo struct.
+ */
+
+static inline const struct geoinfo_t *get_geoinfoptr_by_name(const WCHAR *name)
+{
+ WCHAR buffer[3];
+ int written = 0, len = 0;
+
+ /* Check if str is a 2-letter country code (and make it uppercase) */
+ for (int i = 0; i <= 2; i++)
+ if ((name[i] <= 127 && isalphaW(name[i])))
+ {
+ buffer[i] = toupperW(name[i]);
+ written++;
+ }
+ else
+ {
+ if (!name[i])
+ {
+ buffer[i] = 0;
+ len = i;
+ }
+ break;
+ }
+ if (written == 2 && len == 2)
+ return get_geoinfoptr_by_str(buffer, GEO_ISO2);
+
+ /* Now check if it's a numerical code, up to three digits */
+ for (int i = 0; i <= 3; i++)
+ if (isdigitW(name[i])) continue;
+ else if (!name[i])
+ return get_geoinfoptr_by_str(name, GEO_ISO_UN_NUMBER);
+ else
+ break;
+
+ return NULL;
+}
+
/******************************************************************************
* 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};
static const WCHAR regionW[] = {'R','e','g','i','o','n',0};
WCHAR bufferW[40], *end;
- HANDLE hkey, hsubkey = 0;
+ HANDLE hkey;
UNICODE_STRING keyW;
const KEY_VALUE_PARTIAL_INFORMATION *info = (KEY_VALUE_PARTIAL_INFORMATION *)bufferW;
DWORD count = sizeof(bufferW);
@@ -4026,33 +4119,33 @@ GEOID WINAPI GetUserGeoID(GEOCLASS geoclass)
return ret;
}
- if (!(hkey = create_registry_key())) return ret;
+ if (!(hkey = create_geo_regkey())) return ret;
- if ((hsubkey = NLS_RegOpenKey(hkey, geoW)))
- {
- if((NtQueryValueKey(hsubkey, &keyW, KeyValuePartialInformation,
- bufferW, count, &count) == STATUS_SUCCESS ) && info->DataLength)
+ if((NtQueryValueKey(hkey, &keyW, KeyValuePartialInformation,
+ bufferW, count, &count) == STATUS_SUCCESS ) && info->DataLength)
ret = strtolW((const WCHAR*)info->Data, &end, 10);
- }
NtClose(hkey);
- if (hsubkey) NtClose(hsubkey);
return ret;
}
/******************************************************************************
- * SetUserGeoID (KERNEL32.@)
+ * 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.
*/
-BOOL WINAPI SetUserGeoID(GEOID geoid)
+static int set_geo_reg(const struct geoinfo_t *geoinfo)
{
- 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;
+ static const WCHAR nameW[] = {'N','a','m','e',0};
+ static const WCHAR id_formatW[] = {'%','i',0};
+ UNICODE_STRING idkeyW, namekeyW;
+ WCHAR geoidW[10], geonameW[4];
+ int namelen;
HANDLE hkey;
if (!geoinfo)
@@ -4060,39 +4153,108 @@ BOOL WINAPI SetUserGeoID(GEOID geoid)
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
- if (!(hkey = create_registry_key()))
+ if (!(hkey = create_geo_regkey()))
{
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);
-
+ /* Prepare GeoID */
if (geoinfo->kind == LOCATION_NATION)
- RtlInitUnicodeString(&keyW, nationW);
+ RtlInitUnicodeString(&idkeyW, nationW);
else
- RtlInitUnicodeString(&keyW, regionW);
+ RtlInitUnicodeString(&idkeyW, regionW);
+
+ sprintfW(geoidW, id_formatW, geoinfo->id);
- if (NtCreateKey(&hkey, KEY_ALL_ACCESS, &attr, 0, NULL, 0, NULL) != STATUS_SUCCESS)
+ /* Now prepare GeoName */
+ RtlInitUnicodeString( &namekeyW, nameW );
+
+ if (geoinfo->kind == LOCATION_REGION)
{
- NtClose(attr.RootDirectory);
- SetLastError(ERROR_INTERNAL_ERROR);
- return FALSE;
+ sprintfW(geonameW, geoname_uncode_fmtW, geoinfo->uncode);
+ namelen = 4;
+ }
+ else
+ {
+ strcpyW(geonameW, geoinfo->iso2W);
+ namelen = 3;
}
- sprintfW(bufferW, formatW, geoinfo->id);
- NtSetValueKey(hkey, &keyW, 0, REG_SZ, bufferW, (strlenW(bufferW) + 1) * sizeof(WCHAR));
- NtClose(attr.RootDirectory);
+ NtSetValueKey(hkey, &idkeyW, 0, REG_SZ, geoidW, (strlenW(geoidW) + 1) * sizeof(WCHAR));
+ NtSetValueKey(hkey, &namekeyW, 0, REG_SZ, geonameW, namelen * sizeof(WCHAR));
+
+ TRACE("Set %s to ID %d; set Name to %s\n",
+ wine_dbgstr_w(idkeyW.Buffer), geoinfo->id, wine_dbgstr_w(geonameW));
+
NtClose(hkey);
return TRUE;
}
+/******************************************************************************
+ * SetUserGeoID (KERNEL32.@)
+ *
+ * Sets the ID of the user's geographic location.
+ *
+ * PARAMS
+ * GeoID [I] The geographic ID to be set.
+ *
+ * RETURNS
+ * SUCCESS: TRUE.
+ * FAILURE: FALSE. Call GetLastError() to determine the cause.
+ *
+ * NOTES
+ * On success, the geographic name will be set to the location of the specified GeoID.
+ *
+ * On failure, GetLastError() will return one of the following values:
+ * - ERROR_INVALID_PARAMETER: the specified GeoID was invalid.
+ * - ERROR_INTERNAL_ERROR: an internal error prevented Wine from setting the GeoID.
+ */
+BOOL WINAPI SetUserGeoID(GEOID GeoID)
+{
+ TRACE("(%d)\n", GeoID);
+ return set_geo_reg(get_geoinfoptr_by_id(GeoID));
+}
+
+/******************************************************************************
+ * SetUserGeoName (KERNEL32.@)
+ *
+ * Sets the name of the user's geographic location.
+ *
+ * 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).
+ *
+ * PARAMS
+ * geoName [I] The name to be set.
+ *
+ * RETURNS
+ * SUCCESS: TRUE.
+ * FAILURE: FALSE. Call GetLastError() to determine the cause.
+ *
+ * NOTES
+ * If you specify the UN code of a valid country, its two-letter ISO code
+ * will be used instead.
+ *
+ * On success, the geographic ID for the relevant class will be set to
+ * the location of the specified name.
+ *
+ * On failure, GetLastError() will return one of the following values:
+ * - ERROR_INVALID_PARAMETER: the specified GeoID was invalid.
+ * - ERROR_INTERNAL_ERROR: an internal error prevented Wine from setting the name.
+ */
+BOOL WINAPI SetUserGeoName(const WCHAR *geoName)
+{
+ if (!geoName || !*geoName)
+ {
+ TRACE("(%p = (null))\n", geoName);
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return FALSE;
+ }
+
+ TRACE("(%p = %s)\n", geoName, wine_dbgstr_w(geoName));
+ return set_geo_reg(get_geoinfoptr_by_name(geoName));
+}
+
/******************************************************************************
* GetGeoInfoW (KERNEL32.@)
*/
@@ -4106,7 +4268,7 @@ INT WINAPI GetGeoInfoW(GEOID geoid, GEOTYPE geotype, LPWSTR data, int data_len,
TRACE("%d %d %p %d %d\n", geoid, geotype, data, data_len, lang);
- if (!(ptr = get_geoinfo_dataptr(geoid))) {
+ if (!(ptr = get_geoinfoptr_by_id(geoid))) {
SetLastError(ERROR_INVALID_PARAMETER);
return 0;
}
diff --git a/include/winnls.h b/include/winnls.h
index e810c44af7..a60dd1f1b0 100644
--- a/include/winnls.h
+++ b/include/winnls.h
@@ -968,6 +968,7 @@ WINBASEAPI BOOL WINAPI SetLocaleInfoW(LCID,LCTYPE,LPCWSTR);
WINBASEAPI BOOL WINAPI SetThreadLocale(LCID);
WINBASEAPI LANGID WINAPI SetThreadUILanguage(LANGID);
WINBASEAPI BOOL WINAPI SetUserGeoID(GEOID);
+WINBASEAPI BOOL WINAPI SetUserGeoName(const WCHAR*);
WINBASEAPI INT WINAPI WideCharToMultiByte(UINT,DWORD,LPCWSTR,INT,LPSTR,INT,LPCSTR,LPBOOL);
WINBASEAPI INT WINAPI FindNLSStringEx(const WCHAR *,DWORD,const WCHAR *,INT,const WCHAR *,INT,INT *,NLSVERSIONINFO *,void *,LPARAM);
--
2.23.0