八宝书库 > 文学其他电子书 > VC语言6.0程序设计从入门到精通 >

第59部分

VC语言6.0程序设计从入门到精通-第59部分

小说: VC语言6.0程序设计从入门到精通 字数: 每页4000字

按键盘上方向键 ← 或 → 可快速上下翻页,按键盘上的 Enter 键可回到本书目录页,按键盘上方向键 ↑ 可回到本页顶部!
————未阅读完?加入书签已便下次继续阅读!




下面是通过显式链接调用 DLL 中的 Max 函数的例子,代码如下:  



     #include …  



     void main(void)  



     {  



         typedef int(*pMax)(int a;int b);  



         typedef int(*pMin)(int a;int b);  



         HINSTANCE    hDLL;  



         PMax Max;  



         HDLL=LoadLibrary(〃MyDll。dll〃);//加载动态链接库 MyDll。dll 文件;  



         Max=(pMax)GetProcAddress(hDLL;〃Max〃);  



         A=Max(5;8);  



         printf(〃 比较的结果为%dn〃,a);  



         FreeLibrary(hDLL);//卸载 MyDll。dll 文件;  



     }  



     在上面的程序片断中使用类型定义关键字 typedef ,定义指向和 DLL  中相同的函数原型 



 ·258 ·  


…………………………………………………………Page 270……………………………………………………………

                                                                                   第 10 章    动态链接库  



指针,然后通过 LoadLibray()将 DLL 加载到当前的应用程序中并返回到当前 DLL  文件的句 

柄,然后通过 GetProcAddress()函数获取导入到应用程序中的函数指针。函数调用完毕后,使 

用 FreeLibrary()卸载 DLL 文件。在编译程序之前,首先要将 DLL 文件拷贝到工程所在的目 

录或 Windows 系统目录下。  

     使用显式链接应用程序编译时不需要使用相应的 LIB 文件。另外,使用 GetProcAddress() 

函数时,可以利用  MAKEINTRESOURCE() 函数直接使用  DLL                                  中函数出现的顺序号,如将 

GetProcAddress(hDLL;〃Min〃) 改 为          GetProcAddress(hDLL;MAKEINTRESOURCE(2))                 ( 函 数 

Min()在 DLL 中的顺序号是 2 ),这样调用 DLL 中函数的速度将会很快,但是要记住函数的使 

用序号,否则会发生错误。  



10。4    开发 DLL  



     在   Visual  C++6。0    开发环境下,打开“FileNewProject ”选项,可以通过选择  Win32  

Dynamic…Link Library 或 MFC AppWizard'dll' 的不同方式来创建 Non…MFC Dll 、Regular Dll 、 

Extension Dll 等不同种类的动态链接库。  



10。4。1    创建 Non…MFC DLL 动态链接库  



     每一个 DLL 必须有一个入口点,这就像用 C 编写的应用程序一样,必须有一个 WinMain 

函数一样。在 Non…MFC  DLL  中 DllMain()是一个默认的入口函数,不需要编写自己的 DLL 

入口函数,用 DllMain()函数就能使动态链接库在被调用时得到正确的初始化。如果应用程序 

的 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;  



     }  



     关于 DllMain() 函数,在  10。2。1 节中已经作了详细介绍,这里不再赘述。DLL  是包含若 



                                                                                                 ·259 ·  


…………………………………………………………Page 271……………………………………………………………

Visual C++ 6。0 程序设计从入门到精通  



干个函数的库文件,应用程序使用 DLL 中的函数之前,应该先导出这些函数,以便供给应用 

程 序 使 用 。 要 导 出 这 些 函 数 有 两 种 方 法 , 一 是 在 定 义 函 数 时 使 用 导 出 关 键 字 

_declspec(dllexport) ,另外一种方法是在创建 DLL 文件时使用模块定义 DEF 文件。需要读者 

注意的是在使用第一种方法的时候,不能使用 DEF 文件。下面通过两个例子来说明使用这两 

种方法创建 DLL 文件的方法。  



      1.使用关键字_declspec(dllexport)  



      使用导出函数关键字_declspec(dllexport)创建 MyDll。dll,该动态链接库中有两个函数,分别 

用来实现得到两个数的最大和最小值。在 MyDll。h 和 MyDLL。cpp 文件中分别输入如下原代码:  



      //MyDLL。h  



      extern 〃C〃 _declspec(dllexport) int Max(int a; int b);  



      extern 〃C〃 _declspec(dllexport) int Min(int a; int b);  



      //MyDll。cpp  



      #include  



      #include〃MyDll。h〃  



      int Max(int a; int b)  



      {  



           if(a》=b)  



                 return a;  



           else  



                 return b;  



      }  



      int Min(int a; int b)  



      {  



           if(a》=b)  



                 return b;  



           else  



                 return a;  



      }  



      该动态链接库编译成功后,打开“MyDll ”工程中的“debug ”目录,可以看到 MyDll。dll 、 

MyDll。lib 两个文件。LIB 文件中包含 DLL 文件名和 DLL 文件中的函数名等,该文件只是对 

应 DLL 文件的“映像文件 ”,比 DLL 文件中 LIB 文件的长度小,在进行隐式链接 DLL 时要 

用到它。在 MyDll。h  中有关键字〃extern  C〃,它可以使其他编程语言访问所编写的 DLL  中的 

函数。  



      2 .用 DEF 文件创建工程  



      为了用  DEF        文件创建  DLL ,请先删除上个例子创建的工程中的  MyDll。h                                       文件,保留 

MyDll。cpp     并在该文件中删除#include  MyDll。h                    语句,同时加入一个文本文件,命名为 

MyDll。def ,再添加如下代码:  



      LIBRARY MyDll  



 ·260 ·  


…………………………………………………………Page 272……………………………………………………………

                                                                   第 10 章    动态链接库  



    EXPORTS  



    Max  



    Min  



    其中 LIBRARY 语句说明该 DEF 文件属于相应的 DLL ,可以在 EXPORTS 语句下列出要 

导出的函数名称。如果在 DEF 文件中的导出函数后加@n,如 Max@1 和 Min@2 ,表示要导 

出的函数顺序号,在进行显式连时可以用到它。该 DLL  编译成功后,打开工程中的  Debug 

目录,同样也会看到MyDll。dll 和 MyDll。lib 文件。  



10。4。2    MFC AppWizard'dll'方式生成常规/扩展 DLL  



    在 MFC  AppWizard'dll'下生成 DLL 文件有 3 种方式,在创建 DLL 时,要根据实际情况 

选择创建 DLL  的方式。一种是常规 DLL 静态链接到 MFC ,另一种是常规 DLL 动态链接到 

MFC 。前者使用的是 MFC 的静态链接库,生成的 DLL 文件长度大,一般不使用这种方式; 

后者使用 MFC 的动态链接库,生成的 DLL 文件长度小。动态链接到 MFC 的规则 DLL 所有 

输出的函数应该以如下语句开始:  



    AFX_MANAGE_STATE(AfxGetStaticModuleState()) //此语句用来正确地切换 MFC 模块状态    



    最后一种是 MFC  扩展 DLL ,这种 DLL  特点是用来建立 MFC  的派生类,DLL  只被用 

MFC 类库所编写的应用程序所调用。前面已经介绍过,Extension  DLLs  和 Regular  DLLs 不 

一样,它没有一个从            CWinApp  继承而来的类的对象,编译器默认了一个  DLL                       入口函数 

DLLMain()作为对 DLL 的初始化,可以在此函数中实现初始化,代码如下:  



    BOOL WINAPI APIENTRY DLLMain(HINSTANCE hinstDll ,DWORD reason ,LPVOID flmpload)  



    {  



        switch(reason)  



        {  



             ……………//初始化代码;  



        }  



        return true;  



    }  



    参数 hinstDll 表示存放 DLL 的句柄,参数 reason 指明调用函数的原因 。对于隐式链接是 

一个非零值,对于显式链接值是零。  

    在 MFC 下建立 DLL 文件,会自动生成 def 文件框架,其他与建立传统的 Non…MFC DLL 

没有什么区别,只要在相应的头文件写入关键字_declspec(dllexport) 函数类型和函数名等,或 

在生成的 def 文件中 EXPORTS  下输入函数名就可以了。需要注意的是在向其他开发人员分 

发 MFC 扩展 DLL 时,不要忘记提供描述 DLL 中类的头文件以及相应的 LIB 文件和 DLL 本 

身,此后开发人员就能充分利用开发的扩展 DLL 了。  



10。4。3    导出函数调用约定  



    关于动态链接库输出函数的约定有调用约定和名字修饰约定两种。调用约定决定着函数 

参数传送时入栈和出栈的顺序,以及编译器用来识别函数名字的修饰约定。名字修饰约定随 



                                                                               ·261 ·  


…………………………………………………………Page 273……………………………………………………………

Visual C++ 6。0 程序设计从入门到精通  



调用约定和编译种类(C 或 C++ )的不同而变化。为了让不同的编程语言共享动态链接库带 

来的方便,函数输出时必须使用正确的调用约定,并且最好不带有任何由编译器生成的名字 

修饰。下面详细介绍实现这些需求的方法。  



    1.调用约定  



   Visual C++ 6。0 支持的函数调用约定有多种,在这里主要介绍_stdcall 调用约定、C 调用约 

定和_fastcall 调用约定。  

   _stdcall 调用约定相当于 16 位动态库中经常使用的 PASCAL 调用约定。在 32 位的 Visual  

C++  6。0 中 PASCAL  调用约定不再被支持(实际上它已被定义为_stdcall 。除了_pascal  外, 

_fortran 和_syscall  也不被支持),取而代之的是_stdcall 调用约定。两者实质上是一致的,即 

函数的参数自右向左通过栈传递,被调用的函数在返回前清理传送参数的内存栈,但不同的 

是函数名的修饰部分(具体参见下一节介绍 )。 

                                        

   C  调用约定(即用_cdecl  关键字说明)和_stdcall  调用约定有所不同,虽然在参数传送方 

面是一样的,但 C 调用约定对于传送参数的内存栈却是由调用者来维护的(也正因为如此, 

实现可变参数的函数只能使用该调用约定),另外,在函数名修饰约定方面也有所不同。  

   _fastcall 调用约定的主要特点就是快,因为它是通过寄存器来传送参数的(实际上,它用 

ECX 和 EDX 传送前两个双字或更小的参数,剩下的参数仍旧自右向左压栈传送,被调用的 

                                                                        

函数在返回前清理传送参数的内存栈),在函数名修饰约定方面,它和前两者均不同。 

   关键字_stdcall 、_cdecl 和_fastcall 可以直接加在要输出的函数前,也可以在编译环境中选 

择“Setting。。。|C/C++|Code Generation”菜单命令,在如图 10…3 所示的对话框中设置编译环境。 

当加在输出函数前的关键字与编译环境中的选择不同时,直接加在输出函数前的关键字有效。 

它们对应的命令行参数分别为/Gz 、/Gd 和/Gr 。默认状态为/Gd ,即_cdecl 。  



                                                           



                              图 10…3    编译环境设置  



    如果要完全模仿 PASCAL  调用约定首先必须使用_stdcall  调用约定,至于函数名修饰约 

定,可以通过其他方法模仿。另外需要注意的是 Windows。h 支持 WINAPI 宏,该宏可以将输 

出函数翻译成适当的调用约定,在 WIN32 中,它被定义为_stdcall 。  



    2 .函数名修饰约定  



    函数名修饰约定随编译种类和调用约定的不同而不同,下面分别说明。对于  C                             编译, 



 ·262 ·  


…………………………………………………………Page 274……………………………………………………………

                                                                  第 10 章    动态链接库  



_s

返回目录 上一页 下一页 回到顶部 0 0

你可能喜欢的