显式卸载DLL模块

当进程中的线程不再需要DLL中的引用符号时,可以从进程的地址空间中显式卸载DLL,方法是调用下面的函数:

    BOOL FreeLibrary(HINSTANCE hinstDll);

必须传递HINSTANCE值,以便标识要卸载的DLL。该值是较早的时候调用LoadLibrary(Ex)而返回的值。

也可以通过调用下面的函数从进程的地址空间中卸载DLL:

    VOID FreeLibraryAndExitThread(

        HINSTANCE hinstDll,

        DWORD dwExitCode);

该函数是在Kernel32.dll中实现的,如下所示:

   VOID FreeLibraryAndExitThread(HINSTANCE hinstDll,DWORD dwExitCode) {

        FreeLibrary(hinstDll);

        ExitThread(dwExitCode);

    }

初看起来,这并不是个非常高明的代码,你可能不明白,为什么Microsoft要创建FreeLibraryAndExitThread这个函数。其原因与下面的情况有关:假定你要编写一个DLL,当它被初次映射到进程的地址空间中时,该DLL就创建一个线程。当该线程完成它的操作时,它通过调用FreeLibrary函数,从进程的地址空间中卸载该DLL,并且终止运行,然后立即调用ExitThread。

但是,如果线程分开调用FreeLibrary和ExitThread,就会出现一个严重的问题。这个问题是调用FreeLibrary会立即从进程的地址空间中卸载DLL。当调用的FreeLibrary返回时,包含对ExitThread调用的代码就不再可以使用,因此线程将无法执行任何代码。这将导致访问违规,同时整个进程终止运行。

但是,如果线程调用FreeLibraryAndExitThread,该函数调用FreeLibrary,使DLL立即被卸载。下一个执行的指令是在Kernel32.dll中,而不是在刚刚被卸载的DLL中。这意味着该线程能够继续执行,并且可以调用ExitThread。ExitThread使该线程终止运行并且不返回。

一般来说,并没有很大的必要去调用FreeLibraryAndExitThread函数。我曾经使用过一次,因为我执行了一个非常特殊的任务。另外,我为MicrosoftWindows3.1编写了一个代码,它并没有提供这个函数。因此我高兴地看到Microsoft将这个函数增加到了较新的Windows版本中。

在实际环境中,LoadLibrary和LoadLibraryEx这两个函数用于对与特定的库相关的进程使用计数进行递增,FreeLibrary和FreeLibraryAndExitThread这两个函数则用于对库的每个进程的使用计数进行递减。例如,当第一次调用LoadLibrary函数来加载DLL时,系统将DLL的文件映像映射到调用进程的地址空间中,并将DLL的使用计数设置为1。如果同一个进程中的线程后来调用LoadLibrary来加载同一个DLL文件映像,系统并不第二次将DLL映像文件映射到进程的地址空间中,它只是将与该进程的DLL相关的使用计数递增1。

为了从进程的地址空间中卸载DLL文件映像,进程中的线程必须两次调用FreeLibrary函数。第一次调用只是将DLL的使用计数递减为1,第二次调用则将DLL的使用计数递减为0。当系统发现DLL的使用计数递减为0时,它就从进程的地址空间中卸载DLL的文件映像。试图调用DLL中的函数的任何线程都会产生访问违规,因为特定地址上的代码不再被映射到进程的地址空间中。

系统为每个进程维护了一个DLL的使用计数,也就是说,如果进程A中的一个线程调用下面的函数,然后进程B中的一个线程调用相同的函数,那么MyLib.dll将被映射到两个进程的地址空间中,这样,进程A和进程B的DLL使用计数都将是1。

HINSTANCE hinstDll = LoadLibrary("MyLib.dll");

如果进程B中的线程后来调用下面的函数,那么进程B的DLL使用计数将变成0,并且该DLL将从进程B的地址空间中卸载。但是,进程A的地址空间中的DLL映射不会受到影响,进程A的DLL使用计数仍然是1。

FreeLibrary(hinstDll);

如果调用GetModuleHandle函数,线程就能够确定DLL是否已经被映射到进程的地址空间中:

HINSTANCE GetModuleHandle(PCTSTR pszModuleName);

例如,只有当MyLib.dll尚未被映射到进程的地址空间中时,下面这个代码才能加载该文件:

HINSTANCE hinstDll = GetModuleHandle("MyLib");//DLL externsion assumed

if(hinstDll == NULL) {

   hinstDll = LoadLibrary("MyLib");//DLL externsion assumed

}

如果只有DLL的HINSTANCE值,那么可以调用GetModuleFileName函数,确定DLL(或.exe)的全路径名:

DWORD GetModulefileName(

   HINSTANCE hinstModule,

   PTSTR pszPathName,

   DWORD cchPath);

第一个参数是DLL(或.exe)的HINSTANCE。第二个参数pszPathName是该函数将文件映像的全路径名放入的缓存的地址。第三参数cchPath用于设定缓存的大小(以字符为计量单位)。

原文:http://hi.baidu.com/onlinewan/blog/item/245c72f0e54f11c07931aaf4.html
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章