我在Visual C ++,VS2015社区工作。我写过这个小小的DLL:
#include "stdafx.h"
int showMsgBox(wchar_t* caption, wchar_t* message)
{
MessageBox(NULL, message, caption, 0);
return 0;
}
这个小小的客户:
#include "stdafx.h"
__declspec(dllimport) int showMsgBox(wchar_t* caption, wchar_t* message);
int main()
{
showMsgBox(L"SimpleDLLClient", L"Hello DLL World!");
return 0;
}
要导出showMsgBox函数,我已创建此模块定义文件:
LIBRARY SimpleDLL
EXPORTS
showMsgBox
当我链接我的客户端时,我将它传递给我链接我的DLL时创建的导入库。一切都编译好并且链接得很好。
这让我很困惑。
根据MSDN,"因为Visual C ++编译器对C ++函数使用名称修饰,所以必须使用修饰名称作为entryname或internalname,或者使用extern&#定义导出的函数。 34; C"在源代码中。"
但我没有出口装饰名称,是吗?
如果我不是使用模块定义文件,而是使用__declspec(dllexport)扩展属性为函数添加前缀,我不会感到惊讶(因为它应该具有编译器应用的任何名称装饰匹配编译器应用于客户端的相同装饰。)
同样,如果我将该功能加上' extern" C"'在DLL和客户端代码中,它也应该工作(它确实:我测试了它),因为符号应该匹配。
但是,我原本期望模块定义文件中的未修饰导出无法解析客户端对同一个未修饰名称的引用,此时DLL和客户端代码都不使用' extern&#34 ; C",'这也似乎是MSDN所说的我应该期待的。然而,即使我不使用" extern" C"'任何地方。
有谁能告诉我为什么这样有效?
查看链接器创建的文件,我看到将未修饰的名称放入模块定义文件显然会导致装饰名称包含在导入库中。当我在该文件上使用dumpbin / exports时,我得到的是:
文件类型:LIBRARY
Exports
ordinal name
?showMsgBox@@YAHPEA_W0@Z (int __cdecl showMsgBox(wchar_t *,wchar_t *))
现在,有点惊人(对我来说,无论如何),如果我明确地将装饰名称别名,就像这样:
LIBRARY SimpleDLL
EXPORTS
showMsgBox=?showMsgBox@@YAHPEA_W0@Z
dumpbin告诉我这是导入库中显示的内容:
File Type: LIBRARY
Exports
ordinal name
showMsgBox
当我构建客户端时,使用它作为链接器的输入也可以正常工作,前提是我使用" extern" C"'当我声明我导入的函数时:
extern "C" int showMsgBox(wchar_t* caption, wchar_t* message);
这是有道理的,因为链接器正在寻找的符号是未修饰的" showMsgBox,"我将该符号别名化为编译DLL时创建的装饰名称。
... SOOOO
对我来说,对于我所说的必须在模块定义文件中使用修饰名称的MSDN文档的页面和页面都是错误的。相反,似乎如果在模块定义文件中使用未修饰的名称,则它是实际合并到导入库中的修饰名称,它将解析对编译客户端代码时构造的匹配修饰名称的引用。当然,这是我更喜欢的,而不是必须提取装饰的名称并在我的模块定义文件中使用它们。它只是与MSDN页面重复说的不匹配。
我喜欢认为我是一个聪明的孩子,但任何人都认为微软并不知道自己的产品是如何运作的,这是非常傲慢的。
我在这里缺少什么?
DLL和客户端都使用装饰名称(也就是说,没有使用' extern" C"'随处可见),所有内容都在模块定义中使用未修饰的名称构建得很好文件,就像我说的那样。但是,有趣的是,如果我没有对源代码进行任何更改,那么如果我在模块定义文件中使用装饰名称,事情就会很好地构建:
LIBRARY SimpleDLL
EXPORTS
?showMsgBox@@YAHPEA_W0@Z
在二进制级别,它创建的导入库几乎与我在模块定义文件中使用未修饰名称时创建的库相同。唯一的区别似乎是时间戳,除了接近结尾的一个字节。仍然试图理解那一个,但似乎更加确定的是,链接器以某种方式导出了修饰名称,即使模块定义文件仅指未修饰的名称。