使用没有名字装饰的__stdcall的DLL:为什么它甚至可以工作?

时间:2016-08-02 00:57:31

标签: c winapi dll linker

如果我声明这样的函数:

#ifdef TEST_EXPORTS
#define TEST_API __declspec(dllexport)
#else
#define TEST_API __declspec(dllimport)
#endif

TESTAPI int __stdcall myadd(int a, int b);

DLL中的符号是_myadd@8,这对我来说非常有意义(在这里阅读其他问题几个小时后,就是这样)。

但是Windows库似乎做了不同的事情。他们还使用__stdcall(伪装成WINAPI),但DLL中的符号没有名称装饰。如果上面的方法在windows libs中,则符号为myadd

我的猜测是他们使用def文件来对符号进行别名。但是当我链接到其中一个DLL时,为什么我的链接器知道这个?

windows头文件用WINAPI声明这些函数,所以如果我调用它们,链接器应该查找装饰名称,因为它是__stdcall函数。但不知何故,链接器知道删除名称装饰。

我试图通过编写一个小DLL并使用def文件删除名称修饰来复制它。正如预期的那样,我发现链接器错误,因为链接器仍然在寻找装饰名称。我在纯C中完成了这个,以确保c ++名称修改不会影响它。

编辑:澄清,MSVC 14.0 / VS2015,32位

1 个答案:

答案 0 :(得分:7)

这里有一些勉强记录的魔法。让我们看一些WIN32 API函数,例如RegQueryValueExW。它在winreg.h文件中定义如下:

WINADVAPI LSTATUS APIENTRY RegQueryValueExW(...);

其中WIADVAPI__declspec(dllimport),而APIENTRY__stdcall命名约定的名字对象。另请注意,标头中的所有函数都声明为extern "C"。所以,无论如何,这个函数应该使用名称修饰,其DLL导出应该是_RegQueryValueExW@24。然而,当我们使用advapi32.dll命令查看dumpbin /exports导出时,我们会看到一个未修饰的名称:

advapi exports

现在让我们使用advapi32.lib命令仔细检查dumpbin /headers advapi32.lib文件:

enter image description here

请注意undecorate说明符,它允许将修饰后的名称链接到未修饰的导出。您可以使用包含未修饰名称的def部分的EXPORTS文件为您的dll获得相同的结果。有关其他信息,请参阅this文章和this answer

此外,上面写的所有内容仅对x86应用程序有效。 x64位环境中的C函数链接without name decoration

  

C函数的装饰形式取决于调用   声明中使用的约定,如下表所示。   这也是C ++代码时使用的装饰格式   宣布有外部“C”联动。默认的调用约定是   __cdecl。请注意,在64位环境中,函数不会被修饰。