在Visual Studio中使用dllimport的情况

时间:2014-07-10 07:43:45

标签: c++ visual-c++ linker

我总是想知道__declspec(dllimport)的真实用例是什么。我知道构建共享库需要使用__declspec(dllexport)导出其符号,然后库的用户将这些符号用作__declspec(dllimport)

然后,您应该使用特殊定义构建共享库,该定义启用dllexport,如果未设置标志,则符号定义为dllimport

但是,我从来没有使用dllimport,它只是起作用。

我有两个项目:

ImportExport

有一个使用EXPORTING定义构建的小型Util类

Util.h:

#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 个答案:

答案 0 :(得分:4)

Raymond Chen在this series中详细描述了dll导入机制;总结起来,函数的dllimport本质上是一种性能优化。

如果你没有将函数标记为dllimport编译器和链接器will treat it as a normal function,使用&#34; static&#34;函数调用将其解析为导入库中的存根。存根实际上必须从IAT获取导入函数的地址并在那里执行jmp(即它必须以某种方式将编译器生成的直接调用转换为间接调用),因此有&#39; s在这个两步过程中会有一些性能损失。

相反,

dllimport通过编译阶段的IAT告诉编译器it has to generate code for an indirect call。这减少了间接,并允许编译器缓存(本地到函数)目标函数地址。

请注意,as MSDN says,您只能为功能省略dllimport;对于数据而言,它总是必要的,因为链接器没有一种机制可以重新编写直接访问编译器在间接访问中生成的变量。

(所有这些在&#34;经典&#34;链接器的时代特别相关;现在,启用了链接时代码生成,所有这些点都可以通过简单地让链接器完全生成函数调用来解决数据访问)