是时候总结一下__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. 手机三种SIM卡 你所不知道的剪卡“秘密”

    SIM卡物理尺寸的发展是逐渐轻薄化,尺寸逐渐缩小的一个过程,最早手机中的卡都是2FF,2003年国际标准提出3FF,当前很多终端都使用这种形态的卡,4FF在2011年的国际标准会议中提出,2012年纳 ...

  2. perl 爬取csdn

    <pre name="code" class="python">use LWP::UserAgent; use POSIX; use HTML::T ...

  3. Java中Array.sort()的几种用法

    ****************************************************** * 精品书籍推荐:<Java从入门到经通> * 本书系统全面.浅显易懂,非常适 ...

  4. 【Python脚本】Python创建删除文件-----------我的第一个Python脚本

    Python相对C++和Java来说,是解释性语言,非常适合来编写脚本. 很久之前就开始学习Python的语法了,今天写了第一个Python的脚本,来简化我的一些日常工作. 我平时学习的时候喜欢新建一 ...

  5. Android学习总结——SQLite

    SQLiteDatabase类: 一.使用sql语句操作数据库 SQLiteDatabase db = openOrCreateDatabase("database.db", MO ...

  6. js获取浏览器的keydown事件(附keycode码)

    <script type="text/javascript" language=JavaScript charset="UTF-8"> docume ...

  7. SQL Server 添加一条数据获取自动增长列的几种方法

      数据库表设计  邓老师(老邓教的) insert into TestOne ','Test011') select @@IDENTITY as '自动增长ID' 杨老师(老杨教的) insert ...

  8. ASP.NET在实际开发中验证码的用法

    在网上有看到很多关于验证码的代码,很多都只是生成一张验证码图片,然而在实际登陆验证模块,验证码要怎么添加进去或者说怎么运用.和实际项目开发中要怎么使用验证码,我自己总结了几点. 一.在实际开发登陆模块 ...

  9. jquery 综合使用例子

    效果如下: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w ...

  10. 写sql语句注意事项

    做管理系统的,无论是bs结构的还是cs结构的,都不可避免的涉及到数据库表结构的设计,sql语句的编写等.因此在开发系统的时候,表结构设计是否合理,sql语句是否标准,写出的sql性能是否优化往往会成为 ...