__declspec(dllexport)和__declspec(dllimport)

4 篇文章 0 订阅

一、简介

在Windows中,动态链接库(DLL)是作为函数和资源的共享库的一种可执行文件。动态链接是操作系统功能。它可使执行文件调用函数或使用存储在单独文件中的资源。可从使用这些函数和资源的可执行文件中对其分别进行部署。

DLL不是独立的可执行文件。DLL在调用它们的应用程序的上下文中运行。操作系统将DLL加载到应用程序的内存空间中。此操作要么在加载应用程序时(隐式链接)完成,要么在运行时按需(显示链接)完成。DLL还可以在可执行文件之间轻松共享函数和资源。多个应用程序可同时访问内存中单个DLL副本的内容。

DLL文件的布局于exe文件非常相似,但有一个重要区别:DLL文件包含导出表。导出表包含DLL导出到其他科执行文件的每个函数名称。这些函数时进入DLL中的入口点;只有导出表中的函数才能被其他可执行文件访问。DLL中的任何其他函数都是DLL的私有函数。

二、__declspec(dllexport)

我们可以使用__declspec(dllexport)关键字从DLL中导出数据、函数、类或类成员函数。

若要导出函数,__declspec(dllexport)关键字必须出现在调用约定关键字的左侧(如果指定了关键字的话)。例如:

__declspec(dllexport) void Function1(void);

若要导出类中的所有公共数据成员和成员函数,该关键字必须出现在类名的左侧,例如:

class __declspec(dllexport) CExampleExport : public CObject
{ 
    ... class definition ... 
};

下面的例子展示了导出函数的用法:

#ifndef __EXPORT_H_
#define __EXPORT_H_


__declspec(dllexport) void ExportHello();


#endif // __EXPORT_H_
#include "export.h"

#include <iostream>

void ExportHello()
{
    std::cout << "Hello World!" << std::endl;
}

我们选择动态库:

最终我们将生成相应的.dll(动态链接库)和.lib(导入库)文件。

注:.lib(导入库)是编译器生成dll时自动生成的lib文件(只包含了函数实现的声明索引信息,记录了dll文件中的函数的入库和地址信息)。

导入库的作用:导入库方便程序静态载入动态链接库,否则你可能需要在程序中调用LoadLibary调入dll,然后在调用GetProcAddress来获取对应的函数。而有了导入库则只需要在头文件中使用__declspec(dllimport)关键字声明调用函数即可。

三、__declspec(dllimport)

实际上__declspec(dllimport)是可选的。下面这个例子不使用__declspec(dllimport)关键字。

#ifndef __EXPORT_H_
#define __EXPORT_H_

void ExportHello();

#endif // __EXPORT_H_
#include "export.h"

int main(void)
{
    ExportHello();

    return 0;
}

当然__declspec(dllimport)也有自己的用途。

编译器会生成更高效的代码

未使用__declspec(dllimport)

我们之间使用上述的例子,反汇编查看生成的代码:

int main(void)
{
00007FF747711760  push        rbp  
00007FF747711762  push        rdi  
00007FF747711763  sub         rsp,0E8h  
00007FF74771176A  lea         rbp,[rsp+20h]  
00007FF74771176F  lea         rcx,[__2CB3C88E_main@cpp (07FF747721001h)]  
00007FF747711776  call        __CheckForDebuggerJustMyCode (07FF747711348h)  
    ExportHello();
00007FF74771177B  call        ExportHello (07FF7477110C3h)  

    return 0;
00007FF747711780  xor         eax,eax  
}

我们单步调试调用动态库导出函数的过程:

00007FF7477110C3  jmp         ExportHello (07FF747711798h)
00007FF747711798  jmp         qword ptr [__imp_ExportHello (07FF747720000h)] 
00007FFED06510C8  jmp         ExportHello (07FFED06520F0h) 
void ExportHello()
{
00007FFED06520F0  push        rbp  
00007FFED06520F2  push        rdi  
00007FFED06520F3  sub         rsp,0E8h  
00007FFED06520FA  lea         rbp,[rsp+20h]  
00007FFED06520FF  lea         rcx,[__01D3C14F_export@cpp (07FFED0663067h)]  
00007FFED0652106  call        __CheckForDebuggerJustMyCode (07FFED065136Bh)  
    std::cout << "Hello World!" << std::endl;
00007FFED065210B  lea         rdx,[string "Hello World!" (07FFED065A8F8h)]  
00007FFED0652112  mov         rcx,qword ptr [__imp_std::cout (07FFED06611A0h)]  
00007FFED0652119  call        std::operator<<<std::char_traits<char> > (07FFED065107Dh)  
00007FFED065211E  lea         rdx,[std::endl<char,std::char_traits<char> > (07FFED0651037h)]  
00007FFED0652125  mov         rcx,rax  
00007FFED0652128  call        qword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (07FFED0661190h)]  
}

使用__declspec(dllimport)

通用使用相同的例子,但是这次加上__declspec(dllimport)关键字,然后反汇编查看:

int main(void)
{
00007FF609CC1760  push        rbp  
00007FF609CC1762  push        rdi  
00007FF609CC1763  sub         rsp,0E8h  
00007FF609CC176A  lea         rbp,[rsp+20h]  
00007FF609CC176F  lea         rcx,[__2CB3C88E_main@cpp (07FF609CD1001h)]  
00007FF609CC1776  call        __CheckForDebuggerJustMyCode (07FF609CC1348h)  
    ExportHello();
00007FF609CC177B  call        qword ptr [__imp_ExportHello (07FF609CD0000h)]  

    return 0;
00007FF609CC1781  xor         eax,eax  
}

我们单步调试调用过程:

00007FFE6D3C10C8  jmp         ExportHello (07FFE6D3C20F0h)
void ExportHello()
{
00007FFE6D3C20F0  push        rbp  
00007FFE6D3C20F2  push        rdi  
00007FFE6D3C20F3  sub         rsp,0E8h  
00007FFE6D3C20FA  lea         rbp,[rsp+20h]  
00007FFE6D3C20FF  lea         rcx,[__01D3C14F_export@cpp (07FFE6D3D3067h)]  
00007FFE6D3C2106  call        __CheckForDebuggerJustMyCode (07FFE6D3C136Bh)  
    std::cout << "Hello World!" << std::endl;
00007FFE6D3C210B  lea         rdx,[string "Hello World!" (07FFE6D3CA8F8h)]  
00007FFE6D3C2112  mov         rcx,qword ptr [__imp_std::cout (07FFE6D3D11A0h)]  
00007FFE6D3C2119  call        std::operator<<<std::char_traits<char> > (07FFE6D3C107Dh)  
00007FFE6D3C211E  lea         rdx,[std::endl<char,std::char_traits<char> > (07FFE6D3C1037h)]  
00007FFE6D3C2125  mov         rcx,rax  
00007FFE6D3C2128  call        qword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (07FFE6D3D1190h)]  
}

总结:

使用__declspec(dllimport)时不会生成 thunk 。 Thunk 使代码变大,并可能降低缓存性能。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值