GetUILanguageFallbackList
API 与 MinGW-w64 一起使用。
这是一个非常简单的调用 API 的示例:
#include <windows.h>
#include <muiload.h>
int main() {
GetUILanguageFallbackList(NULL, 0, NULL);
return 0;
}
现在,我并不期望该代码实际上能做任何有用的事情,因为我还没有提供有效的参数组合。但此时我要做的就是编译和链接代码。我实际上有一个更长的示例,尝试正确使用 API,但我创建了上述最小版本只是为了重现我的链接问题。所以我将其保存为
uilang0.c
。
但是当我尝试编译时,链接失败并出现以下错误:
# gcc -o uilang0 uilang0.c
C:/tools/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/14.1.0/../../../../x86_64-w64-mingw32/bin/ld.exe: C:\tools\msys64\tmp\cclvYTlj.o:uilang0.c:(.text+0x1e): undefined reference to `GetUILanguageFallbackList'
collect2.exe: error: ld returned 1 exit status
Windows SDK 中有一个
muiload.lib
,但我找不到 MinGW-w64 的等效项,即使它确实提供了 muiload.h
头文件。我尝试与之联系;修复了上述链接器错误,但引入了更多链接器错误:
# gcc -o uilang0 uilang0.c /c/Program\ Files\ \(x86\)/Windows\ Kits/10/Lib/10.0.22621.0/um/x64/muiload.lib
C:/tools/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/14.1.0/../../../../x86_64-w64-mingw32/bin/ld.exe: C:/Program Files (x86)/Windows Kits/10/Lib/10.0.22621.0/um/x64/muiload.lib(d:/os/obj/amd64fre/minkernel/crts/crtw32/misc/nt/objfre/amd64/guard_support.obj):(.00cfg[__guard_check_icall_fptr]+0x0): undefined reference to `_guard_check_icall_nop'
C:/tools/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/14.1.0/../../../../x86_64-w64-mingw32/bin/ld.exe: C:/Program Files (x86)/Windows Kits/10/Lib/10.0.22621.0/um/x64/muiload.lib(d:/os/obj/amd64fre/minkernel/crts/crtw32/misc/nt/objfre/amd64/guard_support.obj):(.00cfg[__guard_xfg_check_icall_fptr]+0x0): undefined reference to `_guard_check_icall_nop'
C:/tools/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/14.1.0/../../../../x86_64-w64-mingw32/bin/ld.exe: C:/Program Files (x86)/Windows Kits/10/Lib/10.0.22621.0/um/x64/muiload.lib(d:/os/obj/amd64fre/onecore/base/mui/muiload/lib/objfre/amd64/getuilanguagefallbacklist.obj):(.text$mn+0xe): undefined reference to `__security_cookie'
C:/tools/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/14.1.0/../../../../x86_64-w64-mingw32/bin/ld.exe: C:/Program Files (x86)/Windows Kits/10/Lib/10.0.22621.0/um/x64/muiload.lib(d:/os/obj/amd64fre/onecore/base/mui/muiload/lib/objfre/amd64/getuilanguagefallbacklist.obj):(.text$mn+0x92): undefined reference to `__security_check_cookie'
C:/tools/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/14.1.0/../../../../x86_64-w64-mingw32/bin/ld.exe: C:/Program Files (x86)/Windows Kits/10/Lib/10.0.22621.0/um/x64/muiload.lib(d:/os/obj/amd64fre/onecore/base/mui/muiload/lib/objfre/amd64/getuilanguagefallbacklist.obj):(.text$mn+0x13): undefined reference to `__security_cookie'
C:/tools/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/14.1.0/../../../../x86_64-w64-mingw32/bin/ld.exe: C:/Program Files (x86)/Windows Kits/10/Lib/10.0.22621.0/um/x64/muiload.lib(d:/os/obj/amd64fre/onecore/base/mui/muiload/lib/objfre/amd64/getuilanguagefallbacklist.obj):(.text$mn+0xfb): undefined reference to `__security_check_cookie'
C:/tools/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/14.1.0/../../../../x86_64-w64-mingw32/bin/ld.exe: C:/Program Files (x86)/Windows Kits/10/Lib/10.0.22621.0/um/x64/muiload.lib(d:/os/obj/amd64fre/onecore/base/mui/muiload/lib/objfre/amd64/getuilanguagefallbacklist.obj):(.xdata[$unwind$?PreVistaFallback@@YAHKPEAKPEAG0@Z]+0x14): undefined reference to `__GSHandlerCheck'
C:/tools/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/14.1.0/../../../../x86_64-w64-mingw32/bin/ld.exe: C:/Program Files (x86)/Windows Kits/10/Lib/10.0.22621.0/um/x64/muiload.lib(d:/os/obj/amd64fre/onecore/base/mui/muiload/lib/objfre/amd64/getuilanguagefallbacklist.obj):(.xdata[$unwind$?MultiStr_AddLangAndParentNames@@YAHPEAG_KG@Z]+0x10): undefined reference to `__GSHandlerCheck'
C:/tools/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/14.1.0/../../../../x86_64-w64-mingw32/bin/ld.exe: C:/Program Files (x86)/Windows Kits/10/Lib/10.0.22621.0/um/x64/muiload.lib(d:/os/obj/amd64fre/onecore/base/mui/muiload/lib/objfre/amd64/multistr.obj):(.text$mn+0x7f): undefined reference to `StringCchCopyW'
collect2.exe: error: ld returned 1 exit status
我尝试添加更多 Platform SDK
.lib
文件,但无论我使用什么组合,我仍然得到未定义的引用。添加 BufferOverflowU.lib
可以消除对 __security_cookie
/__security_check_cookie
的未定义引用。添加 ntdllp.lib
可以消除对 __GSHandlerCheck
的未定义引用。但我似乎无法摆脱 _guard_check_icall_nop
或 StringCchCopyW
未定义的引用。另外,我忍不住担心在不知道自己在做什么的情况下随机添加库,即使我最终将其链接起来,它也可能由于某些不兼容的组合而无法工作。
更新:如果我创建文件
fakeapi.c
,其内容为:
#define DEPRECATE_SUPPORTED
#include <strsafe.h>
__attribute__((weak))
STRSAFEAPI StringCchCopyW(STRSAFE_LPWSTR pszDest,size_t cchDest,STRSAFE_LPCWSTR pszSrc) {
if(cchDest > STRSAFE_MAX_CCH) return STRSAFE_E_INVALID_PARAMETER;
return StringCopyWorkerW(pszDest,cchDest,pszSrc);
}
__attribute__((fastcall))
extern void _guard_check_icall_nop(uintptr_t Target) {
((void)Target);
}
然后做:
gcc -o uilang0 uilang0.c \
/c/Program\ Files\ \(x86\)/Windows\ Kits/10/Lib/10.0.22621.0/um/x64/{muiload,BufferOverflowU,ntdllp}.lib \
fakeapi.c
这似乎克服了链接时的所有
undefined reference
错误。但是,编译仍然失败:
C:/tools/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/14.1.0/../../../../x86_64-w64-mingw32/bin/ld.exe: uilang0.exe:.retplne: section below image base
我猜要解决这个问题,我需要调整链接器脚本来设置
.retplne
部分的基地址。检查默认链接器脚本(使用 -Wl,--verbose
)我没有看到任何提及的 .retplne
部分。所以我尝试添加-Wl,--section-start=.retplne=0x141000000
。这样,链接成功,但尝试运行结果 .exe
失败。
Hacky,但我成功了:
gcc -o uilang0.exe uilang0.c \
/c/Program\ Files\ \(x86\)/Windows\ Kits/10/Lib/10.0.22621.0/um/x64/{muiload,BufferOverflowU,ntdllp}.lib \
fakeapi.c \
-Wl,--section-start=.retplne=0x141000000
产生的
uilang0.exe
不起作用。但是,让我们使用 objcopy
对其进行编辑以删除 .retplne
部分:
objcopy --remove-section=.retplne ./uilang0.exe
可能应该将链接器脚本编辑为
objcopy --remove-section=
DISCARD
部分,而不是使用 .retplne
。
结果
uilang0.exe
执行成功。此外,GetUILanguageFallbackList
API 确实有效,如下面的 uilang0.c
增强版所示:
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdnoreturn.h>
#include <string.h>
#include <muiload.h>
noreturn void fatal(const char* msg) {
fprintf(stderr, "FATAL: %s\n", msg);
exit(1);
}
noreturn void fatal_error(const char* op) {
fprintf(stderr, "FATAL: %s failed with error: %lu\n", op, GetLastError());
exit(1);
}
void* xmalloc(size_t size) {
void* ptr = malloc(size);
if (ptr == NULL) fatal("memory allocation failed");
return ptr;
}
void *xrealloc(void* ptr, size_t size) {
ptr = realloc(ptr, size);
if (ptr == NULL) fatal("memory allocation failed");
return ptr;
}
char* ConvertUTF16ToUTF8(const wchar_t* utf16Str) {
int utf8Len = WideCharToMultiByte(CP_UTF8, 0, utf16Str, -1, NULL, 0, NULL, NULL);
if (utf8Len == 0) return NULL;
char* utf8Str = (char*)xmalloc(utf8Len);
WideCharToMultiByte(CP_UTF8, 0, utf16Str, -1, utf8Str, utf8Len, NULL, NULL);
return utf8Str;
}
int main() {
ULONG bufferSize = 256;
wchar_t* fallbackList = (wchar_t*)xmalloc(bufferSize * sizeof(wchar_t));
ULONG fallbackListSize;
if (!GetUILanguageFallbackList(fallbackList, bufferSize, &fallbackListSize)) fatal_error("GetUILanguageFallbackList");
if (fallbackListSize > bufferSize) {
fallbackList = (wchar_t*)xrealloc(fallbackList, fallbackListSize * sizeof(wchar_t));
if (!GetUILanguageFallbackList(fallbackList, fallbackListSize, &fallbackListSize)) fatal_error("GetUILanguageFallbackList");
}
char* utf8Str = ConvertUTF16ToUTF8(fallbackList);
if (utf8Str == NULL) fatal("UTF-16 to UTF-8 conversion failed");
printf("UI Language Fallback List: %s\n", utf8Str);
free(fallbackList);
free(utf8Str);
return 0;
}