[PATCH v5 3/7] kernel32: Implement SetUserGeoName().

João Diogo Ferreira devilj at outlook.pt
Thu Nov 7 13:18:34 CST 2019


Because I have the attention span of a goldfish, I forgot to update one doc.
I'll fix that in a separate patch (if this one is approved) or next revision (if explictly rejected).
________________________________
De: João Diogo Ferreira <devilj at outlook.pt>
Enviado: 6 de novembro de 2019 23:33
Para: wine-devel at winehq.org <wine-devel at winehq.org>
Cc: João Diogo Ferreira <devilj at outlook.pt>
Assunto: [PATCH v5 3/7] kernel32: Implement SetUserGeoName().

SetUserGeoName() and SetUserGeoID() are now implemented
by the same internal function, since they behave the same
(they only differ in the arguments they take).

The new UN codes (required for SetUserGeoName()) were tested
and confirmed to match with Windows 10 v1909.

Signed-off-by: João Diogo Craveiro Ferreira <devilj at outlook.pt>
---
Supersedes: 172369
V5: Move doc of SetUserGeoID() to previous patch.
---
 dlls/kernel32/kernel32.spec |   1 +
 dlls/kernel32/locale.c      | 277 ++++++++++++++++++++++++++++--------
 include/winnls.h            |   1 +
 3 files changed, 222 insertions(+), 57 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 cff06c6b6a..5731f42cdf 100644
--- a/dlls/kernel32/locale.c
+++ b/dlls/kernel32/locale.c
@@ -3889,7 +3889,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 +3929,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,6 +4001,91 @@ 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].kind == LOCATION_REGION ? &geoinfodata[i] : NULL;
+            break;
+    }
+    return NULL;
+}
+
+/******************************************************************************
+ *           get_geoinfoptr_by_name
+ *
+ * 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 good = 0, len = 0;
+
+    if (!name)
+        return NULL;
+
+    /* Check if str is a two-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]);
+            good++;
+        }
+        else
+        {
+            if (!name[i])
+            {
+                buffer[i] = 0;
+                len = i;
+            }
+            break;
+        }
+
+    if (good == 2 && len == 2)
+        return get_geoinfoptr_by_str(buffer, GEO_ISO2);
+
+    /* Now check if it's a three-digit code. */
+    good = 0;
+    len = 0;
+
+    for (int i = 0; i <= 3; i++)
+        if (isdigitW(name[i]))
+            good++;
+        else
+        {
+            if (!name[i])
+                len = i;
+            break;
+        }
+
+    if (good == 3 && len == 3)
+        return get_geoinfoptr_by_str(name, GEO_ISO_UN_NUMBER);
+
+    return NULL;
+}
+
 /******************************************************************************
  *           GetUserGeoID (KERNEL32.@)
  *
@@ -4050,28 +4142,25 @@ GEOID WINAPI GetUserGeoID(GEOCLASS geoclass)
 }

 /******************************************************************************
- *           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. GetLastError() will return ERROR_INVALID_PARAMETER if the ID was invalid.
- */
-BOOL WINAPI SetUserGeoID(GEOID geoid)
+*           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)
 {
-    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];
+    static const WCHAR nameW[] = {'N','a','m','e',0};
+    static const WCHAR id_formatW[] = {'%','i',0};
+    UNICODE_STRING geokeyW, idkeyW, namekeyW;
+    WCHAR id_buffer[10], name_buffer[4];
     OBJECT_ATTRIBUTES attr;
     HANDLE hkey;
+    RtlInitUnicodeString(&geokeyW, geoW);

     if (!geoinfo)
     {
@@ -4084,16 +4173,11 @@ BOOL WINAPI SetUserGeoID(GEOID geoid)

     attr.Length = sizeof(attr);
     attr.RootDirectory = hkey;
-    attr.ObjectName = &nameW;
+    attr.ObjectName = &geokeyW;
     attr.Attributes = 0;
     attr.SecurityDescriptor = NULL;
     attr.SecurityQualityOfService = NULL;
-    RtlInitUnicodeString(&nameW, geoW);
-
-    if (geoinfo->kind == LOCATION_NATION)
-        RtlInitUnicodeString(&keyW, nationW);
-    else
-        RtlInitUnicodeString(&keyW, regionW);
+    RtlInitUnicodeString(&geokeyW, geoW);

     if (NtCreateKey(&hkey, KEY_ALL_ACCESS, &attr, 0, NULL, 0, NULL) != STATUS_SUCCESS)
     {
@@ -4101,13 +4185,92 @@ BOOL WINAPI SetUserGeoID(GEOID geoid)
         return FALSE;
     }

-    sprintfW(bufferW, formatW, geoinfo->id);
-    NtSetValueKey(hkey, &keyW, 0, REG_SZ, bufferW, (strlenW(bufferW) + 1) * sizeof(WCHAR));
+    /* Prepare GeoID */
+    if (geoinfo->kind == LOCATION_NATION)
+        RtlInitUnicodeString(&idkeyW, nationW);
+    else
+        RtlInitUnicodeString(&idkeyW, regionW);
+
+    sprintfW(id_buffer, id_formatW, geoinfo->id);
+
+    /* Now prepare geoname */
+    RtlInitUnicodeString(&namekeyW, nameW);
+
+    if (geoinfo->kind == LOCATION_REGION)
+        sprintfW(name_buffer, geoname_uncode_fmtW, geoinfo->uncode);
+    else
+        strcpyW(name_buffer, geoinfo->iso2W);
+
+    NtSetValueKey(hkey, &idkeyW, 0, REG_SZ, id_buffer,
+                  (strlenW(id_buffer) + 1) * sizeof(WCHAR));
+    NtSetValueKey(hkey, &namekeyW, 0, REG_SZ, name_buffer,
+                  (strlenW(name_buffer) + 1) * sizeof(WCHAR));
+
+    TRACE("Set %s to ID %d; set Name to %s.\n",
+          wine_dbgstr_w(idkeyW.Buffer), geoinfo->id, wine_dbgstr_w(name_buffer));
+
     NtClose(attr.RootDirectory);
     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. GetLastError() will return ERROR_INVALID_PARAMETER if the ID was invalid.
+ *
+ * NOTES
+ *   On success, the geographic name will be set to the location of the specified ID.
+ */
+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
+ *   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(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.@)
  */
@@ -4121,7 +4284,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..4f84c64f81 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(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

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://www.winehq.org/pipermail/wine-devel/attachments/20191107/ecaa8859/attachment-0001.htm>


More information about the wine-devel mailing list