[PATCH v2 3/7] kernel32: Implement SetUserGeoName() and clean-up locale.c.

João Diogo Ferreira devilj at outlook.pt
Tue Oct 29 23:30:55 CDT 2019


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 at outlook.pt>
---
Supersedes: 171823, 171824, 171827
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 cd7ec45da3..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



More information about the wine-devel mailing list