_declspec(dllexport) \__declspec(dllimport)

来源:互联网 发布:php 删除文件 编辑:程序博客网 时间:2024/05/29 11:06

__declspec(XXXXXX)是windows扩展C++的编译宏头


_declspec(dllexport) 

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

__declspec(dllimport)

声明一个导入函数,是说这个函数是从别的DLL导入。我要用。一般用于使用某个dll的exe中 。
不使用 __declspec(dllimport) 也能正确编译代码,但使用 __declspec(dllimport) 使编译器可以生成更好的代码。编译器之所以能够生成更好的代码,是因为它可以确定函数是否存在于 DLL 中,这使得编译器可以生成跳过间接寻址级别的代码,而这些代码通常会出现在跨 DLL 边界的函数调用中。但是,必须使用 __declspec(dllimport) 才能导入 DLL 中使用的变量。

    相信写WIN32程序的人,做过DLL,都会很清楚__declspec(dllexport)的作用,它就是为了省掉在DEF文件中手工定义导出哪些函数的一个方法。当然,如果你的DLL里全是C++的类的话,你无法在DEF里指定导出的函数,只能用__declspec(dllexport)导出类。但是,MSDN文档里面,对于__declspec(dllimport)的说明让人感觉有点奇怪,先来看看MSDN里面是怎么说的:

    不使用 __declspec(dllimport) 也能正确编译代码,但使用 __declspec(dllimport) 使编译器可以生成更好的代码。编译器之所以能够生成更好的代码,是因为它可以确定函数是否存在于 DLL 中,这使得编译器可以生成跳过间接寻址级别的代码,而这些代码通常会出现在跨 DLL 边界的函数调用中。但是,必须使用 __declspec(dllimport) 才能导入 DLL 中使用的变量。

extern    "C"   

指示编译器用C语言方法给函数命名。

在制作DLL导出函数时由于C++存在函数重载,因此__declspec(dllexport)    function(int,int)    在DLL会被decorate,例如被decorate成为    function_int_int,而且不同的编译器decorate的方法不同,造成了在用GetProcAddress取得function地址时的不便,使用extern    "C"时,上述的decorate不会发生,因为C没有函数重载,但如此一来被extern"C"修饰的函数,就不具备重载能力,可以说extern    和   extern    "C"不是一回事。

C++编译器在生成DLL时,会对导出的函数进行名字改编,并且不同的编译器使用的改变规则不一样,因此改编后的名字会不一样。这样,如果利用不同的编译器分别生成DLL和访问该DLL的客户端代码程序的话,后者在访问该DLL的导出函数时会出现问题。为了实现通用性,需要加上限定符:extern “C”。

但是利用限定符extern “C”可以解决C++和C之间相互调用时函数命名的问题,但是这种方法有一个缺陷,就是不能用于导出一个类的成员函数,只能用于导出全局函数。
  LoadLibrary导入的函数名,对于非改编的函数,可以写函数名;对于改编的函数,就必须吧@和号码都写上,一样可以加载成功,可以试试看。

解决警告  inconsistent dll linkage

    inconsistent dll linkage警告是写dll时常遇到的一个问题,解决此警告的方法如下:

    一般PREDLL_API工程依赖于是否定义了MYDLL_EXPORTS来决定宏展开为__declspec(dllexport)还是__declspec(dllimport)。展开为__declspec(dllexport)是DLL编译时的需要,通知编译器该函数是需要导出供外部调用的。展开为__declspec(dllimport)是给调用者用的,通知编译器,该函数是个外部导入函数。

对于工程设置里面的预定义宏,是最早被编译器看到的。所以当编译器编译DLL工程中的MYDLL.cpp时,因为看到前面有工程设置有定义MYDLL_EXPORTS,所以就把PREDLL_API展开为__declspec(dllexport)了。

这样做的目的是为了让DLL和调用者共用同一个h文件,在DLL项目中,定义MYDLL_EXPORTS,PREDLL_API就是导出;在调用该DLL的项目中,不定义MYDLL_EXPORTS,PREDLL_API就是导入。

使用dll的两种方式

方法一: load-time dynamic linking (隐式调用)
  在要调用dll的应用程序链接时,将dll的输入库文件(import library,.lib文件)包含进去。具体的做法是在源文件开头加一句#include ,然后就可以在源文件中调用dlldemo.dll中的输出文件了。

方法二: run-time dynamic linking (显示调用)
  不必在链接时包含输入库文件,而是在源程序中使用LoadLibrary或LoadLibraryEx动态的载入dll。
  主要步骤为(以demodll.dll为例): 

1) typedef函数原型和定义函数指针。
 typedef void (CALLBACK* DllFooType)(void) ;
 DllFooType pfnDllFoo = NULL ;
2) 使用LoadLibrary载入dll,并保存dll实例句柄
 HINSTANCE dllHandle = NULL ;
 ... 
 dllHandle = LoadLibrary(TEXT("dlldemo.dll"));
3) 使用GetProcAddress得到dll中函数的指针
 pfnDllFoo = (DllFooType)GetProcAddress(dllHandle,TEXT("DllFoo")) ;
 注意从GetProcAddress返回的指针必须转型为特定类型的函数指针。
4)检验函数指针,如果不为空则可调用该函数 
 if(pfnDllFoo!=NULL)
 DllFoo() ;
5)使用FreeLibrary卸载dll
 FreeLibrary(dllHandle) ;

    使用run-time dynamic linking 比较麻烦,但有它的好处(下面讨论)。MSDN中有一篇文章DLLs the Dynamic Way讨论使用c的宏创建一个基类pDll完成以上复杂的操作,使用时只需定义一个类继承自类pDll并对类和函数使用宏。 
 以上两种方法都要求应用程序能找到dll文件,Windows按以下顺序寻找dll文件:

   如果系统不能找到dll文件,将结束调用dll的进程并弹出一个“启动程序时出错”对话框,告诉你“找不到所需的dll文件-XXX.dll”

原创粉丝点击