• __declspec(dllimport)的作用


    转自:http://blog.csdn.net/mniwc/article/details/7993361

    是时候总结一下__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(dllexportvoid HelloWorld();
    1. __declspec(dllexportvoid 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


  • 相关阅读:
    列"xx"不在表Table中
    asp.net中自定义验证控件
    ASP.NET母版与内容页相对路径的问题
    html点小图看大图最快捷的方法
    ThinkCMF的跳转303 404等页面的方法
    关于ThinkCMF自带插件上传不了图片的解决方法
    js中百分比运算,大型数据会算错
    数据库价格汇总查询的方法
    信息资源5
    操作系统概论
  • 原文地址:https://www.cnblogs.com/pamxy/p/2991440.html
  • Copyright © 2020-2023  润新知