dllimport 和dllexport使用总结

刚学dllimport 和dllexport,对于这两个关键字的使用,网上真是众说纷纭,总之让人导入导出傻傻分不清楚,所以在此整理一下自己的所学所得,方便日后查看。

对于dllimport 和dllexport的使用,首先要说的就是形如下面的这段宏定义:

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

该宏定义用来确定函数是导入还是导出。这段宏定义的意义如下:当创建dll时,肯定是想从外部调用dll中现成的函数、变量等,因此在新建dll项目时勾选附加选项中的“导出符号”,这样在dll的.cpp和.h文件中就会自动给出导出函数、变量、类的模板,而且会自动将_EXPORTING宏定义为1。dll创建完成后,如果直接在创建dll的项目中添加调用程序来调用dll,则创建dll时自动完成的_EXPORTING宏定义仍有效,就会执行__declspec(dllexport);但很多情况下,我们创建的dll很可能是给别人用或者是给自己的其他项目用,我们只给别人提供头文件、.dll和.lib,别人不会也去预定义一个_EXPORTING,所以很明显在他们的程序调用dll中的函数时就会执行#define API_DECLSPEC    __declspec(dllimport),也就是说,该宏定义使得函数、变量等在dll的提供者和使用者之间的声明方式不同,对提供者来说,该函数被声明为__declspec(dllexport),而对使用者来说,该函数被声明为__declspec(dllimport)。当然让别人的程序在调用dll时使用_declspec(dllimport) 有其好处,下面的代码示例就显示了 _declspec(dllimport)是如何影响外部应用程序调用dll的。假定 func1 是某个 DLL 中的函数,此 DLL 与调用程序是分开的。

调用程序中的代码如下:

int main(void) 
{
   func1();
}

如果不使用 __declspec(dllimport),则上面的程序在调用此函数时编译器生成类似下面的代码:

call func1
链接器再翻译成下面的内容:

call 0x4000000         ; The address of 'func1'.
如果 func1 存在于另一个 DLL 中,链接器将无法直接解析此函数,因为它无法得知 func1 的地址。在 16 位环境中,链接器将此代码地址添加到 .exe 文件中的某个列表中,而加载程序在运行时会用正确的地址修补该列表。在 32 位和 64 位环境中,链接器可生成一个知道其地址的 thunk。在 32 位环境中,thunk 类似如下所示:

0x40000000:    jmp DWORD PTR __imp_func1
其中,imp_func1 是 func1 的槽在 .exe 文件的导入地址表中的地址。这样,链接器就知道了所有的地址。加载程序只需在加载时更新 .exe 文件的导入地址表,一切就会正常进行。
但是如果使用 __declspec(dllimport),也就是说调用程序变为:

__declspec(dllimport) void func1(void);
int main(void) 
{
   func1();
}

则调用过程在内部会生成如下指令:

call DWORD PTR __imp_func1
因此,使用 __declspec(dllimport) 更好,因为链接器不会生成不必要的 thunk和 jmp 指令。thunk 会使代码变大(在 RISC 系统上代码它可能是若干指令),并且会降低缓存性能。所以如果通过__declspec(dllimport) 通知编译器函数在 DLL 中,则编译器会为您生成间接调用,代码更小且更快。
所以从代码能否执行成功的角度来说,有__declspec(dllimport)和没有__declspec(dllimport)并无多大差别,因为两种方式都能成功调用dll中的函数,只不过两者效率不一样,但如果从代码效率和优化来说,还是建议用__declspec(dllimport)。另外值得注意的是,如果要导出dll中的全局变量和类的静态变量,则必须使用__declspec(dllimport)。

 利用http://blog.csdn.net/Repeaterbin/article/details/4269666中的例子来说明导出dll中类的静态变量的情况,使用vs2010和c++。

当使用__declspec(dllimport)时:

首先要创建一个dll,新建解决方案命名为Test,项目名为CreatDll,勾选”导出符号“附加选项,CreatDll.h中的代码为:

// 下列 ifdef 块是创建使从 DLL 导出更简单的
// 宏的标准方法。此 DLL 中的所有文件都是用命令行上定义的 CREATDLL_EXPORTS
// 符号编译的。在使用此 DLL 的
// 任何其他项目上不应定义此符号。这样,源文件中包含此文件的任何其他项目都会将
// CREATDLL_API 函数视为是从 DLL 导入的,而此 DLL 则将用此宏定义的
// 符号视为是被导出的。
#ifdef CREATDLL_EXPORTS
#define CREATDLL_API __declspec(dllexport)
#else
#define CREATDLL_API __declspec(dllimport)
#endif

// 此类是从 CreatDll.dll 导出的
class CREATDLL_API CCreatDll {
private:
     static int m_nValue;
public:
     CCreatDll();
    ~CCreatDll();
     int changeValue(int x)  { return m_nValue+=x; }
     int getValue() { return m_nValue;}

};

CreatDll.cpp中的代码为:

#include "stdafx.h"
#include "CreatDll.h"
 
// 这是已导出类的构造函数。
// 有关类定义的信息,请参阅 CreatDll.h
 
CCreatDll::CCreatDll(){}
 
int CCreatDll::m_nValue=0;
 
CCreatDll::~CCreatDll(){}
新建一个项目来调用生成的dll,将生成的CreatDll.dll,CreatDll.lib和CreatDll.h拷至该项目目录下,该项目的main.cpp的代码为:

#include <iostream> 
#include "CreatDll.h" 
using namespacestd; 
#pragma comment(lib, "CreatDll.lib") 
 
int main( int argc, char** argv ){ 
        CCreatDll a,b;
        a.changeValue(2);
        b.changeValue(3);
        cout<<a.getValue()<<endl;
        system("pause");
} 
程序成功调用dll中的类,输出结果5。


当不使用__declspec(dllimport)时,则修改CreatDll.h,去掉__declspec(dllimport)的宏定义,其他不变,CreatDll.h中的代码变为:

// 下列 ifdef 块是创建使从 DLL 导出更简单的
// 宏的标准方法。此 DLL 中的所有文件都是用命令行上定义的 CREATDLL_EXPORTS
// 符号编译的。在使用此 DLL 的
// 任何其他项目上不应定义此符号。这样,源文件中包含此文件的任何其他项目都会将
// CREATDLL_API 函数视为是从 DLL 导入的,而此 DLL 则将用此宏定义的
// 符号视为是被导出的。
#ifdef CREATDLL_EXPORTS
#define CREATDLL_API __declspec(dllexport)
#else
//#define CREATDLL_API __declspec(dllimport)
#define CREATDLL_API 
#endif

// 此类是从 CreatDll.dll 导出的
class CREATDLL_API CCreatDll {
private:
    static int m_nValue;
public:
	CCreatDll();
    ~CCreatDll();
	int changeValue(int x)  { return m_nValue+=x; }
    int getValue() { return m_nValue;}

};
重新生成dll,还是用刚才的main.cpp去调用新生成的dll,结果出现错误,显示错误信息为:
error LNK2001: 无法解析的外部符号"private: static intCCreatDll::m_nValue" (?m_nValue@CCreatDll@@0HA)


其他的一些有价值的参考资料:extern "C" __declspec(dllexport) __declspec(dllimport) 和 def

                                                       __declspec(dllimport)

                                                      总结一下__declspec(dllimport)的作用

 

你可能感兴趣的:(dllimport,和dllexport)