我所谓的dll载入次数是指针对这个dll调用LoadLibrary的次数。
因为Load了多少次就必须Free同样多的次数,如果Free的次数少了dll实际上并没有退出。而且第二次调用LoadLibrary也不会执行InitInstance函数。于是在我的一个dll里产生了问题。因为这个dll处理完事务后就会把自己卸载掉,而如果被多次LoadLibrary后它就无法把自己真正卸载掉。我本想在InitInstance里记录次数,但后来发现这个函数根本就没有在后来的LoadLibrary中被调用。不是说Windows为每个dll准备了一个计数器吗,怎么访问这个计数器?

解决方案 »

  1.   

    DLL应该是不知道的吧。
    说实话,你上面那一段我没有完全看明白。
      

  2.   

    每次载入和卸载都会调用DllMain吧你自己搞个计数器记录一下就行了
      

  3.   

    我大体描述下它的工作方式吧。这个Dll它应用在这么一种情况下(它是一个MFC的Dll):它在InitInstance函数里创建一个工作线程,所以当外部代码调用LoadLibrary的时候这个dll就开始了它的工作,然后当工作完成后工作线程即将退出前,会把dll从进程空间里卸载掉。如果dll被清除出内存了那么就可以在下次LoadLibrary再次启动工作线程。
    但是当工作没完成就又一次LoadLibrary会造成第一次启动的工作线程卸载不了这个dll,错过了这次机会就再没有什么方法能卸载它,则这个dll会一直待在进程里,于是前面设想的工作方式就失效了。
    之所以会采用这种吃力的工作方式是因为这个dll是要被注入到其它进程里的。它的自卸载只有一次机会,所以dll应该要知道自己被LoadLibrary的次数,并且抓住这唯一的一次机会确定调用FreeLibrary的次数,保证自己被真正卸载。
      

  4.   

    DllMain函数的话,可以知道是被载入、被释放、增加引用计数、或者减少引用计数,在引用计数变化时你不用做什么。
    用CWinApp,只有第一次被载入InitInstance被调用,释放在ExitInstance函数中,该做什么你知道吧?
      

  5.   

    我也想搞个计数器,可是根本没办法把这个计数传递给dll。
      

  6.   

    系统确定维护了一个模块的引用次数,但没有文档化的办法来访问这个计数。
    建议楼主
    while(hMode = GetModuleHandle("aaa.dll"))
      FreeLibraray(hMode);
    这样强制卸载模块。
      

  7.   

    其实一开始我也是用循环来释放模块的,但是由于我的模块自释放函数有个错误所以一直非法操作,我还以为此路不通,一直觉得肯定是FreeLibraray函数的参数使用了已卸载的模块句柄所导致。
    得到你的提示我有仔细研究了这段自释放模块的代码。这段代码是汇编写的,所以很不好调试。翻译成C语言大致相当于:
    while(FreeLibraray(hMode));
    不能使用GetModuleHandle("aaa.dll")是因为我的模块名不是固定的。我的问题终于解决了。谢谢大家了。
      

  8.   

    1自己为DLL加个接口,如果主程序不调用它,DLL就不工作,在接口中增加记数功能
    2DLLmain本身也可以判断的
      

  9.   

    可以参考一下com的实现原理做个引用计数
      

  10.   

    原来是在模块内部自释放哟,了解~ 
    label:
     mov eax, hmod
     push eax
     call FreeLibraray
     test eax, eax
     jnz label
     retn N ; 需要返回到模块以外的地址才正常类似这一段汇编放到栈里面执行,就可以了。
      

  11.   

    com没怎么了解过。我一直想在dll里搞个引用计数器,即使我当前的问题解决了,但是我想计数器还是有其它用处的。但是我不知道这个计数器在哪里增减。DLLmain或InitInstance里不行,DLLmain有4个通知消息,但是如果是同一个线程在不FreeLibrary的情况下多次调用LoadLibrary,DLLmain也只得到一次通知。
    如果说是增加一个导出函数用以启动dll工作线程,并在这个函数里操作计数器,也是可行的,但这样外部代码就得复杂很多----至少得增加GetProcAddress函数并且还要处理一个字符串,这些代码是汇编写的,每次修改它都让我感到压力,特别是要增加新的函数进来。
      

  12.   


    改进后的自释放函数如下:/*
    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个字节的内存泄露,不知道要怎么才能解决呢?
      

  13.   

    不是所有DLL 都是COM 的 你可以做数据共享 然后给DLL增加一个数据查询数据的函数