/*========================================================================== * * Copyright (C) 1995 Microsoft Corporation. All Rights Reserved. * * File: dllinit.c * Content: DDRAW.DLL initialization * History: * Date By Reason * ==== == ====== * 20-jan-95 craige initial implementation * 21-feb-95 craige disconnect anyone who forgot to do it themselves, * use critical sections on Win95 * 27-feb-95 craige new sync. macros * 30-mar-95 craige process tracking/cleanup for Win95 * 01-apr-95 craige happy fun joy updated header file * 12-apr-95 craige debug stuff for csects * 12-may-95 craige define GUIDs * 24-jun-95 craige track which processes attach to the DLL * 25-jun-95 craige one ddraw mutex * 13-jul-95 craige ENTER_DDRAW is now the win16 lock; * proper initialization of csects * 16-jul-95 craige work around weird kernel "feature" of getting a * process attach of the same process during process detach * 19-jul-95 craige process detach too much grief; let DDNotify handle it * 20-jul-95 craige internal reorg to prevent thunking during modeset * 19-aug-95 davidmay restored call to disconnect thunk from 19-jul change * 26-sep-95 craige bug 1364: create new csect to avoid dsound deadlock * 08-dec-95 jeffno For NT, critical section macros expand to use mutexes * 16-mar-96 colinmc Callback table initialization now happens on process * attach * 20-mar-96 colinmc Bug 13341: Made MemState() dump in process detach * thread safe * 07-may-96 colinmc Bug 20219: Simultaneous calls to LoadLibrary cause * a deadlock * 09-may-96 colinmc Bug 20219 (again): Yes the deadlock again - previous * fix was not enough. * 19-jan-97 colinmc AGP support * 26-jan-97 ketand Kill globals for multi-mon. * 24-feb-97 ketand Set up callback from DDHelp to update rects. * 03-mar-97 jeffno Structure name change to avoid conflict w/ ActiveAccessibility * 13-mar-97 colinmc Bug 6533: Pass uncached flag to VMM correctly * 31-jul-97 jvanaken Bug 7907: Notify Memphis GDI when ddraw starts up * ***************************************************************************/ /* * unfortunately we have to break our pre-compiled headers to get our * GUIDS defined... */ #define INITGUID #include "ddrawpr.h" #include #ifdef WINNT #undef IUnknown #include #endif #include "apphack.h" #include "aclapi.h" #ifdef WIN95 int main; // this is so we can avoid calling DllMainCRTStartup extern BOOL _stdcall thk3216_ThunkConnect32(LPSTR pszDll16, LPSTR pszDll32, HINSTANCE hInst, DWORD dwReason); extern BOOL _stdcall thk1632_ThunkConnect32(LPSTR pszDll16, LPSTR pszDll32, HINSTANCE hInst, DWORD dwReason); DWORD _stdcall wWinMain(DWORD a, DWORD b, DWORD c, DWORD d) { #ifdef DEBUG OutputDebugString("WARNING: wWinMain called. \n"); #endif // DEBUG return 0; } #endif #ifdef USE_CRITSECTS #define TMPDLLEVENT "__DDRAWDLL_EVENT__" #endif #ifndef WIN16_SEPARATE #ifdef WIN95 #define INITCSINIT() \ ReinitializeCriticalSection( &csInit ); \ MakeCriticalSectionGlobal( &csInit ); #define ENTER_CSINIT() EnterCriticalSection( &csInit ) #define LEAVE_CSINIT() LeaveCriticalSection( &csInit ) extern CRITICAL_SECTION ddcCS; #define INITCSDDC() \ ReinitializeCriticalSection( &ddcCS ); \ MakeCriticalSectionGlobal( &ddcCS ); #else #define CSINITMUTEXNAME "InitMutexName" #define INITCSINIT() \ csInitMutex = CreateMutex(NULL,FALSE,CSINITMUTEXNAME); #define ENTER_CSINIT() \ WaitForSingleObject(csInitMutex,INFINITE); #define LEAVE_CSINIT() \ ReleaseMutex(csInitMutex); #define INITDDC() #endif #endif #ifdef WIN95 #define INITCSWINDLIST() \ ReinitializeCriticalSection( &csWindowList ); \ MakeCriticalSectionGlobal( &csWindowList ); #define INITCSDRIVEROBJECTLIST() \ ReinitializeCriticalSection( &csDriverObjectList ); \ MakeCriticalSectionGlobal( &csDriverObjectList ); #define FINIWINDLIST() #define FINICSDRIVEROBJECTLIST() #else // Each process needs its own handle, so these are not initialised so theyu won't end up in shared mem HANDLE hDirectDrawMutex=(HANDLE)0; //This counts recursions into ddraw, so we don't try to do the mode uniqueness thing on recursive entries into ddraw DWORD gdwRecursionCount=0; HANDLE hWindowListMutex; //=(HANDLE)0; HANDLE hDriverObjectListMutex; //=(HANDLE)0; HANDLE csInitMutex; DWORD dwNumLockedWhenModeSwitched; #define WINDOWLISTMUTEXNAME "DDrawWindowListMutex" #define DRIVEROBJECTLISTMUTEXNAME "DDrawDriverObjectListMutex" #define INITCSWINDLIST() \ hWindowListMutex = CreateMutex(NULL,FALSE,WINDOWLISTMUTEXNAME); #define INITCSDRIVEROBJECTLIST() \ hDriverObjectListMutex = CreateMutex(NULL,FALSE,DRIVEROBJECTLISTMUTEXNAME); #define FINIWINDLIST() CloseHandle(hWindowListMutex); #define FINICSDRIVEROBJECTLIST() CloseHandle(hDriverObjectListMutex); #endif //win95 DWORD dwRefCnt=0; DWORD dwLockCount=0; DWORD dwFakeCurrPid=0; DWORD dwGrimReaperPid=0; LPDDWINDOWINFO lpWindowInfo=0; // the list of WINDOWINFO structures LPDDRAWI_DIRECTDRAW_LCL lpDriverLocalList=0; LPDDRAWI_DIRECTDRAW_INT lpDriverObjectList=0; volatile DWORD dwMarker=0; /* * This is the globally maintained list of clippers not owned by any * DirectDraw object. All clippers created with DirectDrawClipperCreate * are placed on this list. Those created by IDirectDraw_CreateClipper * are placed on the clipper list of thier owning DirectDraw object. * * The objects on this list are NOT released when an app's DirectDraw * object is released. They remain alive until explictly released or * the app. dies. */ LPDDRAWI_DDRAWCLIPPER_INT lpGlobalClipperList=0; HINSTANCE hModule=0; LPATTACHED_PROCESSES lpAttachedProcesses=0; BOOL bFirstTime=0; #ifdef DEBUG int iDLLCSCnt=0; int iWin16Cnt=0; #endif /* * These variable are so we can handle more than one window in the * topmost window timer. */ HWND ghwndTopmostList[MAX_TIMER_HWNDS]; int giTopmostCnt = 0; /* * Winnt specific global statics */ #ifdef WINNT ULONG uDisplaySettingsUnique=0; #endif /* *Hel globals: */ // used to count how many drivers are currently using the HEL DWORD dwHELRefCnt=0; // keep these around to pass to blitlib. everytime we blt to/from a surface, we // construct a BITMAPINFO for that surface using gpbmiSrc and gpbmiDest LPBITMAPINFO gpbmiSrc=0; LPBITMAPINFO gpbmiDest=0; #ifdef DEBUG // these are used by myCreateSurface int gcSurfMem=0; // surface memory in bytes int gcSurf=0; // number of surfaces #endif DWORD dwHelperPid=0; #ifdef USE_CHEAP_MUTEX #ifdef WINNT #pragma data_seg("share") #endif GLOBAL_SHARED_CRITICAL_SECTION CheapMutexCrossProcess={0}; #ifdef WINNT #pragma data_seg(".data") #endif #endif //0 /* * App compatibility stuff. Moved here from apphack.c */ BOOL bReloadReg=FALSE; BOOL bHaveReadReg=FALSE; LPAPPHACKS lpAppList=NULL; LPAPPHACKS *lpAppArray=NULL; DWORD dwAppArraySize=0; /* * Global head of DC/Surface association list * This list is usually very very short, so we take the hit of extra pointers * just so that we don't have to traverse the entire list of surfaces. */ DCINFO *g_pdcinfoHead = NULL; BYTE szDeviceWndClass[] = "DirectDrawDeviceWnd"; /* * Gamma calibration globals. This determines weather a calibrator exists * and the handle to the DLL if it's loaded. */ BOOL bGammaCalibratorExists=FALSE; BYTE szGammaCalibrator[MAX_PATH]=""; /* * Optional refresh rate to force for all modes. */ DWORD dwForceRefreshRate; /* * Spinlocks for startup synchronization. * It's just too hard to use events when NT ddraw is per-process and 9x is cross- */ DWORD dwSpinStartup=0; DWORD dwHelperSpinStartup=0; #ifdef USE_CHEAP_MUTEX /* * This is the global variable pointer. */ GLOBAL_LOCAL_CRITICAL_SECTION CheapMutexPerProcess; #endif /* * These two keep w95help.c happy. They point to the dwHelperPid and hModule entries in the process's * mapping of the GLOBALS structure. */ DWORD * pdwHelperPid=&dwHelperPid; HANDLE * phModule=&hModule; #ifdef WINNT /* * This mutex is owned by the exclusive mode owner */ HANDLE hExclusiveModeMutex=0; HANDLE hCheckExclusiveModeMutex=0; #define EXCLUSIVE_MODE_MUTEX_NAME "__DDrawExclMode__" #define CHECK_EXCLUSIVE_MODE_MUTEX_NAME "__DDrawCheckExclMode__" #endif //#endif /* *------------------------------------------------------------------------- */ #if defined(WIN95) || defined(NT_USES_CRITICAL_SECTION) static CRITICAL_SECTION DirectDrawCSect; CSECT_HANDLE lpDDCS; #endif /* * Win95 specific global statics */ #ifdef WIN95 LPVOID lpWin16Lock; static CRITICAL_SECTION csInit = {0}; CRITICAL_SECTION csWindowList; CRITICAL_SECTION csDriverObjectList; #endif #define HELPERINITDLLEVENT "__DDRAWDLL_HELPERINIT_EVENT__" /* * DllMain */ BOOL WINAPI DllMain(HINSTANCE hmod, DWORD dwReason, LPVOID lpvReserved) { LPATTACHED_PROCESSES lpap; DWORD pid; BOOL didhelp; dwMarker = 0x56414C4D; pid = GetCurrentProcessId(); switch( dwReason ) { case DLL_PROCESS_ATTACH: pdwHelperPid=&dwHelperPid; phModule=&hModule; DisableThreadLibraryCalls( hmod ); DPFINIT(); /* * create the DirectDraw csect */ DPF( 4, "====> ENTER: DLLMAIN(%08lx): Process Attach: %08lx, tid=%08lx", DllMain, pid, GetCurrentThreadId() ); #ifdef WIN95 if( lpWin16Lock == NULL ) { GetpWin16Lock( &lpWin16Lock ); } #endif #ifdef USE_CRITSECTS { #if defined( WIN16_SEPARATE ) && (defined(WIN95) || defined(NT_USES_CRITICAL_SECTION)) lpDDCS = &DirectDrawCSect; #endif /* * is this the first time? */ if( FALSE == InterlockedExchange( &bFirstTime, TRUE ) ) { #ifdef WIN16_SEPARATE INIT_DDRAW_CSECT(); INITCSWINDLIST(); INITCSDRIVEROBJECTLIST(); ENTER_DDRAW_INDLLMAIN(); #else INITCSDDC(); // used in DirectDrawCreate INITCSINIT(); ENTER_CSINIT(); #endif hModule = hmod; /* * This event is signaled when DDHELP has successfully finished * initializing. Threads other that the very first one to connect * and the one spawned by DDHELP must wait for this event to * be signaled as deadlock will result if they run through * process attach before the DDHELP thread has. * * NOTE: The actual deadlock this prevents is pretty unusual so * if we fail to create this event we will simply continue. Its * highly unlikely anyone will notice (famous last words). * * CMcC */ /* * Replaced events with spinlocks to work around a handle leak */ InterlockedExchange( & dwSpinStartup , 1); } /* * second or later time through, wait for first time to * finish and then take the csect */ else { /* * Spin waiting for the first thread to exit the clause above * This strange construction works around a compiler bug. * while (dwHelperSpinStartup==1); generates an infinite loop. */ while (1) { if (dwSpinStartup==1) break; } #ifdef WIN16_SEPARATE #if defined( WINNT ) //Each process needs its own handle in NT INIT_DDRAW_CSECT(); #endif ENTER_DDRAW_INDLLMAIN(); #else ENTER_CSINIT(); #endif } } #endif #ifdef WINNT { SECURITY_ATTRIBUTES sa; SID_IDENTIFIER_AUTHORITY sia = SECURITY_WORLD_SID_AUTHORITY; PSID adminSid = 0; ULONG cbAcl; PACL acl=0; PSECURITY_DESCRIPTOR pSD; BYTE buffer[SECURITY_DESCRIPTOR_MIN_LENGTH]; BOOL bSecurityGooSucceeded = FALSE; //Granny's old fashioned LocalAlloc: BYTE Buffer1[256]; BYTE Buffer2[16]; // Create the SID for world cbAcl = GetSidLengthRequired(1); if (cbAcl < sizeof(Buffer2)) { adminSid = (PSID) Buffer2; InitializeSid( adminSid, &sia, 1 ); *GetSidSubAuthority(adminSid, 0) = SECURITY_WORLD_RID; // Create an ACL giving World all access. cbAcl = sizeof(ACL) + (sizeof(ACCESS_ALLOWED_ACE) - sizeof(DWORD)) + GetLengthSid(adminSid); if (cbAcl < sizeof(Buffer1)) { acl = (PACL)&Buffer1; if (InitializeAcl( acl, cbAcl, ACL_REVISION )) { if (AddAccessAllowedAce( acl, ACL_REVISION, SYNCHRONIZE|MUTANT_QUERY_STATE|DELETE|READ_CONTROL, //|WRITE_OWNER|WRITE_DAC, adminSid )) { // Create a security descriptor with the above ACL. pSD = (PSECURITY_DESCRIPTOR)buffer; if (InitializeSecurityDescriptor(pSD, SECURITY_DESCRIPTOR_REVISION)) { if (SetSecurityDescriptorDacl(pSD, TRUE, acl, FALSE)) { // Fill in the SECURITY_ATTRIBUTES struct. sa.nLength = sizeof(sa); sa.lpSecurityDescriptor = pSD; sa.bInheritHandle = TRUE; bSecurityGooSucceeded = TRUE; } } } } } } // Use the security attributes to create the mutexes DDASSERT(0 == hExclusiveModeMutex); hExclusiveModeMutex = CreateMutex( bSecurityGooSucceeded ? &sa : NULL, //use default access if security goo failed. FALSE, EXCLUSIVE_MODE_MUTEX_NAME ); if (0 == hExclusiveModeMutex) { hExclusiveModeMutex = OpenMutex( SYNCHRONIZE|DELETE, // access flag FALSE, // inherit flag EXCLUSIVE_MODE_MUTEX_NAME // pointer to mutex-object name ); } if( hExclusiveModeMutex == 0 ) { DPF_ERR("Could not create exclusive mode mutex. exiting" ); #ifdef WIN16_SEPARATE LEAVE_DDRAW(); #else LEAVE_CSINIT(); #endif return FALSE; } DDASSERT(0 == hCheckExclusiveModeMutex); hCheckExclusiveModeMutex = CreateMutex( bSecurityGooSucceeded ? &sa : NULL, //use default access if security goo failed. FALSE, CHECK_EXCLUSIVE_MODE_MUTEX_NAME ); if (0 == hCheckExclusiveModeMutex) { hCheckExclusiveModeMutex = OpenMutex( SYNCHRONIZE|DELETE, // access flag FALSE, // inherit flag CHECK_EXCLUSIVE_MODE_MUTEX_NAME // pointer to mutex-object name ); } if( hCheckExclusiveModeMutex == 0 ) { DPF_ERR("Could not create exclusive mode check mutex. exiting" ); CloseHandle( hExclusiveModeMutex ); #ifdef WIN16_SEPARATE LEAVE_DDRAW(); #else LEAVE_CSINIT(); #endif return FALSE; } } #endif #ifdef WIN95 { DWORD hpid; /* * get the helper process started */ didhelp = CreateHelperProcess( &hpid ); if( hpid == 0 ) { DPF( 0, "Could not start helper; exiting" ); #ifdef WIN16_SEPARATE LEAVE_DDRAW(); #else LEAVE_CSINIT(); #endif return FALSE; } /* * You get three kinds of threads coming through * process attach: * * 1) A thread belonging to the first process to * connect to DDRAW.DLL. This is distinguished as * it performs lots of one time initialization * including starting DDHELP and getting DDHELP * to load its own copy of DDRAW.DLL. Threads * of this type are identified by didhelp being * TRUE in their context * 2) A thread belonging to DDHELP when it loads * its own copy of DDHELP in response to a * request from a thread of type 1. Threads of * this type are identified by having a pid * which is equal to hpid (DDHELP's pid) * 3) Any other threads belonging to subsequent * processes connecting to DDRAW.DLL * * As a thread of type 1 causes a thread of type 2 * to enter process attach before it itself has finished * executing process attach itself we open our selves up * to lots of deadlock problems if we let threads of * type 3 through process attach before the other threads * have completed their work. * * Therefore, the rule is that subsequent process * attachement can only be allowed to execute the * remainder of process attach if both the type 1 * and type 2 thread have completed their execution * of process attach. We assure this with a combination * of the critical section and an event which is signaled * once DDHELP has initialized. Threads of type 3 MUST * wait on this event before continuing through the * process attach code. This is what the following * code fragment does. */ /* * These events have been replaced with spinlocks, since * the old way leaked events, and it's just too hard to make them work. */ if( !didhelp && ( pid != hpid ) ) { { /* * NOTE: If we hold the DirectDraw critical * section when we wait on this event we WILL * DEADLOCK. Don't do it! Release the critical * section before and take it again after. This * guarantees that we won't complete process * attach before the initial thread and the * DDHELP thread have exited process attach. */ #ifdef WIN16_SEPARATE LEAVE_DDRAW(); #else LEAVE_CSINIT(); #endif /* * This strange construction works around a compiler bug. * while (dwHelperSpinStartup==1); generates an infinite loop. */ while (1) { if ( dwHelperSpinStartup == 1) break; } #ifdef WIN16_SEPARATE ENTER_DDRAW_INDLLMAIN(); #else ENTER_CSINIT(); #endif } } } /* * Win95 thunk connection... */ DPF( 4, "Thunk connects" ); if (!(thk3216_ThunkConnect32(DDHAL_DRIVER_DLLNAME, DDHAL_APP_DLLNAME, hmod, dwReason))) { #ifdef WIN16_SEPARATE LEAVE_DDRAW(); #else LEAVE_CSINIT(); #endif DPF( 0, "LEAVING, COULD NOT thk3216_THUNKCONNECT32" ); return FALSE; } if (!(thk1632_ThunkConnect32(DDHAL_DRIVER_DLLNAME, DDHAL_APP_DLLNAME, hmod, dwReason))) { #ifdef WIN16_SEPARATE LEAVE_DDRAW(); #else LEAVE_CSINIT(); #endif DPF( 0, "LEAVING, COULD NOT thk1632_THUNKCONNECT32" ); return FALSE; } /* * initialize memory used to be done here. Jeffno 960609 */ /* * signal the new process being added */ if( didhelp ) { DPF( 4, "Waiting for DDHELP startup" ); #ifdef WIN16_SEPARATE LEAVE_DDRAW(); #else LEAVE_CSINIT(); #endif if( !WaitForHelperStartup() ) { /* * NT Setup loads DDRAW.DLL and sometimes this fails, so we don't * actually want fail loading the DLL or else setup might fail. * Instead, we will suceed the load but then fail any other ddraw * calls. */ DPF( 0, "WaitForHelperStartup FAILED - disabling DDRAW" ); dwHelperPid = 0; return TRUE; } HelperLoadDLL( DDHAL_APP_DLLNAME, NULL, 0 ); /* * For now, only call this on a multi-monitor system because * it does cause a behavior change and we aren't able to * provide adequate test covereage in the DX5 timeframe. */ if( IsMultiMonitor() ) { HelperSetOnDisplayChangeNotify( (void *)&UpdateAllDeviceRects); } #ifdef WIN16_SEPARATE ENTER_DDRAW_INDLLMAIN(); #else ENTER_CSINIT(); #endif /* * As we were the first process through we now signal * the completion of DDHELP initialization. This will * release any subsequent threads waiting to complete * process attach. * * NOTE: Threads waiting on this event will not immediately * run process attach to completion as they will immediately * try to take the DirectDraw critical section which we hold. * Thus, they will not be allowed to continue until we have * released the critical section just prior to existing * below. */ InterlockedExchange( & dwHelperSpinStartup , 1); } SignalNewProcess( pid, DDNotify ); #endif //w95 /* * We call MemInit here in order to guarantee that MemInit is called for * the first time on ddhelp's process. Why? Glad you asked. On wx86 * (NT's 486 emulator) controlled instances of ddraw apps, we get a fault * whenever the ddraw app exits. This is because the app creates the RTL * heap inside a view of a file mapping which gets uncomitted (rightly) * when the app calls MemFini on exit. In this scenario, imagehlp.dll has * also created a heap, and calls a ntdll function which attempts to walk * the list of heaps, which requires a peek at the ddraw app's heap which * has been mapped out. Krunch. * We can't destroy the heap on MemFini because of the following scenario: * App A starts, creates heap. App b starts, maps a view of heap. App A * terminates, destroys heap. App b tries to use destroyed heap. Krunch * Jeffno 960609 */ if( dwRefCnt == 0 ) { if ( !MemInit() ) { #ifdef WINNT CloseHandle( hExclusiveModeMutex ); CloseHandle( hCheckExclusiveModeMutex ); #endif #ifdef WIN16_SEPARATE LEAVE_DDRAW(); #else LEAVE_CSINIT(); #endif DPF( 0,"LEAVING, COULD NOT MemInit"); return FALSE; } #ifdef WIN95 /* * The Memphis version of GDI calls into DirectDraw, but GDI * needs to be notified that DirectDraw has actually loaded. * (While GDI could check for itself to see whether ddraw.dll * has loaded, this would be sloooooow if it hasn't yet.) * The code below executes when ddraw.dll first starts up. */ { HANDLE h; VOID (WINAPI *p)(); h = LoadLibrary("msimg32.dll"); // GDI DLL if (h) { p = (VOID(WINAPI *)())GetProcAddress(h, "vSetDdrawflag"); if (p) { // vSetDdrawflag is a private call to (*p)(); // signal GDI that DDraw has loaded } FreeLibrary(h); } } #endif //WIN95 } dwRefCnt++; /* * remember this process (moved this below MemInit when it moved -Jeffno 960609 */ lpap = MemAlloc( sizeof( ATTACHED_PROCESSES ) ); if( lpap != NULL ) { lpap->lpLink = lpAttachedProcesses; lpap->dwPid = pid; #ifdef WINNT lpap->dwNTToldYet=0; #endif lpAttachedProcesses = lpap; } /* * Initialize callback tables for this process. */ InitCallbackTables(); #ifdef WIN16_SEPARATE LEAVE_DDRAW(); #else LEAVE_CSINIT(); #endif DPF( 4, "====> EXIT: DLLMAIN(%08lx): Process Attach: %08lx", DllMain, pid ); break; case DLL_PROCESS_DETACH: DPF( 4, "====> ENTER: DLLMAIN(%08lx): Process Detach %08lx, tid=%08lx", DllMain, pid, GetCurrentThreadId() ); /* * disconnect from thunk, even if other cleanup code commented out... */ #ifdef WIN95 thk3216_ThunkConnect32(DDHAL_DRIVER_DLLNAME, DDHAL_APP_DLLNAME, hmod, dwReason); thk1632_ThunkConnect32(DDHAL_DRIVER_DLLNAME, DDHAL_APP_DLLNAME, hmod, dwReason); #endif #ifdef WINNT //win NT needs to close file mapping handle for each process FreeAppHackData(); RemoveProcessFromDLL(pid); FINI_DDRAW_CSECT(); //Cheap mutexes need to close semaphore handle for each process MemFini(); DDASSERT(0 != hExclusiveModeMutex); CloseHandle( hCheckExclusiveModeMutex ); CloseHandle( hExclusiveModeMutex ); FINIWINDLIST(); FINICSDRIVEROBJECTLIST(); #endif DPF( 4, "====> EXIT: DLLMAIN(%08lx): Process Detach %08lx", DllMain, pid ); break; /* * we don't ever want to see thread attach/detach */ #ifdef DEBUG case DLL_THREAD_ATTACH: DPF( 4, "THREAD_ATTACH"); break; case DLL_THREAD_DETACH: DPF( 4,"THREAD_DETACH"); break; #endif default: break; } return TRUE; } /* DllMain */ /* * RemoveProcessFromDLL * * Find & remove a pid from the list. * Assumes ddlock taken */ BOOL RemoveProcessFromDLL( DWORD pid ) { LPATTACHED_PROCESSES lpap; LPATTACHED_PROCESSES prev; lpap = lpAttachedProcesses; prev = NULL; while( lpap != NULL ) { if( lpap->dwPid == pid ) { if( prev == NULL ) { lpAttachedProcesses = lpap->lpLink; } else { prev->lpLink = lpap->lpLink; } MemFree( lpap ); DPF( 5, "Removing process %08lx from list", pid ); return TRUE; } prev = lpap; lpap = lpap->lpLink; } DPF( 5, "Process %08lx not in DLL list", pid ); return FALSE; } /* RemoveProcessFromDLL */