/* $Id: windllbase.cpp,v 1.36 2004-04-27 08:25:43 sandervl Exp $ */ /* * Win32 Dll base class * * Copyright 1998-2000 Sander van Leeuwen (sandervl@xs4all.nl) * Copyright 2003 Innotek Systemberatung GmbH (sandervl@innotek.de) * * Unloading of a dll always happens in order of dependency (taking nr of * loads into account) * Unloading of dynamically loaded dll (with LoadLibrary) in deleteAll * is done in LIFO order (NT exhibits the same behaviour) * * RemoveCircularDependency: TODO: Send process detach message here?? * * Project Odin Software License can be found in LICENSE.TXT * */ #define INCL_DOSFILEMGR /* File Manager values */ #define INCL_DOSERRORS /* DOS Error values */ #define INCL_DOSPROCESS /* DOS Process values */ #define INCL_DOSMODULEMGR #define INCL_DOSMISC /* DOS Miscellanous values */ #define INCL_WIN #include //Odin32 OS/2 api wrappers #include #include #include #ifndef __GNUC__ #include #include #endif #include #include #include #include "windllbase.h" #include "winimagepe2lx.h" #include "windllpe2lx.h" #include "winimagelx.h" #include "windlllx.h" #include #include "exceptions.h" #include "exceptutil.h" #include "vmutex.h" #include "oslibmisc.h" #include "oslibdos.h" #include "profile.h" #define DBG_LOCALLOG DBG_windllbase #include "dbglocal.h" VMutex dlllistmutex; //protects linked lists of heaps //****************************************************************************** //****************************************************************************** Win32DllBase::Win32DllBase(HINSTANCE hinstance, WIN32DLLENTRY DllEntryPoint, Win32ImageBase *parent) : Win32ImageBase(hinstance), referenced(0), fSkipThreadEntryCalls(FALSE), next(NULL), fInserted(FALSE), fAttachedToProcess(FALSE), fUnloaded(FALSE), nrDynamicLibRef(0), fDisableUnload(FALSE), fSkipEntryCalls(FALSE) { dllEntryPoint = DllEntryPoint; setUnloadOrder(parent); dprintf(("Win32DllBase::Win32DllBase %x %s", hinstance, szModule)); } //****************************************************************************** //****************************************************************************** Win32DllBase::~Win32DllBase() { dprintf(("Win32DllBase::~Win32DllBase %s", szModule)); if(errorState == NO_ERROR && !fUnloaded) { detachProcess(); } dlllistmutex.enter(); if(head == this) { head = next; } else { Win32DllBase *dll = head; while(dll && dll->next != this) { dll = dll->next; } if(dll == NULL) { dprintf(("~Win32DllBase: Can't find dll!\n")); dlllistmutex.leave(); return; } dll->next = next; } dlllistmutex.leave(); } //****************************************************************************** //****************************************************************************** void Win32DllBase::incDynamicLib() { if(nrDynamicLibRef == 0) { //NOTE: //Must be called *after* attachprocess, since attachprocess may also //trigger LoadLibrary calls //Those dlls must not be put in front of this dll in the dynamic //dll list; or else the unload order is wrong: //i.e. RPAP3260 loads PNRS3260 in DLL_PROCESS_ATTACH // this means that in ExitProcess, PNRS3260 needs to be removed // first since RPAP3260 depends on it dlllistmutex.enter(); loadLibDlls.Push((ULONG)this); dlllistmutex.leave(); } nrDynamicLibRef++; } //****************************************************************************** //****************************************************************************** void Win32DllBase::decDynamicLib() { nrDynamicLibRef--; if(nrDynamicLibRef == 0) { dlllistmutex.enter(); loadLibDlls.Remove((ULONG)this); dlllistmutex.leave(); } } //****************************************************************************** //unload of dlls needs to be done in reverse order of dependencies //Note: Only necessary for pe loader; the OS/2 loader takes care of this //for win32k/pe2lx //****************************************************************************** void Win32DllBase::setUnloadOrder(Win32ImageBase *parent) { Win32DllBase *dll; Win32DllBase *parentdll = NULL; dlllistmutex.enter(); if(parent) { dll = head; while(dll) { if(dll->getInstanceHandle() == parent->getInstanceHandle()) { parentdll = dll; break; } dll = dll->next; } } //first check if this dll is already at a lower position (further down the list) //than the parent if(parentdll && fInserted) {//already in the list? dll = parentdll->next; while(dll) { if(dll->getInstanceHandle() == getInstanceHandle()) { dlllistmutex.leave(); return; //it's at a lower position, so no need to change anything } dll = dll->next; } //it's already in the list but not at the right position; remove it now if(head == this) { head = next; } else { dll = head; while(dll->next) { if(dll->next == this) { dll->next = next; break; } dll = dll->next; } } } else if(fInserted) {//already in the list? dlllistmutex.leave(); return; } //(re)insert it in the list after it's parent if(parentdll) { next = parentdll->next; parentdll->next = this; } else {//no parent or exe, just add it at the start of the list next = head; head = this; } fInserted = TRUE; //Now do the same thing for the child dependencies QueueItem *item; item = loadedDlls.Head(); while(item) { dll = (Win32DllBase *)loadedDlls.getItem(item); //Check for circular dependencies (i.e. in Lotus Notes) if(dll != parentdll) { dll->setUnloadOrder(this); } item = loadedDlls.getNext(item); } dlllistmutex.leave(); } //****************************************************************************** //****************************************************************************** #ifdef DEBUG ULONG Win32DllBase::AddRef(char *parentname) #else ULONG Win32DllBase::AddRef() #endif { Win32DllBase *dll; #ifdef DEBUG dprintf(("Win32DllBase::AddRef %s->%s %d", parentname, getModuleName(), referenced+1)); #endif ++referenced; #ifdef DEBUG if(referenced == 1) { printListOfDlls(); //printDependencies(NULL); } #endif return referenced; } //****************************************************************************** //****************************************************************************** ULONG Win32DllBase::Release() { Queue queue; QueueItem *item; Win32DllBase *dll; LONG ret; char szModuleName[256]; dprintf(("Win32DllBase::Release %s %d", getModuleName(), referenced-1)); ret = --referenced; if(ret <= 0) { //make copy of linked list of dependencies queue = loadedDlls; //remove any circular dependencies on this dll that might be present item = queue.Head(); while(item) { dll = (Win32DllBase *)queue.getItem(item); if(dll == NULL) { dprintf(("ERROR: Win32DllBase::Release: dll item == NULL!!")); DebugInt3(); return -1; } dll->RemoveCircularDependency(this); item = queue.getNext(item); } #ifdef DEBUG // printDependencies(getName()); #endif dprintf(("Win32DllBase::Release %s referenced == 0", getModuleName())); strncpy(szModuleName, getModuleName(), sizeof(szModuleName)); //delete dll object delete this; //unreference all of it's dependencies item = queue.Head(); while(item) { dll = (Win32DllBase *)queue.getItem(item); if(dll == NULL) { dprintf(("ERROR: Win32DllBase::Release: dll item == NULL!!")); DebugInt3(); return -1; } dprintf(("Remove %s->%s dependency", szModuleName, dll->getName())); dll->Release(); item = queue.getNext(item); } } return(ret); } //****************************************************************************** //Lotus Notes has several ugly circular dependencies in it's dlls. //Remove them before they cause problems. //****************************************************************************** BOOL Win32DllBase::RemoveCircularDependency(Win32DllBase *parent) { QueueItem *item, *tmp; Win32DllBase *dll; BOOL ret = FALSE; //remove any circular dependencies on this dll that might be present item = loadedDlls.Head(); while(item) { dll = (Win32DllBase *)loadedDlls.getItem(item); if(dll == NULL) { dprintf(("ERROR: Win32DllBase::Release: dll item == NULL!!")); DebugInt3(); return FALSE; } tmp = loadedDlls.getNext(item); if(dll == parent) { dprintf(("Removing CIRCULAR dependency %s->%s", parent->getModuleName(), dll->getModuleName())); loadedDlls.Remove(item); ret = TRUE; } //// else ret |= dll->RemoveCircularDependency(parent); item = tmp; } //TODO: Send process detach message here?? return ret; } //****************************************************************************** //There's a slight problem with our dependency procedure. //example: gdi32 is loaded -> depends on kernel32 (which is already loaded) // kernel32's reference count isn't updated // (when we load gdi32, we don't know which dlls it depends on and which // of those are already loaded and which aren't) //-> solution: Determine reference counts of dependant lx dlls and update those // reference counts //****************************************************************************** void Win32DllBase::updateDependencies() { QueueItem *item; Win32DllBase *dll, *depdll; ULONG refcount; dlllistmutex.enter(); item = loadedDlls.Head(); while(item) { depdll = (Win32DllBase *)loadedDlls.getItem(item); if(depdll == NULL) { dprintf(("updateDependencies: depdll == NULL!!")); DebugInt3(); return; } refcount = 0; dll = head; while(dll) { if(dll->dependsOn(depdll)) { refcount++; } dll = dll->getNext(); } if(refcount > depdll->referenced) { dprintf(("Win32DllBase::updateDependencies changing refcount of %s to %d (old=%d)", depdll->getModuleName(), refcount, depdll->referenced)); depdll->referenced = refcount; } item = loadedDlls.getNext(item); } dlllistmutex.leave(); } //****************************************************************************** //****************************************************************************** #ifdef DEBUG void Win32DllBase::printDependencies(char *parent) { QueueItem *item; Win32DllBase *dll; ULONG ret; dprintf(("Dependency list: %s->%s %d", parent, getModuleName(), referenced)); item = loadedDlls.Head(); while(item) { dll = (Win32DllBase *)loadedDlls.getItem(item); if(dll == NULL) { return; } dll->printDependencies(getModuleName()); item = loadedDlls.getNext(item); } } //****************************************************************************** //****************************************************************************** void Win32DllBase::printListOfDlls() { Win32DllBase *dll; dll = head; dprintf2(("Win32DllBase::Win32DllBase: List of loaded dlls:")); while(dll) { dprintf2(("DLL %s %d", dll->szModule, dll->referenced)); dll = dll->next; } } #endif //****************************************************************************** //****************************************************************************** BOOL Win32DllBase::attachProcess() { WINEXCEPTION_FRAME exceptFrame; USHORT sel; TEB *teb; BOOL rc, fSetExceptionHandler; if(fAttachedToProcess || fSkipEntryCalls) return TRUE; fAttachedToProcess = TRUE; teb = GetThreadTEB(); fSetExceptionHandler = (!teb || teb->teb_sel != GetFS()); //Note: The Win32 exception structure references by FS:[0] is the same // in OS/2 if(fSetExceptionHandler) { OS2SetExceptionHandler((void *)&exceptFrame); sel = SetWin32TIB(isPEImage() ? TIB_SWITCH_FORCE_WIN32 : TIB_SWITCH_DEFAULT); } if(dllEntryPoint == NULL) { dprintf(("attachProcess not required for dll %s", szModule)); if(fSetExceptionHandler) { SetFS(sel); OS2UnsetExceptionHandler((void *)&exceptFrame); } return(TRUE); } // @@@PH 2000/06/13 lpvReserved, Starcraft STORM.DLL // if DLL_PROCESS_ATTACH, lpvReserved is NULL for dynamic loads // and non-NULL for static loads. // same goes for process termination LPVOID lpvReserved; if (isDynamicLib()) lpvReserved = NULL; else lpvReserved = (LPVOID)0xdeadface; // some arbitrary value #ifdef DEBUG int time1, time2; dprintf(("attachProcess to dll %s (%x)", szModule, dllEntryPoint)); time1 = GetTickCount(); #endif rc = dllEntryPoint(hinstance, DLL_PROCESS_ATTACH, lpvReserved); #ifdef DEBUG time2 = GetTickCount(); dprintf(("attachProcess to dll %s DONE in %x msec rc %d", szModule, time2-time1, rc)); #endif if(fSetExceptionHandler) { SetFS(sel); OS2UnsetExceptionHandler((void *)&exceptFrame); } else if(teb) { if(teb->teb_sel != GetFS()) { dprintf(("Win32DllBase::attachProcess: FS was changed by dll entrypoint!!!!")); DebugInt3(); } } return rc; } //****************************************************************************** //****************************************************************************** BOOL Win32DllBase::detachProcess() { WINEXCEPTION_FRAME exceptFrame; USHORT sel; BOOL rc; if(fSkipEntryCalls) { fUnloaded = TRUE; return TRUE; } if(dllEntryPoint == NULL) { tlsDetachThread(); //destroy TLS (main thread) fUnloaded = TRUE; return(TRUE); } dprintf(("detachProcess from dll %s (%x)", szModule, dllEntryPoint)); //Note: The Win32 exception structure references by FS:[0] is the same // in OS/2 OS2SetExceptionHandler((void *)&exceptFrame); fUnloaded = TRUE; sel = SetWin32TIB(isPEImage() ? TIB_SWITCH_FORCE_WIN32 : TIB_SWITCH_DEFAULT); // @@@PH 2000/06/13 lpvReserved, Starcraft STORM.DLL // if DLL_PROCESS_ATTACH, lpvReserved is NULL for dynamic loads // and non-NULL for static loads. // same goes for process termination LPVOID lpvReserved; if (isDynamicLib()) lpvReserved = NULL; else lpvReserved = (LPVOID)0xdeadface; // some arbitrary value rc = dllEntryPoint(hinstance, DLL_PROCESS_DETACH, lpvReserved); SetFS(sel); //restore FS tlsDetachThread(); //destroy TLS (main thread) tlsDelete(); OS2UnsetExceptionHandler((void *)&exceptFrame); return rc; } //****************************************************************************** //****************************************************************************** BOOL Win32DllBase::attachThread() { WINEXCEPTION_FRAME exceptFrame; BOOL rc; if(fSkipThreadEntryCalls || dllEntryPoint == NULL) return(TRUE); dprintf(("attachThread to dll %s", szModule)); rc = dllEntryPoint(hinstance, DLL_THREAD_ATTACH, 0); dprintf(("attachThread to dll %s DONE", szModule)); return rc; } //****************************************************************************** //****************************************************************************** BOOL Win32DllBase::detachThread() { WINEXCEPTION_FRAME exceptFrame; BOOL rc; if(fSkipThreadEntryCalls || dllEntryPoint == NULL) return(TRUE); dprintf(("detachThread from dll %s", szModule)); rc = dllEntryPoint(hinstance, DLL_THREAD_DETACH, 0); return rc; } //****************************************************************************** //Send DLL_THREAD_ATTACH message to all dlls for a new thread //****************************************************************************** void Win32DllBase::attachThreadToAllDlls() { dlllistmutex.enter(); Win32DllBase *dll = Win32DllBase::head; while(dll) { dll->attachThread(); dll = dll->getNext(); } dlllistmutex.leave(); } //****************************************************************************** //Send DLL_THREAD_DETACH message to all dlls for thread that's about to die //****************************************************************************** void Win32DllBase::detachThreadFromAllDlls() { dlllistmutex.enter(); Win32DllBase *dll = Win32DllBase::head; while(dll) { dll->detachThread(); dll = dll->getNext(); } dlllistmutex.leave(); } //****************************************************************************** //Send DLL_PROCESS_DETACH message to all dlls for process that's about to end //****************************************************************************** void Win32DllBase::detachProcessFromAllDlls() { dlllistmutex.enter(); Win32DllBase *dll = Win32DllBase::head; while(dll) { dll->detachProcess(); dll = dll->getNext(); } dlllistmutex.leave(); } //****************************************************************************** //Setup TLS structure for all dlls for a new thread //****************************************************************************** void Win32DllBase::tlsAttachThreadToAllDlls() { dlllistmutex.enter(); Win32DllBase *dll = Win32DllBase::head; while(dll) { dll->tlsAttachThread(); dll = dll->getNext(); } dlllistmutex.leave(); } //****************************************************************************** //Destroy TLS structure for all dlls for a thread that's about to die //****************************************************************************** void Win32DllBase::tlsDetachThreadFromAllDlls() { dlllistmutex.enter(); Win32DllBase *dll = Win32DllBase::head; while(dll) { dll->tlsDetachThread(); dll = dll->getNext(); } dlllistmutex.leave(); } //****************************************************************************** //****************************************************************************** void Win32DllBase::deleteAll() { Win32DllBase *dll = Win32DllBase::head; dprintf(("Win32DllBase::deleteAll")); #ifdef DEBUG if(dll) dll->printListOfDlls(); #endif dlllistmutex.enter(); while(dll) { dll->Release(); dll = Win32DllBase::head; } dlllistmutex.leave(); dprintf(("Win32DllBase::deleteAll Done!")); } //****************************************************************************** //Delete dlls loaded by LoadLibrary(Ex) in LIFO order //****************************************************************************** void Win32DllBase::deleteDynamicLibs() { Win32DllBase *dll = head; QueueItem *item; dprintf(("Win32DllBase::deleteDynamicLibs")); #ifdef DEBUG if(dll) dll->printListOfDlls(); #endif dlllistmutex.enter(); item = loadLibDlls.Head(); while(item) { dll = (Win32DllBase *)loadLibDlls.getItem(item); int dynref = dll->nrDynamicLibRef; if(dynref) { dprintf(("delete dynamic library references for %s", dll->getName())); while(dynref) { dynref--; dll->decDynamicLib(); dll->Release(); } } else DebugInt3(); item = loadLibDlls.Head(); //queue could have been changed, so start from the beginning } dlllistmutex.leave(); dprintf(("Win32DllBase::deleteDynamicLibs end")); } //****************************************************************************** //****************************************************************************** Win32DllBase *Win32DllBase::getFirst() { return head; } //****************************************************************************** //Add renaming profile strings for ole32 & netapi32 to odin.ini if they aren't //already there //****************************************************************************** void Win32DllBase::setDefaultRenaming() { char renameddll[CCHMAXPATH]; if(PROFILE_GetOdinIniString(DLLRENAMEWIN_SECTION, "OLE32", "", renameddll, sizeof(renameddll)-1) <= 1) { PROFILE_SetOdinIniString(DLLRENAMEWIN_SECTION, "OLE32", "OLE32OS2"); PROFILE_SetOdinIniString(DLLRENAMEOS2_SECTION, "OLE32OS2", "OLE32"); } if(PROFILE_GetOdinIniString(DLLRENAMEWIN_SECTION, "OLEAUT32", "", renameddll, sizeof(renameddll)-1) <= 1) { PROFILE_SetOdinIniString(DLLRENAMEWIN_SECTION, "OLEAUT32", "OLAUTOS2"); PROFILE_SetOdinIniString(DLLRENAMEOS2_SECTION, "OLAUTOS2", "OLEAUT32"); } if(PROFILE_GetOdinIniString(DLLRENAMEWIN_SECTION, "NETAPI32", "", renameddll, sizeof(renameddll)-1) <= 1) { PROFILE_SetOdinIniString(DLLRENAMEWIN_SECTION, "NETAPI32", "WNETAP32"); PROFILE_SetOdinIniString(DLLRENAMEOS2_SECTION, "WNETAP32", "NETAPI32"); } if(PROFILE_GetOdinIniString(DLLRENAMEWIN_SECTION, "WINSPOOL", "", renameddll, sizeof(renameddll)-1) <= 1) { PROFILE_SetOdinIniString(DLLRENAMEWIN_SECTION, "WINSPOOL.DRV", "WINSPOOL.DLL"); PROFILE_SetOdinIniString(DLLRENAMEOS2_SECTION, "WINSPOOL", "WINSPOOL.DRV"); } if(PROFILE_GetOdinIniString(DLLRENAMEWIN_SECTION, "MCICDA", "", renameddll, sizeof(renameddll)-1) <= 1) { PROFILE_SetOdinIniString(DLLRENAMEWIN_SECTION, "MCICDA.DRV", "MCICDA.DLL"); PROFILE_SetOdinIniString(DLLRENAMEOS2_SECTION, "MCICDA", "MCICDA.DRV"); } if(PROFILE_GetOdinIniString(DLLRENAMEWIN_SECTION, "CRTDLL", "", renameddll, sizeof(renameddll)-1) <= 1) { PROFILE_SetOdinIniString(DLLRENAMEWIN_SECTION, "CRTDLL", "CRTDLL32"); PROFILE_SetOdinIniString(DLLRENAMEOS2_SECTION, "CRTDLL32", "CRTDLL"); } if(PROFILE_GetOdinIniString(DLLRENAMEWIN_SECTION, "IMM32", "", renameddll, sizeof(renameddll)-1) <= 1) { PROFILE_SetOdinIniString(DLLRENAMEWIN_SECTION, "IMM32", "IMM32OS2"); PROFILE_SetOdinIniString(DLLRENAMEOS2_SECTION, "IMM32OS2", "IMM32"); } } //****************************************************************************** //rename dll if necessary: // Win32 to OS/2 : (i.e. OLE32 -> OLE32OS2) // or // OS/2 to Win32 : (i.e. OLE32OS2 -> OLE32) //****************************************************************************** void Win32DllBase::renameDll(char *dllname, BOOL fWinToOS2) { char modname[CCHMAXPATH]; char renameddll[CCHMAXPATH]; char *namestart; const char *sectionname; if(fWinToOS2) { sectionname = DLLRENAMEWIN_SECTION; } else { sectionname = DLLRENAMEOS2_SECTION; } namestart = OSLibStripPath(dllname); strcpy(modname, namestart); char *dot = strrchr(modname, '.'); if((dot) && !(stricmp(dot,".DLL"))) *dot = 0; strupr(modname); if(PROFILE_GetOdinIniString(sectionname, modname, "", renameddll, sizeof(renameddll)-1) > 1) { if(namestart == dllname) { strcpy(dllname, renameddll); } else { *namestart = 0; strcat(dllname, renameddll); } if(!strchr(dllname, '.')) { strcat(dllname, DLL_EXTENSION); } strupr(dllname); } return; } //****************************************************************************** //****************************************************************************** Win32DllBase *Win32DllBase::findModule(char *dllname, BOOL fRenameFirst) { Win32DllBase *dll; char szDllName[CCHMAXPATH]; char *dot, *temp; //// dprintf2(("findModule %s", dllname)); strcpy(szDllName, OSLibStripPath(dllname)); strupr(szDllName); if(fRenameFirst) { renameDll(szDllName, FALSE); } dot = strstr(szDllName, "."); if(dot == NULL) { //if there's no extension or trainling dot, we //assume it's a dll (see Win32 SDK docs) strcat(szDllName, DLL_EXTENSION); } else { if(dot[1] == 0) { //a trailing dot means the module has no extension (SDK docs) *dot = 0; } } dlllistmutex.enter(); dll = head; while(dll) { if(stricmp(szDllName, dll->szModule) == 0) { dlllistmutex.leave(); return(dll); } dll = dll->next; } dlllistmutex.leave(); return(NULL); } //****************************************************************************** //****************************************************************************** Win32DllBase *Win32DllBase::findModule(WIN32DLLENTRY DllEntryPoint) { dprintf2(("findModule %X", DllEntryPoint)); dlllistmutex.enter(); Win32DllBase *mod = Win32DllBase::head; while(mod != NULL) { dbgCheckObj(mod); if(mod->dllEntryPoint == DllEntryPoint) { dlllistmutex.leave(); return(mod); } mod = mod->next; } dlllistmutex.leave(); return(NULL); } //****************************************************************************** //****************************************************************************** Win32DllBase *Win32DllBase::findModule(HINSTANCE hinstance) { dlllistmutex.enter(); Win32DllBase *mod = Win32DllBase::head; while(mod != NULL) { dbgCheckObj(mod); if(mod->hinstance == hinstance) { dlllistmutex.leave(); return(mod); } mod = mod->next; } dlllistmutex.leave(); return(NULL); } //****************************************************************************** //****************************************************************************** Win32DllBase *Win32DllBase::findModuleByAddr(ULONG address) { dlllistmutex.enter(); Win32DllBase *mod = Win32DllBase::head; while(mod != NULL) { dbgCheckObj(mod); if(mod->insideModule(address)) { dlllistmutex.leave(); return(mod); } mod = mod->next; } dlllistmutex.leave(); return(NULL); } //****************************************************************************** //****************************************************************************** Win32DllBase *Win32DllBase::findModuleByOS2Handle(HINSTANCE hinstance) { dlllistmutex.enter(); for (Win32DllBase *pMod = Win32DllBase::getFirst(); pMod; pMod = pMod->getNext()) { if (pMod->isLxDll()) { if (((Win32LxDll *)pMod)->getHMOD() == hinstance) { dlllistmutex.leave(); return(pMod); } } else if (pMod->isPe2LxDll()) { if (((Win32Pe2LxDll *)pMod)->getHMOD() == hinstance) { dlllistmutex.leave(); return(pMod); } } } dlllistmutex.leave(); return(NULL); } //****************************************************************************** //****************************************************************************** BOOL Win32DllBase::isDll() { return TRUE; } //****************************************************************************** //****************************************************************************** int Win32DllBase::enumDlls(HMODULE *lphModule, int countMax) { int count = 0; HMODULE hModule; dlllistmutex.enter(); Win32DllBase *mod = Win32DllBase::head; while(mod != NULL) { dbgCheckObj(mod); hModule = mod->getInstanceHandle(); if ( count < countMax ) lphModule[count] = hModule; count++; mod = mod->next; } dlllistmutex.leave(); return count; } //****************************************************************************** //****************************************************************************** Win32DllBase *Win32DllBase::head = NULL; Queue Win32DllBase::loadLibDlls;