是时候总结一下__declspec(dllimport)的作用了。可能有人会问:__declspec(dllimport)和__declspec(dllexport)是一对的,在动态链接库中__declspec(dllexport)管导出,__declspec(dllimport)管导出,就像一个国家一样,有出口也有进口,有什么难理解的呢?这是一种很自然的思路,开始我也是这样理解。

但是在两年前的一个项目中,我发现不用__declspec(dllimport)似乎也可以。比如现在我新建一个使用共享MFC DLL的规则DLL工程:DllDlg。然后我新建两个文件:DllApi.h和DllApi.cpp。DllApi.h作为接口文 件,DllApi.cpp作为实现文件。

接着在DllApi.h声明一个函数:

  1. __declspec(dllexport) void HelloWorld();
  1. __declspec(dllexport) void HelloWorld();

在DllApi.cpp写这个函数的实现:

  1. void HelloWorld()
  2. {
  3. AfxMessageBox(_T("HelloWorld"));
  4. }
  1. void HelloWorld()
  2. {
  3. AfxMessageBox(_T("HelloWorld"));
  4. }

这样外部的应用程序或dll就能调用HelloWorld函数。这里要特别提醒的是:有些网友说要把DllApi.h中的__declspec(dllexport) void HelloWorld();改为__declspec(dllimport) void HelloWorld();才能提供给外部调用,实际上这并不需要,这个我已经测试过。从那时我就产生一个疑问:照这样,像类似下面的:

  1. #ifdef _EXPORTING
  2. #define API_DECLSPEC __declspec(dllexport)
  3. #else
  4. #define API_DECLSPEC __declspec(dllimport)
  5. #endif
  1. #ifdef _EXPORTING
  2. #define API_DECLSPEC    __declspec(dllexport)
  3. #else
  4. #define API_DECLSPEC    __declspec(dllimport)
  5. #endif

是不是就只剩下一种作用:让外部调用者看得更自然些,知道哪些接口是自己工程需要导入的?__declspec(dllimport)是不是一点实际作用都没有呢?这个疑问一直盘旋在我的脑海。直到最近,我在CSDN论坛上发了一个帖子:

__declspec(dllimport) 的作用到底在哪里呢?

总结了各位大虾的发言,特得出如下结论:
1. 在导入动态链接库中的全局变量方面起作用:
使用类似

  1. #ifdef _EXPORTING
  2. #define API_DECLSPEC __declspec(dllexport)
  3. #else
  4. #define API_DECLSPEC __declspec(dllimport)
  5. #endif
  1. #ifdef _EXPORTING
  2. #define API_DECLSPEC    __declspec(dllexport)
  3. #else
  4. #define API_DECLSPEC    __declspec(dllimport)
  5. #endif

可以更好地导出dll中的全局变量,比如按照的宏,可以在dll中这样导出全局变量:

  1. API_DECLSPEC CBtt g_Btt;
  1. API_DECLSPEC CBtt g_Btt;

然后在调用程序这样导入:

  1. API_DECLSPEC CBtt g_Btt;
  1. API_DECLSPEC CBtt g_Btt;

当然也可以使用extern关键字,比如在dll中这样导出全局变量:

  1. CBtt g_Btt;
  1. CBtt g_Btt;

然后在调用程序这样导入:

  1. extern CBtt g_Btt;
  1. extern CBtt g_Btt;

但据说使用__declspec(dllimport)更有效。

2. __declspec(dllimport)的作用主要体现在导出类的静态成员方面,
比如在动态链接库中定义这样一个导出类:

  1. class __declspec(dllexport) CBtt
  2. {
  3. public:
  4. CBtt(void);
  5. ~CBtt(void);
  6. public:
  7. CString m_str;
  8. static int GetValue()
  9. {
  10. return m_nValue;
  11. }
  12. private:
  13. static int m_nValue;
  14. };
  1. class __declspec(dllexport) CBtt
  2. {
  3. public:
  4. CBtt(void);
  5. ~CBtt(void);
  6. public:
  7. CString m_str;
  8. static int GetValue()
  9. {
  10. return m_nValue;
  11. }
  12. private:
  13. static int m_nValue;
  14. };

照上面这样声明,外部虽然可以使用CBtt类,但不能使用CBtt类的GetValue函数,一使用就会出现无法解析的外部符号 "public: static int CBtt::m_nValue" (?m_nValue@CBtt@@2HA)。只有如下声明才能使用CBtt类的GetValue函数:

  1. #ifdef _EXPORTING
  2. #define API_DECLSPEC __declspec(dllexport)
  3. #else
  4. #define API_DECLSPEC __declspec(dllimport)
  5. #endif
  6. class API_DECLSPEC CBtt
  7. {
  8. public:
  9. CBtt(void);
  10. ~CBtt(void);
  11. public:
  12. CString m_str;
  13. static int GetValue()
  14. {
  15. return m_nValue;
  16. }
  17. private:
  18. static int m_nValue;
  19. };
  1. #ifdef _EXPORTING
  2. #define API_DECLSPEC    __declspec(dllexport)
  3. #else
  4. #define API_DECLSPEC    __declspec(dllimport)
  5. #endif
  6. class API_DECLSPEC CBtt
  7. {
  8. public:
  9. CBtt(void);
  10. ~CBtt(void);
  11. public:
  12. CString m_str;
  13. static int GetValue()
  14. {
  15. return m_nValue;
  16. }
  17. private:
  18. static int m_nValue;
  19. };

3. 使用隐式使用dll时,不加__declspec(dllimport)完全可以,使用上没什么区别,只是在生成的二进制代码上稍微有点效率损失。

a、 不加__declspec(dllimport)时,在使用dll中的函数时,编译器并不能区别这是个普通函数,还是从其它dll里导入的函数,所以其生 成的代码如下:

call 地址1

地址1:
jmp 实际函数地址

b、有 __declspec(dllimport)时,编译器知道这是要从外部dll导入的函数,从而在生成的exe的输入表里留有该项,以便在运行 exe,PE载入器加载exe时对输入地址表IAT进行填写,这样生成的代码如下:

call dword ptr[输入表里哪项对应的内存地址] (注意:现在就不需要jmp stub了)。这里
有兴趣的朋友可以参看《编译原理》和 PE文件格式。

4.使用__declspec(dllimport)体现了语言的一种对称美,比如虽然!true就是表示false,但是我们还是需要false这个关键字,这里体现了一种对称美。

在此特别感谢CSDN的众位大侠:superdiablo、wltg2001、ccpaishi、jszj、WizardK、hurryboylqs、jingzhongrong、jameshooo、glacier3d、winnuke、starnight1981、laiyiling、yang79tao、ForestDB、zhouzhipen、lxlsymbome、Beyond_cn。

参考文献:

1. __declspec(dllimport) 到底有什么用?

from: http://blog.csdn.net/clever101/article/details/5421782

__declspec(dllimport)的作用的更多相关文章

  1. __declspec(dllexport) 和 __declspec(dllimport)的作用

    operatordll.h #include <iostream> #ifndef _WIN32 #define DLL_EXPORT#else #ifdef OPERATORDLL_EX ...

  2. __declspec(dllimport)与__declspec(dllexport)作用总结

    参考自:http://bbs.csdn.net/topics/330169671 __declspec(dllexport):导出符号,也就是定义需要导出函数的dll中给导出函数的函数声明前面加上导出 ...

  3. __declspec(dllimport)

    我相信写WIN32程序的人,做过DLL,都会很清楚__declspec(dllexport)的作用,它就是为了省掉在DEF文件中手工定义导出哪些函数的一个方法.当然,如果你的DLL里全是C++的类的话 ...

  4. extern "C" __declspec(dllexport) __declspec(dllimport) 和 def

    原文:extern "C" __declspec(dllexport) __declspec(dllimport) 和 def 前面的extern "C"  _ ...

  5. __declspec(dllexport) & __declspec(dllimport)

    __declspec(dllexport) 声明一个导出函数,是说这个函数要从本DLL导出.我要给别人用.一般用于dll中 省掉在DEF文件中手工定义导出哪些函数的一个方法.当然,如果你的DLL里全是 ...

  6. 浅谈__declspec(dllexport)和__declspec(dllimport)

    __declspec(dllimport)和__declspec(dllexport)经常是成对的,在动态链接库中__declspec(dllexport)导出dll中的成员,__declspec(d ...

  7. 动态链接库(dll) __declspec(dllimport) __declspec(dllexport)

    一. __declspec(dllexport) Microsoft 在 Visual C++ 的 16 位编译器版本中引入了 __export,使编译器得以自动生成导出名并将它们放到一个 .lib ...

  8. 【转载】extern "C" __declspec(dllexport) __declspec(dllimport) 和 def

    转自:http://www.cppblog.com/FateNo13/archive/2009/08/03/92052.html 前面的extern "C"  __declspec ...

  9. 【转载】 __declspec(dllexport) 和__declspec(dllimport)

    转自:http://www.cppblog.com/Dutyboy/archive/2010/11/15/133699.html __declspec(dllexport)   __declspec( ...

随机推荐

  1. 序列化与反序列化 - BinaryFormatter二进制(.dat)、SoapFormatter(.soap)、XmlSerializer(.xml)

    序列化的作用是什么?为什么要序列化? 1.在进程下次启动时读取上次保存的对象的信息. 2.在不同的应用程序域或进程之间传递数据. 3.在分布式应用程序中的各应用程序之间传输对象. 所为序列化,就是将对 ...

  2. Kth Largest Element in an Array 解答

    Question Find the kth largest element in an unsorted array. Note that it is the kth largest element ...

  3. 剑指offer-面试题18.树的子结构

    题目:输入两棵二叉树A和B,判断B是不是A的子结构. 二叉树节点定义如下: struct BinaryTreeNode { int m_nValue; BinaryTreeNode* m_pLeft; ...

  4. FZU1862(线段树 或者 DP)

    Problem 1862 QueryProblem Accept: 100    Submit: 249Time Limit: 2000 mSec    Memory Limit : 32768 KB ...

  5. gdb 调试coredump文件过程

    gdb 调试coredump文件过程: 第一步:首先需要一个进程的coredump文件,怎么搞出coredump文件呢? 1. ps -fax|grep                 进程名称 找到 ...

  6. IOS 掉用系统发短信

    #import <MessageUI/MessageUI.h> MFMessageComposeViewControllerDelegate #pragma mark - 调用ios系统短 ...

  7. 评教数据整理专用VBA小程序

    这次评教的所有数据存放在两个数据库中,比如说给某教师评论的学生有100个,可是结果有40个的数据在数据库A中,另外60人的数据在数据库B中.那么,如何将两个库中的数据整合,最后得到教师的准确成绩成为了 ...

  8. crm操作知识库文章实体

    using System;     using Microsoft.Xrm.Sdk;     using Microsoft.Crm.Sdk.Messages;     using Microsoft ...

  9. 理解java设计模式之观察者模式

    在生活实际中,我们经常会遇到关注一个事物数据变化的情况,例如生活中的温度记录仪,当温度变化时,我们观察它温度变化的曲线,温度记录日志等.对于这一类问题,很接近java设计模式里面的“观察者模式”,它适 ...

  10. CSS清除浮动的方法

    CSS清除浮动的方法有哪些呢?经常性地会使用到float,很多邪门的事儿都有可能是浮动在作怪,清除浮动是必须要做的,而且随时性地对父级元素清除浮动的做法也被认为是书写CSS的良好习惯之一.下面看今天的 ...