This is an undocumented API used by some programs to fetch the user's selected languages.
Example is Fusion360 (during its login process).
Signature currently taken from here: https://stackoverflow.com/questions/63877075/what-is-the-right-way-to-get-th... (Maybe that stackoverflow answer is also why Fusion has implemented this...)
I'm a first-time contributor, please point out if I'm doing anything wrong
-- v11: bcp47langs: Implement GetUserLanguages()
From: Marius Schiffer marius@mschiffer.de
Implement an undocumented API used by some programs to fetch the user's selected languages. Returns current locale.
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=56915 --- configure.ac | 1 + dlls/bcp47langs/Makefile.in | 4 +++ dlls/bcp47langs/bcp47langs.spec | 2 +- dlls/bcp47langs/main.c | 23 ++++++++++++++ dlls/bcp47langs/tests/Makefile.in | 6 ++++ dlls/bcp47langs/tests/bcp47langs.c | 50 ++++++++++++++++++++++++++++++ include/Makefile.in | 1 + include/bcp47langs.h | 35 +++++++++++++++++++++ 8 files changed, 121 insertions(+), 1 deletion(-) create mode 100644 dlls/bcp47langs/main.c create mode 100644 dlls/bcp47langs/tests/Makefile.in create mode 100644 dlls/bcp47langs/tests/bcp47langs.c create mode 100644 include/bcp47langs.h
diff --git a/configure.ac b/configure.ac index 6d093b52526..534ce5cb092 100644 --- a/configure.ac +++ b/configure.ac @@ -2467,6 +2467,7 @@ WINE_CONFIG_MAKEFILE(dlls/avifil32/tests) WINE_CONFIG_MAKEFILE(dlls/avifile.dll16) WINE_CONFIG_MAKEFILE(dlls/avrt) WINE_CONFIG_MAKEFILE(dlls/bcp47langs) +WINE_CONFIG_MAKEFILE(dlls/bcp47langs/tests) WINE_CONFIG_MAKEFILE(dlls/bcrypt) WINE_CONFIG_MAKEFILE(dlls/bcrypt/tests) WINE_CONFIG_MAKEFILE(dlls/bcryptprimitives) diff --git a/dlls/bcp47langs/Makefile.in b/dlls/bcp47langs/Makefile.in index 5eb4caaf0e7..dfe10916951 100644 --- a/dlls/bcp47langs/Makefile.in +++ b/dlls/bcp47langs/Makefile.in @@ -1,2 +1,6 @@ MODULE = bcp47langs.dll IMPORTLIB = bcp47langs +IMPORTS = combase + +SOURCES = \ + main.c diff --git a/dlls/bcp47langs/bcp47langs.spec b/dlls/bcp47langs/bcp47langs.spec index a054c1486a9..969f77b56b8 100644 --- a/dlls/bcp47langs/bcp47langs.spec +++ b/dlls/bcp47langs/bcp47langs.spec @@ -48,7 +48,7 @@ @ stub GetUserDisplayLanguageOverride @ stub GetUserLanguageInputMethods @ stub GetUserLanguageInputMethodsForUser -@ stub GetUserLanguages +@ stdcall GetUserLanguages(long ptr) @ stub GetUserLanguagesForAllUsers @ stub GetUserLanguagesForUser @ stub GetUserLocaleFromLanguageProfileOptOut diff --git a/dlls/bcp47langs/main.c b/dlls/bcp47langs/main.c new file mode 100644 index 00000000000..0732aeff581 --- /dev/null +++ b/dlls/bcp47langs/main.c @@ -0,0 +1,23 @@ +#include <winstring.h> +#include "winnls.h" + +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(bcp47langs); + +HRESULT WINAPI WINAPI GetUserLanguages(WCHAR delimiter, HSTRING *user_languages) +{ + HRESULT hr; + WCHAR locale[LOCALE_NAME_MAX_LENGTH]; + + if (!user_languages) + return E_INVALIDARG; + + if (!GetUserDefaultLocaleName(locale, LOCALE_NAME_MAX_LENGTH)) + return E_FAIL; + + if (FAILED(hr = WindowsCreateString(locale, wcslen(locale), user_languages))) + return hr; + + return S_OK; +} diff --git a/dlls/bcp47langs/tests/Makefile.in b/dlls/bcp47langs/tests/Makefile.in new file mode 100644 index 00000000000..498e1b34afe --- /dev/null +++ b/dlls/bcp47langs/tests/Makefile.in @@ -0,0 +1,6 @@ +TESTDLL = bcp47langs.dll +IMPORTS = bcp47langs msvcrt combase + +SOURCES = \ + bcp47langs.c + diff --git a/dlls/bcp47langs/tests/bcp47langs.c b/dlls/bcp47langs/tests/bcp47langs.c new file mode 100644 index 00000000000..a6bdf3ed9e4 --- /dev/null +++ b/dlls/bcp47langs/tests/bcp47langs.c @@ -0,0 +1,50 @@ +/* + * Copyright 2025 Marius Schiffer + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include <winbase.h> + +#include "winstring.h" +#include "msvcrt/locale.h" +#include "bcp47langs.h" + +#include "wine/test.h" + +static void test_GetUserLanguages(void) +{ + HSTRING result; + const WCHAR *user_languages; + + ok(GetUserLanguages(',', NULL) == E_INVALIDARG, "unknown return code\n"); + + setlocale(LC_ALL, "enu"); + ok(GetUserLanguages(',', &result) == 0, "unknown return code\n"); + user_languages = WindowsGetStringRawBuffer(result, NULL); + ok(!lstrcmpW(user_languages, L"en-US"), "languages=%s\n", debugstr_w(user_languages)); + + setlocale(LC_ALL, "deu"); + ok(GetUserLanguages(',', &result) == 0, "unknown return code\n"); + user_languages = WindowsGetStringRawBuffer(result, NULL); + ok(!lstrcmpW(user_languages, L"de-DE"), "languages=%s\n", debugstr_w(user_languages)); + + WindowsDeleteString(user_languages); +} + +START_TEST(bcp47langs) +{ + test_get_user_languages(); +} diff --git a/include/Makefile.in b/include/Makefile.in index 650d69815f8..694c6f78312 100644 --- a/include/Makefile.in +++ b/include/Makefile.in @@ -45,6 +45,7 @@ SOURCES = \ axextendenums.h \ basetsd.h \ basetyps.h \ + bcp47langs.h \ bcrypt.h \ bdaiface.idl \ bdaiface_enums.h \ diff --git a/include/bcp47langs.h b/include/bcp47langs.h new file mode 100644 index 00000000000..4aba6e6195c --- /dev/null +++ b/include/bcp47langs.h @@ -0,0 +1,35 @@ +/* + * bcp47langs definitions + * + * Copyright 2025 Marius Schiffer + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + * + */ + +#ifndef _BCP47LANGS_ +#define _BCP47LANGS_ + +#ifdef __cplusplus +extern "C" { +#endif + +DWORD WINAPI GetUserLanguages(char delimiter, HSTRING *user_languages); + +#ifdef __cplusplus +} +#endif + +#endif /* _BCP47LANGS_ */
Zhiyi Zhang (@zhiyi) commented about include/bcp47langs.h:
+/*
- bcp47langs definitions
- Copyright 2025 Marius Schiffer
There is no bcp47langs.h in Windows SDK. Let's remove this file and declare the function in tests.
Zhiyi Zhang (@zhiyi) commented about dlls/bcp47langs/main.c:
+#include <winstring.h> +#include "winnls.h"
+#include "wine/debug.h"
+WINE_DEFAULT_DEBUG_CHANNEL(bcp47langs);
+HRESULT WINAPI WINAPI GetUserLanguages(WCHAR delimiter, HSTRING *user_languages) +{
Let's add a FIXME("... stub!\n", ...) here.
Let's rename the commit subject to "bcp47langs: Add GetUserLanguages() stub.
This hasn't been addressed.
Zhiyi Zhang (@zhiyi) commented about dlls/bcp47langs/main.c:
+#include <winstring.h> +#include "winnls.h"
+#include "wine/debug.h"
+WINE_DEFAULT_DEBUG_CHANNEL(bcp47langs);
+HRESULT WINAPI WINAPI GetUserLanguages(WCHAR delimiter, HSTRING *user_languages)
There are two WINAPI here.
Zhiyi Zhang (@zhiyi) commented about dlls/bcp47langs/tests/bcp47langs.c:
- Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
- */
+#include <winbase.h>
+#include "winstring.h" +#include "msvcrt/locale.h" +#include "bcp47langs.h"
+#include "wine/test.h"
+static void test_GetUserLanguages(void) +{
- HSTRING result;
- const WCHAR *user_languages;
Let's use a loop and static test data to test it. For example ``` static const struct { const char *locale; const WCHAR *expected_languages; } tests[] = { {"enu", L"en-US"}, ... add usual locales, for example, English, Chinese, Japanese, Korean, Arabic, French, German. }
for (int i =0; i < ARRAY_SIZE(tests); i++) { winetest_push_context("%d", i); setlocale(LC_ALL, tests[i].locale); ok(GetUserLanguages(',', &result) == 0, "unknown return code\n"); user_languages = WindowsGetStringRawBuffer(result, NULL); ok(!lstrcmpW(user_languages, tests[i].expected_languages), "languages=%s\n", debugstr_w(user_languages)); winetest_pop_context(); } ```
And remember to restore the original locale when the test ends.
Zhiyi Zhang (@zhiyi) commented about dlls/bcp47langs/tests/Makefile.in:
+TESTDLL = bcp47langs.dll +IMPORTS = bcp47langs msvcrt combase
+SOURCES = \
Please rebase this patch on top of master. The bcp47langs tests are added after MR8448
You need to fix those CI failures.