Winrt 环境中有许多 API 不能使用,为此需要通过一些手段模拟实现缺失的 API,使许多老代码能够继续工作。但是在 VS 编译环境中提供的链接库又包含这些 API 符号的定义,使得链接时链接到这些不能使用的 API 上,应用不能正常运行,也不能通过 Windows Store 验证。
比如:
void test()
{
Sleep(0);
}
在链接的时候,就会链接到 kernel32.lib 的 _Sleep@4 API
方案1—— C++ 语法
将模拟 API 定义为 C++ 语法,不加 extern "C" 修饰。
因为C++有函数重载机制,所以 API 定义成 C 语法的名称还是 C++ 语法的名称,都可以匹配,调用方不需要修改。在链接时寻找的是 C++ 语法的名称,不会与系统库的符号冲突。
这种方法只适用于老代码是 C++ 语言编写的,或者可以在 C++ 编译器上编译的情形。对于很多开源库,比如 ffmpeg,就无法通过该方案移植到 Winrt 平台。
方案2—— __cdecel调用方式
将模拟 API 定义为 __cdecl 调用方式,保持 C 语法格式。
Windows api 都是 __stdcall 调用方式,修改为 __cdecl ,不需要修改调用方的代码。在编译时,会引用 __cdecl 格式的名称,比如 _Sleep@4 在 __cdecl 方式下的名称为 _Sleep,这样也与系统库里面的 API 名称区分开来了。
这种方法在 ARM、X64 平台无效,因为这些平台不采用 __stdcall __cdecl 之类的调用方式,不管定义函数为 __stdcall 还是 __cdecl ,结果都是一样,解决不了符号冲突。
方案3—— 调整链接顺序
VC 链接器的工作原理是:
- 记录所有未定义符号(外部引用),集合 U
- 对所有链接库,依次处理,对每个库,搜寻 U 中的符号,比且有可能增加新的未定义符号到 U 集合
- 循环2直到 U 集合没有变化
VC IDE 环境中链接库的顺序一般为:
- 手动指定的库
- 工程依赖项目生成的库
- 默认引用的库