我所谓的dll载入次数是指针对这个dll调用LoadLibrary的次数。
因为Load了多少次就必须Free同样多的次数,如果Free的次数少了dll实际上并没有退出。而且第二次调用LoadLibrary也不会执行InitInstance函数。于是在我的一个dll里产生了问题。因为这个dll处理完事务后就会把自己卸载掉,而如果被多次LoadLibrary后它就无法把自己真正卸载掉。我本想在InitInstance里记录次数,但后来发现这个函数根本就没有在后来的LoadLibrary中被调用。不是说Windows为每个dll准备了一个计数器吗,怎么访问这个计数器?
因为Load了多少次就必须Free同样多的次数,如果Free的次数少了dll实际上并没有退出。而且第二次调用LoadLibrary也不会执行InitInstance函数。于是在我的一个dll里产生了问题。因为这个dll处理完事务后就会把自己卸载掉,而如果被多次LoadLibrary后它就无法把自己真正卸载掉。我本想在InitInstance里记录次数,但后来发现这个函数根本就没有在后来的LoadLibrary中被调用。不是说Windows为每个dll准备了一个计数器吗,怎么访问这个计数器?
解决方案 »
- 端午节: 散分。。。。。。。。。。。。直到裸为止
- 求助:在地图上画圆圈
- 进程外组件调用在某台XPSP2上 导致2个进程都崩溃
- msxml 中的相同的节点 该怎么分别去里面的属性和值
- 注释的写法问题
- 在线请教:在MFC程序中能否调用一个DOS程序???急急呀,谢谢啦
- 线程结束后,有何消息呢???/
- Dialog内有个combobox是在哪里初始化?(或者是怎样初始化)
- 了解SOCKS的请进!关于SOCKS的BIND命令
- 哪里有vc中文版下载阿,谢谢,呵呵
- 点击按钮的时候,为什么调用了ON_WM_SETFOCUS但是没有调用ON_CONTROL_REFLECT
- 如何恢复VC2008资源视图中误删内容?
说实话,你上面那一段我没有完全看明白。
但是当工作没完成就又一次LoadLibrary会造成第一次启动的工作线程卸载不了这个dll,错过了这次机会就再没有什么方法能卸载它,则这个dll会一直待在进程里,于是前面设想的工作方式就失效了。
之所以会采用这种吃力的工作方式是因为这个dll是要被注入到其它进程里的。它的自卸载只有一次机会,所以dll应该要知道自己被LoadLibrary的次数,并且抓住这唯一的一次机会确定调用FreeLibrary的次数,保证自己被真正卸载。
用CWinApp,只有第一次被载入InitInstance被调用,释放在ExitInstance函数中,该做什么你知道吧?
建议楼主
while(hMode = GetModuleHandle("aaa.dll"))
FreeLibraray(hMode);
这样强制卸载模块。
得到你的提示我有仔细研究了这段自释放模块的代码。这段代码是汇编写的,所以很不好调试。翻译成C语言大致相当于:
while(FreeLibraray(hMode));
不能使用GetModuleHandle("aaa.dll")是因为我的模块名不是固定的。我的问题终于解决了。谢谢大家了。
2DLLmain本身也可以判断的
label:
mov eax, hmod
push eax
call FreeLibraray
test eax, eax
jnz label
retn N ; 需要返回到模块以外的地址才正常类似这一段汇编放到栈里面执行,就可以了。
如果说是增加一个导出函数用以启动dll工作线程,并在这个函数里操作计数器,也是可行的,但这样外部代码就得复杂很多----至少得增加GetProcAddress函数并且还要处理一个字符串,这些代码是汇编写的,每次修改它都让我感到压力,特别是要增加新的函数进来。
改进后的自释放函数如下:/*
01000000 8B5424 04 mov edx, dword ptr [esp+4]
01000004 8B1A mov ebx, dword ptr [edx]
01000006 68 00040000 push 400
0100000B FFD3 call ebx ; Sleep(0x400)
0100000D 8B5424 04 mov edx, dword ptr [esp+4]
01000011 8B5A 04 mov ebx, dword ptr [edx+4]
01000014 FF72 08 push dword ptr [edx+8]
01000017 FFD3 call ebx ; FreeLibrary
01000019 83F8 00 cmp eax, 0
0100001C ^ 75 EF jnz short 0100000D ; 循环调用FreeLibrary直到这个函数失败。一开始这里是跳到01000014,一直出错,还以为是FreeLibrary不适合这种循环应用
0100001E C2 0400 retn 4
01000021 90 nop
01000022 90 nop
01000023 90 nop
01000024 90 nop ; 将会存放Sleep函数的地址,(0x01000000+0x24)这个地址也是函数的传入参数
01000025 90 nop
01000026 90 nop
01000027 90 nop
01000028 90 nop ; 将会存放FreeLibrary函数的地址
01000029 90 nop
0100002A 90 nop
0100002B 90 nop
0100002C 90 nop ; 将会存放模块载入地址,或者叫模块句柄、模块实例
0100002D 90 nop
0100002E 90 nop
0100002F 90 nop
*/void FreeSelfEx(HMODULE hModule) //可以在模块所有线程退出的时候调用这个函数卸载掉本模块。循环调用FreeLibrary直到这个函数失败,所以即使这个模块被其它线程重复LoadLibrary过也能卸载
{
BYTE buff[48]=
{
0x8B,0x54,0x24,0x04,0x8B,0x1A,0x68,0x00,0x04,0x00,0x00,0xFF,0xD3,0x8B,0x54,0x24,
0x04,0x8B,0x5A,0x04,0xFF,0x72,0x08,0xFF,0xD3,0x83,0xF8,0x00,0x75,0xEF,0xC2,0x04,
0x00,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90
};//buff的内容即前面的汇编代码。
HMODULE hm=::GetModuleHandle(_T("kernel32"));
DWORD FreLib=(DWORD)::GetProcAddress(hm,"FreeLibrary");
DWORD Slp=(DWORD)::GetProcAddress(hm,"Sleep");
if(FreLib && Slp)
{
*(DWORD*)(&buff[36])=Slp;
*(DWORD*)(&buff[40])=FreLib;
*(DWORD*)(&buff[44])=(DWORD)hModule;
BYTE* rBuff=(BYTE*)::VirtualAlloc(0,48,MEM_RESERVE | MEM_COMMIT,PAGE_EXECUTE_READWRITE); //这48个字节的内存会泄露
::memcpy(rBuff,buff,48);
::CloseHandle(::CreateThread(0,0,(LPTHREAD_START_ROUTINE)rBuff,rBuff+36,0,0)); //新线程会等待1024毫秒调用FreeLibrary,所以当前线程必须在这段时间内退出
}
}
以上的这个函数总是会产生48个字节的内存泄露,不知道要怎么才能解决呢?