我一直想知道__declspec(dllimport)
的真实的用例是什么。我知道构建一个共享库需要使用__declspec(dllexport)
导出它的符号,然后库的用户使用这些符号作为__declspec(dllimport)
。
然后,您应该使用启用dllexport
的特殊定义来构建共享库,如果未设置标志,则将符号定义为dllimport
。
然而,我从来没有使用过dllimport
在所有和它只是工作。
我有两个项目:
进出口
有一个小的Util类,它是用定义的EXPORTING构建的
使用时间:
#ifndef _UTIL_H_
#define _UTIL_H_
#if defined(EXPORTING)
# define EXPORT __declspec(dllexport)
#else
# define EXPORT // I should use __declspec(dllimport) but client will try out
#endif
class EXPORT Util {
public:
static void test();
};
#endif // !_UTIL_H_
字符串
然后在源文件Util.cpp中:
#include <iostream>
#include "Util.h"
void Util::test()
{
std::cout << "Testing..." << std::endl;
}
型
没什么复杂的,正如你所看到的,当用户使用这个文件时,EXPORT根本不会被定义(应该定义为dllimport)。
客户端exe
Main.cpp:
#include <Util.h>
int main(void)
{
Util::test();
return 0;
}
型
链接到ImportExport.lib而不需要任何定义集,就可以正常工作。没有未定义的引用。
我想知道为什么dllimport的用例?它是为了向后兼容而存在的吗?
注意:所有代码都在VisualStudio 2012 Express上进行了测试。
1条答案
按热度按时间kb5ga3dv1#
Raymond Chen在this series中详细描述了dll导入机制;总结起来,
dllimport
for functions本质上是一种性能优化。如果你没有将一个函数标记为
dllimport
,编译器和链接器will treat it as a normal function,使用“静态”函数调用将其解析为导入库中的存根。存根实际上必须从IAT获取导入函数的地址,并在那里执行jmp
。(即,它必须以某种方式将编译器生成的直接调用转换为间接调用),因此在这个两步过程中会有一些性能损失。相反,
dllimport
从编译阶段就通过IAT告诉编译器it has to generate code for an indirect call。这减少了间接性,并允许编译器缓存(本地到函数)目标函数地址。注意,as MSDN says,你可以只在函数中省略
dllimport
;对于数据,它总是必要的,因为没有一种机制可供链接器修改对编译器生成的变量的直接访问。(all这在“经典”链接器的时代尤其重要;如今,启用了链接时代码生成,所有这些问题都可以通过简单地让链接器完全生成函数调用/数据访问来解决)