易语言教程_易语言源码_易语言写挂_易语言论坛_看流星社区

 找回密码
 注册
零基础辅助入门教学 原创 高清 专业课程售后(每日解答)
零基础辅助入门教学 原创 高清 专业课程售后(每日解答)
零基础辅助入门教学 原创 高清 专业课程售后(每日解答)
零基础辅助入门教学 原创 高清 专业课程售后(每日解答)
零基础辅助入门教学 原创 高清 专业课程售后(每日解答)
零基础辅助入门教学 原创 高清 专业课程售后(每日解答)
赞助广告位 请点击这里联系站长 QQ20209081
赞助广告位 请点击这里联系站长 QQ20209081
赞助广告位 请点击这里联系站长 QQ20209081
查看: 366|回复: 0

DLL中线程的安全退出FreeLibraryAndExitThread

[复制链接]
发表于 2017-4-4 12:46:58 | 显示全部楼层 |阅读模式
首先我们先来看一下FreeLibraryAndExitThread这个API

VOID FreeLibraryAndExitThread(
  HMODULE hModule,
  DWORD dwExitCode
);

    MSDN:The FreeLibraryAndExitThread function decrements the reference count of a loaded dynamic-link library (DLL) by one, then calls ExitThread to terminate the calling thread. The function does not return.

    这个API会减少动态库的引用计数,然后调用ExitThread函数结束调用线程,这个API是没有返回值的。(插播一点小知识,LoadLibrary和LoadLibraryEx,会增加一个动态库的引用计数,而且如果在同一个进程对一个完全相同的DLL进行Load操作,只会增加该dll模块的引用计数,而且入口点函数也不会调用,当一个动态库的引用计数减少到0时,会从进程中将该模块卸载。)

    以前遇到的一个比较麻烦的问题是,如果dll里面启动了一个线程,外面去FreeLibary这个dll的时候,如果dll里面的线程还没还有退出,程序马上就会崩溃,原因就是FreeLibary的之后,整个DLL模块的代码空间已经无效了,此时DLL的线程如果继续运行自然就会崩溃了。这种情况在一个函数里面加载一个dll是最明显的,释放这个dll时机就很废脑筋了,以前用到的一些方法有向这个dll查询线程是否退出了,或者强杀dll里面的线程等等方法,都用得不太爽。

    今天发现了FreeLibraryAndExitThread这个API处理这个问题很不错,这个API的诞生貌似就是为了处理这个问题,方法如下,调用dll的线程流程不变,依然LoadLibrary,再在函数退出前FreeLibary,而在DLL中加入一套卸载机制,代码如下:


#include "stdafx.h"  
#include <stdio.h>  
#include <Windows.h>  
     
HMODULE g_hDll = NULL;  
  
DWORD WINAPI FreeSelfProc(PVOID param)  
{  
    printf("UnloadProc!/n");  
    ::MessageBox(NULL, TEXT("Press ok to unload me."), TEXT("MsgBox in dll"), MB_OK);  
//  FreeLibrary(g_hDll);  
//  ExitThread(0);  
    ::FreeLibraryAndExitThread(g_hDll, 0);  
    return 0;  
}  
  
BOOL APIENTRY DllMain( HANDLE hModule, DWORD  ul_reason_for_call, LPVOID lpReserved)  
{  
    if (DLL_PROCESS_ATTACH == ul_reason_for_call)  
    {   
        char szPath[MAX_PATH + 1] = {0};  
        ::GetModuleFileName((HMODULE)hModule, szPath, MAX_PATH);  
        ::LoadLibrary(szPath);  
        printf("DLL_PROCESS_ATTACH!/n");  
        g_hDll = (HMODULE)hModule;  
        HANDLE hThread = ::CreateThread(NULL, 0, FreeSelfProc, NULL, 0, NULL);  
        ::CloseHandle(hThread);  
    }  
    return TRUE;  
}  


    原理:在dll的入口点再加载一次自己,使自己的引用计数增加1,然后在线程函数的最后调用FreeLibraryAndExitThread来卸载自身并退出线程,这样的话,如果调用dll的线程先调用了FreeLibary来释放这个dll,因为引用计数依然大于0,所以这个DLL不会卸载,dll里面的线程函数可以安全的跑完知道自己卸载自己;而如果dll中的线程函数先调用FreeLibraryAndExitThread,这个时候因为dll的引用技术也大于0,dll还是不会卸载,只是线程退出而已,直到外面调用FreeLibary来将其卸载。

    这个地方要注意的是,dll的线程函数里面不能调用FreeLibary来卸载,因为FreeLibary返回后依然是DLL的代码空间,而此时如果dll被真的卸载了,返回空间已经是无效的代码空间了,程序马上会崩溃。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

小黑屋|手机版|Archiver|看流星社区  |网站地图

GMT+8, 2017-9-27 00:11 易语言论坛 易语言导航

Powered by 看流星社区 X3.2

©2011-2016 最好的辅助编程技术论坛

快速回复 返回顶部 返回列表