现在的位置: 首页 > 综合 > 正文

__declspec(dllimport)有什么用?

2018年03月29日 ⁄ 综合 ⁄ 共 2169字 ⁄ 字号 评论关闭

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

    我们通常在编写DLL时会用到这两个宏。比如现在我新建一个DLL工程:DllDlg。然后我新建两个文件:DllApi.h和DllApi.cpp。DllApi.h作为接口文 件,DllApi.cpp作为实现文件。

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

__declspec(dllexport) void HelloWorld(); </span>

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

    void HelloWorld()  
    {  
        AfxMessageBox(_T("HelloWorld"));  
    }  

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


    而我们经常的用法是这样的

    #ifdef _EXPORTING  
    #define API_DECLSPEC    __declspec(dllexport)  
    #else  
    #define API_DECLSPEC    __declspec(dllimport)  
    #endif  </span>

    是不是就只剩下一种作用:让外部调用者看得更自然些,知道哪些接口是自己工程需要导入的?__declspec(dllimport)是不是一点实际作用都没有呢?看到mniwc的文章,才明白其实不然,结论如下:

1. 在导入动态链接库中的全局变量方面起作用

    使用

   #ifdef _EXPORTING  
   #define API_DECLSPEC    __declspec(dllexport)  
   #else  
   #define API_DECLSPEC    __declspec(dllimport)  
   #endif 

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

   API_DECLSPEC CBtt g_Btt;  

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

   API_DECLSPEC CBtt g_Btt;  

  

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


   CBtt g_Btt;  

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

   extern CBtt g_Btt;  

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


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

    class __declspec(dllexport) CBtt  
    {  
    public:  
        CBtt(void);  
        ~CBtt(void);  
    public:  
        CString m_str;  
        static int GetValue()  
        {  
            return m_nValue;  
        }  
    private:  
        static int m_nValue;  
    };  

    照上面这样声明,外部虽然可以使用CBtt类,但不能使用CBtt类的GetValue函数,一使用就会出现无法解析的外部符号  

        "public: static int CBtt::m_nValue" (?m_nValue@CBtt@@2HA)。只有如下声明才能使用CBtt类的GetValue函数:

    #ifdef _EXPORTING  
    #define API_DECLSPEC    __declspec(dllexport)  
    #else  
    #define API_DECLSPEC    __declspec(dllimport)  
    #endif  
    class API_DECLSPEC CBtt  
    {  
    public:  
        CBtt(void);  
        ~CBtt(void);  
    public:  
        CString m_str;  
        static int GetValue()  
        {  
            return m_nValue;  
        }  
    private:  
        static int m_nValue;  
    };  


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这个关键字,这里体现了一种对称美。


抱歉!评论已关闭.