基于Visual C++6.0的DDL编程实现

来源:互联网 发布:高仿app源码下载 编辑:程序博客网 时间:2024/06/05 08:35

在Windows操作系统中使用DLL有很多优点,最主要的一点是多个应用程序、甚至是不同语言编写的应用程序可以共享一个DLL文件,真正实现了资源"共享",大大缩小了应用程序的执行代码,更加有效的利用了内存;使用DLL的另一个优点是DLL文件作为一个单独的程序模块,封装性、独立性好,在软件需要升级的时候,开发人员只需要修改相应的DLL文件就可以了,而且,当DLL中的函数改变后,只要不是参数的改变,程序代码并不需要重新编译。这在编程时十分有用,大大提高了软件开发和维护的效率。   
    
  二、DLL的概念   
    
    DLL是建立在客户/服务器通信的概念上,包含若干函数、类或资源的库文件,函数和数据被存储在一个DLL(服务器)上并由一个或多个客户导出而使用,这些客户可以是应用程序或者是其它的DLL。DLL库不同于静态库,在静态库情况下,函数和数据被编译进一个二进制文件(通常扩展名为*.LIB),Visual   C++的编译器在处理程序代码时将从静态库中恢复这些函数和数据并把他们和应用程序中的其他模块组合在一起生成可执行文件。这个过程称为"静态链接",此时因为应用程序所需的全部内容都是从库中复制了出来,所以静态库本身并不需要与可执行文件一起发行。   
    
    在动态库的情况下,有两个文件,一个是引入库(.LIB)文件,一个是DLL文件。引入库文件包含被DLL导出的函数的名称和位置,DLL文件包含实际的函数和数据,应用程序使用LIB文件链接到所需要使用的DLL文件,库中的函数和数据并不复制到可执行文件中,因此在应用程序的可执行文件中,存放的不是被调用的函数代码,而是DLL中所要调用的函数的内存地址,这样当一个或多个应用程序运行时,再把程序代码和被调用的函数代码链接起来,从而节省了内存资源。从上面的说明可以看出,DLL和.LIB文件必须随应用程序一起发行,否则应用程序将会产生错误。   
    
  微软的Visual   C++支持三种DLL,它们分别是Non-MFC   Dll(非MFC动态库)、Regular   Dll(常规DLL)、Extension   Dll(扩展DLL)。Non-MFC   DLL指的是不用MFC的类库结构,直接用C语言写的DLL,其导出的函数是标准的C接口,能被非MFC或MFC编写的应用程序所调用。Regular   DLL:和下述的Extension   Dlls一样,是用MFC类库编写的,它的一个明显的特点是在源文件里有一个继承CWinApp的类(注意:此类DLL虽然从CWinApp派生,但没有消息循环),被导出的函数是C函数、C++类或者C++成员函数(注意不要把术语C++类与MFC的微软基础C++类相混淆),调用常规DLL的应用程序不必是MFC应用程序,只要是能调用类C函数的应用程序就可以,它们可以是在Visual   C++、Dephi、Visual   Basic、Borland   C等编译环境下利用DLL开发应用程序。   

三、动态链接库的创建   
    
    在Visual   C++6.0开发环境下,打开File/New/Project选项,可以选择Win32   Dynamic-Link   Library或MFC   AppWizard[dll]来以不同的方式来创建Non-MFC   Dll、Regular   Dll、Extension   Dll等不同种类的动态链接库。   
    
    1.   Win32   Dynamic-Link   Library方式创建Non-MFC   DLL动态链接库   
    
    每一个DLL必须有一个入口点,这就象我们用C编写的应用程序一样,必须有一个WINMAIN函数一样。在Non-MFC   DLL中DllMain是一个缺省的入口函数,你不需要编写自己的DLL入口函数,用这个缺省的入口函数就能使动态链接库被调用时得到正确的初始化。如果应用程序的DLL需要分配额外的内存或资源时,或者说需要对每个进程或线程初始化和清除操作时,需要在相应的DLL工程的.CPP文件中对DllMain()函数按照下面的格式书写。   
  BOOL   APIENTRY   DllMain(HANDLE   hModule,DWORD   ul_reason_for_call,LPVOID   lpReserved)   
  {   
  switch(   ul_reason_for_call   )   
  {   
  case   DLL_PROCESS_ATTACH:   
  .......   
  case   DLL_THREAD_ATTACH:   
  .......   
  case   DLL_THREAD_DETACH:   
  .......   
  case   DLL_PROCESS_DETACH:   
  .......   
  }   
  return   TRUE;   
  }   
    参数中,hMoudle是动态库被调用时所传递来的一个指向自己的句柄(实际上,它是指向_DGROUP段的一个选择符);ul_reason_for_call是一个说明动态库被调原因的标志,当进程或线程装入或卸载动态链接库的时候,操作系统调用入口函数,并说明动态链接库被调用的原因,它所有的可能值为:DLL_PROCESS_ATTACH:   进程被调用、DLL_THREAD_ATTACH:   线程被调用、DLL_PROCESS_DETACH:   进程被停止、DLL_THREAD_DETACH:   线程被停止;lpReserved为保留参数。到此为止,DLL的入口函数已经写了,剩下部分的实现也不难,你可以在DLL工程中加入你所想要输出的函数或变量了。   
  我们已经知道DLL是包含若干个函数的库文件,应用程序使用DLL中的函数之前,应该先导出这些函数,以便供给应用程序使用。要导出这些函数有两种方法,一是在定义函数时使用导出关键字_declspec(dllexport),另外一种方法是在创建DLL文件时使用模块定义文件.Def。需要读者注意的是在使用第一种方法的时候,不能使用DEF文件。下面通过两个例子来说明如何使用这两种方法创建DLL文件。   
    
    1)使用导出函数关键字_declspec(dllexport)创建MyDll.dll,该动态链接库中有两个函数,分别用来实现得到两个数的最大和最小数。在MyDll.h和MyDLL.cpp文件中分别输入如下原代码:   
    
  该动态链接库编译成功后,打开MyDll工程中的debug目录,可以看到MyDll.dll、MyDll.lib两个文件。LIB文件中包含DLL文件名和DLL文件中的函数名等,该LIB文件只是对应该DLL文件的"映像文件",与DLL文件中,LIB文件的长度要小的多,在进行隐式链接DLL时要用到它。读者可能已经注意到在MyDll.h中有关键字"extern   C",它可以使其他编程语言访问你编写的DLL中的函数。   
  2)用.def文件创建工程MyDll   
    
    为了用.def文件创建DLL,请先删除上个例子创建的工程中的MyDll.h文件,保留MyDll.cpp并在该文件头删除#include   MyDll.h语句,同时往该工程中加入一个文本文件,命名为MyDll.def,再在该文件中加入如下代码:   
    
  LIBRARY   MyDll   
  EXPORTS   
  Max   
  Min   
    
    其中LIBRARY语句说明该def文件是属于相应DLL的,EXPORTS语句下列出要导出的函数名称。我们可以在.def文件中的导出函数后加@n,如Max@1,Min@2,表示要导出的函数顺序号,在进行显式连时可以用到它。该DLL编译成功后,打开工程中的Debug目录,同样也会看到MyDll.dll和MyDll.lib文件。   
    
  2.MFC   AppWizard[dll]方式生成常规/扩展DLL   
    
    在MFC   AppWizard[dll]下生成DLL文件又有三种方式,在创建DLL是,要根据实际情况选择创建DLL的方式。一种是常规DLL静态链接到MFC,另一种是常规DLL动态链接到MFC。两者的区别是:前者使用的是MFC的静态链接库,生成的DLL文件长度大,一般不使用这种方式,后者使用MFC的动态链接库,生成的DLL文件长度小;动态链接到MFC的规则DLL所有输出的函数应该以如下语句开始:     
  AFX_MANAGE_STATE(AfxGetStaticModuleState(   ))   //此语句用来正确地切换MFC模块状态   
    最后一种是MFC扩展DLL,这种DLL特点是用来建立MFC的派生类,Dll只被用MFC类库所编写的应用程序所调用。   
    
  在MFC下建立DLL文件,会自动生成def文件框架,其它与建立传统的Non-MFC   DLL没有什么区别,只要在相应的头文件写入关键字_declspec(dllexport)函数类型和函数名等,或在生成的def文件中EXPORTS下输入函数名就可以了。需要注意的是在向其它开发人员分发MFC扩展DLL   时,不要忘记提供描述DLL中类的头文件以及相应的.LIB文件和DLL本身,此后开发人员就能充分利用你开发的扩展DLL了。

四、动态链接库DLL的链接   
    
    应用程序使用DLL可以采用两种方式:一种是隐式链接,另一种是显式链接。在使用DLL之前首先要知道DLL中函数的结构信息。Visual   C++6.0在VC/bin目录下提供了一个名为Dumpbin.exe的小程序,用它可以查看DLL文件中的函数结构。另外,Windows系统将遵循下面的搜索顺序来定位DLL:   1.包含EXE文件的目录,2.进程的当前工作目录,   3.Windows系统目录,   4.Windows目录,5.列在Path环境变量中的一系列目录。   
    
    1.隐式链接   
    
    隐式链接就是在程序开始执行时就将DLL文件加载到应用程序当中。实现隐式链接很容易,只要将导入函数关键字_declspec(dllimport)函数名等写到应用程序相应的头文件中就可以了。下面的例子通过隐式链接调用MyDll.dll库中的Min函数。首先生成一个项目为TestDll,在DllTest.h、DllTest.cpp文件中分别输入如下代码:   
    
  在创建DllTest.exe文件之前,要先将MyDll.dll和MyDll.lib拷贝到当前工程所在的目录下面,也可以拷贝到windows的System目录下。如果DLL使用的是def文件,要删除TestDll.h文件中关键字extern   "C"。TestDll.h文件中的关键字Progam   commit是要Visual   C+的编译器在link时,链接到MyDll.lib文件,当然,开发人员也可以不使用#pragma   comment(lib,"MyDll.lib")语句,而直接在工程的Setting->Link页的Object/Moduls栏填入MyDll.lib既可。   
    
    2.显式链接   
    
    显式链接是应用程序在执行过程中随时可以加载DLL文件,也可以随时卸载DLL文件,这是隐式链接所无法作到的,所以显式链接具有更好的灵活性,对于解释性语言更为合适。不过实现显式链接要麻烦一些。在应用程序中用LoadLibrary或MFC提供的AfxLoadLibrary显式的将自己所做的动态链接库调进来,动态链接库的文件名即是上述两个函数的参数,此后再用GetProcAddress()获取想要引入的函数。自此,你就可以象使用如同在应用程序自定义的函数一样来调用此引入函数了。在应用程序退出之前,应该用FreeLibrary或MFC提供的AfxFreeLibrary释放动态链接库。下面是通过显式链接调用DLL中的Max函数的例子。   
  #include   <iostream>   
  #include   <windows.h>   
    
  int   main()   
  {   
  typedef   int(*pMax)(int   a,   int   b);   
  typedef   int(*pMin)(int   a,   int   b);   
  HINSTANCE   hDLL=NULL;   
    
  pMax   Max;   
  pMin   Min;   
  hDLL   =   LoadLibrary("myDLL.dll");//加载动态链接库myDLL.dll文件   
    
  Max   =   (pMax)GetProcAddress(hDLL,"Max");//通过GetProcAddress()函数获取导入到应用程序中的函数指针   
  Min   =   (pMin)GetProcAddress(hDLL,"Min");   
    
  int   A,   B;   
  A   =   Max(5,8);   
  B   =   Min(5,8);   
  std::cout   <<   "比较的结果是:"   <<   A   <<   std::endl;   
  std::cout   <<   "比较的结果是:"   <<   B   <<   std::endl;   
    
  FreeLibrary(hDLL);//卸载myDLL.dll文件   
  return   0;   
  }   
    在上例中使用类型定义关键字typedef,定义指向和DLL中相同的函数原型指针,然后通过LoadLibray()将DLL加载到当前的应用程序中并返回当前DLL文件的句柄,然后通过GetProcAddress()函数获取导入到应用程序中的函数指针,函数调用完毕后,使用FreeLibrary()卸载DLL文件。在编译程序之前,首先要将DLL文件拷贝到工程所在的目录或Windows系统目录下。   
    
    使用显式链接应用程序编译时不需要使用相应的Lib文件。另外,使用GetProcAddress()函数时,可以利用MAKEINTRESOURCE()函数直接使用DLL中函数出现的顺序号,如将GetProcAddress(hDLL,"Min")改为GetProcAddress(hDLL,   MAKEINTRESOURCE(2))(函数Min()在DLL中的顺序号是2),这样调用DLL中的函数速度很快,但是要记住函数的使用序号,否则会发生错误。  

原创粉丝点击