2003-05-18 17:16:18 +00:00
/*
2008-04-04 23:35:43 +00:00
* PROJECT : ReactOS win32 kernel mode subsystem
* LICENSE : GPL - See COPYING in the top level directory
* FILE : subsystems / win32 / win32k / objects / gdiobj . c
* PURPOSE : General GDI object manipulation routines
* PROGRAMMERS : . . .
1999-09-10 21:17:07 +00:00
*/
2005-06-29 07:09:25 +00:00
2008-04-04 23:35:43 +00:00
/** INCLUDES ******************************************************************/
2009-07-09 13:36:42 +00:00
//#define GDI_DEBUG
2004-05-10 17:07:20 +00:00
# include <w32k.h>
2004-12-12 01:40:39 +00:00
# define NDEBUG
# include <debug.h>
2004-02-28 21:16:55 +00:00
2004-12-12 01:40:39 +00:00
# define GDI_ENTRY_TO_INDEX(ht, e) \
( ( ( ULONG_PTR ) ( e ) - ( ULONG_PTR ) & ( ( ht ) - > Entries [ 0 ] ) ) / sizeof ( GDI_TABLE_ENTRY ) )
# define GDI_HANDLE_GET_ENTRY(HandleTable, h) \
( & ( HandleTable ) - > Entries [ GDI_HANDLE_GET_INDEX ( ( h ) ) ] )
/* apparently the first 10 entries are never used in windows as they are empty */
# define RESERVE_ENTRIES_COUNT 10
2003-08-21 20:16:55 +00:00
2008-09-28 21:25:24 +00:00
# define BASE_OBJTYPE_COUNT 32
2008-04-04 23:35:43 +00:00
# define DelayExecution() \
DPRINT ( " %s:%i: Delay \n " , __FILE__ , __LINE__ ) ; \
KeDelayExecutionThread ( KernelMode , FALSE , & ShortDelay )
2009-07-09 13:36:42 +00:00
# include "gdidbg.c"
2008-11-30 19:28:11 +00:00
/* static */ /* FIXME: -fno-unit-at-a-time breaks this */
BOOL INTERNAL_CALL GDI_CleanupDummy ( PVOID ObjectBody ) ;
2008-04-04 23:35:43 +00:00
/** GLOBALS *******************************************************************/
2004-12-12 01:40:39 +00:00
2007-09-06 01:04:18 +00:00
typedef struct
{
BOOL bUseLookaside ;
ULONG_PTR ulBodySize ;
ULONG Tag ;
GDICLEANUPPROC CleanupProc ;
} OBJ_TYPE_INFO , * POBJ_TYPE_INFO ;
static const
2008-09-28 21:25:24 +00:00
OBJ_TYPE_INFO ObjTypeInfo [ BASE_OBJTYPE_COUNT ] =
2007-09-06 01:04:18 +00:00
{
2008-02-18 23:05:41 +00:00
{ 0 , 0 , 0 , NULL } , /* 00 reserved entry */
{ 1 , sizeof ( DC ) , TAG_DC , DC_Cleanup } , /* 01 DC */
{ 1 , 0 , 0 , NULL } , /* 02 UNUSED1 */
{ 1 , 0 , 0 , NULL } , /* 03 UNUSED2 */
2008-03-19 00:56:40 +00:00
{ 1 , sizeof ( ROSRGNDATA ) , TAG_REGION , REGION_Cleanup } , /* 04 RGN */
2009-01-08 16:33:40 +00:00
{ 1 , sizeof ( SURFACE ) , TAG_SURFACE , SURFACE_Cleanup } , /* 05 SURFACE */
2008-05-15 11:38:46 +00:00
{ 1 , sizeof ( CLIENTOBJ ) , TAG_CLIENTOBJ , GDI_CleanupDummy } , /* 06 CLIENTOBJ: METADC,... */
2008-06-19 01:47:57 +00:00
{ 1 , sizeof ( PATH ) , TAG_PATH , GDI_CleanupDummy } , /* 07 PATH */
2009-04-09 22:28:54 +00:00
{ 1 , sizeof ( PALETTE ) , TAG_PALETTE , PALETTE_Cleanup } , /* 08 PAL */
2008-05-15 11:38:46 +00:00
{ 1 , sizeof ( COLORSPACE ) , TAG_ICMLCS , GDI_CleanupDummy } , /* 09 ICMLCS, */
2008-02-18 23:05:41 +00:00
{ 1 , sizeof ( TEXTOBJ ) , TAG_LFONT , GDI_CleanupDummy } , /* 0a LFONT */
{ 0 , 0 , TAG_RFONT , NULL } , /* 0b RFONT, unused */
{ 0 , 0 , TAG_PFE , NULL } , /* 0c PFE, unused */
{ 0 , 0 , TAG_PFT , NULL } , /* 0d PFT, unused */
2008-05-15 11:38:46 +00:00
{ 0 , sizeof ( GDICLRXFORM ) , TAG_ICMCXF , GDI_CleanupDummy } , /* 0e ICMCXF, */
2008-02-18 23:05:41 +00:00
{ 0 , 0 , TAG_SPRITE , NULL } , /* 0f SPRITE, unused */
2009-03-20 22:40:14 +00:00
{ 1 , sizeof ( BRUSH ) , TAG_BRUSH , BRUSH_Cleanup } , /* 10 BRUSH, PEN, EXTPEN */
2008-02-18 23:05:41 +00:00
{ 0 , 0 , TAG_UMPD , NULL } , /* 11 UMPD, unused */
{ 0 , 0 , 0 , NULL } , /* 12 UNUSED4 */
{ 0 , 0 , TAG_SPACE , NULL } , /* 13 SPACE, unused */
{ 0 , 0 , 0 , NULL } , /* 14 UNUSED5 */
{ 0 , 0 , TAG_META , NULL } , /* 15 META, unused */
{ 0 , 0 , TAG_EFSTATE , NULL } , /* 16 EFSTATE, unused */
{ 0 , 0 , TAG_BMFD , NULL } , /* 17 BMFD, unused */
2008-03-31 13:44:38 +00:00
{ 0 , 0 , TAG_VTFD , NULL } , /* 18 VTFD, unused */
2008-02-18 23:05:41 +00:00
{ 0 , 0 , TAG_TTFD , NULL } , /* 19 TTFD, unused */
{ 0 , 0 , TAG_RC , NULL } , /* 1a RC, unused */
{ 0 , 0 , TAG_TEMP , NULL } , /* 1b TEMP, unused */
2009-03-17 00:30:15 +00:00
{ 0 , sizeof ( EDRIVEROBJ ) , TAG_DRVOBJ , DRIVEROBJ_Cleanup } , /* 1c DRVOBJ */
2008-02-18 23:05:41 +00:00
{ 0 , 0 , TAG_DCIOBJ , NULL } , /* 1d DCIOBJ, unused */
{ 0 , 0 , TAG_SPOOL , NULL } , /* 1e SPOOL, unused */
2008-09-28 21:25:24 +00:00
{ 0 , 0 , 0 , NULL } , /* 1f reserved entry */
2007-09-06 01:04:18 +00:00
} ;
2004-12-12 01:40:39 +00:00
static LARGE_INTEGER ShortDelay ;
2001-03-31 15:35:08 +00:00
2008-04-04 23:35:43 +00:00
/** INTERNAL FUNCTIONS ********************************************************/
/*
* Dummy GDI Cleanup Callback
*/
2008-11-30 19:28:11 +00:00
/* static */ /* FIXME: -fno-unit-at-a-time breaks this */
BOOL INTERNAL_CALL
2008-04-04 23:35:43 +00:00
GDI_CleanupDummy ( PVOID ObjectBody )
{
return TRUE ;
}
/*!
* Allocate GDI object table .
* \ param Size - number of entries in the object table .
*/
PGDI_HANDLE_TABLE INTERNAL_CALL
GDIOBJ_iAllocHandleTable ( OUT PSECTION_OBJECT * SectionObject )
{
PGDI_HANDLE_TABLE HandleTable = NULL ;
LARGE_INTEGER htSize ;
UINT ObjType ;
ULONG ViewSize = 0 ;
NTSTATUS Status ;
ASSERT ( SectionObject ! = NULL ) ;
htSize . QuadPart = sizeof ( GDI_HANDLE_TABLE ) ;
Status = MmCreateSection ( ( PVOID * ) SectionObject ,
SECTION_ALL_ACCESS ,
NULL ,
& htSize ,
PAGE_READWRITE ,
SEC_COMMIT ,
NULL ,
NULL ) ;
if ( ! NT_SUCCESS ( Status ) )
return NULL ;
/* FIXME - use MmMapViewInSessionSpace once available! */
Status = MmMapViewInSystemSpace ( * SectionObject ,
( PVOID * ) & HandleTable ,
& ViewSize ) ;
if ( ! NT_SUCCESS ( Status ) )
{
ObDereferenceObject ( * SectionObject ) ;
* SectionObject = NULL ;
return NULL ;
}
RtlZeroMemory ( HandleTable , sizeof ( GDI_HANDLE_TABLE ) ) ;
HandleTable - > LookasideLists = ExAllocatePoolWithTag ( NonPagedPool ,
BASE_OBJTYPE_COUNT * sizeof ( PAGED_LOOKASIDE_LIST ) ,
TAG_GDIHNDTBLE ) ;
if ( HandleTable - > LookasideLists = = NULL )
{
MmUnmapViewInSystemSpace ( HandleTable ) ;
ObDereferenceObject ( * SectionObject ) ;
* SectionObject = NULL ;
return NULL ;
}
for ( ObjType = 0 ; ObjType < BASE_OBJTYPE_COUNT ; ObjType + + )
{
if ( ObjTypeInfo [ ObjType ] . bUseLookaside )
{
ExInitializePagedLookasideList ( HandleTable - > LookasideLists + ObjType ,
NULL ,
NULL ,
0 ,
ObjTypeInfo [ ObjType ] . ulBodySize ,
ObjTypeInfo [ ObjType ] . Tag ,
0 ) ;
}
}
ShortDelay . QuadPart = - 5000LL ; /* FIXME - 0.5 ms? */
HandleTable - > FirstFree = 0 ;
HandleTable - > FirstUnused = RESERVE_ENTRIES_COUNT ;
return HandleTable ;
}
2007-07-25 22:39:14 +00:00
static void FASTCALL
LockErrorDebugOutput ( HGDIOBJ hObj , PGDI_TABLE_ENTRY Entry , LPSTR Function )
{
2008-02-26 01:00:34 +00:00
if ( ( Entry - > Type & GDI_ENTRY_BASETYPE_MASK ) = = 0 )
2007-07-25 22:39:14 +00:00
{
DPRINT1 ( " %s: Attempted to lock object 0x%x that is deleted! \n " , Function , hObj ) ;
2008-06-03 23:56:09 +00:00
GDIDBG_TRACEDELETER ( hObj ) ;
2007-07-25 22:39:14 +00:00
}
else if ( GDI_HANDLE_GET_REUSECNT ( hObj ) ! = GDI_ENTRY_GET_REUSECNT ( Entry - > Type ) )
{
DPRINT1 ( " %s: Attempted to lock object 0x%x, wrong reuse counter (Handle: 0x%x, Entry: 0x%x) \n " ,
Function , hObj , GDI_HANDLE_GET_REUSECNT ( hObj ) , GDI_ENTRY_GET_REUSECNT ( Entry - > Type ) ) ;
}
2007-09-20 16:13:55 +00:00
else if ( GDI_HANDLE_GET_TYPE ( hObj ) ! = ( ( Entry - > Type < < GDI_ENTRY_UPPER_SHIFT ) & GDI_HANDLE_TYPE_MASK ) )
2007-07-25 22:39:14 +00:00
{
DPRINT1 ( " %s: Attempted to lock object 0x%x, type mismatch (Handle: 0x%x, Entry: 0x%x) \n " ,
2007-09-20 16:13:55 +00:00
Function , hObj , GDI_HANDLE_GET_TYPE ( hObj ) , ( Entry - > Type < < GDI_ENTRY_UPPER_SHIFT ) & GDI_HANDLE_TYPE_MASK ) ;
2007-07-25 22:39:14 +00:00
}
else
{
DPRINT1 ( " %s: Attempted to lock object 0x%x, something went wrong, typeinfo = 0x%x \n " ,
Function , hObj , Entry - > Type ) ;
}
2008-03-04 00:18:45 +00:00
GDIDBG_TRACECALLER ( ) ;
2007-07-25 22:39:14 +00:00
}
2008-01-08 05:04:52 +00:00
ULONG
FASTCALL
2009-08-24 20:09:58 +00:00
InterlockedPopFreeEntry ( VOID )
2008-01-08 05:04:52 +00:00
{
2009-02-16 23:00:15 +00:00
ULONG idxFirst , idxNext , idxPrev ;
PGDI_TABLE_ENTRY pEntry ;
DWORD PrevProcId ;
2008-03-04 00:14:11 +00:00
DPRINT ( " Enter InterLockedPopFreeEntry \n " ) ;
2009-02-16 23:00:15 +00:00
while ( TRUE )
2008-03-04 00:14:11 +00:00
{
2009-02-16 23:00:15 +00:00
idxFirst = GdiHandleTable - > FirstFree ;
if ( ! idxFirst )
2008-03-04 00:14:11 +00:00
{
2009-02-16 23:00:15 +00:00
/* Increment FirstUnused and get the new index */
idxFirst = InterlockedIncrement ( ( LONG * ) & GdiHandleTable - > FirstUnused ) - 1 ;
/* Check if we have entries left */
if ( idxFirst > = GDI_HANDLE_COUNT )
2008-03-04 00:14:11 +00:00
{
DPRINT1 ( " No more gdi handles left! \n " ) ;
return 0 ;
}
2009-02-16 23:00:15 +00:00
/* Return the old index */
return idxFirst ;
}
/* Get a pointer to the first free entry */
pEntry = GdiHandleTable - > Entries + idxFirst ;
/* Try to lock the entry */
PrevProcId = InterlockedCompareExchange ( ( LONG * ) & pEntry - > ProcessId , 1 , 0 ) ;
if ( PrevProcId ! = 0 )
{
/* The entry was locked or not free, wait and start over */
DelayExecution ( ) ;
continue ;
}
/* Sanity check: is entry really free? */
ASSERT ( ( ( ULONG_PTR ) pEntry - > KernelData & ~ GDI_HANDLE_INDEX_MASK ) = = 0 ) ;
/* Try to exchange the FirstFree value */
idxNext = ( ULONG_PTR ) pEntry - > KernelData ;
idxPrev = InterlockedCompareExchange ( ( LONG * ) & GdiHandleTable - > FirstFree ,
idxNext ,
idxFirst ) ;
/* Unlock the free entry */
( void ) InterlockedExchange ( ( LONG * ) & pEntry - > ProcessId , 0 ) ;
/* If we succeeded, break out of the loop */
if ( idxPrev = = idxFirst )
{
break ;
2008-03-04 00:14:11 +00:00
}
}
2009-02-16 23:00:15 +00:00
return idxFirst ;
2008-01-08 05:04:52 +00:00
}
/* Pushes an entry of the handle table to the free list,
The entry must be unlocked and the base type field must be 0 */
VOID
FASTCALL
2008-02-26 23:09:20 +00:00
InterlockedPushFreeEntry ( ULONG idxToFree )
2008-01-08 05:04:52 +00:00
{
2008-03-04 00:14:11 +00:00
ULONG idxFirstFree , idxPrev ;
PGDI_TABLE_ENTRY pFreeEntry ;
2008-01-08 05:04:52 +00:00
2008-03-04 00:14:11 +00:00
DPRINT ( " Enter InterlockedPushFreeEntry \n " ) ;
2008-01-08 05:04:52 +00:00
2008-03-04 00:14:11 +00:00
pFreeEntry = GdiHandleTable - > Entries + idxToFree ;
ASSERT ( ( pFreeEntry - > Type & GDI_ENTRY_BASETYPE_MASK ) = = 0 ) ;
ASSERT ( pFreeEntry - > ProcessId = = 0 ) ;
pFreeEntry - > UserData = NULL ;
2008-01-08 05:04:52 +00:00
2008-03-04 00:14:11 +00:00
do
{
idxFirstFree = GdiHandleTable - > FirstFree ;
2009-02-16 23:00:15 +00:00
pFreeEntry - > KernelData = ( PVOID ) ( ULONG_PTR ) idxFirstFree ;
2008-01-08 05:04:52 +00:00
2009-02-16 23:00:15 +00:00
idxPrev = InterlockedCompareExchange ( ( LONG * ) & GdiHandleTable - > FirstFree ,
idxToFree ,
idxFirstFree ) ;
2008-03-04 00:14:11 +00:00
}
while ( idxPrev ! = idxFirstFree ) ;
2008-01-08 05:04:52 +00:00
}
2008-02-04 03:17:37 +00:00
BOOL
INTERNAL_CALL
GDIOBJ_ValidateHandle ( HGDIOBJ hObj , ULONG ObjectType )
{
2008-03-04 00:14:11 +00:00
PGDI_TABLE_ENTRY Entry = GDI_HANDLE_GET_ENTRY ( GdiHandleTable , hObj ) ;
if ( ( ( ( ULONG_PTR ) hObj & GDI_HANDLE_TYPE_MASK ) = = ObjectType ) & &
( Entry - > Type < < GDI_ENTRY_UPPER_SHIFT ) = = GDI_HANDLE_GET_UPPER ( hObj ) )
2008-02-04 03:17:37 +00:00
{
2008-03-04 00:14:11 +00:00
HANDLE pid = ( HANDLE ) ( ( ULONG_PTR ) Entry - > ProcessId & ~ 0x1 ) ;
if ( pid = = NULL | | pid = = PsGetCurrentProcessId ( ) )
{
return TRUE ;
}
2008-02-04 03:17:37 +00:00
}
2008-03-04 00:14:11 +00:00
return FALSE ;
2008-02-04 03:17:37 +00:00
}
2008-03-19 00:56:40 +00:00
POBJ INTERNAL_CALL
GDIOBJ_AllocObj ( UCHAR BaseType )
{
POBJ pObject ;
ASSERT ( ( BaseType & ~ GDIObjTypeTotal ) = = 0 ) ;
// BaseType &= GDI_HANDLE_BASETYPE_MASK;
if ( ObjTypeInfo [ BaseType ] . bUseLookaside )
{
PPAGED_LOOKASIDE_LIST LookasideList ;
LookasideList = GdiHandleTable - > LookasideLists + BaseType ;
pObject = ExAllocateFromPagedLookasideList ( LookasideList ) ;
}
else
{
pObject = ExAllocatePoolWithTag ( PagedPool ,
ObjTypeInfo [ BaseType ] . ulBodySize ,
ObjTypeInfo [ BaseType ] . Tag ) ;
}
if ( pObject )
{
RtlZeroMemory ( pObject , ObjTypeInfo [ BaseType ] . ulBodySize ) ;
}
return pObject ;
}
2003-01-18 20:53:57 +00:00
/*!
* Allocate memory for GDI object and return handle to it .
*
2003-08-20 07:45:02 +00:00
* \ param ObjectType - type of object \ ref GDI object types
2003-01-18 20:53:57 +00:00
*
2008-03-19 00:56:40 +00:00
* \ return Pointer to the allocated object , which is locked .
2003-01-18 20:53:57 +00:00
*/
2008-03-19 00:56:40 +00:00
POBJ INTERNAL_CALL
GDIOBJ_AllocObjWithHandle ( ULONG ObjectType )
2002-07-13 21:37:27 +00:00
{
2009-07-26 16:17:50 +00:00
PPROCESSINFO W32Process ;
2008-03-04 00:14:11 +00:00
POBJ newObject = NULL ;
HANDLE CurrentProcessId , LockedProcessId ;
2008-03-19 00:56:40 +00:00
UCHAR TypeIndex ;
2009-07-31 18:21:24 +00:00
UINT Index ;
PGDI_TABLE_ENTRY Entry ;
LONG TypeInfo ;
2008-04-04 23:35:43 +00:00
GDIDBG_INITLOOPTRACE ( ) ;
2004-04-03 20:33:39 +00:00
2008-03-04 00:14:11 +00:00
W32Process = PsGetCurrentProcessWin32Process ( ) ;
/* HACK HACK HACK: simplest-possible quota implementation - don't allow a process
to take too many GDI objects , itself . */
2009-03-30 03:56:53 +00:00
if ( W32Process & & W32Process - > GDIHandleCount > = 0x2710 )
2008-06-16 18:27:18 +00:00
{
DPRINT1 ( " Too many objects for process!!! \n " ) ;
GDIDBG_DUMPHANDLETABLE ( ) ;
2008-03-04 00:14:11 +00:00
return NULL ;
2008-06-16 18:27:18 +00:00
}
2004-12-19 00:03:56 +00:00
2008-03-04 00:14:11 +00:00
ASSERT ( ObjectType ! = GDI_OBJECT_TYPE_DONTCARE ) ;
2003-08-01 16:08:14 +00:00
2008-03-04 00:14:11 +00:00
TypeIndex = GDI_OBJECT_GET_TYPE_INDEX ( ObjectType ) ;
2008-03-19 00:56:40 +00:00
newObject = GDIOBJ_AllocObj ( TypeIndex ) ;
if ( ! newObject )
2008-03-04 00:14:11 +00:00
{
2008-03-19 00:56:40 +00:00
DPRINT1 ( " Not enough memory to allocate gdi object! \n " ) ;
return NULL ;
2008-03-04 00:14:11 +00:00
}
2004-12-12 01:40:39 +00:00
2008-03-19 00:56:40 +00:00
CurrentProcessId = PsGetCurrentProcessId ( ) ;
LockedProcessId = ( HANDLE ) ( ( ULONG_PTR ) CurrentProcessId | 0x1 ) ;
2004-12-12 01:40:39 +00:00
2008-04-04 23:35:43 +00:00
// RtlZeroMemory(newObject, ObjTypeInfo[TypeIndex].ulBodySize);
2004-12-18 21:41:17 +00:00
2008-03-19 00:56:40 +00:00
/* On Windows the higher 16 bit of the type field don't contain the
full type from the handle , but the base type .
( type = BRSUH , PEN , EXTPEN , basetype = BRUSH ) */
TypeInfo = ( ObjectType & GDI_HANDLE_BASETYPE_MASK ) | ( ObjectType > > GDI_ENTRY_UPPER_SHIFT ) ;
Index = InterlockedPopFreeEntry ( ) ;
if ( Index ! = 0 )
{
HANDLE PrevProcId ;
2004-12-18 21:41:17 +00:00
2008-03-19 00:56:40 +00:00
Entry = & GdiHandleTable - > Entries [ Index ] ;
2004-12-13 21:59:28 +00:00
LockHandle :
2009-02-16 23:00:15 +00:00
PrevProcId = InterlockedCompareExchangePointer ( ( PVOID * ) & Entry - > ProcessId , LockedProcessId , 0 ) ;
2008-03-19 00:56:40 +00:00
if ( PrevProcId = = NULL )
{
2009-07-26 16:17:50 +00:00
PTHREADINFO Thread = ( PTHREADINFO ) PsGetCurrentThreadWin32Thread ( ) ;
2008-03-19 00:56:40 +00:00
HGDIOBJ Handle ;
2005-05-08 02:11:54 +00:00
2008-03-19 00:56:40 +00:00
Entry - > KernelData = newObject ;
2005-05-08 02:11:54 +00:00
2008-03-19 00:56:40 +00:00
/* copy the reuse-counter */
TypeInfo | = Entry - > Type & GDI_ENTRY_REUSE_MASK ;
2004-12-12 01:40:39 +00:00
2008-03-19 00:56:40 +00:00
/* we found a free entry, no need to exchange this field atomically
since we ' re holding the lock */
Entry - > Type = TypeInfo ;
2004-12-12 01:40:39 +00:00
2008-03-19 00:56:40 +00:00
/* Create a handle */
Handle = ( HGDIOBJ ) ( ( Index & 0xFFFF ) | ( TypeInfo < < GDI_ENTRY_UPPER_SHIFT ) ) ;
2004-12-12 01:40:39 +00:00
2008-03-19 00:56:40 +00:00
/* Initialize BaseObject fields */
newObject - > hHmgr = Handle ;
newObject - > ulShareCount = 0 ;
newObject - > cExclusiveLock = 1 ;
newObject - > Tid = Thread ;
2004-12-18 21:41:17 +00:00
2008-03-19 00:56:40 +00:00
/* unlock the entry */
2009-02-16 23:00:15 +00:00
( void ) InterlockedExchangePointer ( ( PVOID * ) & Entry - > ProcessId , CurrentProcessId ) ;
2007-09-06 02:54:17 +00:00
2008-03-19 00:56:40 +00:00
GDIDBG_CAPTUREALLOCATOR ( Index ) ;
if ( W32Process ! = NULL )
2008-03-04 00:14:11 +00:00
{
2009-03-30 03:56:53 +00:00
InterlockedIncrement ( & W32Process - > GDIHandleCount ) ;
2008-03-04 00:14:11 +00:00
}
2004-12-12 01:40:39 +00:00
2008-03-19 00:56:40 +00:00
DPRINT ( " GDIOBJ_AllocObj: 0x%x ob: 0x%x \n " , Handle , newObject ) ;
return newObject ;
2008-03-04 00:14:11 +00:00
}
else
{
2008-04-04 23:35:43 +00:00
GDIDBG_TRACELOOP ( Index , PrevProcId , CurrentProcessId ) ;
2008-03-19 00:56:40 +00:00
/* damn, someone is trying to lock the object even though it doesn't
even exist anymore , wait a little and try again !
FIXME - we shouldn ' t loop forever ! Give up after some time ! */
DelayExecution ( ) ;
/* try again */
goto LockHandle ;
2008-03-04 00:14:11 +00:00
}
2008-03-19 00:56:40 +00:00
}
GDIOBJ_FreeObj ( newObject , TypeIndex ) ;
DPRINT1 ( " Failed to insert gdi object into the handle table, no handles left! \n " ) ;
GDIDBG_DUMPHANDLETABLE ( ) ;
return NULL ;
}
VOID INTERNAL_CALL
GDIOBJ_FreeObj ( POBJ pObject , UCHAR BaseType )
{
/* Object must not have a handle! */
ASSERT ( pObject - > hHmgr = = NULL ) ;
if ( ObjTypeInfo [ BaseType ] . bUseLookaside )
{
PPAGED_LOOKASIDE_LIST LookasideList ;
LookasideList = GdiHandleTable - > LookasideLists + BaseType ;
ExFreeToPagedLookasideList ( LookasideList , pObject ) ;
2004-12-12 01:40:39 +00:00
}
else
{
2008-03-19 00:56:40 +00:00
ExFreePool ( pObject ) ;
2004-12-12 01:40:39 +00:00
}
2002-07-13 21:37:27 +00:00
}
2003-01-18 20:53:57 +00:00
/*!
* Free memory allocated for the GDI object . For each object type this function calls the
* appropriate cleanup routine .
*
2003-08-20 07:45:02 +00:00
* \ param hObj - handle of the object to be deleted .
2003-01-18 20:53:57 +00:00
*
* \ return Returns TRUE if succesful .
2004-12-12 01:40:39 +00:00
* \ return Returns FALSE if the cleanup routine returned FALSE or the object doesn ' t belong
* to the calling process .
2009-03-23 23:49:00 +00:00
*
* \ bug This function should return VOID and kill the object no matter what . . .
2003-01-18 20:53:57 +00:00
*/
2004-12-12 01:40:39 +00:00
BOOL INTERNAL_CALL
2008-03-19 00:56:40 +00:00
GDIOBJ_FreeObjByHandle ( HGDIOBJ hObj , DWORD ExpectedType )
2002-07-13 21:37:27 +00:00
{
2008-03-04 00:14:11 +00:00
PGDI_TABLE_ENTRY Entry ;
HANDLE ProcessId , LockedProcessId , PrevProcId ;
ULONG HandleType , HandleUpper , TypeIndex ;
BOOL Silent ;
2008-04-04 23:35:43 +00:00
GDIDBG_INITLOOPTRACE ( ) ;
2003-01-18 20:53:57 +00:00
2008-03-04 00:14:11 +00:00
DPRINT ( " GDIOBJ_FreeObj: hObj: 0x%08x \n " , hObj ) ;
2004-12-18 21:41:17 +00:00
2008-03-04 00:14:11 +00:00
if ( GDI_HANDLE_IS_STOCKOBJ ( hObj ) )
{
DPRINT1 ( " GDIOBJ_FreeObj() failed, can't delete stock object handle: 0x%x !!! \n " , hObj ) ;
GDIDBG_TRACECALLER ( ) ;
return FALSE ;
}
2003-08-21 20:16:55 +00:00
2008-03-04 00:14:11 +00:00
ProcessId = PsGetCurrentProcessId ( ) ;
LockedProcessId = ( HANDLE ) ( ( ULONG_PTR ) ProcessId | 0x1 ) ;
2003-01-18 20:53:57 +00:00
2008-03-04 00:14:11 +00:00
Silent = ( ExpectedType & GDI_OBJECT_TYPE_SILENT ) ;
ExpectedType & = ~ GDI_OBJECT_TYPE_SILENT ;
2007-07-25 22:39:14 +00:00
2008-03-04 00:14:11 +00:00
HandleType = GDI_HANDLE_GET_TYPE ( hObj ) ;
HandleUpper = GDI_HANDLE_GET_UPPER ( hObj ) ;
2005-01-30 12:56:12 +00:00
2008-03-04 00:14:11 +00:00
/* Check if we have the requested type */
if ( ( ExpectedType ! = GDI_OBJECT_TYPE_DONTCARE & &
HandleType ! = ExpectedType ) | |
HandleType = = 0 )
{
DPRINT1 ( " Attempted to free object 0x%x of wrong type (Handle: 0x%x, expected: 0x%x) \n " ,
hObj , HandleType , ExpectedType ) ;
GDIDBG_TRACECALLER ( ) ;
return FALSE ;
}
2004-12-12 01:40:39 +00:00
2008-03-04 00:14:11 +00:00
Entry = GDI_HANDLE_GET_ENTRY ( GdiHandleTable , hObj ) ;
2002-07-13 21:37:27 +00:00
2004-12-12 01:40:39 +00:00
LockHandle :
2008-03-04 00:14:11 +00:00
/* lock the object, we must not delete global objects, so don't exchange the locking
process ID to zero when attempting to lock a global object . . . */
2009-02-16 23:00:15 +00:00
PrevProcId = InterlockedCompareExchangePointer ( ( PVOID * ) & Entry - > ProcessId , LockedProcessId , ProcessId ) ;
2008-03-04 00:14:11 +00:00
if ( PrevProcId = = ProcessId )
2003-08-20 07:45:02 +00:00
{
2008-03-04 00:14:11 +00:00
if ( ( Entry - > KernelData ! = NULL ) & &
( ( Entry - > Type < < GDI_ENTRY_UPPER_SHIFT ) = = HandleUpper ) & &
( ( Entry - > Type & GDI_ENTRY_BASETYPE_MASK ) = = ( HandleUpper & GDI_ENTRY_BASETYPE_MASK ) ) )
2004-12-13 12:51:51 +00:00
{
2008-03-04 00:14:11 +00:00
POBJ Object ;
2004-12-12 01:40:39 +00:00
2008-03-04 00:14:11 +00:00
Object = Entry - > KernelData ;
2004-12-12 01:40:39 +00:00
2009-03-23 23:49:00 +00:00
if ( ( Object - > cExclusiveLock = = 0 | |
2009-07-26 16:17:50 +00:00
Object - > Tid = = ( PTHREADINFO ) PsGetCurrentThreadWin32Thread ( ) ) & &
2009-03-23 23:49:00 +00:00
Object - > ulShareCount = = 0 )
2008-03-04 00:14:11 +00:00
{
BOOL Ret ;
2009-07-26 16:17:50 +00:00
PPROCESSINFO W32Process = PsGetCurrentProcessWin32Process ( ) ;
2008-03-04 00:14:11 +00:00
/* Clear the basetype field so when unlocking the handle it gets finally deleted and increment reuse counter */
Entry - > Type = ( Entry - > Type + GDI_ENTRY_REUSE_INC ) & ~ GDI_ENTRY_BASETYPE_MASK ;
/* unlock the handle slot */
2009-02-16 23:00:15 +00:00
( void ) InterlockedExchangePointer ( ( PVOID * ) & Entry - > ProcessId , NULL ) ;
2008-03-04 00:14:11 +00:00
/* push this entry to the free list */
InterlockedPushFreeEntry ( GDI_ENTRY_TO_INDEX ( GdiHandleTable , Entry ) ) ;
2008-03-19 00:56:40 +00:00
Object - > hHmgr = NULL ;
2008-03-04 00:14:11 +00:00
if ( W32Process ! = NULL )
{
2009-03-30 03:56:53 +00:00
InterlockedDecrement ( & W32Process - > GDIHandleCount ) ;
2008-03-04 00:14:11 +00:00
}
/* call the cleanup routine. */
TypeIndex = GDI_OBJECT_GET_TYPE_INDEX ( HandleType ) ;
2008-04-04 23:35:43 +00:00
Ret = ObjTypeInfo [ TypeIndex ] . CleanupProc ( Object ) ;
2008-03-04 00:14:11 +00:00
/* Now it's time to free the memory */
2008-03-19 00:56:40 +00:00
GDIOBJ_FreeObj ( Object , TypeIndex ) ;
2008-03-04 00:14:11 +00:00
2008-06-03 23:56:09 +00:00
GDIDBG_CAPTUREDELETER ( hObj ) ;
2008-03-04 00:14:11 +00:00
return Ret ;
}
2009-03-23 23:49:00 +00:00
else if ( Object - > ulShareCount ! = 0 )
{
2009-04-01 17:32:06 +00:00
Object - > BaseFlags | = BASEFLAG_READY_TO_DIE ;
2009-03-23 23:49:00 +00:00
DPRINT ( " Object %p, ulShareCount = %d \n " , Object - > hHmgr , Object - > ulShareCount ) ;
2009-07-09 13:36:42 +00:00
//GDIDBG_TRACECALLER();
//GDIDBG_TRACESHARELOCKER(GDI_HANDLE_GET_INDEX(hObj));
2009-03-23 23:49:00 +00:00
( void ) InterlockedExchangePointer ( ( PVOID * ) & Entry - > ProcessId , PrevProcId ) ;
/* Don't wait on shared locks */
return FALSE ;
}
2008-03-04 00:14:11 +00:00
else
{
/*
2009-03-17 00:30:15 +00:00
* The object is currently locked by another thread , so freeing is forbidden !
2008-03-04 00:14:11 +00:00
*/
DPRINT1 ( " Object->cExclusiveLock = %d \n " , Object - > cExclusiveLock ) ;
2008-03-20 14:30:42 +00:00
GDIDBG_TRACECALLER ( ) ;
2008-03-04 00:14:11 +00:00
GDIDBG_TRACELOCKER ( GDI_HANDLE_GET_INDEX ( hObj ) ) ;
2009-02-16 23:00:15 +00:00
( void ) InterlockedExchangePointer ( ( PVOID * ) & Entry - > ProcessId , PrevProcId ) ;
2008-04-19 22:11:39 +00:00
/* do not assert here for it will call again from dxg.sys it being call twice */
2009-03-17 00:30:15 +00:00
DelayExecution ( ) ;
goto LockHandle ;
2008-03-04 00:14:11 +00:00
}
2007-09-06 02:54:17 +00:00
}
else
2004-12-12 01:40:39 +00:00
{
2008-03-04 00:14:11 +00:00
LockErrorDebugOutput ( hObj , Entry , " GDIOBJ_FreeObj " ) ;
2009-02-16 23:00:15 +00:00
( void ) InterlockedExchangePointer ( ( PVOID * ) & Entry - > ProcessId , PrevProcId ) ;
2004-12-12 01:40:39 +00:00
}
}
2008-03-04 00:14:11 +00:00
else if ( PrevProcId = = LockedProcessId )
2003-08-20 07:45:02 +00:00
{
2008-04-04 23:35:43 +00:00
GDIDBG_TRACELOOP ( hObj , PrevProcId , ProcessId ) ;
2008-03-04 00:14:11 +00:00
/* the object is currently locked, wait some time and try again.
FIXME - we shouldn ' t loop forever ! Give up after some time ! */
DelayExecution ( ) ;
/* try again */
goto LockHandle ;
}
else
2004-12-12 01:40:39 +00:00
{
2008-03-04 00:14:11 +00:00
if ( ! Silent )
{
2008-05-20 20:39:32 +00:00
if ( ( Entry - > Type & GDI_ENTRY_BASETYPE_MASK ) = = 0 )
{
DPRINT1 ( " Attempted to free gdi handle 0x%x that is already deleted! \n " , hObj ) ;
}
else if ( ( ( ULONG_PTR ) PrevProcId & ~ 0x1 ) = = 0 )
2008-03-04 00:14:11 +00:00
{
DPRINT1 ( " Attempted to free global gdi handle 0x%x, caller needs to get ownership first!!! \n " , hObj ) ;
}
else
{
DPRINT1 ( " Attempted to free foreign handle: 0x%x Owner: 0x%x from Caller: 0x%x \n " , hObj , ( ULONG_PTR ) PrevProcId & ~ 0x1 , ( ULONG_PTR ) ProcessId & ~ 0x1 ) ;
}
2008-05-20 20:39:32 +00:00
DPRINT1 ( " Type = 0x%lx, KernelData = 0x%p, ProcessId = 0x%p \n " , Entry - > Type , Entry - > KernelData , Entry - > ProcessId ) ;
2008-03-04 00:14:11 +00:00
GDIDBG_TRACECALLER ( ) ;
GDIDBG_TRACEALLOCATOR ( GDI_HANDLE_GET_INDEX ( hObj ) ) ;
}
2005-01-30 12:56:12 +00:00
}
2002-07-13 21:37:27 +00:00
2008-03-04 00:14:11 +00:00
return FALSE ;
2002-07-13 21:37:27 +00:00
}
2007-11-20 05:25:13 +00:00
BOOL
FASTCALL
IsObjectDead ( HGDIOBJ hObject )
{
2008-03-04 00:14:11 +00:00
INT Index = GDI_HANDLE_GET_INDEX ( hObject ) ;
PGDI_TABLE_ENTRY Entry = & GdiHandleTable - > Entries [ Index ] ;
// We check to see if the objects are knocking on deaths door.
2008-06-03 23:56:09 +00:00
if ( ( Entry - > Type & GDI_ENTRY_BASETYPE_MASK ) ! = 0 )
2008-03-04 00:14:11 +00:00
return FALSE ;
else
{
DPRINT1 ( " Object 0x%x currently being destroyed!!! \n " , hObject ) ;
return TRUE ; // return true and move on.
}
2007-11-20 05:25:13 +00:00
}
2009-12-30 22:56:09 +00:00
BOOL
FASTCALL
bPEBCacheHandle ( HGDIOBJ Handle , int oType , PVOID pAttr )
{
PGDIHANDLECACHE GdiHandleCache ;
HGDIOBJ * hPtr ;
BOOL Ret = FALSE ;
int Offset = 0 , Number ;
HANDLE Lock ;
GdiHandleCache = ( PGDIHANDLECACHE ) NtCurrentTeb ( ) - > ProcessEnvironmentBlock - > GdiHandleBuffer ;
switch ( oType )
{
case hctBrushHandle :
Offset = 0 ;
break ;
case hctPenHandle :
Offset = CACHE_BRUSH_ENTRIES ;
break ;
case hctRegionHandle :
Offset = CACHE_BRUSH_ENTRIES + CACHE_PEN_ENTRIES ;
break ;
default :
return FALSE ;
}
Lock = InterlockedCompareExchangePointer ( ( PVOID * ) & GdiHandleCache - > ulLock ,
NtCurrentTeb ( ) ,
NULL ) ;
if ( Lock ) return FALSE ;
_SEH2_TRY
{
Number = GdiHandleCache - > ulNumHandles [ oType ] ;
hPtr = GdiHandleCache - > Handle + Offset ;
if ( oType = = hctRegionHandle )
{
if ( Number < CACHE_REGION_ENTRIES )
{
( ( PRGN_ATTR ) pAttr ) - > AttrFlags | = ATTR_CACHED ;
hPtr [ Number ] = Handle ;
GdiHandleCache - > ulNumHandles [ oType ] + + ;
DPRINT ( " Put Handle Count %d \n " , GdiHandleCache - > ulNumHandles [ oType ] ) ;
Ret = TRUE ;
}
}
}
_SEH2_EXCEPT ( EXCEPTION_EXECUTE_HANDLER )
{
Ret = FALSE ;
}
_SEH2_END ;
( void ) InterlockedExchangePointer ( ( PVOID * ) & GdiHandleCache - > ulLock , Lock ) ;
return Ret ;
}
2003-01-18 20:53:57 +00:00
/*!
* Delete GDI object
* \ param hObject object handle
2003-08-21 20:16:55 +00:00
* \ return if the function fails the returned value is FALSE .
2003-01-18 20:53:57 +00:00
*/
2007-11-04 22:10:59 +00:00
BOOL
FASTCALL
2009-03-25 20:24:34 +00:00
GreDeleteObject ( HGDIOBJ hObject )
2002-07-13 21:37:27 +00:00
{
2009-12-29 21:07:04 +00:00
INT Index ;
PGDI_TABLE_ENTRY Entry ;
DWORD dwObjectType ;
PVOID pAttr = NULL ;
2008-03-04 00:14:11 +00:00
DPRINT ( " NtGdiDeleteObject handle 0x%08x \n " , hObject ) ;
if ( ! IsObjectDead ( hObject ) )
{
2009-12-29 21:07:04 +00:00
dwObjectType = GDIOBJ_GetObjectType ( hObject ) ;
Index = GDI_HANDLE_GET_INDEX ( hObject ) ;
Entry = & GdiHandleTable - > Entries [ Index ] ;
pAttr = Entry - > UserData ;
2009-12-30 22:56:09 +00:00
2009-12-29 21:07:04 +00:00
switch ( dwObjectType )
{
2009-12-30 22:56:09 +00:00
case GDI_OBJECT_TYPE_BRUSH :
break ;
2009-12-29 21:07:04 +00:00
case GDI_OBJECT_TYPE_REGION :
2009-12-30 22:56:09 +00:00
if ( bPEBCacheHandle ( hObject , hctRegionHandle , pAttr ) )
{
return TRUE ;
}
2010-01-03 00:05:15 +00:00
if ( pAttr )
{
KeEnterCriticalRegion ( ) ;
2010-01-21 00:14:06 +00:00
if ( pAttr ) FreeObjectAttr ( pAttr ) ;
Entry - > UserData = NULL ;
2010-01-03 00:05:15 +00:00
KeLeaveCriticalRegion ( ) ;
}
2009-12-29 21:07:04 +00:00
break ;
case GDI_OBJECT_TYPE_DC :
DC_FreeDcAttr ( hObject ) ;
break ;
}
return NULL ! = hObject
? GDIOBJ_FreeObjByHandle ( hObject , dwObjectType ) : FALSE ;
2008-03-04 00:14:11 +00:00
}
else
{
DPRINT1 ( " Attempt DeleteObject 0x%x currently being destroyed!!! \n " , hObject ) ;
return TRUE ; // return true and move on.
}
2002-10-01 06:41:57 +00:00
}
2008-05-20 20:39:32 +00:00
VOID
FASTCALL
IntDeleteHandlesForProcess ( struct _EPROCESS * Process , ULONG ObjectType )
2002-10-01 06:41:57 +00:00
{
2008-03-04 00:14:11 +00:00
PGDI_TABLE_ENTRY Entry , End ;
ULONG Index = RESERVE_ENTRIES_COUNT ;
2008-05-20 20:39:32 +00:00
HANDLE ProcId ;
2009-07-26 16:17:50 +00:00
PPROCESSINFO W32Process ;
2008-03-04 00:14:11 +00:00
2009-07-26 16:17:50 +00:00
W32Process = ( PPROCESSINFO ) Process - > Win32Process ;
2008-03-04 00:14:11 +00:00
ASSERT ( W32Process ) ;
2009-03-30 03:56:53 +00:00
if ( W32Process - > GDIHandleCount > 0 )
2003-08-18 10:18:14 +00:00
{
2008-05-20 20:39:32 +00:00
ProcId = Process - > UniqueProcessId ;
/* FIXME - Instead of building the handle here and delete it using GDIOBJ_FreeObj
we should delete it directly here ! */
2008-03-04 00:14:11 +00:00
End = & GdiHandleTable - > Entries [ GDI_HANDLE_COUNT ] ;
for ( Entry = & GdiHandleTable - > Entries [ RESERVE_ENTRIES_COUNT ] ;
2008-05-20 20:39:32 +00:00
Entry ! = End ;
Entry + + , Index + + )
2003-12-14 19:39:50 +00:00
{
2008-03-04 00:14:11 +00:00
/* ignore the lock bit */
2008-05-20 20:39:32 +00:00
if ( ( HANDLE ) ( ( ULONG_PTR ) Entry - > ProcessId & ~ 0x1 ) = = ProcId )
2008-03-04 00:14:11 +00:00
{
2008-05-20 20:39:32 +00:00
if ( ( Entry - > Type & GDI_ENTRY_BASETYPE_MASK ) = = ObjectType | |
ObjectType = = GDI_OBJECT_TYPE_DONTCARE )
{
HGDIOBJ ObjectHandle ;
2008-03-04 00:14:11 +00:00
2008-05-20 20:39:32 +00:00
/* Create the object handle for the entry, the lower(!) 16 bit of the
Type field includes the type of the object including the stock
object flag - but since stock objects don ' t have a process id we can
simply ignore this fact here . */
ObjectHandle = ( HGDIOBJ ) ( Index | ( Entry - > Type < < GDI_ENTRY_UPPER_SHIFT ) ) ;
2008-03-04 00:14:11 +00:00
2009-03-23 23:49:00 +00:00
if ( ! GDIOBJ_FreeObjByHandle ( ObjectHandle , GDI_OBJECT_TYPE_DONTCARE ) )
{
DPRINT1 ( " Failed to delete object %p! \n " , ObjectHandle ) ;
}
2009-03-30 03:56:53 +00:00
if ( W32Process - > GDIHandleCount = = 0 )
2008-05-20 20:39:32 +00:00
{
/* there are no more gdi handles for this process, bail */
break ;
}
2008-03-04 00:14:11 +00:00
}
}
2004-07-04 01:23:32 +00:00
}
}
2008-05-20 20:39:32 +00:00
}
/*!
* Internal function . Called when the process is destroyed to free the remaining GDI handles .
* \ param Process - PID of the process that will be destroyed .
*/
BOOL INTERNAL_CALL
GDI_CleanupForProcess ( struct _EPROCESS * Process )
{
PEPROCESS CurrentProcess ;
2009-07-26 16:17:50 +00:00
PPROCESSINFO W32Process ;
2008-05-20 20:39:32 +00:00
DPRINT ( " Starting CleanupForProcess prochandle %x Pid %d \n " , Process , Process - > UniqueProcessId ) ;
CurrentProcess = PsGetCurrentProcess ( ) ;
if ( CurrentProcess ! = Process )
{
KeAttachProcess ( & Process - > Pcb ) ;
}
2009-07-26 16:17:50 +00:00
W32Process = ( PPROCESSINFO ) CurrentProcess - > Win32Process ;
2009-03-23 23:49:00 +00:00
2008-05-20 20:39:32 +00:00
/* Delete objects. Begin with types that are not referenced by other types */
IntDeleteHandlesForProcess ( Process , GDILoObjType_LO_DC_TYPE ) ;
IntDeleteHandlesForProcess ( Process , GDILoObjType_LO_BRUSH_TYPE ) ;
IntDeleteHandlesForProcess ( Process , GDILoObjType_LO_BITMAP_TYPE ) ;
/* Finally finish with what's left */
IntDeleteHandlesForProcess ( Process , GDI_OBJECT_TYPE_DONTCARE ) ;
2003-12-14 19:39:50 +00:00
2008-03-04 00:14:11 +00:00
if ( CurrentProcess ! = Process )
2003-08-04 00:24:07 +00:00
{
2008-03-04 00:14:11 +00:00
KeDetachProcess ( ) ;
2003-08-04 00:24:07 +00:00
}
2003-08-20 07:45:02 +00:00
2008-05-20 20:39:32 +00:00
# ifdef GDI_DEBUG
GdiDbgHTIntegrityCheck ( ) ;
# endif
2008-03-04 00:14:11 +00:00
DPRINT ( " Completed cleanup for process %d \n " , Process - > UniqueProcessId ) ;
2009-03-30 03:56:53 +00:00
if ( W32Process - > GDIHandleCount > 0 )
2009-03-23 23:49:00 +00:00
{
2009-03-30 03:56:53 +00:00
DPRINT1 ( " Leaking %d handles! \n " , W32Process - > GDIHandleCount ) ;
2009-03-23 23:49:00 +00:00
}
2003-08-21 20:16:55 +00:00
2008-03-04 00:14:11 +00:00
return TRUE ;
2003-08-01 20:54:03 +00:00
}
/*!
* Return pointer to the object by handle .
*
* \ param hObj Object handle
2003-08-20 07:45:02 +00:00
* \ return Pointer to the object .
2003-08-01 20:54:03 +00:00
*
* \ note Process can only get pointer to the objects it created or global objects .
*
2007-07-25 22:39:14 +00:00
* \ todo Get rid of the ExpectedType parameter !
2003-08-01 20:54:03 +00:00
*/
2004-12-12 01:40:39 +00:00
PGDIOBJ INTERNAL_CALL
2008-03-04 00:14:11 +00:00
GDIOBJ_LockObj ( HGDIOBJ hObj , DWORD ExpectedType )
2003-08-01 20:54:03 +00:00
{
2008-03-04 00:14:11 +00:00
ULONG HandleIndex ;
PGDI_TABLE_ENTRY Entry ;
HANDLE ProcessId , HandleProcessId , LockedProcessId , PrevProcId ;
POBJ Object = NULL ;
ULONG HandleType , HandleUpper ;
2005-06-07 16:34:07 +00:00
2008-03-04 00:14:11 +00:00
HandleIndex = GDI_HANDLE_GET_INDEX ( hObj ) ;
HandleType = GDI_HANDLE_GET_TYPE ( hObj ) ;
HandleUpper = GDI_HANDLE_GET_UPPER ( hObj ) ;
/* Check that the handle index is valid. */
if ( HandleIndex > = GDI_HANDLE_COUNT )
return NULL ;
Entry = & GdiHandleTable - > Entries [ HandleIndex ] ;
/* Check if we have the requested type */
if ( ( ExpectedType ! = GDI_OBJECT_TYPE_DONTCARE & &
HandleType ! = ExpectedType ) | |
HandleType = = 0 )
{
2009-10-29 18:28:27 +00:00
DPRINT ( " Attempted to lock object 0x%x of wrong type (Handle: 0x%x, requested: 0x%x) \n " ,
2008-03-04 00:14:11 +00:00
hObj , HandleType , ExpectedType ) ;
GDIDBG_TRACECALLER ( ) ;
2008-06-03 23:56:09 +00:00
GDIDBG_TRACEALLOCATOR ( hObj ) ;
GDIDBG_TRACEDELETER ( hObj ) ;
2008-03-04 00:14:11 +00:00
return NULL ;
}
ProcessId = ( HANDLE ) ( ( ULONG_PTR ) PsGetCurrentProcessId ( ) & ~ 1 ) ;
HandleProcessId = ( HANDLE ) ( ( ULONG_PTR ) Entry - > ProcessId & ~ 1 ) ;
/* Check for invalid owner. */
if ( ProcessId ! = HandleProcessId & & HandleProcessId ! = NULL )
{
DPRINT1 ( " Tried to lock object (0x%p) of wrong owner! ProcessId = %p, HandleProcessId = %p \n " , hObj , ProcessId , HandleProcessId ) ;
GDIDBG_TRACECALLER ( ) ;
GDIDBG_TRACEALLOCATOR ( GDI_HANDLE_GET_INDEX ( hObj ) ) ;
return NULL ;
}
/*
* Prevent the thread from being terminated during the locking process .
* It would result in undesired effects and inconsistency of the global
* handle table .
*/
KeEnterCriticalRegion ( ) ;
/*
* Loop until we either successfully lock the handle entry & object or
* fail some of the check .
*/
for ( ; ; )
{
/* Lock the handle table entry. */
LockedProcessId = ( HANDLE ) ( ( ULONG_PTR ) HandleProcessId | 0x1 ) ;
2009-02-16 23:00:15 +00:00
PrevProcId = InterlockedCompareExchangePointer ( ( PVOID * ) & Entry - > ProcessId ,
2008-03-04 00:14:11 +00:00
LockedProcessId ,
HandleProcessId ) ;
if ( PrevProcId = = HandleProcessId )
{
/*
* We ' re locking an object that belongs to our process or it ' s a
* global object if HandleProcessId is 0 here .
*/
if ( ( Entry - > KernelData ! = NULL ) & &
( ( Entry - > Type < < GDI_ENTRY_UPPER_SHIFT ) = = HandleUpper ) )
2005-06-07 16:34:07 +00:00
{
2009-07-26 16:17:50 +00:00
PTHREADINFO Thread = ( PTHREADINFO ) PsGetCurrentThreadWin32Thread ( ) ;
2008-03-04 00:14:11 +00:00
Object = Entry - > KernelData ;
if ( Object - > cExclusiveLock = = 0 )
{
Object - > Tid = Thread ;
Object - > cExclusiveLock = 1 ;
GDIDBG_CAPTURELOCKER ( GDI_HANDLE_GET_INDEX ( hObj ) )
}
else
{
if ( Object - > Tid ! = Thread )
{
/* Unlock the handle table entry. */
2009-02-16 23:00:15 +00:00
( void ) InterlockedExchangePointer ( ( PVOID * ) & Entry - > ProcessId , PrevProcId ) ;
2008-03-04 00:14:11 +00:00
DelayExecution ( ) ;
continue ;
}
2009-02-16 23:00:15 +00:00
InterlockedIncrement ( ( PLONG ) & Object - > cExclusiveLock ) ;
2008-03-04 00:14:11 +00:00
}
2005-06-07 16:34:07 +00:00
}
else
{
2008-03-04 00:14:11 +00:00
/*
* Debugging code . Report attempts to lock deleted handles and
* locking type mismatches .
*/
LockErrorDebugOutput ( hObj , Entry , " GDIOBJ_LockObj " ) ;
2005-06-07 16:34:07 +00:00
}
2004-12-12 01:40:39 +00:00
2008-03-04 00:14:11 +00:00
/* Unlock the handle table entry. */
2009-02-16 23:00:15 +00:00
( void ) InterlockedExchangePointer ( ( PVOID * ) & Entry - > ProcessId , PrevProcId ) ;
2004-12-18 21:41:17 +00:00
2008-03-04 00:14:11 +00:00
break ;
}
else
{
/*
* The handle is currently locked , wait some time and try again .
*/
2005-06-07 16:34:07 +00:00
2008-03-04 00:14:11 +00:00
DelayExecution ( ) ;
continue ;
}
}
2004-12-12 01:40:39 +00:00
2008-03-04 00:14:11 +00:00
KeLeaveCriticalRegion ( ) ;
2003-08-01 20:54:03 +00:00
2008-03-04 00:14:11 +00:00
return Object ;
2003-08-01 20:54:03 +00:00
}
2004-12-12 01:40:39 +00:00
2003-08-01 20:54:03 +00:00
/*!
2005-06-07 16:34:07 +00:00
* Return pointer to the object by handle ( and allow sharing of the handle
* across threads ) .
*
2003-08-01 20:54:03 +00:00
* \ param hObj Object handle
2005-06-07 16:34:07 +00:00
* \ return Pointer to the object .
2003-08-01 20:54:03 +00:00
*
2005-06-07 16:34:07 +00:00
* \ note Process can only get pointer to the objects it created or global objects .
*
2007-07-25 22:39:14 +00:00
* \ todo Get rid of the ExpectedType parameter !
2003-08-01 20:54:03 +00:00
*/
2005-06-07 16:34:07 +00:00
PGDIOBJ INTERNAL_CALL
2008-03-04 00:14:11 +00:00
GDIOBJ_ShareLockObj ( HGDIOBJ hObj , DWORD ExpectedType )
2003-08-01 20:54:03 +00:00
{
2008-03-04 00:14:11 +00:00
ULONG HandleIndex ;
PGDI_TABLE_ENTRY Entry ;
HANDLE ProcessId , HandleProcessId , LockedProcessId , PrevProcId ;
POBJ Object = NULL ;
ULONG_PTR HandleType , HandleUpper ;
2005-06-07 16:34:07 +00:00
2008-03-04 00:14:11 +00:00
HandleIndex = GDI_HANDLE_GET_INDEX ( hObj ) ;
HandleType = GDI_HANDLE_GET_TYPE ( hObj ) ;
HandleUpper = GDI_HANDLE_GET_UPPER ( hObj ) ;
/* Check that the handle index is valid. */
if ( HandleIndex > = GDI_HANDLE_COUNT )
return NULL ;
/* Check if we have the requested type */
if ( ( ExpectedType ! = GDI_OBJECT_TYPE_DONTCARE & &
HandleType ! = ExpectedType ) | |
HandleType = = 0 )
{
DPRINT1 ( " Attempted to lock object 0x%x of wrong type (Handle: 0x%x, requested: 0x%x) \n " ,
hObj , HandleType , ExpectedType ) ;
return NULL ;
}
Entry = & GdiHandleTable - > Entries [ HandleIndex ] ;
ProcessId = ( HANDLE ) ( ( ULONG_PTR ) PsGetCurrentProcessId ( ) & ~ 1 ) ;
HandleProcessId = ( HANDLE ) ( ( ULONG_PTR ) Entry - > ProcessId & ~ 1 ) ;
/* Check for invalid owner. */
if ( ProcessId ! = HandleProcessId & & HandleProcessId ! = NULL )
{
return NULL ;
}
/*
* Prevent the thread from being terminated during the locking process .
* It would result in undesired effects and inconsistency of the global
* handle table .
*/
KeEnterCriticalRegion ( ) ;
/*
* Loop until we either successfully lock the handle entry & object or
* fail some of the check .
*/
for ( ; ; )
{
/* Lock the handle table entry. */
LockedProcessId = ( HANDLE ) ( ( ULONG_PTR ) HandleProcessId | 0x1 ) ;
2009-02-16 23:00:15 +00:00
PrevProcId = InterlockedCompareExchangePointer ( ( PVOID * ) & Entry - > ProcessId ,
2008-03-04 00:14:11 +00:00
LockedProcessId ,
HandleProcessId ) ;
if ( PrevProcId = = HandleProcessId )
{
/*
* We ' re locking an object that belongs to our process or it ' s a
* global object if HandleProcessId is 0 here .
*/
if ( ( Entry - > KernelData ! = NULL ) & &
( HandleUpper = = ( Entry - > Type < < GDI_ENTRY_UPPER_SHIFT ) ) )
2005-06-07 20:02:22 +00:00
{
2008-03-04 00:14:11 +00:00
Object = ( POBJ ) Entry - > KernelData ;
2009-03-23 23:49:00 +00:00
GDIDBG_CAPTURESHARELOCKER ( HandleIndex ) ;
# ifdef GDI_DEBUG3
2009-02-16 23:00:15 +00:00
if ( InterlockedIncrement ( ( PLONG ) & Object - > ulShareCount ) = = 1 )
2008-03-04 00:14:11 +00:00
{
memset ( GDIHandleLocker [ HandleIndex ] , 0x00 , GDI_STACK_LEVELS * sizeof ( ULONG ) ) ;
2009-03-23 23:49:00 +00:00
RtlCaptureStackBackTrace ( 1 , GDI_STACK_LEVELS , ( PVOID * ) GDIHandleShareLocker [ HandleIndex ] , NULL ) ;
2008-03-04 00:14:11 +00:00
}
2005-06-07 20:02:22 +00:00
# else
2009-02-16 23:00:15 +00:00
InterlockedIncrement ( ( PLONG ) & Object - > ulShareCount ) ;
2005-06-07 20:02:22 +00:00
# endif
2008-03-04 00:14:11 +00:00
}
else
{
/*
* Debugging code . Report attempts to lock deleted handles and
* locking type mismatches .
*/
LockErrorDebugOutput ( hObj , Entry , " GDIOBJ_ShareLockObj " ) ;
}
2004-12-18 21:41:17 +00:00
2008-03-04 00:14:11 +00:00
/* Unlock the handle table entry. */
2009-02-16 23:00:15 +00:00
( void ) InterlockedExchangePointer ( ( PVOID * ) & Entry - > ProcessId , PrevProcId ) ;
2004-12-18 21:41:17 +00:00
2008-03-04 00:14:11 +00:00
break ;
}
else
{
/*
* The handle is currently locked , wait some time and try again .
*/
2004-12-18 21:41:17 +00:00
2008-03-04 00:14:11 +00:00
DelayExecution ( ) ;
continue ;
}
}
2004-12-18 21:41:17 +00:00
2008-03-04 00:14:11 +00:00
KeLeaveCriticalRegion ( ) ;
2004-12-18 21:41:17 +00:00
2008-03-04 00:14:11 +00:00
return Object ;
2005-06-07 16:34:07 +00:00
}
2004-12-12 01:40:39 +00:00
BOOL INTERNAL_CALL
2008-02-26 23:09:20 +00:00
GDIOBJ_OwnedByCurrentProcess ( HGDIOBJ ObjectHandle )
2003-10-20 17:57:05 +00:00
{
2008-03-04 00:14:11 +00:00
PGDI_TABLE_ENTRY Entry ;
HANDLE ProcessId ;
BOOL Ret ;
2003-10-20 17:57:05 +00:00
2008-03-04 00:14:11 +00:00
DPRINT ( " GDIOBJ_OwnedByCurrentProcess: ObjectHandle: 0x%08x \n " , ObjectHandle ) ;
2003-10-20 17:57:05 +00:00
2008-03-04 00:14:11 +00:00
if ( ! GDI_HANDLE_IS_STOCKOBJ ( ObjectHandle ) )
{
ProcessId = PsGetCurrentProcessId ( ) ;
2004-12-12 01:40:39 +00:00
2008-03-04 00:14:11 +00:00
Entry = GDI_HANDLE_GET_ENTRY ( GdiHandleTable , ObjectHandle ) ;
Ret = Entry - > KernelData ! = NULL & &
2008-06-03 23:56:09 +00:00
( Entry - > Type & GDI_ENTRY_BASETYPE_MASK ) ! = 0 & &
2008-03-04 00:14:11 +00:00
( HANDLE ) ( ( ULONG_PTR ) Entry - > ProcessId & ~ 0x1 ) = = ProcessId ;
2004-12-12 01:40:39 +00:00
2008-03-04 00:14:11 +00:00
return Ret ;
}
2004-12-12 01:40:39 +00:00
2008-03-04 00:14:11 +00:00
return FALSE ;
2003-10-20 17:57:05 +00:00
}
2004-12-12 01:40:39 +00:00
BOOL INTERNAL_CALL
2008-02-26 23:09:20 +00:00
GDIOBJ_ConvertToStockObj ( HGDIOBJ * phObj )
2003-10-20 17:57:05 +00:00
{
2008-03-04 00:14:11 +00:00
/*
* FIXME ! ! ! ! ! THIS FUNCTION NEEDS TO BE FIXED - IT IS NOT SAFE WHEN OTHER THREADS
* MIGHT ATTEMPT TO LOCK THE OBJECT DURING THIS CALL ! ! !
*/
PGDI_TABLE_ENTRY Entry ;
HANDLE ProcessId , LockedProcessId , PrevProcId ;
2009-07-26 16:17:50 +00:00
PTHREADINFO Thread ;
2008-03-04 00:14:11 +00:00
HGDIOBJ hObj ;
2008-04-04 23:35:43 +00:00
GDIDBG_INITLOOPTRACE ( ) ;
2004-12-18 21:41:17 +00:00
2008-03-04 00:14:11 +00:00
ASSERT ( phObj ) ;
hObj = * phObj ;
2003-10-20 17:57:05 +00:00
2008-03-04 00:14:11 +00:00
DPRINT ( " GDIOBJ_ConvertToStockObj: hObj: 0x%08x \n " , hObj ) ;
2004-12-18 21:41:17 +00:00
2009-07-26 16:17:50 +00:00
Thread = ( PTHREADINFO ) PsGetCurrentThreadWin32Thread ( ) ;
2003-10-20 17:57:05 +00:00
2008-03-04 00:14:11 +00:00
if ( ! GDI_HANDLE_IS_STOCKOBJ ( hObj ) )
{
ProcessId = PsGetCurrentProcessId ( ) ;
LockedProcessId = ( HANDLE ) ( ( ULONG_PTR ) ProcessId | 0x1 ) ;
2004-12-12 01:40:39 +00:00
2008-03-04 00:14:11 +00:00
Entry = GDI_HANDLE_GET_ENTRY ( GdiHandleTable , hObj ) ;
2004-12-12 01:40:39 +00:00
LockHandle :
2008-03-04 00:14:11 +00:00
/* lock the object, we must not convert stock objects, so don't check!!! */
2009-02-16 23:00:15 +00:00
PrevProcId = InterlockedCompareExchangePointer ( ( PVOID * ) & Entry - > ProcessId , LockedProcessId , ProcessId ) ;
2008-03-04 00:14:11 +00:00
if ( PrevProcId = = ProcessId )
2003-11-26 21:48:35 +00:00
{
2008-03-04 00:14:11 +00:00
LONG NewType , PrevType , OldType ;
/* we're locking an object that belongs to our process. First calculate
the new object type including the stock object flag and then try to
exchange it . */
/* On Windows the higher 16 bit of the type field don't contain the
full type from the handle , but the base type .
( type = BRSUH , PEN , EXTPEN , basetype = BRUSH ) */
OldType = ( ( ULONG ) hObj & GDI_HANDLE_BASETYPE_MASK ) | ( ( ULONG ) hObj > > GDI_ENTRY_UPPER_SHIFT ) ;
/* We are currently not using bits 24..31 (flags) of the type field, but for compatibility
we copy them as we can ' t get them from the handle */
OldType | = Entry - > Type & GDI_ENTRY_FLAGS_MASK ;
/* As the object should be a stock object, set it's flag, but only in the lower 16 bits */
NewType = OldType | GDI_ENTRY_STOCK_MASK ;
/* Try to exchange the type field - but only if the old (previous type) matches! */
2009-02-16 23:00:15 +00:00
PrevType = InterlockedCompareExchange ( & Entry - > Type , NewType , OldType ) ;
2008-03-04 00:14:11 +00:00
if ( PrevType = = OldType & & Entry - > KernelData ! = NULL )
2003-11-26 21:48:35 +00:00
{
2009-07-26 16:17:50 +00:00
PTHREADINFO PrevThread ;
2008-03-04 00:14:11 +00:00
POBJ Object ;
/* We successfully set the stock object flag.
KernelData should never be NULL here ! ! ! */
ASSERT ( Entry - > KernelData ) ;
Object = Entry - > KernelData ;
PrevThread = Object - > Tid ;
if ( Object - > cExclusiveLock = = 0 | | PrevThread = = Thread )
{
/* dereference the process' object counter */
if ( PrevProcId ! = GDI_GLOBAL_PROCESS )
{
PEPROCESS OldProcess ;
2009-07-26 16:17:50 +00:00
PPROCESSINFO W32Process ;
2008-03-04 00:14:11 +00:00
NTSTATUS Status ;
/* FIXME */
Status = PsLookupProcessByProcessId ( ( HANDLE ) ( ( ULONG_PTR ) PrevProcId & ~ 0x1 ) , & OldProcess ) ;
if ( NT_SUCCESS ( Status ) )
{
2009-07-26 16:17:50 +00:00
W32Process = ( PPROCESSINFO ) OldProcess - > Win32Process ;
2008-03-04 00:14:11 +00:00
if ( W32Process ! = NULL )
{
2009-03-30 03:56:53 +00:00
InterlockedDecrement ( & W32Process - > GDIHandleCount ) ;
2008-03-04 00:14:11 +00:00
}
ObDereferenceObject ( OldProcess ) ;
}
}
hObj = ( HGDIOBJ ) ( ( ULONG ) ( hObj ) | GDI_HANDLE_STOCK_MASK ) ;
* phObj = hObj ;
2008-05-20 20:39:32 +00:00
Object - > hHmgr = hObj ;
/* remove the process id lock and make it global */
2009-02-16 23:00:15 +00:00
( void ) InterlockedExchangePointer ( ( PVOID * ) & Entry - > ProcessId , GDI_GLOBAL_PROCESS ) ;
2008-03-04 00:14:11 +00:00
/* we're done, successfully converted the object */
return TRUE ;
}
else
{
2008-04-04 23:35:43 +00:00
GDIDBG_TRACELOOP ( hObj , PrevThread , Thread ) ;
2008-03-04 00:14:11 +00:00
/* WTF?! The object is already locked by a different thread!
Release the lock , wait a bit and try again !
FIXME - we should give up after some time unless we want to wait forever ! */
2009-02-16 23:00:15 +00:00
( void ) InterlockedExchangePointer ( ( PVOID * ) & Entry - > ProcessId , PrevProcId ) ;
2008-03-04 00:14:11 +00:00
DelayExecution ( ) ;
goto LockHandle ;
}
}
else
{
DPRINT1 ( " Attempted to convert object 0x%x that is deleted! Should never get here!!! \n " , hObj ) ;
DPRINT1 ( " OldType = 0x%x, Entry->Type = 0x%x, NewType = 0x%x, Entry->KernelData = 0x%x \n " , OldType , Entry - > Type , NewType , Entry - > KernelData ) ;
2003-11-26 21:48:35 +00:00
}
}
2008-03-04 00:14:11 +00:00
else if ( PrevProcId = = LockedProcessId )
2004-12-12 01:40:39 +00:00
{
2008-04-04 23:35:43 +00:00
GDIDBG_TRACELOOP ( hObj , PrevProcId , ProcessId ) ;
2008-03-04 00:14:11 +00:00
/* the object is currently locked, wait some time and try again.
FIXME - we shouldn ' t loop forever ! Give up after some time ! */
DelayExecution ( ) ;
/* try again */
goto LockHandle ;
}
else
{
DPRINT1 ( " Attempted to convert invalid handle: 0x%x \n " , hObj ) ;
2004-12-12 01:40:39 +00:00
}
2003-11-26 21:48:35 +00:00
}
2004-12-12 01:40:39 +00:00
2008-03-04 00:14:11 +00:00
return FALSE ;
2003-11-26 21:48:35 +00:00
}
2008-05-30 04:56:10 +00:00
BOOL INTERNAL_CALL
2008-02-26 23:09:20 +00:00
GDIOBJ_SetOwnership ( HGDIOBJ ObjectHandle , PEPROCESS NewOwner )
2003-11-26 21:48:35 +00:00
{
2008-03-04 00:14:11 +00:00
PGDI_TABLE_ENTRY Entry ;
HANDLE ProcessId , LockedProcessId , PrevProcId ;
2009-07-26 16:17:50 +00:00
PTHREADINFO Thread ;
2008-05-30 04:56:10 +00:00
BOOL Ret = TRUE ;
2008-04-04 23:35:43 +00:00
GDIDBG_INITLOOPTRACE ( ) ;
2004-12-12 01:40:39 +00:00
2008-03-04 00:14:11 +00:00
DPRINT ( " GDIOBJ_SetOwnership: hObj: 0x%x, NewProcess: 0x%x \n " , ObjectHandle , ( NewOwner ? PsGetProcessId ( NewOwner ) : 0 ) ) ;
2003-11-26 21:48:35 +00:00
2009-07-26 16:17:50 +00:00
Thread = ( PTHREADINFO ) PsGetCurrentThreadWin32Thread ( ) ;
2004-12-12 01:40:39 +00:00
2008-03-04 00:14:11 +00:00
if ( ! GDI_HANDLE_IS_STOCKOBJ ( ObjectHandle ) )
2003-10-20 17:57:05 +00:00
{
2008-03-04 00:14:11 +00:00
ProcessId = PsGetCurrentProcessId ( ) ;
LockedProcessId = ( HANDLE ) ( ( ULONG_PTR ) ProcessId | 0x1 ) ;
2004-12-12 01:40:39 +00:00
2008-03-04 00:14:11 +00:00
Entry = GDI_HANDLE_GET_ENTRY ( GdiHandleTable , ObjectHandle ) ;
2004-12-18 21:41:17 +00:00
2008-03-04 00:14:11 +00:00
LockHandle :
/* lock the object, we must not convert stock objects, so don't check!!! */
2009-02-16 23:00:15 +00:00
PrevProcId = InterlockedCompareExchangePointer ( ( PVOID * ) & Entry - > ProcessId , ProcessId , LockedProcessId ) ;
2008-03-04 00:14:11 +00:00
if ( PrevProcId = = ProcessId )
2004-12-12 01:40:39 +00:00
{
2009-07-26 16:17:50 +00:00
PTHREADINFO PrevThread ;
2008-03-04 00:14:11 +00:00
2008-06-03 23:56:09 +00:00
if ( ( Entry - > Type & GDI_ENTRY_BASETYPE_MASK ) ! = 0 )
2004-12-12 01:40:39 +00:00
{
2008-03-04 00:14:11 +00:00
POBJ Object = Entry - > KernelData ;
PrevThread = Object - > Tid ;
if ( Object - > cExclusiveLock = = 0 | | PrevThread = = Thread )
{
PEPROCESS OldProcess ;
2009-07-26 16:17:50 +00:00
PPROCESSINFO W32Process ;
2008-03-04 00:14:11 +00:00
NTSTATUS Status ;
/* dereference the process' object counter */
/* FIXME */
if ( ( ULONG_PTR ) PrevProcId & ~ 0x1 )
{
Status = PsLookupProcessByProcessId ( ( HANDLE ) ( ( ULONG_PTR ) PrevProcId & ~ 0x1 ) , & OldProcess ) ;
if ( NT_SUCCESS ( Status ) )
{
2009-07-26 16:17:50 +00:00
W32Process = ( PPROCESSINFO ) OldProcess - > Win32Process ;
2008-03-04 00:14:11 +00:00
if ( W32Process ! = NULL )
{
2009-03-30 03:56:53 +00:00
InterlockedDecrement ( & W32Process - > GDIHandleCount ) ;
2008-03-04 00:14:11 +00:00
}
ObDereferenceObject ( OldProcess ) ;
}
}
if ( NewOwner ! = NULL )
{
ProcessId = PsGetProcessId ( NewOwner ) ;
/* Increase the new process' object counter */
2009-07-26 16:17:50 +00:00
W32Process = ( PPROCESSINFO ) NewOwner - > Win32Process ;
2008-03-04 00:14:11 +00:00
if ( W32Process ! = NULL )
{
2009-03-30 03:56:53 +00:00
InterlockedIncrement ( & W32Process - > GDIHandleCount ) ;
2008-03-04 00:14:11 +00:00
}
}
else
ProcessId = 0 ;
/* remove the process id lock and change it to the new process id */
2009-02-16 23:00:15 +00:00
( void ) InterlockedExchangePointer ( ( PVOID * ) & Entry - > ProcessId , ProcessId ) ;
2008-03-04 00:14:11 +00:00
/* we're done! */
2008-05-30 04:56:10 +00:00
return Ret ;
2008-03-04 00:14:11 +00:00
}
else
{
2008-04-04 23:35:43 +00:00
GDIDBG_TRACELOOP ( ObjectHandle , PrevThread , Thread ) ;
2008-03-04 00:14:11 +00:00
/* WTF?! The object is already locked by a different thread!
Release the lock , wait a bit and try again ! DO reset the pid lock
so we make sure we don ' t access invalid memory in case the object is
being deleted in the meantime ( because we don ' t have aquired a reference
at this point ) .
FIXME - we should give up after some time unless we want to wait forever ! */
2009-02-16 23:00:15 +00:00
( void ) InterlockedExchangePointer ( ( PVOID * ) & Entry - > ProcessId , PrevProcId ) ;
2008-03-04 00:14:11 +00:00
DelayExecution ( ) ;
goto LockHandle ;
}
2004-12-12 01:40:39 +00:00
}
2008-03-04 00:14:11 +00:00
else
2004-12-12 01:40:39 +00:00
{
2008-03-04 00:14:11 +00:00
DPRINT1 ( " Attempted to change ownership of an object 0x%x currently being destroyed!!! \n " , ObjectHandle ) ;
DPRINT1 ( " Entry->Type = 0x%lx, Entry->KernelData = 0x%p \n " , Entry - > Type , Entry - > KernelData ) ;
2008-05-30 04:56:10 +00:00
Ret = FALSE ;
2004-12-12 01:40:39 +00:00
}
}
2008-03-04 00:14:11 +00:00
else if ( PrevProcId = = LockedProcessId )
2003-11-26 21:48:35 +00:00
{
2008-04-04 23:35:43 +00:00
GDIDBG_TRACELOOP ( ObjectHandle , PrevProcId , ProcessId ) ;
2008-03-04 00:14:11 +00:00
/* the object is currently locked, wait some time and try again.
FIXME - we shouldn ' t loop forever ! Give up after some time ! */
DelayExecution ( ) ;
/* try again */
goto LockHandle ;
}
else if ( ( ( ULONG_PTR ) PrevProcId & ~ 0x1 ) = = 0 )
{
/* allow changing ownership of global objects */
ProcessId = NULL ;
LockedProcessId = ( HANDLE ) ( ( ULONG_PTR ) ProcessId | 0x1 ) ;
goto LockHandle ;
}
else if ( ( HANDLE ) ( ( ULONG_PTR ) PrevProcId & ~ 0x1 ) ! = PsGetCurrentProcessId ( ) )
{
DPRINT1 ( " Attempted to change ownership of object 0x%x (pid: 0x%x) from pid 0x%x!!! \n " , ObjectHandle , ( ULONG_PTR ) PrevProcId & ~ 0x1 , PsGetCurrentProcessId ( ) ) ;
2008-05-30 04:56:10 +00:00
Ret = FALSE ;
2008-03-04 00:14:11 +00:00
}
else
{
DPRINT1 ( " Attempted to change owner of invalid handle: 0x%x \n " , ObjectHandle ) ;
2008-05-30 04:56:10 +00:00
Ret = FALSE ;
2003-11-26 21:48:35 +00:00
}
2004-12-12 01:40:39 +00:00
}
2008-05-30 04:56:10 +00:00
return Ret ;
2004-12-12 01:40:39 +00:00
}
2008-05-30 04:56:10 +00:00
BOOL INTERNAL_CALL
2008-02-26 23:09:20 +00:00
GDIOBJ_CopyOwnership ( HGDIOBJ CopyFrom , HGDIOBJ CopyTo )
2004-12-12 01:40:39 +00:00
{
2008-03-04 00:14:11 +00:00
PGDI_TABLE_ENTRY FromEntry ;
2009-07-26 16:17:50 +00:00
PTHREADINFO Thread ;
2008-03-04 00:14:11 +00:00
HANDLE FromProcessId , FromLockedProcessId , FromPrevProcId ;
2008-05-30 04:56:10 +00:00
BOOL Ret = TRUE ;
2008-04-04 23:35:43 +00:00
GDIDBG_INITLOOPTRACE ( ) ;
2004-12-12 01:40:39 +00:00
2008-03-04 00:14:11 +00:00
DPRINT ( " GDIOBJ_CopyOwnership: from: 0x%x, to: 0x%x \n " , CopyFrom , CopyTo ) ;
2004-12-12 01:40:39 +00:00
2009-07-26 16:17:50 +00:00
Thread = ( PTHREADINFO ) PsGetCurrentThreadWin32Thread ( ) ;
2004-12-12 01:40:39 +00:00
2008-03-04 00:14:11 +00:00
if ( ! GDI_HANDLE_IS_STOCKOBJ ( CopyFrom ) & & ! GDI_HANDLE_IS_STOCKOBJ ( CopyTo ) )
2004-12-12 01:40:39 +00:00
{
2008-03-04 00:14:11 +00:00
FromEntry = GDI_HANDLE_GET_ENTRY ( GdiHandleTable , CopyFrom ) ;
2004-12-12 01:40:39 +00:00
2008-03-04 00:14:11 +00:00
FromProcessId = ( HANDLE ) ( ( ULONG_PTR ) FromEntry - > ProcessId & ~ 0x1 ) ;
FromLockedProcessId = ( HANDLE ) ( ( ULONG_PTR ) FromProcessId | 0x1 ) ;
2004-12-12 01:40:39 +00:00
2008-03-04 00:14:11 +00:00
LockHandleFrom :
/* lock the object, we must not convert stock objects, so don't check!!! */
2009-02-16 23:00:15 +00:00
FromPrevProcId = InterlockedCompareExchangePointer ( ( PVOID * ) & FromEntry - > ProcessId , FromProcessId , FromLockedProcessId ) ;
2008-03-04 00:14:11 +00:00
if ( FromPrevProcId = = FromProcessId )
2003-11-26 21:48:35 +00:00
{
2009-07-26 16:17:50 +00:00
PTHREADINFO PrevThread ;
2008-03-04 00:14:11 +00:00
POBJ Object ;
2004-12-12 01:40:39 +00:00
2008-06-03 23:56:09 +00:00
if ( ( FromEntry - > Type & GDI_ENTRY_BASETYPE_MASK ) ! = 0 )
2008-03-04 00:14:11 +00:00
{
Object = FromEntry - > KernelData ;
/* save the pointer to the calling thread so we know it was this thread
that locked the object */
PrevThread = Object - > Tid ;
if ( Object - > cExclusiveLock = = 0 | | PrevThread = = Thread )
{
/* now let's change the ownership of the target object */
if ( ( ( ULONG_PTR ) FromPrevProcId & ~ 0x1 ) ! = 0 )
{
PEPROCESS ProcessTo ;
/* FIXME */
if ( NT_SUCCESS ( PsLookupProcessByProcessId ( ( HANDLE ) ( ( ULONG_PTR ) FromPrevProcId & ~ 0x1 ) , & ProcessTo ) ) )
{
GDIOBJ_SetOwnership ( CopyTo , ProcessTo ) ;
ObDereferenceObject ( ProcessTo ) ;
}
}
else
{
/* mark the object as global */
GDIOBJ_SetOwnership ( CopyTo , NULL ) ;
}
2009-02-16 23:00:15 +00:00
( void ) InterlockedExchangePointer ( ( PVOID * ) & FromEntry - > ProcessId , FromPrevProcId ) ;
2008-03-04 00:14:11 +00:00
}
else
{
2008-04-04 23:35:43 +00:00
GDIDBG_TRACELOOP ( CopyFrom , PrevThread , Thread ) ;
2008-03-04 00:14:11 +00:00
/* WTF?! The object is already locked by a different thread!
Release the lock , wait a bit and try again ! DO reset the pid lock
so we make sure we don ' t access invalid memory in case the object is
being deleted in the meantime ( because we don ' t have aquired a reference
at this point ) .
FIXME - we should give up after some time unless we want to wait forever ! */
2009-02-16 23:00:15 +00:00
( void ) InterlockedExchangePointer ( ( PVOID * ) & FromEntry - > ProcessId , FromPrevProcId ) ;
2008-03-04 00:14:11 +00:00
DelayExecution ( ) ;
goto LockHandleFrom ;
}
}
else
2003-11-26 21:48:35 +00:00
{
2008-03-04 00:14:11 +00:00
DPRINT1 ( " Attempted to copy ownership from an object 0x%x currently being destroyed!!! \n " , CopyFrom ) ;
2008-05-30 04:56:10 +00:00
Ret = FALSE ;
2003-11-26 21:48:35 +00:00
}
2004-12-12 01:40:39 +00:00
}
2008-03-04 00:14:11 +00:00
else if ( FromPrevProcId = = FromLockedProcessId )
2004-12-12 01:40:39 +00:00
{
2008-04-04 23:35:43 +00:00
GDIDBG_TRACELOOP ( CopyFrom , FromPrevProcId , FromProcessId ) ;
2008-03-04 00:14:11 +00:00
/* the object is currently locked, wait some time and try again.
FIXME - we shouldn ' t loop forever ! Give up after some time ! */
DelayExecution ( ) ;
/* try again */
goto LockHandleFrom ;
}
else if ( ( HANDLE ) ( ( ULONG_PTR ) FromPrevProcId & ~ 0x1 ) ! = PsGetCurrentProcessId ( ) )
{
/* FIXME - should we really allow copying ownership from objects that we don't even own? */
DPRINT1 ( " WARNING! Changing copying ownership of object 0x%x (pid: 0x%x) to pid 0x%x!!! \n " , CopyFrom , ( ULONG_PTR ) FromPrevProcId & ~ 0x1 , PsGetCurrentProcessId ( ) ) ;
FromProcessId = ( HANDLE ) ( ( ULONG_PTR ) FromPrevProcId & ~ 0x1 ) ;
FromLockedProcessId = ( HANDLE ) ( ( ULONG_PTR ) FromProcessId | 0x1 ) ;
goto LockHandleFrom ;
}
else
{
DPRINT1 ( " Attempted to copy ownership from invalid handle: 0x%x \n " , CopyFrom ) ;
2008-05-30 04:56:10 +00:00
Ret = FALSE ;
2003-11-26 21:48:35 +00:00
}
2004-12-12 01:40:39 +00:00
}
2008-05-30 04:56:10 +00:00
return Ret ;
2004-12-12 01:40:39 +00:00
}
PVOID INTERNAL_CALL
2006-04-01 15:25:40 +00:00
GDI_MapHandleTable ( PSECTION_OBJECT SectionObject , PEPROCESS Process )
2004-12-12 01:40:39 +00:00
{
2006-04-01 15:25:40 +00:00
PVOID MappedView = NULL ;
NTSTATUS Status ;
LARGE_INTEGER Offset ;
ULONG ViewSize = sizeof ( GDI_HANDLE_TABLE ) ;
Offset . QuadPart = 0 ;
ASSERT ( SectionObject ! = NULL ) ;
ASSERT ( Process ! = NULL ) ;
Status = MmMapViewOfSection ( SectionObject ,
Process ,
& MappedView ,
0 ,
0 ,
& Offset ,
& ViewSize ,
ViewUnmap ,
SEC_NO_CHANGE ,
PAGE_READONLY ) ;
if ( ! NT_SUCCESS ( Status ) )
return NULL ;
return MappedView ;
2003-10-20 17:57:05 +00:00
}
2008-04-04 23:35:43 +00:00
/** PUBLIC FUNCTIONS **********************************************************/
2010-01-21 01:39:19 +00:00
BOOL
FASTCALL
IntGdiSetRegeionOwner ( HRGN hRgn , DWORD OwnerMask )
{
INT Index ;
PGDI_TABLE_ENTRY Entry ;
/*
System Regions :
These regions do not use attribute sections and when allocated , use gdiobj
level functions .
*/
// FIXME! HAX!!! Remove this once we get everything right!
KeEnterCriticalRegion ( ) ;
Index = GDI_HANDLE_GET_INDEX ( hRgn ) ;
Entry = & GdiHandleTable - > Entries [ Index ] ;
if ( Entry - > UserData ) FreeObjectAttr ( Entry - > UserData ) ;
Entry - > UserData = NULL ;
KeLeaveCriticalRegion ( ) ;
//
if ( ( OwnerMask = = GDI_OBJ_HMGR_PUBLIC ) | | OwnerMask = = GDI_OBJ_HMGR_NONE )
{
return GDIOBJ_SetOwnership ( hRgn , NULL ) ;
}
if ( OwnerMask = = GDI_OBJ_HMGR_POWNED )
{
return GDIOBJ_SetOwnership ( ( HGDIOBJ ) hRgn , PsGetCurrentProcess ( ) ) ;
}
return FALSE ;
}
2008-05-30 04:56:10 +00:00
BOOL
FASTCALL
2009-03-20 22:40:14 +00:00
IntGdiSetBrushOwner ( PBRUSH pbr , DWORD OwnerMask )
2008-05-30 04:56:10 +00:00
{
2008-06-04 05:43:11 +00:00
HBRUSH hBR ;
PEPROCESS Owner = NULL ;
PGDI_TABLE_ENTRY pEntry = NULL ;
if ( ! pbr ) return FALSE ;
hBR = pbr - > BaseObject . hHmgr ;
if ( ! hBR | | ( GDI_HANDLE_GET_TYPE ( hBR ) ! = GDI_OBJECT_TYPE_BRUSH ) )
return FALSE ;
else
{
INT Index = GDI_HANDLE_GET_INDEX ( ( HGDIOBJ ) hBR ) ;
pEntry = & GdiHandleTable - > Entries [ Index ] ;
}
if ( pbr - > flAttrs & GDIBRUSH_IS_GLOBAL )
{
GDIOBJ_ShareUnlockObjByPtr ( ( POBJ ) pbr ) ;
return TRUE ;
}
if ( ( OwnerMask = = GDI_OBJ_HMGR_PUBLIC ) | | OwnerMask = = GDI_OBJ_HMGR_NONE )
{
// Set this Brush to inaccessible mode and to an Owner of NONE.
// if (OwnerMask == GDI_OBJ_HMGR_NONE) Owner = OwnerMask;
if ( ! GDIOBJ_SetOwnership ( ( HGDIOBJ ) hBR , Owner ) )
return FALSE ;
// Deny user access to User Data.
pEntry - > UserData = NULL ; // This hBR is inaccessible!
}
if ( OwnerMask = = GDI_OBJ_HMGR_POWNED )
{
if ( ! GDIOBJ_SetOwnership ( ( HGDIOBJ ) hBR , PsGetCurrentProcess ( ) ) )
return FALSE ;
// Allow user access to User Data.
2008-11-30 19:28:11 +00:00
pEntry - > UserData = pbr - > pBrushAttr ;
2008-06-04 05:43:11 +00:00
}
2008-05-30 04:56:10 +00:00
return TRUE ;
}
BOOL
FASTCALL
IntGdiSetDCOwnerEx ( HDC hDC , DWORD OwnerMask , BOOL NoSetBrush )
{
PDC pDC ;
BOOL Ret = FALSE ;
if ( ! hDC | | ( GDI_HANDLE_GET_TYPE ( hDC ) ! = GDI_OBJECT_TYPE_DC ) ) return FALSE ;
if ( ( OwnerMask = = GDI_OBJ_HMGR_PUBLIC ) | | OwnerMask = = GDI_OBJ_HMGR_NONE )
{
pDC = DC_LockDc ( hDC ) ;
2009-03-20 14:16:01 +00:00
MmCopyFromCaller ( & pDC - > dcattr , pDC - > pdcattr , sizeof ( DC_ATTR ) ) ;
2008-05-30 04:56:10 +00:00
DC_UnlockDc ( pDC ) ;
DC_FreeDcAttr ( hDC ) ; // Free the dcattr!
if ( ! DC_SetOwnership ( hDC , NULL ) ) // This hDC is inaccessible!
return Ret ;
}
if ( OwnerMask = = GDI_OBJ_HMGR_POWNED )
{
pDC = DC_LockDc ( hDC ) ;
2009-03-20 14:16:01 +00:00
ASSERT ( pDC - > pdcattr = = & pDC - > dcattr ) ;
2008-05-30 04:56:10 +00:00
DC_UnlockDc ( pDC ) ;
if ( ! DC_SetOwnership ( hDC , PsGetCurrentProcess ( ) ) ) return Ret ;
DC_AllocateDcAttr ( hDC ) ; // Allocate new dcattr
DCU_SynchDcAttrtoUser ( hDC ) ; // Copy data from dc to dcattr
}
if ( ( OwnerMask ! = GDI_OBJ_HMGR_NONE ) & & ! NoSetBrush )
{
pDC = DC_LockDc ( hDC ) ;
2009-03-20 22:40:14 +00:00
if ( IntGdiSetBrushOwner ( ( PBRUSH ) pDC - > dclevel . pbrFill , OwnerMask ) )
IntGdiSetBrushOwner ( ( PBRUSH ) pDC - > dclevel . pbrLine , OwnerMask ) ;
2008-05-30 04:56:10 +00:00
DC_UnlockDc ( pDC ) ;
}
return TRUE ;
}
2009-03-25 01:07:02 +00:00
INT
FASTCALL
GreGetObjectOwner ( HGDIOBJ Handle , GDIOBJTYPE ObjType )
{
INT Ret = GDI_OBJ_HMGR_RESTRICTED ;
if ( GDI_HANDLE_GET_INDEX ( Handle ) < GDI_HANDLE_COUNT )
{
PGDI_TABLE_ENTRY pEntry = & GdiHandleTable - > Entries [ GDI_HANDLE_GET_INDEX ( Handle ) ] ;
if ( pEntry - > ObjectType = = ObjType )
{
if ( pEntry - > FullUnique = = ( GDI_HANDLE_GET_UPPER ( Handle ) > > GDI_ENTRY_UPPER_SHIFT ) )
Ret = pEntry - > ProcessId & ~ 1 ;
}
}
return Ret ;
}
2007-07-31 07:06:06 +00:00
W32KAPI
HANDLE
APIENTRY
NtGdiCreateClientObj (
IN ULONG ulType
2008-03-04 00:14:11 +00:00
)
2007-07-31 07:06:06 +00:00
{
2008-03-19 00:56:40 +00:00
POBJ pObject ;
HANDLE handle ;
/* Mask out everything that would change the type in a wrong manner */
ulType & = ( GDI_HANDLE_TYPE_MASK & ~ GDI_HANDLE_BASETYPE_MASK ) ;
/* Allocate a new object */
pObject = GDIOBJ_AllocObjWithHandle ( GDI_OBJECT_TYPE_CLIOBJ | ulType ) ;
if ( ! pObject )
{
return NULL ;
}
/* get the handle */
handle = pObject - > hHmgr ;
/* Unlock it */
GDIOBJ_UnlockObjByPtr ( pObject ) ;
2008-03-04 00:14:11 +00:00
return handle ;
2007-07-31 07:06:06 +00:00
}
W32KAPI
BOOL
APIENTRY
NtGdiDeleteClientObj (
IN HANDLE h
2008-03-04 00:14:11 +00:00
)
2007-07-31 07:06:06 +00:00
{
2008-03-19 00:56:40 +00:00
/* We first need to get the real type from the handle */
ULONG type = GDI_HANDLE_GET_TYPE ( h ) ;
/* Check if it's really a CLIENTOBJ */
if ( ( type & GDI_HANDLE_BASETYPE_MASK ) ! = GDILoObjType_LO_CLIENTOBJ_TYPE )
{
/* FIXME: SetLastError? */
return FALSE ;
}
return GDIOBJ_FreeObjByHandle ( h , type ) ;
2007-07-31 07:06:06 +00:00
}
2008-03-15 01:10:53 +00:00
INT
FASTCALL
IntGdiGetObject ( IN HANDLE Handle ,
IN INT cbCount ,
IN LPVOID lpBuffer )
{
PVOID pGdiObject ;
INT Result = 0 ;
DWORD dwObjectType ;
pGdiObject = GDIOBJ_LockObj ( Handle , GDI_OBJECT_TYPE_DONTCARE ) ;
if ( ! pGdiObject )
{
SetLastWin32Error ( ERROR_INVALID_HANDLE ) ;
return 0 ;
}
dwObjectType = GDIOBJ_GetObjectType ( Handle ) ;
switch ( dwObjectType )
{
case GDI_OBJECT_TYPE_PEN :
case GDI_OBJECT_TYPE_EXTPEN :
2009-03-20 22:40:14 +00:00
Result = PEN_GetObject ( ( PBRUSH ) pGdiObject , cbCount , ( PLOGPEN ) lpBuffer ) ; // IntGdiCreatePenIndirect
2008-03-15 01:10:53 +00:00
break ;
case GDI_OBJECT_TYPE_BRUSH :
2009-03-20 22:40:14 +00:00
Result = BRUSH_GetObject ( ( PBRUSH ) pGdiObject , cbCount , ( LPLOGBRUSH ) lpBuffer ) ;
2008-03-15 01:10:53 +00:00
break ;
case GDI_OBJECT_TYPE_BITMAP :
2009-01-08 16:33:40 +00:00
Result = BITMAP_GetObject ( ( SURFACE * ) pGdiObject , cbCount , lpBuffer ) ;
2008-03-15 01:10:53 +00:00
break ;
case GDI_OBJECT_TYPE_FONT :
Result = FontGetObject ( ( PTEXTOBJ ) pGdiObject , cbCount , lpBuffer ) ;
#if 0
// Fix the LOGFONT structure for the stock fonts
if ( FIRST_STOCK_HANDLE < = Handle & & Handle < = LAST_STOCK_HANDLE )
{
FixStockFontSizeW ( Handle , cbCount , lpBuffer ) ;
}
# endif
break ;
case GDI_OBJECT_TYPE_PALETTE :
2009-04-09 22:28:54 +00:00
Result = PALETTE_GetObject ( ( PPALETTE ) pGdiObject , cbCount , lpBuffer ) ;
2008-03-15 01:10:53 +00:00
break ;
default :
DPRINT1 ( " GDI object type 0x%08x not implemented \n " , dwObjectType ) ;
break ;
}
GDIOBJ_UnlockObjByPtr ( pGdiObject ) ;
return Result ;
}
2008-04-29 02:00:56 +00:00
2008-04-04 23:35:43 +00:00
W32KAPI
2008-03-15 01:10:53 +00:00
INT
2008-04-04 23:35:43 +00:00
APIENTRY
2008-03-15 01:10:53 +00:00
NtGdiExtGetObjectW ( IN HANDLE hGdiObj ,
IN INT cbCount ,
OUT LPVOID lpBuffer )
{
INT iRetCount = 0 ;
INT cbCopyCount ;
union
{
BITMAP bitmap ;
DIBSECTION dibsection ;
LOGPEN logpen ;
LOGBRUSH logbrush ;
LOGFONTW logfontw ;
EXTLOGFONTW extlogfontw ;
ENUMLOGFONTEXDVW enumlogfontexdvw ;
} Object ;
// Normalize to the largest supported object size
cbCount = min ( ( UINT ) cbCount , sizeof ( Object ) ) ;
// Now do the actual call
iRetCount = IntGdiGetObject ( hGdiObj , cbCount , lpBuffer ? & Object : NULL ) ;
cbCopyCount = min ( ( UINT ) cbCount , ( UINT ) iRetCount ) ;
// Make sure we have a buffer and a copy size
if ( ( cbCopyCount ) & & ( lpBuffer ) )
{
// Enter SEH for buffer transfer
2008-11-30 19:28:11 +00:00
_SEH2_TRY
2008-03-15 01:10:53 +00:00
{
// Probe the buffer and copy it
ProbeForWrite ( lpBuffer , cbCopyCount , sizeof ( WORD ) ) ;
RtlCopyMemory ( lpBuffer , & Object , cbCopyCount ) ;
}
2008-11-30 19:28:11 +00:00
_SEH2_EXCEPT ( EXCEPTION_EXECUTE_HANDLER )
2008-03-15 01:10:53 +00:00
{
// Clear the return value.
// Do *NOT* set last error here!
iRetCount = 0 ;
}
2008-11-30 19:28:11 +00:00
_SEH2_END ;
2008-03-15 01:10:53 +00:00
}
// Return the count
return iRetCount ;
}
2003-05-18 17:16:18 +00:00
/* EOF */