在代码中使用 _declspec(dllimport) 和 _declspec(dllexport)

这篇文章补充了下面 Microsoft 知识库中相应的文章中介绍的信息:
107501  (http://support.microsoft.com/kb/107501/EN-US/ ) 信息: __export 按 __declspec Visual c + + 32-位中替换
本文讨论的优点和您的应用程序中使用 _declspec(dllimport) 和 _declspec(dllexport) 的机制。
回到顶端
更多信息32 位版本的 Visual c + + 使用 _declspec(dllimport) 和 $ _declspec(dllexport) 来替换以前的 Vis...32 位版本的 Visual c + + 使用 _declspec(dllimport) 和 $ _declspec(dllexport) 来替换以前的 Visual c + + 的 16 位版本中使用该 __export 关键字。

您不需要使用您的代码的 _declspec(dllimport) 正确,编译,但这样做所以允许编译器以生成更好的代码。编译器将能够生成更好的代码,因为它肯定知道或不函数是否存在在 DLL 中,因此编译器可以生成跳过正常的间接寻址级别的代码会跨 DLL 边界的函数调用中出现。

使用正确的.def 文件导出部分,_declspec(dllexport) 不是必需的。_declspec(dllexport) 已添加到提供了一种从.exe 或.dll 导出函数,而无需使用.def 文件的简便方法。

本文的其余部分提供了相当低级别、 全面讨论这些问题。

Win32 可移植可执行文件格式旨在最大限度地减少必须能接触到修复的导入的页面数。若要执行此操作它将任何程序的所有导入地址放在一个称为导入地址表的位置。这就允许访问这些导入时修改只能有一个或两个页加载程序。
回到顶端
使用函数调用 _declspec(dllimport)
在下面的代码示例假定 func1 驻留在独立于.exe 文件包含 main () 函数的 DLL 中的函数。

_declspec(dllimport),不给出此代码:
void main(void) {
    func1();
}
    

编译器生成类似下面的代码:
call func1
    

和链接器将为该调用到类似于下面这样:
call 0x4000000         ; The address of 'func1'.
    

func1 存在另一个 DLL 中,如果链接器无法解析此直接因为它无法知道什么 func1 的地址。在 16 位环境中链接器将此代码地址添加到该加载程序在运行时用正确的地址修补该.exe 中的列表。在 32 位环境中链接器生成的它不会知道该地址的 thunk。在 thunk 如下所示:
   0x40000000:    jmp DWORD PTR __imp_func1
    

__imp_func1 此处是 func1 的插槽中导入地址表.exe 文件的地址。链接器因此已知的所有地址。若要更新在加载时,一切就会正常工作的.exe 文件的导入地址表仅有加载程序。

因此,使用 _declspec(dllimport) 是更好的因为它是更好的如果链接器不生成一个 thunk,如果没有到。thunk 使代码更大 (RISC 系统上可以是几个说明) 并可能降低您的高速缓存的性能。如果您通知编译器函数是 DLL 中,它可以为您生成间接调用。

因此,现在此代码:
__declspec(dllimport) void func1(void);

void main(void) {
    func1();
}
    

生成此指令:
call DWORD PTR __imp_func1
    

没有没有 thunk 和没有的 jmp 指令使代码更小、 更快。

另一方面,对于 DLL 内部的函数调用,您不希望不必使用间接调用。您已经知道函数的地址。时间和空间所需加载和存储前一个间接调用函数的地址,因此直接调用总是更快、 更小。您只想从外部调用的 DLL 函数时,请使用 __declspec(dllimport) DLL 本身。不要在一个 DLL 内部的函数上使用 __declspec(dllimport),生成该 DLL 时。
回到顶端
使用 _declspec(dllexport)
Microsoft 推出了 __export 16 位编译器版本允许编译器自动生成导出名称,并将它们放在.lib 文件中。此.lib 文件可被用像静态.lib DLL 与链接。

Microsoft 添加 __declspec(dllexport) 继续这种便利。其目的是将导出指令添加到对象文件,因此您不需要.def 文件。

当试图导出修饰 c + + 函数名时,这种便利是最明显。对名称修饰没有标准规范因此导出函数的名称可能会更改不同的编译器版本。如果您使用 _declspec(dllexport),重新编译 DLL 和依赖的.exe 文件只有在才需要帐户的任何命名约定更改。

例如,仅在一个.def 文件中进行序号、 NONAME,或专用,并没有方法来指定这些属性没有.def 文件的情况下,许多导出指令。但是,使用 _declspec(dllexport) 除了使用.def 文件并不会导致生成错误。

作为一个引用搜索 Win32 WINBASE.H 头文件。它包含首选 __declspec(dllexport) 和 __declspec(dllimport) 用法示例。
回到顶端
在数据中使用 _declspec(dllexport) 和 _declspec(dllimport)
在数据的情况下使用 _declspec(dllimport) 是间接的移除层的一个方便的时候项。当您从 DLL 导入数据时,您仍必须通过导入地址表。_declspec(dllimport) 之前在 Win32 天内这意味着您不得不请记住在从 DLL 导出访问数据时执行一个额外的间接寻址级别:
// project.h
#ifdef _DLL     // If accessing the data from inside the DLL
   ULONG ulDataInDll;

else            // If accessing the data from outside the DLL
   ULONG *ulDataInDll;
#endif
    

然后,您会将数据导出在.def 文件中:
// project.def
LIBRARY project
EXPORTS
    ulDataInDll   CONSTANT
    

和 DLL 的外部进行访问:
if (*ulDataInDll == 0L) {
   // Do stuff here
}
    

时您将数据作为 __declspec(dllimport),编译器自动生成间接寻址代码为您。您再也不必担心上述步骤。如所述,以前并不使用 _declspec(dllimport) 声明对数据生成 DLL 时。若要访问该数据对象在 DLL 中的函数将不会使用导入地址表。因此,您不需要额外的存在的间接寻址级别。

若要将数据从 DLL 中会自动导出使用此声明:
__declspec(dllexport) ULONG ulDataInDLL;
    

回到顶端
使用.def 文件
如果您选择使用 __declspec(dllimport) 和.def 文件,您应更改.def 文件使用 CONSTANT 的位置的数据以减少不正确的编码将导致问题的可能性:
// project.def
LIBRARY project
EXPORTS
    ulDataInDll   DATA
    

以下图表显示了为什么:
Keyword     Emits in the import lib     Exports
CONSTANT    __imp_ulDataInDll           ulDataInDll
            __ulDataInDll

DATA        __imp_ulDataInDll           ulDataInDll
    

使用 _declspec (dllimport) 和 CONSTANT 列出 __imp_ 版本和未修饰的名创建以允许在显式链接下.lib DLL 导入库中。使用 _declspec(dllimport) 和数据列表只是 __imp_ 版本的名称。

如果您使用 CONSTANT,下面的代码构造之一可能被用来访问该 ulDataInDll:
__declspec(dllimport) ULONG ulDataInDll; /*prototype*/
   if (ulDataInDll == 0L)   /*sample code fragment*/
    

-或者-
ULONG *ulDataInDll;      /*prototype*/
if (*ulDataInDll == 0L)  /*sample code fragment*/
    

但是,如果您使用.def 文件中的数据,使用下面的定义编译的代码可以访问变量 ulDataInDll:
__declspec(dllimport) ULONG ulDataInDll;
if (ulDataInDll == 0L)   /*sample code fragment*/
    

持续使用是更危险,因为如果您忘记使用额外的间接寻址级别,您可能会访问该变量--不在变量自身的导入地址表的指针。由于导入地址表当前由只读由 Microsoft 编译器和连接器,这种类型的问题可以经常清单作为访问冲突。

如果它发现 CONSTANT.def 文件中的这种情况下,当前的 Visual c + + 链接器将发出警告。使用 CONSTANT 唯一的真正原因是如果您不能重新编译头文件没有在其中列出 dllimport 原型上的某些对象文件。
回到顶端
参考在 Visual c + + 联机丛书提供大量的文档上 dllexport 和 dllimport 存储类属性。这包括编程技术参考的"创建 dll 的 Win3...在 Visual c + + 联机丛书提供大量的文档上 dllexport 和 dllimport 存储类属性。这包括编程技术参考的"创建 dll 的 Win32"一章中的"的 dllexport 和 dllimport 属性"和"使用 dllimport 和 c + + 中的 dllexport"主题中的 c + + 语言参考中,"特定于 Microsoft 的修饰符"一章和"导出符号"主题。 有关完整列表相关的主题搜索联机丛书"dllimport"或"dllexport"。

有关详细的信息,请参阅下面 Microsoft 知识库中相应的文章:
90530  (http://support.microsoft.com/kb/90530/EN-US/ ) 如何从 DLL 或应用程序中导出数据
107501  (http://support.microsoft.com/kb/107501/EN-US/ ) 信息: __export 按 __declspec Visual c + + 32-位中替换

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值