VisualStudio中dllimport的用例

Use case of dllimport in VisualStudio

本文关键字:dllimport VisualStudio      更新时间:2023-10-16

我一直想知道__declspec(dllimport)的真正用例是什么。我知道建立一个共享库需要使用__declspec(dllexport)导出其符号,然后库的用户使用这些符号作为__declspec(dllimport)

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

然而,我从来没有使用dllimport,它只是工作。

我有两个项目:

进出口

有一个小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就可以工作。没有未定义的引用

我想知道为什么用例是limport?它的存在是为了向后兼容吗?

注意:所有代码都在VisualStudio 2012 Express上进行了测试。

Raymond Chen在本系列中详细描述了dll导入机制;综上所述,函数dllimport本质上是一种性能优化。

如果你没有将一个函数标记为dllimport,编译器和链接器将把它当作一个普通的函数,用"静态"函数调用将它解析为在导入库中找到的存根。存根实际上必须从IAT中获取导入函数的地址,并在那里执行jmp(即,它必须以某种方式将编译器生成的直接调用转换为间接调用),因此在这两步过程中存在一些性能损失。

相反,

dllimport告诉编译器,它必须在编译阶段通过IAT为间接调用生成代码。这减少了间接性,并允许编译器缓存(本地到函数中)目标函数地址。

请注意,正如MSDN所说,您只能为函数省略dllimport;对于数据来说,这总是必要的,因为没有一种机制可以让链接器在间接访问中对编译器生成的变量进行直接访问。

(所有这些都与"经典"链接的时代特别相关;现在,随着链接时代码生成功能的启用,所有这些问题都可以通过让链接器完全生成函数调用(数据访问)

来解决。