mirror of
https://github.com/reactos/reactos.git
synced 2024-10-30 11:35:58 +00:00
a5aa252f47
- clean up some debugging code - set NULL owner when setting BASEFLAG_READY_TO_DIE, so that the handle can be freed. svn path=/branches/reactos-yarotows/; revision=47217
1968 lines
62 KiB
C
1968 lines
62 KiB
C
/*
|
|
* 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: ...
|
|
*/
|
|
|
|
/** INCLUDES ******************************************************************/
|
|
|
|
#define GDI_DEBUG
|
|
|
|
#include <win32k.h>
|
|
#define NDEBUG
|
|
#include <debug.h>
|
|
|
|
#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
|
|
|
|
#define BASE_OBJTYPE_COUNT 32
|
|
|
|
#define DelayExecution() \
|
|
DPRINT("%s:%i: Delay\n", __FILE__, __LINE__); \
|
|
KeDelayExecutionThread(KernelMode, FALSE, &ShortDelay)
|
|
|
|
#include "gdidbg.c"
|
|
|
|
static
|
|
BOOL INTERNAL_CALL GDI_CleanupDummy(PVOID ObjectBody);
|
|
|
|
/** GLOBALS *******************************************************************/
|
|
|
|
typedef struct
|
|
{
|
|
BOOL bUseLookaside;
|
|
ULONG_PTR ulBodySize;
|
|
ULONG Tag;
|
|
GDICLEANUPPROC CleanupProc;
|
|
} OBJ_TYPE_INFO, *POBJ_TYPE_INFO;
|
|
|
|
static const
|
|
OBJ_TYPE_INFO ObjTypeInfo[BASE_OBJTYPE_COUNT] =
|
|
{
|
|
{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 */
|
|
{1, sizeof(ROSRGNDATA), TAG_REGION, REGION_Cleanup}, /* 04 RGN */
|
|
{1, sizeof(SURFACE), TAG_SURFACE, SURFACE_Cleanup}, /* 05 SURFACE */
|
|
{1, sizeof(CLIENTOBJ), TAG_CLIENTOBJ, GDI_CleanupDummy}, /* 06 CLIENTOBJ: METADC,... */
|
|
{1, sizeof(PATH), TAG_PATH, GDI_CleanupDummy}, /* 07 PATH */
|
|
{1, sizeof(PALETTE), TAG_PALETTE, PALETTE_Cleanup}, /* 08 PAL */
|
|
{1, sizeof(COLORSPACE), TAG_ICMLCS, GDI_CleanupDummy}, /* 09 ICMLCS, */
|
|
{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 */
|
|
{0, sizeof(GDICLRXFORM), TAG_ICMCXF, GDI_CleanupDummy}, /* 0e ICMCXF, */
|
|
{0, 0, TAG_SPRITE, NULL}, /* 0f SPRITE, unused */
|
|
{1, sizeof(BRUSH), TAG_BRUSH, BRUSH_Cleanup}, /* 10 BRUSH, PEN, EXTPEN */
|
|
{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 */
|
|
{0, 0, TAG_VTFD, NULL}, /* 18 VTFD, unused */
|
|
{0, 0, TAG_TTFD, NULL}, /* 19 TTFD, unused */
|
|
{0, 0, TAG_RC, NULL}, /* 1a RC, unused */
|
|
{0, 0, TAG_TEMP, NULL}, /* 1b TEMP, unused */
|
|
{0, sizeof(EDRIVEROBJ), TAG_DRVOBJ, DRIVEROBJ_Cleanup},/* 1c DRVOBJ */
|
|
{0, 0, TAG_DCIOBJ, NULL}, /* 1d DCIOBJ, unused */
|
|
{0, 0, TAG_SPOOL, NULL}, /* 1e SPOOL, unused */
|
|
{0, 0, 0, NULL}, /* 1f reserved entry */
|
|
};
|
|
|
|
static LARGE_INTEGER ShortDelay;
|
|
|
|
/** INTERNAL FUNCTIONS ********************************************************/
|
|
|
|
// Audit Functions
|
|
int tDC = 0;
|
|
int tBRUSH = 0;
|
|
int tBITMAP = 0;
|
|
int tFONT = 0;
|
|
int tRGN = 0;
|
|
|
|
VOID
|
|
AllocTypeDataDump(INT TypeInfo)
|
|
{
|
|
switch( TypeInfo & GDI_HANDLE_TYPE_MASK )
|
|
{
|
|
case GDILoObjType_LO_BRUSH_TYPE:
|
|
tBRUSH++;
|
|
break;
|
|
case GDILoObjType_LO_DC_TYPE:
|
|
tDC++;
|
|
break;
|
|
case GDILoObjType_LO_BITMAP_TYPE:
|
|
tBITMAP++;
|
|
break;
|
|
case GDILoObjType_LO_FONT_TYPE:
|
|
tFONT++;
|
|
break;
|
|
case GDILoObjType_LO_REGION_TYPE:
|
|
tRGN++;
|
|
break;
|
|
}
|
|
}
|
|
|
|
VOID
|
|
DeAllocTypeDataDump(INT TypeInfo)
|
|
{
|
|
switch( TypeInfo & GDI_HANDLE_TYPE_MASK )
|
|
{
|
|
case GDILoObjType_LO_BRUSH_TYPE:
|
|
tBRUSH--;
|
|
break;
|
|
case GDILoObjType_LO_DC_TYPE:
|
|
tDC--;
|
|
break;
|
|
case GDILoObjType_LO_BITMAP_TYPE:
|
|
tBITMAP--;
|
|
break;
|
|
case GDILoObjType_LO_FONT_TYPE:
|
|
tFONT--;
|
|
break;
|
|
case GDILoObjType_LO_REGION_TYPE:
|
|
tRGN--;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Dummy GDI Cleanup Callback
|
|
*/
|
|
static
|
|
BOOL INTERNAL_CALL
|
|
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;
|
|
}
|
|
|
|
static void FASTCALL
|
|
LockErrorDebugOutput(HGDIOBJ hObj, PGDI_TABLE_ENTRY Entry, LPSTR Function)
|
|
{
|
|
if ((Entry->Type & GDI_ENTRY_BASETYPE_MASK) == 0)
|
|
{
|
|
DPRINT1("%s: Attempted to lock object 0x%x that is deleted!\n", Function, hObj);
|
|
GDIDBG_TRACEDELETER(hObj);
|
|
}
|
|
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));
|
|
}
|
|
else if (GDI_HANDLE_GET_TYPE(hObj) != ((Entry->Type << GDI_ENTRY_UPPER_SHIFT) & GDI_HANDLE_TYPE_MASK))
|
|
{
|
|
DPRINT1("%s: Attempted to lock object 0x%x, type mismatch (Handle: 0x%x, Entry: 0x%x)\n",
|
|
Function, hObj, GDI_HANDLE_GET_TYPE(hObj), (Entry->Type << GDI_ENTRY_UPPER_SHIFT) & GDI_HANDLE_TYPE_MASK);
|
|
}
|
|
else
|
|
{
|
|
DPRINT1("%s: Attempted to lock object 0x%x, something went wrong, typeinfo = 0x%x\n",
|
|
Function, hObj, Entry->Type);
|
|
}
|
|
GDIDBG_TRACECALLER();
|
|
}
|
|
|
|
ULONG
|
|
FASTCALL
|
|
InterlockedPopFreeEntry(VOID)
|
|
{
|
|
ULONG idxFirst, idxNext, idxPrev;
|
|
PGDI_TABLE_ENTRY pEntry;
|
|
DWORD PrevProcId;
|
|
|
|
DPRINT("Enter InterLockedPopFreeEntry\n");
|
|
|
|
while (TRUE)
|
|
{
|
|
idxFirst = GdiHandleTable->FirstFree;
|
|
|
|
if (!idxFirst)
|
|
{
|
|
/* Increment FirstUnused and get the new index */
|
|
idxFirst = InterlockedIncrement((LONG*)&GdiHandleTable->FirstUnused) - 1;
|
|
|
|
/* Check if we have entries left */
|
|
if (idxFirst >= GDI_HANDLE_COUNT)
|
|
{
|
|
DPRINT1("No more gdi handles left!\n");
|
|
return 0;
|
|
}
|
|
|
|
/* 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;
|
|
}
|
|
}
|
|
|
|
return idxFirst;
|
|
}
|
|
|
|
/* 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
|
|
InterlockedPushFreeEntry(ULONG idxToFree)
|
|
{
|
|
ULONG idxFirstFree, idxPrev;
|
|
PGDI_TABLE_ENTRY pFreeEntry;
|
|
|
|
DPRINT("Enter InterlockedPushFreeEntry\n");
|
|
|
|
pFreeEntry = GdiHandleTable->Entries + idxToFree;
|
|
ASSERT((pFreeEntry->Type & GDI_ENTRY_BASETYPE_MASK) == 0);
|
|
ASSERT(pFreeEntry->ProcessId == 0);
|
|
pFreeEntry->UserData = NULL;
|
|
|
|
do
|
|
{
|
|
idxFirstFree = GdiHandleTable->FirstFree;
|
|
pFreeEntry->KernelData = (PVOID)(ULONG_PTR)idxFirstFree;
|
|
|
|
idxPrev = InterlockedCompareExchange((LONG*)&GdiHandleTable->FirstFree,
|
|
idxToFree,
|
|
idxFirstFree);
|
|
}
|
|
while (idxPrev != idxFirstFree);
|
|
}
|
|
|
|
|
|
BOOL
|
|
INTERNAL_CALL
|
|
GDIOBJ_ValidateHandle(HGDIOBJ hObj, ULONG ObjectType)
|
|
{
|
|
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))
|
|
{
|
|
HANDLE pid = (HANDLE)((ULONG_PTR)Entry->ProcessId & ~0x1);
|
|
if (pid == NULL || pid == PsGetCurrentProcessId())
|
|
{
|
|
return TRUE;
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
|
|
/*!
|
|
* Allocate memory for GDI object and return handle to it.
|
|
*
|
|
* \param ObjectType - type of object \ref GDI object types
|
|
*
|
|
* \return Pointer to the allocated object, which is locked.
|
|
*/
|
|
POBJ INTERNAL_CALL
|
|
GDIOBJ_AllocObjWithHandle(ULONG ObjectType)
|
|
{
|
|
PPROCESSINFO W32Process;
|
|
POBJ newObject = NULL;
|
|
HANDLE CurrentProcessId, LockedProcessId;
|
|
UCHAR TypeIndex;
|
|
UINT Index;
|
|
PGDI_TABLE_ENTRY Entry;
|
|
LONG TypeInfo;
|
|
|
|
GDIDBG_INITLOOPTRACE();
|
|
|
|
W32Process = PsGetCurrentProcessWin32Process();
|
|
/* HACK HACK HACK: simplest-possible quota implementation - don't allow a process
|
|
to take too many GDI objects, itself. */
|
|
if (W32Process && W32Process->GDIHandleCount >= 0x2710)
|
|
{
|
|
DPRINT1("Too many objects for process!!!\n");
|
|
DPRINT1("DC %d BRUSH %d BITMAP %d FONT %d RGN %d\n",tDC,tBRUSH,tBITMAP,tFONT,tRGN);
|
|
GDIDBG_DUMPHANDLETABLE();
|
|
return NULL;
|
|
}
|
|
|
|
ASSERT(ObjectType != GDI_OBJECT_TYPE_DONTCARE);
|
|
|
|
TypeIndex = GDI_OBJECT_GET_TYPE_INDEX(ObjectType);
|
|
|
|
newObject = GDIOBJ_AllocObj(TypeIndex);
|
|
if (!newObject)
|
|
{
|
|
DPRINT1("Not enough memory to allocate gdi object!\n");
|
|
return NULL;
|
|
}
|
|
|
|
CurrentProcessId = PsGetCurrentProcessId();
|
|
LockedProcessId = (HANDLE)((ULONG_PTR)CurrentProcessId | 0x1);
|
|
|
|
// RtlZeroMemory(newObject, ObjTypeInfo[TypeIndex].ulBodySize);
|
|
|
|
/* 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;
|
|
|
|
Entry = &GdiHandleTable->Entries[Index];
|
|
|
|
LockHandle:
|
|
PrevProcId = InterlockedCompareExchangePointer((PVOID*)&Entry->ProcessId, LockedProcessId, 0);
|
|
if (PrevProcId == NULL)
|
|
{
|
|
PTHREADINFO Thread = (PTHREADINFO)PsGetCurrentThreadWin32Thread();
|
|
HGDIOBJ Handle;
|
|
|
|
Entry->KernelData = newObject;
|
|
|
|
/* copy the reuse-counter */
|
|
TypeInfo |= Entry->Type & GDI_ENTRY_REUSE_MASK;
|
|
|
|
/* we found a free entry, no need to exchange this field atomically
|
|
since we're holding the lock */
|
|
Entry->Type = TypeInfo;
|
|
|
|
/* Create a handle */
|
|
Handle = (HGDIOBJ)((Index & 0xFFFF) | (TypeInfo << GDI_ENTRY_UPPER_SHIFT));
|
|
|
|
/* Initialize BaseObject fields */
|
|
newObject->hHmgr = Handle;
|
|
newObject->ulShareCount = 0;
|
|
newObject->cExclusiveLock = 1;
|
|
newObject->Tid = Thread;
|
|
|
|
AllocTypeDataDump(TypeInfo);
|
|
|
|
/* unlock the entry */
|
|
(void)InterlockedExchangePointer((PVOID*)&Entry->ProcessId, CurrentProcessId);
|
|
|
|
GDIDBG_CAPTUREALLOCATOR(Index);
|
|
|
|
if (W32Process != NULL)
|
|
{
|
|
InterlockedIncrement(&W32Process->GDIHandleCount);
|
|
}
|
|
|
|
DPRINT("GDIOBJ_AllocObj: 0x%x ob: 0x%x\n", Handle, newObject);
|
|
return newObject;
|
|
}
|
|
else
|
|
{
|
|
GDIDBG_TRACELOOP(Index, PrevProcId, CurrentProcessId);
|
|
/* 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;
|
|
}
|
|
}
|
|
|
|
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);
|
|
}
|
|
else
|
|
{
|
|
ExFreePool(pObject);
|
|
}
|
|
}
|
|
|
|
/*!
|
|
* Free memory allocated for the GDI object. For each object type this function calls the
|
|
* appropriate cleanup routine.
|
|
*
|
|
* \param hObj - handle of the object to be deleted.
|
|
*
|
|
* \return Returns TRUE if succesful.
|
|
* \return Returns FALSE if the cleanup routine returned FALSE or the object doesn't belong
|
|
* to the calling process.
|
|
*
|
|
* \bug This function should return VOID and kill the object no matter what...
|
|
*/
|
|
BOOL INTERNAL_CALL
|
|
GDIOBJ_FreeObjByHandle(HGDIOBJ hObj, DWORD ExpectedType)
|
|
{
|
|
PGDI_TABLE_ENTRY Entry;
|
|
HANDLE ProcessId, LockedProcessId, PrevProcId;
|
|
ULONG HandleType, HandleUpper, TypeIndex;
|
|
BOOL Silent;
|
|
|
|
GDIDBG_INITLOOPTRACE();
|
|
|
|
DPRINT("GDIOBJ_FreeObj: hObj: 0x%08x\n", hObj);
|
|
|
|
if (GDI_HANDLE_IS_STOCKOBJ(hObj))
|
|
{
|
|
DPRINT1("GDIOBJ_FreeObj() failed, can't delete stock object handle: 0x%x !!!\n", hObj);
|
|
GDIDBG_TRACECALLER();
|
|
return FALSE;
|
|
}
|
|
|
|
ProcessId = PsGetCurrentProcessId();
|
|
LockedProcessId = (HANDLE)((ULONG_PTR)ProcessId | 0x1);
|
|
|
|
Silent = (ExpectedType & GDI_OBJECT_TYPE_SILENT);
|
|
ExpectedType &= ~GDI_OBJECT_TYPE_SILENT;
|
|
|
|
HandleType = GDI_HANDLE_GET_TYPE(hObj);
|
|
HandleUpper = GDI_HANDLE_GET_UPPER(hObj);
|
|
|
|
/* 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;
|
|
}
|
|
|
|
Entry = GDI_HANDLE_GET_ENTRY(GdiHandleTable, hObj);
|
|
|
|
LockHandle:
|
|
/* 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... */
|
|
PrevProcId = InterlockedCompareExchangePointer((PVOID*)&Entry->ProcessId, LockedProcessId, ProcessId);
|
|
if (PrevProcId == ProcessId)
|
|
{
|
|
if ( (Entry->KernelData != NULL) &&
|
|
((Entry->Type << GDI_ENTRY_UPPER_SHIFT) == HandleUpper) &&
|
|
((Entry->Type & GDI_ENTRY_BASETYPE_MASK) == (HandleUpper & GDI_ENTRY_BASETYPE_MASK)) )
|
|
{
|
|
POBJ Object;
|
|
|
|
Object = Entry->KernelData;
|
|
|
|
if ((Object->cExclusiveLock == 0 ||
|
|
Object->Tid == (PTHREADINFO)PsGetCurrentThreadWin32Thread()) &&
|
|
Object->ulShareCount == 0)
|
|
{
|
|
BOOL Ret;
|
|
PPROCESSINFO W32Process = PsGetCurrentProcessWin32Process();
|
|
|
|
/* 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 */
|
|
(void)InterlockedExchangePointer((PVOID*)&Entry->ProcessId, NULL);
|
|
|
|
/* push this entry to the free list */
|
|
InterlockedPushFreeEntry(GDI_ENTRY_TO_INDEX(GdiHandleTable, Entry));
|
|
|
|
Object->hHmgr = NULL;
|
|
|
|
if (W32Process != NULL)
|
|
{
|
|
InterlockedDecrement(&W32Process->GDIHandleCount);
|
|
}
|
|
|
|
/* call the cleanup routine. */
|
|
TypeIndex = GDI_OBJECT_GET_TYPE_INDEX(HandleType);
|
|
Ret = ObjTypeInfo[TypeIndex].CleanupProc(Object);
|
|
|
|
DeAllocTypeDataDump(HandleType);
|
|
|
|
/* Now it's time to free the memory */
|
|
GDIOBJ_FreeObj(Object, TypeIndex);
|
|
|
|
GDIDBG_CAPTUREDELETER(hObj);
|
|
return Ret;
|
|
}
|
|
else if (Object->ulShareCount != 0)
|
|
{
|
|
NTSTATUS Status;
|
|
PEPROCESS OldProcess;
|
|
Object->BaseFlags |= BASEFLAG_READY_TO_DIE;
|
|
DPRINT("Object %p, ulShareCount = %d\n", Object->hHmgr, Object->ulShareCount);
|
|
/* Set NULL owner. Do the work here to avoid race conditions */
|
|
Status = PsLookupProcessByProcessId((HANDLE)((ULONG_PTR)PrevProcId & ~0x1), &OldProcess);
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
PPROCESSINFO W32Process = (PPROCESSINFO)OldProcess->Win32Process;
|
|
if (W32Process != NULL)
|
|
{
|
|
InterlockedDecrement(&W32Process->GDIHandleCount);
|
|
}
|
|
ObDereferenceObject(OldProcess);
|
|
}
|
|
(void)InterlockedExchangePointer((PVOID*)&Entry->ProcessId, NULL);
|
|
/* Don't wait on shared locks */
|
|
return FALSE;
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* The object is currently locked by another thread, so freeing is forbidden!
|
|
*/
|
|
DPRINT1("Object->cExclusiveLock = %d\n", Object->cExclusiveLock);
|
|
GDIDBG_TRACECALLER();
|
|
GDIDBG_TRACELOCKER(hObj);
|
|
(void)InterlockedExchangePointer((PVOID*)&Entry->ProcessId, PrevProcId);
|
|
/* do not assert here for it will call again from dxg.sys it being call twice */
|
|
|
|
DelayExecution();
|
|
goto LockHandle;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
LockErrorDebugOutput(hObj, Entry, "GDIOBJ_FreeObj");
|
|
(void)InterlockedExchangePointer((PVOID*)&Entry->ProcessId, PrevProcId);
|
|
}
|
|
}
|
|
else if (PrevProcId == LockedProcessId)
|
|
{
|
|
GDIDBG_TRACELOOP(hObj, PrevProcId, ProcessId);
|
|
|
|
/* 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 (!Silent)
|
|
{
|
|
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)
|
|
{
|
|
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);
|
|
}
|
|
DPRINT1("Type = 0x%lx, KernelData = 0x%p, ProcessId = 0x%p\n", Entry->Type, Entry->KernelData, Entry->ProcessId);
|
|
GDIDBG_TRACECALLER();
|
|
GDIDBG_TRACEALLOCATOR(hObj);
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL
|
|
FASTCALL
|
|
IsObjectDead(HGDIOBJ hObject)
|
|
{
|
|
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.
|
|
if ((Entry->Type & GDI_ENTRY_BASETYPE_MASK) != 0)
|
|
return FALSE;
|
|
else
|
|
{
|
|
DPRINT1("Object 0x%x currently being destroyed!!!\n",hObject);
|
|
return TRUE; // return true and move on.
|
|
}
|
|
}
|
|
|
|
|
|
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 ( pAttr && oType == hctRegionHandle)
|
|
{
|
|
if ( Number < CACHE_REGION_ENTRIES )
|
|
{
|
|
((PRGN_ATTR)pAttr)->AttrFlags |= ATTR_CACHED;
|
|
hPtr[Number] = Handle;
|
|
GdiHandleCache->ulNumHandles[oType]++;
|
|
DPRINT("Put Handle Count %d PEB 0x%x\n", GdiHandleCache->ulNumHandles[oType], NtCurrentTeb()->ProcessEnvironmentBlock);
|
|
Ret = TRUE;
|
|
}
|
|
}
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
Ret = FALSE;
|
|
}
|
|
_SEH2_END;
|
|
|
|
(void)InterlockedExchangePointer((PVOID*)&GdiHandleCache->ulLock, Lock);
|
|
return Ret;
|
|
}
|
|
|
|
/*!
|
|
* Delete GDI object
|
|
* \param hObject object handle
|
|
* \return if the function fails the returned value is FALSE.
|
|
*/
|
|
BOOL
|
|
FASTCALL
|
|
GreDeleteObject(HGDIOBJ hObject)
|
|
{
|
|
INT Index;
|
|
PGDI_TABLE_ENTRY Entry;
|
|
DWORD dwObjectType;
|
|
PVOID pAttr = NULL;
|
|
|
|
DPRINT("NtGdiDeleteObject handle 0x%08x\n", hObject);
|
|
if (!IsObjectDead(hObject))
|
|
{
|
|
dwObjectType = GDIOBJ_GetObjectType(hObject);
|
|
|
|
Index = GDI_HANDLE_GET_INDEX(hObject);
|
|
Entry = &GdiHandleTable->Entries[Index];
|
|
pAttr = Entry->UserData;
|
|
|
|
switch (dwObjectType)
|
|
{
|
|
case GDI_OBJECT_TYPE_BRUSH:
|
|
break;
|
|
|
|
case GDI_OBJECT_TYPE_REGION:
|
|
/* If pAttr NULL, the probability is high for System Region. */
|
|
if ( pAttr &&
|
|
bPEBCacheHandle(hObject, hctRegionHandle, pAttr))
|
|
{
|
|
/* User space handle only! */
|
|
return TRUE;
|
|
}
|
|
if (pAttr)
|
|
{
|
|
FreeObjectAttr(pAttr);
|
|
Entry->UserData = NULL;
|
|
}
|
|
break;
|
|
|
|
case GDI_OBJECT_TYPE_DC:
|
|
// DC_FreeDcAttr(hObject);
|
|
break;
|
|
}
|
|
|
|
return NULL != hObject
|
|
? GDIOBJ_FreeObjByHandle(hObject, dwObjectType) : FALSE;
|
|
}
|
|
else
|
|
{
|
|
DPRINT1("Attempt DeleteObject 0x%x currently being destroyed!!!\n",hObject);
|
|
return TRUE; // return true and move on.
|
|
}
|
|
}
|
|
|
|
VOID
|
|
FASTCALL
|
|
IntDeleteHandlesForProcess(struct _EPROCESS *Process, ULONG ObjectType)
|
|
{
|
|
PGDI_TABLE_ENTRY Entry, End;
|
|
ULONG Index = RESERVE_ENTRIES_COUNT;
|
|
HANDLE ProcId;
|
|
PPROCESSINFO W32Process;
|
|
|
|
W32Process = (PPROCESSINFO)Process->Win32Process;
|
|
ASSERT(W32Process);
|
|
|
|
if (W32Process->GDIHandleCount > 0)
|
|
{
|
|
ProcId = Process->UniqueProcessId;
|
|
|
|
/* FIXME - Instead of building the handle here and delete it using GDIOBJ_FreeObj
|
|
we should delete it directly here! */
|
|
|
|
End = &GdiHandleTable->Entries[GDI_HANDLE_COUNT];
|
|
for (Entry = &GdiHandleTable->Entries[RESERVE_ENTRIES_COUNT];
|
|
Entry != End;
|
|
Entry++, Index++)
|
|
{
|
|
/* ignore the lock bit */
|
|
if ( (HANDLE)((ULONG_PTR)Entry->ProcessId & ~0x1) == ProcId)
|
|
{
|
|
if ( (Entry->Type & GDI_ENTRY_BASETYPE_MASK) == ObjectType ||
|
|
ObjectType == GDI_OBJECT_TYPE_DONTCARE)
|
|
{
|
|
HGDIOBJ ObjectHandle;
|
|
|
|
/* 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));
|
|
|
|
if (!GDIOBJ_FreeObjByHandle(ObjectHandle, GDI_OBJECT_TYPE_DONTCARE))
|
|
{
|
|
DPRINT1("Failed to delete object %p!\n", ObjectHandle);
|
|
}
|
|
|
|
if (W32Process->GDIHandleCount == 0)
|
|
{
|
|
/* there are no more gdi handles for this process, bail */
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*!
|
|
* 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;
|
|
PPROCESSINFO W32Process;
|
|
|
|
DPRINT("Starting CleanupForProcess prochandle %x Pid %d\n", Process, Process->UniqueProcessId);
|
|
CurrentProcess = PsGetCurrentProcess();
|
|
if (CurrentProcess != Process)
|
|
{
|
|
KeAttachProcess(&Process->Pcb);
|
|
}
|
|
|
|
W32Process = (PPROCESSINFO)CurrentProcess->Win32Process;
|
|
|
|
/* 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);
|
|
|
|
if (CurrentProcess != Process)
|
|
{
|
|
KeDetachProcess();
|
|
}
|
|
|
|
#ifdef GDI_DEBUG
|
|
GdiDbgHTIntegrityCheck();
|
|
#endif
|
|
|
|
DPRINT("Completed cleanup for process %d\n", Process->UniqueProcessId);
|
|
if (W32Process->GDIHandleCount > 0)
|
|
{
|
|
DPRINT1("Leaking %d handles!\n", W32Process->GDIHandleCount);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/*!
|
|
* Return pointer to the object by handle.
|
|
*
|
|
* \param hObj Object handle
|
|
* \return Pointer to the object.
|
|
*
|
|
* \note Process can only get pointer to the objects it created or global objects.
|
|
*
|
|
* \todo Get rid of the ExpectedType parameter!
|
|
*/
|
|
PGDIOBJ INTERNAL_CALL
|
|
GDIOBJ_LockObj(HGDIOBJ hObj, DWORD ExpectedType)
|
|
{
|
|
ULONG HandleIndex;
|
|
PGDI_TABLE_ENTRY Entry;
|
|
HANDLE ProcessId, HandleProcessId, LockedProcessId, PrevProcId;
|
|
POBJ Object = NULL;
|
|
ULONG HandleType, HandleUpper;
|
|
|
|
/* Check for dummy call */
|
|
if(hObj == NULL)
|
|
return NULL ;
|
|
|
|
GDIDBG_INITLOOPTRACE();
|
|
|
|
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 )
|
|
{
|
|
DPRINT("Attempted to lock object 0x%x of wrong type (Handle: 0x%x, requested: 0x%x)\n",
|
|
hObj, HandleType, ExpectedType);
|
|
GDIDBG_TRACECALLER();
|
|
GDIDBG_TRACEALLOCATOR(hObj);
|
|
GDIDBG_TRACEDELETER(hObj);
|
|
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(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);
|
|
PrevProcId = InterlockedCompareExchangePointer((PVOID*)&Entry->ProcessId,
|
|
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) )
|
|
{
|
|
PTHREADINFO Thread = (PTHREADINFO)PsGetCurrentThreadWin32Thread();
|
|
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. */
|
|
(void)InterlockedExchangePointer((PVOID*)&Entry->ProcessId, PrevProcId);
|
|
|
|
DelayExecution();
|
|
continue;
|
|
}
|
|
InterlockedIncrement((PLONG)&Object->cExclusiveLock);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* Debugging code. Report attempts to lock deleted handles and
|
|
* locking type mismatches.
|
|
*/
|
|
LockErrorDebugOutput(hObj, Entry, "GDIOBJ_LockObj");
|
|
}
|
|
|
|
/* Unlock the handle table entry. */
|
|
(void)InterlockedExchangePointer((PVOID*)&Entry->ProcessId, PrevProcId);
|
|
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* The handle is currently locked, wait some time and try again.
|
|
*/
|
|
GDIDBG_TRACELOOP(hObj, PrevProcId, NULL);
|
|
|
|
DelayExecution();
|
|
continue;
|
|
}
|
|
}
|
|
|
|
KeLeaveCriticalRegion();
|
|
|
|
return Object;
|
|
}
|
|
|
|
|
|
/*!
|
|
* Return pointer to the object by handle (and allow sharing of the handle
|
|
* across threads).
|
|
*
|
|
* \param hObj Object handle
|
|
* \return Pointer to the object.
|
|
*
|
|
* \note Process can only get pointer to the objects it created or global objects.
|
|
*
|
|
* \todo Get rid of the ExpectedType parameter!
|
|
*/
|
|
PGDIOBJ INTERNAL_CALL
|
|
GDIOBJ_ShareLockObj(HGDIOBJ hObj, DWORD ExpectedType)
|
|
{
|
|
ULONG HandleIndex;
|
|
PGDI_TABLE_ENTRY Entry;
|
|
HANDLE ProcessId, HandleProcessId, LockedProcessId, PrevProcId;
|
|
POBJ Object = NULL;
|
|
ULONG_PTR HandleType, HandleUpper;
|
|
|
|
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);
|
|
PrevProcId = InterlockedCompareExchangePointer((PVOID*)&Entry->ProcessId,
|
|
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)) )
|
|
{
|
|
Object = (POBJ)Entry->KernelData;
|
|
|
|
GDIDBG_CAPTURESHARELOCKER(HandleIndex);
|
|
#ifdef GDI_DEBUG3
|
|
if (InterlockedIncrement((PLONG)&Object->ulShareCount) == 1)
|
|
{
|
|
memset(GDIHandleLocker[HandleIndex], 0x00, GDI_STACK_LEVELS * sizeof(ULONG));
|
|
RtlCaptureStackBackTrace(1, GDI_STACK_LEVELS, (PVOID*)GDIHandleShareLocker[HandleIndex], NULL);
|
|
}
|
|
#else
|
|
InterlockedIncrement((PLONG)&Object->ulShareCount);
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* Debugging code. Report attempts to lock deleted handles and
|
|
* locking type mismatches.
|
|
*/
|
|
LockErrorDebugOutput(hObj, Entry, "GDIOBJ_ShareLockObj");
|
|
}
|
|
|
|
/* Unlock the handle table entry. */
|
|
(void)InterlockedExchangePointer((PVOID*)&Entry->ProcessId, PrevProcId);
|
|
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* The handle is currently locked, wait some time and try again.
|
|
*/
|
|
|
|
DelayExecution();
|
|
continue;
|
|
}
|
|
}
|
|
|
|
KeLeaveCriticalRegion();
|
|
|
|
return Object;
|
|
}
|
|
|
|
BOOL INTERNAL_CALL
|
|
GDIOBJ_OwnedByCurrentProcess(HGDIOBJ ObjectHandle)
|
|
{
|
|
PGDI_TABLE_ENTRY Entry;
|
|
HANDLE ProcessId;
|
|
BOOL Ret;
|
|
|
|
DPRINT("GDIOBJ_OwnedByCurrentProcess: ObjectHandle: 0x%08x\n", ObjectHandle);
|
|
|
|
if (!GDI_HANDLE_IS_STOCKOBJ(ObjectHandle))
|
|
{
|
|
ProcessId = PsGetCurrentProcessId();
|
|
|
|
Entry = GDI_HANDLE_GET_ENTRY(GdiHandleTable, ObjectHandle);
|
|
Ret = Entry->KernelData != NULL &&
|
|
(Entry->Type & GDI_ENTRY_BASETYPE_MASK) != 0 &&
|
|
(HANDLE)((ULONG_PTR)Entry->ProcessId & ~0x1) == ProcessId;
|
|
|
|
return Ret;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL INTERNAL_CALL
|
|
GDIOBJ_ConvertToStockObj(HGDIOBJ *phObj)
|
|
{
|
|
/*
|
|
* 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;
|
|
PTHREADINFO Thread;
|
|
HGDIOBJ hObj;
|
|
|
|
GDIDBG_INITLOOPTRACE();
|
|
|
|
ASSERT(phObj);
|
|
hObj = *phObj;
|
|
|
|
DPRINT("GDIOBJ_ConvertToStockObj: hObj: 0x%08x\n", hObj);
|
|
|
|
Thread = (PTHREADINFO)PsGetCurrentThreadWin32Thread();
|
|
|
|
if (!GDI_HANDLE_IS_STOCKOBJ(hObj))
|
|
{
|
|
ProcessId = PsGetCurrentProcessId();
|
|
LockedProcessId = (HANDLE)((ULONG_PTR)ProcessId | 0x1);
|
|
|
|
Entry = GDI_HANDLE_GET_ENTRY(GdiHandleTable, hObj);
|
|
|
|
LockHandle:
|
|
/* lock the object, we must not convert stock objects, so don't check!!! */
|
|
PrevProcId = InterlockedCompareExchangePointer((PVOID*)&Entry->ProcessId, LockedProcessId, ProcessId);
|
|
if (PrevProcId == ProcessId)
|
|
{
|
|
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! */
|
|
PrevType = InterlockedCompareExchange(&Entry->Type, NewType, OldType);
|
|
if (PrevType == OldType && Entry->KernelData != NULL)
|
|
{
|
|
PTHREADINFO PrevThread;
|
|
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;
|
|
PPROCESSINFO W32Process;
|
|
NTSTATUS Status;
|
|
|
|
/* FIXME */
|
|
Status = PsLookupProcessByProcessId((HANDLE)((ULONG_PTR)PrevProcId & ~0x1), &OldProcess);
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
W32Process = (PPROCESSINFO)OldProcess->Win32Process;
|
|
if (W32Process != NULL)
|
|
{
|
|
InterlockedDecrement(&W32Process->GDIHandleCount);
|
|
}
|
|
ObDereferenceObject(OldProcess);
|
|
}
|
|
}
|
|
|
|
hObj = (HGDIOBJ)((ULONG)(hObj) | GDI_HANDLE_STOCK_MASK);
|
|
*phObj = hObj;
|
|
Object->hHmgr = hObj;
|
|
|
|
/* remove the process id lock and make it global */
|
|
(void)InterlockedExchangePointer((PVOID*)&Entry->ProcessId, GDI_GLOBAL_PROCESS);
|
|
|
|
/* we're done, successfully converted the object */
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
GDIDBG_TRACELOOP(hObj, PrevThread, Thread);
|
|
|
|
/* 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! */
|
|
(void)InterlockedExchangePointer((PVOID*)&Entry->ProcessId, PrevProcId);
|
|
|
|
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);
|
|
}
|
|
}
|
|
else if (PrevProcId == LockedProcessId)
|
|
{
|
|
GDIDBG_TRACELOOP(hObj, PrevProcId, ProcessId);
|
|
|
|
/* 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);
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL INTERNAL_CALL
|
|
GDIOBJ_SetOwnership(HGDIOBJ ObjectHandle, PEPROCESS NewOwner)
|
|
{
|
|
PGDI_TABLE_ENTRY Entry;
|
|
HANDLE ProcessId, LockedProcessId, PrevProcId;
|
|
PTHREADINFO Thread;
|
|
BOOL Ret = TRUE;
|
|
|
|
GDIDBG_INITLOOPTRACE();
|
|
|
|
DPRINT("GDIOBJ_SetOwnership: hObj: 0x%x, NewProcess: 0x%x\n", ObjectHandle, (NewOwner ? PsGetProcessId(NewOwner) : 0));
|
|
|
|
Thread = (PTHREADINFO)PsGetCurrentThreadWin32Thread();
|
|
|
|
if (!GDI_HANDLE_IS_STOCKOBJ(ObjectHandle))
|
|
{
|
|
ProcessId = PsGetCurrentProcessId();
|
|
LockedProcessId = (HANDLE)((ULONG_PTR)ProcessId | 0x1);
|
|
|
|
Entry = GDI_HANDLE_GET_ENTRY(GdiHandleTable, ObjectHandle);
|
|
|
|
LockHandle:
|
|
/* lock the object, we must not convert stock objects, so don't check!!! */
|
|
PrevProcId = InterlockedCompareExchangePointer((PVOID*)&Entry->ProcessId, ProcessId, LockedProcessId);
|
|
if (PrevProcId == ProcessId)
|
|
{
|
|
PTHREADINFO PrevThread;
|
|
|
|
if ((Entry->Type & GDI_ENTRY_BASETYPE_MASK) != 0)
|
|
{
|
|
POBJ Object = Entry->KernelData;
|
|
|
|
PrevThread = Object->Tid;
|
|
if (Object->cExclusiveLock == 0 || PrevThread == Thread)
|
|
{
|
|
PEPROCESS OldProcess;
|
|
PPROCESSINFO W32Process;
|
|
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))
|
|
{
|
|
W32Process = (PPROCESSINFO)OldProcess->Win32Process;
|
|
if (W32Process != NULL)
|
|
{
|
|
InterlockedDecrement(&W32Process->GDIHandleCount);
|
|
}
|
|
ObDereferenceObject(OldProcess);
|
|
}
|
|
}
|
|
|
|
if (NewOwner != NULL)
|
|
{
|
|
ProcessId = PsGetProcessId(NewOwner);
|
|
|
|
/* Increase the new process' object counter */
|
|
W32Process = (PPROCESSINFO)NewOwner->Win32Process;
|
|
if (W32Process != NULL)
|
|
{
|
|
InterlockedIncrement(&W32Process->GDIHandleCount);
|
|
}
|
|
}
|
|
else
|
|
ProcessId = 0;
|
|
|
|
/* remove the process id lock and change it to the new process id */
|
|
(void)InterlockedExchangePointer((PVOID*)&Entry->ProcessId, ProcessId);
|
|
|
|
/* we're done! */
|
|
return Ret;
|
|
}
|
|
else
|
|
{
|
|
GDIDBG_TRACELOOP(ObjectHandle, PrevThread, Thread);
|
|
|
|
/* 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! */
|
|
(void)InterlockedExchangePointer((PVOID*)&Entry->ProcessId, PrevProcId);
|
|
|
|
DelayExecution();
|
|
goto LockHandle;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
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);
|
|
Ret = FALSE;
|
|
}
|
|
}
|
|
else if (PrevProcId == LockedProcessId)
|
|
{
|
|
GDIDBG_TRACELOOP(ObjectHandle, PrevProcId, ProcessId);
|
|
|
|
/* 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());
|
|
Ret = FALSE;
|
|
}
|
|
else
|
|
{
|
|
DPRINT1("Attempted to change owner of invalid handle: 0x%x\n", ObjectHandle);
|
|
Ret = FALSE;
|
|
}
|
|
}
|
|
return Ret;
|
|
}
|
|
|
|
BOOL INTERNAL_CALL
|
|
GDIOBJ_CopyOwnership(HGDIOBJ CopyFrom, HGDIOBJ CopyTo)
|
|
{
|
|
PGDI_TABLE_ENTRY FromEntry;
|
|
PTHREADINFO Thread;
|
|
HANDLE FromProcessId, FromLockedProcessId, FromPrevProcId;
|
|
BOOL Ret = TRUE;
|
|
|
|
GDIDBG_INITLOOPTRACE();
|
|
|
|
DPRINT("GDIOBJ_CopyOwnership: from: 0x%x, to: 0x%x\n", CopyFrom, CopyTo);
|
|
|
|
Thread = (PTHREADINFO)PsGetCurrentThreadWin32Thread();
|
|
|
|
if (!GDI_HANDLE_IS_STOCKOBJ(CopyFrom) && !GDI_HANDLE_IS_STOCKOBJ(CopyTo))
|
|
{
|
|
FromEntry = GDI_HANDLE_GET_ENTRY(GdiHandleTable, CopyFrom);
|
|
|
|
FromProcessId = (HANDLE)((ULONG_PTR)FromEntry->ProcessId & ~0x1);
|
|
FromLockedProcessId = (HANDLE)((ULONG_PTR)FromProcessId | 0x1);
|
|
|
|
LockHandleFrom:
|
|
/* lock the object, we must not convert stock objects, so don't check!!! */
|
|
FromPrevProcId = InterlockedCompareExchangePointer((PVOID*)&FromEntry->ProcessId, FromProcessId, FromLockedProcessId);
|
|
if (FromPrevProcId == FromProcessId)
|
|
{
|
|
PTHREADINFO PrevThread;
|
|
POBJ Object;
|
|
|
|
if ((FromEntry->Type & GDI_ENTRY_BASETYPE_MASK) != 0)
|
|
{
|
|
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);
|
|
}
|
|
|
|
(void)InterlockedExchangePointer((PVOID*)&FromEntry->ProcessId, FromPrevProcId);
|
|
}
|
|
else
|
|
{
|
|
GDIDBG_TRACELOOP(CopyFrom, PrevThread, Thread);
|
|
|
|
/* 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! */
|
|
(void)InterlockedExchangePointer((PVOID*)&FromEntry->ProcessId, FromPrevProcId);
|
|
|
|
DelayExecution();
|
|
goto LockHandleFrom;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DPRINT1("Attempted to copy ownership from an object 0x%x currently being destroyed!!!\n", CopyFrom);
|
|
Ret = FALSE;
|
|
}
|
|
}
|
|
else if (FromPrevProcId == FromLockedProcessId)
|
|
{
|
|
GDIDBG_TRACELOOP(CopyFrom, FromPrevProcId, FromProcessId);
|
|
|
|
/* 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);
|
|
Ret = FALSE;
|
|
}
|
|
}
|
|
return Ret;
|
|
}
|
|
|
|
PVOID INTERNAL_CALL
|
|
GDI_MapHandleTable(PSECTION_OBJECT SectionObject, PEPROCESS Process)
|
|
{
|
|
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;
|
|
}
|
|
|
|
/* Locks 2 or 3 objects at a time */
|
|
VOID
|
|
INTERNAL_CALL
|
|
GDIOBJ_LockMultipleObjs(ULONG ulCount,
|
|
IN HGDIOBJ* ahObj,
|
|
OUT PGDIOBJ* apObj)
|
|
{
|
|
UINT auiIndices[3] = {0,1,2};
|
|
UINT i, tmp ;
|
|
BOOL bUnsorted = TRUE;
|
|
|
|
/* First is greatest */
|
|
while(bUnsorted)
|
|
{
|
|
bUnsorted = FALSE;
|
|
for(i=1; i<ulCount; i++)
|
|
{
|
|
if((ULONG_PTR)ahObj[auiIndices[i-1]] < (ULONG_PTR)ahObj[auiIndices[i]])
|
|
{
|
|
tmp = auiIndices[i-1];
|
|
auiIndices[i-1] = auiIndices[i];
|
|
auiIndices[i] = tmp;
|
|
bUnsorted = TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
for(i=0;i<ulCount;i++)
|
|
apObj[auiIndices[i]] = GDIOBJ_LockObj(ahObj[auiIndices[i]], GDI_OBJECT_TYPE_DONTCARE);
|
|
}
|
|
|
|
|
|
/** PUBLIC FUNCTIONS **********************************************************/
|
|
|
|
BOOL
|
|
FASTCALL
|
|
IntGdiSetRegionOwner(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!
|
|
Index = GDI_HANDLE_GET_INDEX(hRgn);
|
|
Entry = &GdiHandleTable->Entries[Index];
|
|
if (Entry->UserData) FreeObjectAttr(Entry->UserData);
|
|
Entry->UserData = NULL;
|
|
//
|
|
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;
|
|
}
|
|
|
|
BOOL
|
|
FASTCALL
|
|
IntGdiSetBrushOwner(PBRUSH pbr, DWORD OwnerMask)
|
|
{
|
|
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.
|
|
pEntry->UserData = pbr->pBrushAttr;
|
|
}
|
|
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 );
|
|
MmCopyFromCaller(&pDC->dcattr, pDC->pdcattr, sizeof(DC_ATTR));
|
|
DC_vFreeDcAttr(pDC);
|
|
DC_UnlockDc( pDC );
|
|
|
|
if (!DC_SetOwnership( hDC, NULL )) // This hDC is inaccessible!
|
|
return Ret;
|
|
}
|
|
|
|
if (OwnerMask == GDI_OBJ_HMGR_POWNED)
|
|
{
|
|
pDC = DC_LockDc ( hDC );
|
|
ASSERT(pDC->pdcattr == &pDC->dcattr);
|
|
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 );
|
|
if (IntGdiSetBrushOwner((PBRUSH)pDC->dclevel.pbrFill, OwnerMask))
|
|
IntGdiSetBrushOwner((PBRUSH)pDC->dclevel.pbrLine, OwnerMask);
|
|
DC_UnlockDc( pDC );
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
W32KAPI
|
|
HANDLE
|
|
APIENTRY
|
|
NtGdiCreateClientObj(
|
|
IN ULONG ulType
|
|
)
|
|
{
|
|
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);
|
|
|
|
return handle;
|
|
}
|
|
|
|
W32KAPI
|
|
BOOL
|
|
APIENTRY
|
|
NtGdiDeleteClientObj(
|
|
IN HANDLE h
|
|
)
|
|
{
|
|
/* 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);
|
|
}
|
|
|
|
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:
|
|
Result = PEN_GetObject((PBRUSH) pGdiObject, cbCount, (PLOGPEN) lpBuffer); // IntGdiCreatePenIndirect
|
|
break;
|
|
|
|
case GDI_OBJECT_TYPE_BRUSH:
|
|
Result = BRUSH_GetObject((PBRUSH ) pGdiObject, cbCount, (LPLOGBRUSH)lpBuffer);
|
|
break;
|
|
|
|
case GDI_OBJECT_TYPE_BITMAP:
|
|
Result = BITMAP_GetObject((SURFACE *) pGdiObject, cbCount, lpBuffer);
|
|
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:
|
|
Result = PALETTE_GetObject((PPALETTE) pGdiObject, cbCount, lpBuffer);
|
|
break;
|
|
|
|
default:
|
|
DPRINT1("GDI object type 0x%08x not implemented\n", dwObjectType);
|
|
break;
|
|
}
|
|
|
|
GDIOBJ_UnlockObjByPtr(pGdiObject);
|
|
|
|
return Result;
|
|
}
|
|
|
|
|
|
|
|
W32KAPI
|
|
INT
|
|
APIENTRY
|
|
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
|
|
_SEH2_TRY
|
|
{
|
|
// Probe the buffer and copy it
|
|
ProbeForWrite(lpBuffer, cbCopyCount, sizeof(WORD));
|
|
RtlCopyMemory(lpBuffer, &Object, cbCopyCount);
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
// Clear the return value.
|
|
// Do *NOT* set last error here!
|
|
iRetCount = 0;
|
|
}
|
|
_SEH2_END;
|
|
}
|
|
// Return the count
|
|
return iRetCount;
|
|
}
|
|
|
|
/* EOF */
|