友情提示:如果本网页打开太慢或显示不完整,请尝试鼠标右键“刷新”本网页!
第三电子书 返回本书目录 加入书签 我的书架 我的书签 TXT全本下载 『收藏到我的浏览器』

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

快捷操作: 按键盘上方向键 ← 或 → 可快速上下翻页 按键盘上的 Enter 键可回到本书目录页 按键盘上方向键 ↑ 可回到本页顶部! 如果本书没有阅读完,想下次继续接着阅读,可使用上方 "收藏到我的浏览器" 功能 和 "加入书签" 功能!


的代码。在正常情况下,这是应用程序的主线程。当进入点函数返回到 C/C++运行期库的启 

动代码时,该启动代码将显式调用 ExitProcess()函数,终止进程的运行。  

    如果因为进程中的线程调用 FreeLibrary()或 FreeLibraryAndExitThread() 函数而将 DLL 卸 

载,那么调用函数的线程将负责执行 DllMain()函数的代码。如果使用 FreeLibrary ,那么要等 

到  DllMain() 函数完成对   DLL_PROCESS_DETACH   通知的执行后,该线程才可以从对 

FreeLibrary 函数的调用中返回。  



    3 .DLL_THREAD_ATTACH 通知  



    当在一个进程中创建线程时,系统查看当前映射到该进程的地址空间中的所有 DLL 文件 

映像,并调用每个带有 DLL_THREAD_ATTACH  值的 DllMain() 函数文件映像。这样,DLL 

就可以执行每个线程的初始化操作。新创建的线程负责执行 DLL  的所有 DllMain()函数中的 

代码。只有当所有的 DLL 都有机会处理该通知时,系统才允许新线程开始执行它的线程函数。  

    当一个新 DLL 被映射到进程的地址空间时,如果该进程内已经有若干个线程正在运行, 

那么系统将不为现有的线程调用带有 DLL_THREAD_ATTACH 值的 DDL 的 DllMain()函数。 

只 有 当 新 线 程 创 建 时 , DLL   被 映 射 到 进 程 的 地 址 空 间 中 , 它 才 可 以 调 用 带 有 

DLL_THREAD_ATTACH 值的 DLL 的 DllMain() 函数。  

    另外要注意,系统并不为进程的主线程调用带有 DLL_THREAD_ATTACH 值的任何 DllMain() 

函数。进程初次启动时映射到进程的地址空间中的任何 DLL 均接收 DLL_PROCESS_ATTACH 通 

知,而不是 DLL_THREAD_ATTACH 通知。  



    4 .DLL_THREAD_DETACH 通知  



    让线程终止运行的首选方法是使它的线程函数返回。这使得系统可以调用  ExitThread() 

函数来撤消该线程。如果 ExitThread() 函数要终止运行该线程,系统不会立即将它撤消,而是 

取 出 这 个 即 将 被 撤 消 的 线 程 , 并 让 它 调 用 已 经 映 射 的          DLL  中 所 有 带 有 

DLL_THREAD_DETACH 值的 DllMain() 函数。这个通知告诉所有的 DLL 执行每个线程的清 

除操作。例如,DLL 版本的 C/C++运行期库能够释放它用于管理多线程应用程序的数据块。  



    注意:DLL 能够防止线程终止运行。例如,当 DllMain() 函数接收到 DLL_THREAD_DETACH 通 



        知时,它就能够进入一个无限循环。只有当每个 DLL  已经完成对 DLL_THREAD_DETACH 



        通知的处理时,操作系统才会终止线程的运行。  



    如 果 当  DLL 被 撤 消 时 仍 然 有 线 程 在 运 行 , 那 么 就 不 会 有 任 何 线 程 调 用 带 有 

DLL_THREAD_DETACH 值的 DllMain() 函数。可以在进行 DLL_THREAD_DETACH 的处理 

时查看这个情况,这样就能够执行必要的清除操作。  



10。2。2    DLL 的导出函数  



    当 Microsoft 的 C/C++编译器看到变量、函数原型或 C++类之前的这个修改符的时候,它 

就将某些附加信息嵌入产生的。obj 文件中。当链接 DLL 的所有。obj 文件时,链接程序将对这 

些信息进行分析。  

      



                                                                  ·255 ·  


…………………………………………………………Page 267……………………………………………………………

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



     当 DLL  被链接时,链接程序要查找关于输出变量、函数或  C++类的信息,并自动生成 

一个。lib 文件。该。lib 文件包含一个 DLL 输出的符号列表。当然,如果要链接引用该 DLL 的 

输出符号的任何可执行模块,该。lib 文件是必不可少的 。除了创建。lib 文件外,链接程序还要 

将一个输出符号表嵌入产生的 DLL 文件。这个输出节包含输出变量、函数和类符号的列表(按 

字母顺序排列)。该链接程序还将能够找到每个符号的相对虚拟地址(RVA ),并在该地址中 

放入 DLL 模块。  

    使用 Microsoft 的 Visual Studio 的 DumpBin。exe 实用程序(带有…exports 开关),能够看到 

DLL 的输出节是个什么样子。Kernel32。dll 的输出结果如图 10…1 所示。  



                                                                       



                         图 10…1    dumpbin 输出动态链接库的导出函数  



    通过 Visual  Studio 所提供的 Dependency  Walker 的可视化工具也可以查看动态链接库的 

导出函数信息,如图 10…2 所示。  



                                                                         



                     图 10…2    利用 Dependency Walker 工具查看导出函数信息  



 ·256 ·  


…………………………………………………………Page 268……………………………………………………………

                                                                  第 10 章    动态链接库  



10。3    两种链接 DLL 的方式  



    如果线程需要调用 DLL 模块中的函数,那么 DLL 文件映像必须映射到调用线程的进程 

地址空间中。可以用两种方法进行这项操作 。第一种方法是让应用程序的源代码只引用 DLL 

中包含的符号。这样,当应用程序启动运行时,加载程序就能够隐含加载(或链接)需要的 

DLL 。第二种方法是在运行时让应用程序显式加载需要的 DLL 并且显式链接到需要的输出符 

号。换句话说,当应用程序运行时,其中的线程决定它是否要调用 DLL 中的函数。该线程可 

以将 DLL 显式加载到进程的地址空间,获得 DLL  中包含的函数的虚拟内存地址,然后使用 

该内存地址调用该函数。该方法的一切操作都是在应用程序运行时进行的。  

    当线程加载动态链接库的时候,是按照下面的搜索顺序查找并加载动态链接库文件的。  

    o  当前目录下(首先将动态链接库拷贝至 DEBUG  目录下,因为可执行文件在该目录下)。  

    o  Windows  目录。  

    o  Windows 系统目录。  

    o  PATH 环境变量中设置的目录。  

    o  列入映射网络的目录表中的目录。  

    下面将介绍隐式链接和显式链接这两种调用 DLL 的方式。  



10。3。1    隐式链接  



    如果程序员采用隐式链接方式建立一个 DLL 文件,链接程序会自动生成一个与之对应的 

LIB 导入文件。该文件包含了每一个 DLL 导出函数的符号名和可选的标识号,但是并不含有 

            LIB 文件作为 DLL 的替代文件被编译到应用程序项目中。当程序员通过静态链 

实际的代码。 

接方式编译生成应用程序时,应用程序中的调用函数与 LIB 文件中导出符号相匹配,这些符 

号或标识号进入生成的 EXE 文件中。LIB 文件中也包含了对应的 DLL 文件名(但不是完全 

的路径名),链接程序将其存储在 EXE 文件内部。当应用程序运行过程中需要加载 DLL 文件 

时,Windows 根据这些信息发现并加载 DLL ,然后通过符号名或标识号实现对 DLL  函数的 

动态链接。  

    下面的例子通过隐式链接调用 MyDll。dll 库中的 Min 函数。首先生成一个 TestDll 项目, 

在 DllTest。h 、DllTest。cpp 文件中分别输入如下代码:  



    //Dlltest。h  



    #pragma ment(lib ,〃MyDll。lib〃)  



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



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



    //TestDll。cpp  



    #include〃Dlltest。h〃  



    void main()  



    {  



        int a;  



        a=min(8;10);  



                                                                             ·257 ·  


…………………………………………………………Page 269……………………………………………………………

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



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



     }  



       在创建 DllTest。exe 文件之前,要先将 MyDll。dll 和 MyDll。lib 拷贝到当前工程所在的目 

录下,也可以拷贝到 windows 的 System 目录下。如果DLL 使用的是 DEF 文件,要删除 TestDll。h 

文件中关键字 extern 〃C〃 。TestDll。h 文件中的关键字 Progam mit 是要 Visual C++ 的编译器 

在  link 时,链接到  MyDll。lib       文件。当然,开发人员也可以不使用#pragma  ment(lib , 

〃MyDll。lib〃)语句,而直接在工程的 Setting→Link 页的 Object/Moduls 栏填入 MyDll。lib 即可。  



10。3。2    显式链接  



     显式链接方式对于集成化的开发语言(例如 Visual Basic )比较适合。有了显式链接,程 

序员就不必再使用导入文件,而是直接调用 Win32  的 LoadLibary 函数,并指定 DLL  的路径 

作为参数。LoadLibary 返回 HINSTANCE 参数,应用程序在调用 GetProcAddress 函数时使用 

这一参数。GetProcAddress 函数将符号名或标识号转换为 DLL  内部的地址。假设有一个导出 

如下函数的 DLL 文件:  



     extern 〃C〃 __declspec(dllexport) double SquareRoot(double d);  



     在隐式链接方式中,所有被应用程序调用的 DLL 文件都会在应用程序 EXE 文件加载时 

被加载在到内存中 。但如果采用显式链接方式,程序员可以决定 DLL 文件何时加载或不加载。 

显式链接在运行时决定加载哪个 DLL 文件。例如,可以将一个带有字符串资源的 DLL 模块 

以英语加载,而另一个以西班牙语加载。应用程序在用户选择了合适的语种后再加载与之对 

应的 DLL 文件。  

     在显式链接方式中,应用程序在执行过程中随时可以加载  DLL                                 文件,也可以随时卸载 

DLL 文件,这是隐式链接所无法作到的,所以显式链接具有更好的灵活性,更适合解释性语 

言。不过实现显式链接比较复杂,除了要调用特定的Win32 的LoadLibrary 函数动态链接DLL , 

在应用程序退出之前,还应该用 FreeLibrary 或 MFC 提供的 AfxFreeLibrary 释放动态链接库。 

下面是通过显式链接调用 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…………………………………………
返回目录 上一页 下一页 回到顶部 1 2
快捷操作: 按键盘上方向键 ← 或 → 可快速上下翻页 按键盘上的 Enter 键可回到本书目录页 按键盘上方向键 ↑ 可回到本页顶部!
温馨提示: 温看小说的同时发表评论,说出自己的看法和其它小伙伴们分享也不错哦!发表书评还可以获得积分和经验奖励,认真写原创书评 被采纳为精评可以获得大量金币、积分和经验奖励哦!