Windows NT 4.0 source code leak
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

768 lines
20 KiB

/*==========================================================================
*
* 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.
*
***************************************************************************/
/*
* unfortunately we have to break our pre-compiled headers to get our
* GUIDS defined...
*/
#define INITGUID
#include "ddrawpr.h"
#include <initguid.h>
#ifdef WINNT
#undef IUnknown
#include <objbase.h>
#endif
#ifdef WIN95
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);
#endif
#if 0
DWORD dwPidDetaching;
BOOL bInDetach;
#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 );
#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;
HANDLE hWindowListMutex; //=(HANDLE)0;
HANDLE csInitMutex;
#define WINDOWLISTMUTEXNAME "DDrawWindowListMutex"
#define INITCSWINDLIST() \
hWindowListMutex = CreateMutex(NULL,FALSE,WINDOWLISTMUTEXNAME);
#endif //win95
#ifdef WINNT
#pragma data_seg("share")
#endif
PVOID pHeap=0; // pointer to shared heap
DWORD dwRefCnt=0;
DWORD dwLockCount=0;
DWORD dwFakeCurrPid=0;
DWORD dwGrimReaperPid=0;
LPWINDOWINFO lpWindowInfo=0; // the list of WINDOWINFO structures
LPDDRAWI_DIRECTDRAW_GBL lpFakeDD=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
/*
* Winnt specific global statics
*/
#ifdef WINNT
ULONG uDisplaySettingsUnique=0;
#endif
/*
*Hel globals:
*/
#ifdef WINNT
LPVOID gpdci;
#endif
// 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;
GLOBAL_SHARED_CRITICAL_SECTION CheapMutexCrossProcess={0};
#ifdef WINNT
#pragma data_seg(".data")
#endif
/*
* This is the global variable pointer.
*/
GLOBAL_LOCAL_CRITICAL_SECTION CheapMutexPerProcess;
/*
* 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;
//#endif
/*
*-------------------------------------------------------------------------
*/
/*
* Win95 specific global statics
*/
#ifdef WIN95
LPVOID lpWin16Lock;
static CRITICAL_SECTION DirectDrawCSect;
static CRITICAL_SECTION csInit = {0};
CRITICAL_SECTION csWindowList;
CSECT_HANDLE lpDDCS;
#endif
#define HELPERINITDLLEVENT "__DDRAWDLL_HELPERINIT_EVENT__"
/*
* DllMain
*/
BOOL WINAPI DllMain(HINSTANCE hmod, DWORD dwReason, LPVOID lpvReserved)
{
LPATTACHED_PROCESSES lpap;
DWORD pid;
BOOL didhelp;
HANDLE hhelperinit;
dwMarker = 0x56414C4D;
pid = GetCurrentProcessId();
switch( dwReason )
{
case DLL_PROCESS_ATTACH:
pdwHelperPid=&dwHelperPid;
phModule=&hModule;
DisableThreadLibraryCalls( hmod );
DPFINIT();
/*
* create the DirectDraw csect
*/
DPF( 2, "====> ENTER: DLLMAIN(%08lx): Process Attach: %08lx, tid=%08lx", DllMain,
pid, GetCurrentThreadId() );
#if 0
if( pid == dwPidDetaching )
{
if( bInDetach )
{
DPF( 2, "?????? ATTACH FOR PROCESS THAT IS DETACHING!" );
return TRUE;
}
else
{
dwPidDetaching = 0;
}
}
#endif
#ifdef WIN95
if( lpWin16Lock == NULL )
{
GetpWin16Lock( &lpWin16Lock );
}
#endif
/*
* 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
*/
hhelperinit = CreateEvent( NULL, TRUE, FALSE, HELPERINITDLLEVENT );
#ifdef DEBUG
if( NULL == hhelperinit )
DPF( 1, "Could not create the DDHELP init event - continuing anyway" );
#endif
#ifdef USE_CRITSECTS
{
HANDLE hevent;
hevent = CreateEvent( NULL, TRUE, FALSE, TMPDLLEVENT );
#if defined( WIN16_SEPARATE ) && !defined(WINNT)
lpDDCS = &DirectDrawCSect;
#endif
/*
* is this the first time?
*/
if( FALSE == InterlockedExchange( &bFirstTime, TRUE ) )
{
#ifdef WIN16_SEPARATE
INIT_DDRAW_CSECT();
INITCSWINDLIST();
ENTER_DDRAW();
#else
INITCSDDC(); // used in DirectDrawCreate
INITCSINIT();
ENTER_CSINIT();
#endif
if( hevent != NULL )
{
SetEvent( hevent );
}
hModule = hmod;
}
/*
* second or later time through, wait for first time to
* finish and then take the csect
*/
else
{
if( hevent != NULL )
{
WaitForSingleObject( hevent, INFINITE );
}
#ifdef WIN16_SEPARATE
#if defined( WINNT )
//Each process needs its own handle in NT
INIT_DDRAW_CSECT();
#endif
ENTER_DDRAW();
#else
ENTER_CSINIT();
#endif
}
}
#endif
// #ifdef WIN95
{
DWORD hpid;
/*
* get the helper process started
*/
didhelp = CreateHelperProcess( &hpid );
if( hpid == 0 )
{
DPF( 1, "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.
*/
if( !didhelp && ( pid != hpid ) )
{
if( NULL != hhelperinit )
{
/*
* 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
WaitForSingleObject( hhelperinit, INFINITE );
#ifdef WIN16_SEPARATE
ENTER_DDRAW();
#else
ENTER_CSINIT();
#endif
}
}
}
// #endif
/*
* Win95 thunk connection...
*/
#ifdef WIN95
DPF( 1, "Thunk connects" );
if (!(thk3216_ThunkConnect32(DDHAL_DRIVER_DLLNAME,
DDHAL_APP_DLLNAME,
hmod,
dwReason)))
{
#ifdef WIN16_SEPARATE
LEAVE_DDRAW();
#else
LEAVE_CSINIT();
#endif
DPF( 1, "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( 1, "LEAVING, COULD NOT thk1632_THUNKCONNECT32" );
return FALSE;
}
#endif
/*
* initialize memory used to be done here. Jeffno 960609
*/
//#ifdef WIN95
/*
* signal the new process being added
*/
if( didhelp )
{
DPF( 2, "Waiting for DDHELP startup" );
#ifdef WIN16_SEPARATE
LEAVE_DDRAW();
#else
LEAVE_CSINIT();
#endif
if( !WaitForHelperStartup() )
{
DPF( 1, "LEAVING, WaitForHelperStartup FAILED" );
return FALSE;
}
HelperLoadDLL( DDHAL_APP_DLLNAME, NULL, 0 );
#ifdef WIN16_SEPARATE
ENTER_DDRAW();
#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 note 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.
*/
if( NULL != hhelperinit )
{
DPF( 3, "Signalling the completion of DDHELP's initialization" );
SetEvent( hhelperinit );
}
}
SignalNewProcess( pid, DDNotify );
//#endif
/*
* 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 )
{
#ifdef WINNT //win NT needs to map in the shared area, so MemInit is called for every process
}
{
#endif
if( !MemInit() )
{
#ifdef WIN16_SEPARATE
LEAVE_DDRAW();
#else
LEAVE_CSINIT();
#endif
DPF( 1, "LEAVING, COULD NOT MemInit" );
return FALSE;
}
}
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
GdiSetBatchLimit(1);
lpap->dwNTToldYet=0;
#endif
lpAttachedProcesses = lpap;
}
/*
* Initialize callback tables for this process.
*/
InitCallbackTables();
#ifdef WIN16_SEPARATE
LEAVE_DDRAW();
#else
LEAVE_CSINIT();
#endif
DPF( 2, "====> EXIT: DLLMAIN(%08lx): Process Attach: %08lx", DllMain,
pid );
break;
case DLL_PROCESS_DETACH:
DPF( 2, "====> ENTER: DLLMAIN(%08lx): Process Detach %08lx, tid=%08lx",
DllMain, pid, GetCurrentThreadId() );
/*
* This causes too much grief; let DDHELP tell us to clean up
*/
#if 0
#ifndef WIN16_SEPARATE
ENTER_CSINIT();
#endif
ENTER_DDRAW();
bInDetach = TRUE;
if( dwPidDetaching == pid )
{
DPF( 2, "RANDOM EXTRA DETACH FOUND" );
LEAVE_DDRAW();
LEAVE_CSINIT();
bInDetach = FALSE;
dwPidDetaching = 0;
return 1;
}
dwPidDetaching = pid;
/*
* disconnect from thunk...
*/
#ifdef WIN95
thk3216_ThunkConnect32(DDHAL_DRIVER_DLLNAME,
DDHAL_APP_DLLNAME,
hmod,
dwReason);
thk1632_ThunkConnect32(DDHAL_DRIVER_DLLNAME,
DDHAL_APP_DLLNAME,
hmod,
dwReason);
#endif //WIN95
if( !RemoveProcessFromDLL( pid ) )
{
DPF( 1, "ERROR: PROCESS %08lx NOT ATTACHED TO DLL!", pid );
}
/*
* make sure this process did a clean up after itself
*/
CurrentProcessCleanup( FALSE );
/*
* see if it is time to clean up... we clean up on a reference
* count of 2 instead of 1, because DDHELP has a count on us as
* well...
*/
DPF( 2, "DLL RefCnt = %lu", dwRefCnt );
if( dwRefCnt == 2 )
{
DPF( 2, "On last refcnt, safe to ditch DDHELP.EXE" );
dwRefCnt = 1;
#ifdef DEBUG
MemState();
#endif
// #ifdef WIN95
LEAVE_DDRAW();
DoneWithHelperProcess();
ENTER_DDRAW();
// #endif
#ifdef WINNT //win NT needs to close file mapping handle for each process
MemFini();
#endif
}
else if( dwRefCnt == 1 )
{
DPF( 2, "Cleaning up DLL" );
MemFini();
dwRefCnt = 0;
}
else if (dwRefCnt == 0)
{
DPF( 2, "PROCESS_DETACH with reference count = %d!", dwRefCnt );
}
else if( dwRefCnt > 0 )
{
#ifdef WINNT //win NT needs to close file mapping handle for each process
MemFini();
#endif
dwRefCnt--;
}
bInDetach = FALSE;
LEAVE_DDRAW();
LEAVE_CSINIT();
#if defined( WIN95 ) && !defined( USE_CHEAP_MUTEX )
if( dwRefCnt == 0 )
{
FINI_DDRAW_CSECT();
}
#else
FINI_DDRAW_CSECT(); //Cheap mutexes need to close semaphore handle for each process
#endif
#else //not 0
/*
* 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
#endif //end #if 0
#ifdef DEBUG
ENTER_DDRAW();
MemState();
LEAVE_DDRAW();
#endif
#ifdef WINNT //win NT needs to close file mapping handle for each process
MemFini();
FINI_DDRAW_CSECT(); //Cheap mutexes need to close semaphore handle for each process
#endif
DPF( 2, "====> 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( 1, "THREAD_ATTACH");
break;
case DLL_THREAD_DETACH:
DPF( 1,"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 );
#ifdef WINNT
GdiSetBatchLimit(0);
#endif
DPF( 2, "Removing process %08lx from list", pid );
return TRUE;
}
prev = lpap;
lpap = lpap->lpLink;
}
DPF( 2, "Process %08lx not in DLL list", pid );
return FALSE;
} /* RemoveProcessFromDLL */