/*
- * GDIOBJ.C - GDI object manipulation routines
+ * ReactOS W32 Subsystem
+ * Copyright (C) 1998 - 2004 ReactOS Team
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
*
- * $Id: gdiobj.c,v 1.20 2003/01/18 20:50:43 ei Exp $
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+/*
+ * GDIOBJ.C - GDI object manipulation routines
*
+ * $Id: gdiobj.c,v 1.79 2004/12/18 21:41:17 royce Exp $
*/
+#include <w32k.h>
-#undef WIN32_LEAN_AND_MEAN
-#include <windows.h>
-#include <ddk/ntddk.h>
-#include <win32k/gdiobj.h>
-#include <win32k/brush.h>
-#include <win32k/pen.h>
-#include <win32k/text.h>
-#include <win32k/dc.h>
-#include <win32k/bitmaps.h>
-#include <win32k/region.h>
#define NDEBUG
-#include <win32k/debug1.h>
-
-// GDI stock objects
-
-static LOGBRUSH WhiteBrush =
-{ BS_SOLID, RGB(255,255,255), 0 };
-
-static LOGBRUSH LtGrayBrush =
-/* FIXME : this should perhaps be BS_HATCHED, at least for 1 bitperpixel */
-{ BS_SOLID, RGB(192,192,192), 0 };
-
-static LOGBRUSH GrayBrush =
-/* FIXME : this should perhaps be BS_HATCHED, at least for 1 bitperpixel */
-{ BS_SOLID, RGB(128,128,128), 0 };
+#include <debug.h>
-static LOGBRUSH DkGrayBrush =
-/* This is BS_HATCHED, for 1 bitperpixel. This makes the spray work in pbrush */
-/* NB_HATCH_STYLES is an index into HatchBrushes */
-{ BS_HATCHED, RGB(0,0,0), NB_HATCH_STYLES };
+#ifdef __USE_W32API
+/* F*(&#$ header mess!!!! */
+HANDLE
+STDCALL PsGetProcessId(
+ PEPROCESS Process
+ );
+#endif /* __USE_W32API */
-static LOGBRUSH BlackBrush =
-{ BS_SOLID, RGB(0,0,0), 0 };
-static LOGBRUSH NullBrush =
-{ BS_NULL, 0, 0 };
-static LOGPEN WhitePen =
-{ PS_SOLID, { 0, 0 }, RGB(255,255,255) };
-static LOGPEN BlackPen =
-{ PS_SOLID, { 0, 0 }, RGB(0,0,0) };
+#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))])
-static LOGPEN NullPen =
-{ PS_NULL, { 0, 0 }, 0 };
+#define GDIBdyToHdr(body) \
+ ((PGDIOBJHDR)(body) - 1)
+#define GDIHdrToBdy(hdr) \
+ (PGDIOBJ)((PGDIOBJHDR)(hdr) + 1)
-static LOGFONT OEMFixedFont =
-{ 0, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, OEM_CHARSET,
- 0, 0, DEFAULT_QUALITY, FIXED_PITCH | FF_MODERN, L"" };
+/* apparently the first 10 entries are never used in windows as they are empty */
+#define RESERVE_ENTRIES_COUNT 10
-/* Filler to make the location counter dword aligned again. This is necessary
- since (a) LOGFONT is packed, (b) gcc places initialised variables in the code
- segment, and (c) Solaris assembler is stupid. */
-static UINT align_OEMFixedFont = 1;
-
-static LOGFONT AnsiFixedFont =
-{ 0, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, ANSI_CHARSET,
- 0, 0, DEFAULT_QUALITY, FIXED_PITCH | FF_MODERN, L"" };
-
-static UINT align_AnsiFixedFont = 1;
+typedef struct _GDI_HANDLE_TABLE
+{
+ PPAGED_LOOKASIDE_LIST LookasideLists;
-static LOGFONT AnsiVarFont =
-{ 0, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, ANSI_CHARSET,
- 0, 0, DEFAULT_QUALITY, VARIABLE_PITCH | FF_SWISS, L"MS Sans Serif" };
+ SLIST_HEADER FreeEntriesHead;
+ SLIST_ENTRY FreeEntries[((GDI_HANDLE_COUNT * sizeof(GDI_TABLE_ENTRY)) << 3) /
+ (sizeof(SLIST_ENTRY) << 3)];
-static UINT align_AnsiVarFont = 1;
+ GDI_TABLE_ENTRY Entries[GDI_HANDLE_COUNT];
+} GDI_HANDLE_TABLE, *PGDI_HANDLE_TABLE;
-static LOGFONT SystemFont =
-{ 0, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, ANSI_CHARSET,
- 0, 0, DEFAULT_QUALITY, VARIABLE_PITCH | FF_SWISS, L"System" };
+typedef struct
+{
+ ULONG Type;
+ ULONG Size;
+ GDICLEANUPPROC CleanupProc;
+} GDI_OBJ_INFO, *PGDI_OBJ_INFO;
-static UINT align_SystemFont = 1;
+/*
+ * Dummy GDI Cleanup Callback
+ */
+BOOL INTERNAL_CALL
+GDI_CleanupDummy(PVOID ObjectBody)
+{
+ return TRUE;
+}
-static LOGFONT DeviceDefaultFont =
-{ 0, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, ANSI_CHARSET,
- 0, 0, DEFAULT_QUALITY, VARIABLE_PITCH | FF_SWISS, L"" };
+/* Testing shows that regions are the most used GDIObj type,
+ so put that one first for performance */
+const
+GDI_OBJ_INFO ObjInfo[] =
+{
+ /* Type */ /* Size */ /* CleanupProc */
+ {GDI_OBJECT_TYPE_REGION, sizeof(ROSRGNDATA), RGNDATA_Cleanup},
+ {GDI_OBJECT_TYPE_BITMAP, sizeof(BITMAPOBJ), BITMAP_Cleanup},
+ {GDI_OBJECT_TYPE_DC, sizeof(DC), DC_Cleanup},
+ {GDI_OBJECT_TYPE_PALETTE, sizeof(PALGDI), PALETTE_Cleanup},
+ {GDI_OBJECT_TYPE_BRUSH, sizeof(GDIBRUSHOBJ), BRUSH_Cleanup},
+ {GDI_OBJECT_TYPE_PEN, sizeof(GDIBRUSHOBJ), GDI_CleanupDummy},
+ {GDI_OBJECT_TYPE_FONT, sizeof(TEXTOBJ), GDI_CleanupDummy},
+ {GDI_OBJECT_TYPE_DCE, sizeof(DCE), DCE_Cleanup},
+/*{GDI_OBJECT_TYPE_DIRECTDRAW, sizeof(DD_DIRECTDRAW), DD_Cleanup},
+ {GDI_OBJECT_TYPE_DD_SURFACE, sizeof(DD_SURFACE), DDSURF_Cleanup},*/
+ {GDI_OBJECT_TYPE_EXTPEN, 0, GDI_CleanupDummy},
+ {GDI_OBJECT_TYPE_METADC, 0, GDI_CleanupDummy},
+ {GDI_OBJECT_TYPE_METAFILE, 0, GDI_CleanupDummy},
+ {GDI_OBJECT_TYPE_ENHMETAFILE, 0, GDI_CleanupDummy},
+ {GDI_OBJECT_TYPE_ENHMETADC, 0, GDI_CleanupDummy},
+ {GDI_OBJECT_TYPE_MEMDC, 0, GDI_CleanupDummy},
+ {GDI_OBJECT_TYPE_EMF, 0, GDI_CleanupDummy}
+};
+
+#define OBJTYPE_COUNT (sizeof(ObjInfo) / sizeof(ObjInfo[0]))
+
+static PGDI_HANDLE_TABLE HandleTable = NULL;
+static LARGE_INTEGER ShortDelay;
+
+#define DelayExecution() \
+ DPRINT("%s:%i: Delay\n", __FILE__, __LINE__); \
+ KeDelayExecutionThread(KernelMode, FALSE, &ShortDelay)
-static UINT align_DeviceDefaultFont = 1;
+/*!
+ * Allocate GDI object table.
+ * \param Size - number of entries in the object table.
+*/
+static PGDI_HANDLE_TABLE INTERNAL_CALL
+GDIOBJ_iAllocHandleTable(VOID)
+{
+ PGDI_HANDLE_TABLE handleTable;
+ UINT ObjType;
+ UINT i;
+ PGDI_TABLE_ENTRY Entry;
-static LOGFONT SystemFixedFont =
-{ 0, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, ANSI_CHARSET,
- 0, 0, DEFAULT_QUALITY, FIXED_PITCH | FF_MODERN, L"" };
+ handleTable = ExAllocatePoolWithTag(NonPagedPool, sizeof(GDI_HANDLE_TABLE), TAG_GDIHNDTBLE);
+ ASSERT( handleTable );
+ RtlZeroMemory(handleTable, sizeof(GDI_HANDLE_TABLE));
+
+ /*
+ * initialize the free entry cache
+ */
+ InitializeSListHead(&handleTable->FreeEntriesHead);
+ Entry = &HandleTable->Entries[RESERVE_ENTRIES_COUNT];
+ for(i = GDI_HANDLE_COUNT - 1; i >= RESERVE_ENTRIES_COUNT; i--)
+ {
+ InterlockedPushEntrySList(&handleTable->FreeEntriesHead, &handleTable->FreeEntries[i]);
+ }
-static UINT align_SystemFixedFont = 1;
+ handleTable->LookasideLists = ExAllocatePoolWithTag(NonPagedPool,
+ OBJTYPE_COUNT * sizeof(PAGED_LOOKASIDE_LIST),
+ TAG_GDIHNDTBLE);
+ if(handleTable->LookasideLists == NULL)
+ {
+ ExFreePool(handleTable);
+ return NULL;
+ }
-/* FIXME: Is this correct? */
-static LOGFONT DefaultGuiFont =
-{ 0, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, ANSI_CHARSET,
- 0, 0, DEFAULT_QUALITY, VARIABLE_PITCH | FF_SWISS, L"MS Sans Serif" };
+ for(ObjType = 0; ObjType < OBJTYPE_COUNT; ObjType++)
+ {
+ ExInitializePagedLookasideList(handleTable->LookasideLists + ObjType, NULL, NULL, 0,
+ ObjInfo[ObjType].Size + sizeof(GDIOBJHDR), TAG_GDIOBJ, 0);
+ }
-static UINT align_DefaultGuiFont = 1;
+ ShortDelay.QuadPart = -5000LL; /* FIXME - 0.5 ms? */
-static HGDIOBJ *StockObjects[NB_STOCK_OBJECTS]; // we dont assign these statically as WINE does because we might redesign
- // the way handles work, so it's more dynamic now
+ return handleTable;
+}
+static inline PPAGED_LOOKASIDE_LIST
+FindLookasideList(DWORD ObjectType)
+{
+ int Index;
-HBITMAP hPseudoStockBitmap; /*! 1x1 bitmap for memory DCs */
+ for (Index = 0; Index < OBJTYPE_COUNT; Index++)
+ {
+ if (ObjInfo[Index].Type == ObjectType)
+ {
+ return HandleTable->LookasideLists + Index;
+ }
+ }
-static PGDI_HANDLE_TABLE HandleTable = 0;
-static FAST_MUTEX HandleTableMutex;
-static FAST_MUTEX RefCountHandling;
+ DPRINT1("Can't find lookaside list for object type 0x%08x\n", ObjectType);
-/*! Size of the GDI handle table
- * per http://www.wd-mag.com/articles/1999/9902/9902b/9902b.htm?topic=articles
- * gdi handle table can hold 0x4000 handles
-*/
-#define GDI_HANDLE_NUMBER 0x4000
+ return NULL;
+}
-/*!
- * Allocate GDI object table.
- * \param Size - number of entries in the object table.
-*/
-static PGDI_HANDLE_TABLE
-GDIOBJ_iAllocHandleTable (WORD Size)
+static inline BOOL
+RunCleanupCallback(PGDIOBJ pObj, DWORD ObjectType)
{
- PGDI_HANDLE_TABLE handleTable;
-
- ExAcquireFastMutexUnsafe (&HandleTableMutex);
- handleTable = ExAllocatePool(PagedPool,
- sizeof (GDI_HANDLE_TABLE) +
- sizeof (GDI_HANDLE_ENTRY) * Size);
- ASSERT( handleTable );
- memset (handleTable,
- 0,
- sizeof (GDI_HANDLE_TABLE) + sizeof (GDI_HANDLE_ENTRY) * Size);
- handleTable->wTableSize = Size;
- ExReleaseFastMutexUnsafe (&HandleTableMutex);
+ int Index;
- return handleTable;
-}
+ for (Index = 0; Index < OBJTYPE_COUNT; Index++)
+ {
+ if (ObjInfo[Index].Type == ObjectType)
+ {
+ return ((GDICLEANUPPROC)ObjInfo[Index].CleanupProc)(pObj);
+ }
+ }
-/*!
- * Returns the entry into the handle table by index.
-*/
-static PGDI_HANDLE_ENTRY
-GDIOBJ_iGetHandleEntryForIndex (WORD TableIndex)
-{
- //DPRINT("GDIOBJ_iGetHandleEntryForIndex: TableIndex: %d,\n handle: %x, ptr: %x\n", TableIndex, HandleTable->Handles [TableIndex], &(HandleTable->Handles [TableIndex]) );
- //DPRINT("GIG: HandleTable: %x, Handles: %x, \n TableIndex: %x, pt: %x\n", HandleTable, HandleTable->Handles, TableIndex, ((PGDI_HANDLE_ENTRY)HandleTable->Handles+TableIndex));
- //DPRINT("GIG: Hndl: %x, mag: %x\n", ((PGDI_HANDLE_ENTRY)HandleTable->Handles+TableIndex), ((PGDI_HANDLE_ENTRY)HandleTable->Handles+TableIndex)->wMagic);
- return ((PGDI_HANDLE_ENTRY)HandleTable->Handles+TableIndex);
+ DPRINT1("Can't find cleanup callback for object type 0x%08x\n", ObjectType);
+ return TRUE;
}
-/*!
- * Finds next free entry in the GDI handle table.
- * \return index into the table is successful, zero otherwise.
-*/
-static WORD
-GDIOBJ_iGetNextOpenHandleIndex (void)
+static inline ULONG
+GetObjectSize(DWORD ObjectType)
{
- WORD tableIndex;
+ int Index;
- ExAcquireFastMutexUnsafe (&HandleTableMutex);
- for (tableIndex = 1; tableIndex < HandleTable->wTableSize; tableIndex++)
+ for (Index = 0; Index < OBJTYPE_COUNT; Index++)
{
- if (HandleTable->Handles [tableIndex].wMagic == 0)
+ if (ObjInfo[Index].Type == ObjectType)
{
- HandleTable->Handles [tableIndex].wMagic = GO_MAGIC_DONTCARE;
- break;
+ return ObjInfo[Index].Size;
}
}
- ExReleaseFastMutexUnsafe (&HandleTableMutex);
- return (tableIndex < HandleTable->wTableSize) ? tableIndex : 0;
+ DPRINT1("Can't find size for object type 0x%08x\n", ObjectType);
+ return 0;
}
+#ifdef DBG
+
+static int leak_reported = 0;
+#define GDI_STACK_LEVELS 4
+static ULONG GDIHandleAllocator[GDI_STACK_LEVELS][GDI_HANDLE_COUNT];
+struct DbgOpenGDIHandle
+{
+ ULONG loc;
+ int count;
+};
+#define H 1024
+static struct DbgOpenGDIHandle h[H];
+
+void IntDumpHandleTable ( int which )
+{
+ int i, n = 0, j;
+
+ // step through GDI handle table and find out who our culprit is...
+ for ( i = RESERVE_ENTRIES_COUNT; i < GDI_HANDLE_COUNT; i++ )
+ {
+ for ( j = 0; j < n; j++ )
+ {
+ if ( GDIHandleAllocator[which][i] == h[j].loc )
+ break;
+ }
+ if ( j < H )
+ {
+ if ( j == n )
+ {
+ h[j].loc = GDIHandleAllocator[which][i];
+ h[j].count = 1;
+ n = n + 1;
+ }
+ else
+ h[j].count++;
+ }
+ }
+ // bubble sort time! weeeeee!!
+ for ( i = 0; i < n-1; i++ )
+ {
+ if ( h[i].count < h[i+1].count )
+ {
+ struct DbgOpenGDIHandle t;
+ t.loc = h[i+1].loc;
+ t.count = h[i+1].count;
+ h[i+1].loc = h[i].loc;
+ h[i+1].count = h[i].count;
+ j = i;
+ while ( j > 0 && h[j-1].count < t.count )
+ j--;
+ h[j] = t;
+ }
+ }
+ // print the first 30 offenders...
+ DbgPrint ( "Worst GDI Handle leak offenders - stack trace level %i (out of %i unique locations):\n", which, n );
+ for ( i = 0; i < 30 && i < n; i++ )
+ {
+ DbgPrint ( "\t" );
+ if ( !KeRosPrintAddress ( (PVOID)h[i].loc ) )
+ DbgPrint ( "<%X>", h[i].loc );
+ DbgPrint ( " (%i allocations)\n", h[i].count );
+ }
+}
+#endif//DBG
+
/*!
* Allocate memory for GDI object and return handle to it.
*
- * \param Size - size of the GDI object. This shouldn't to include the size of GDIOBJHDR.
- * The actual amount of allocated memory is sizeof(GDIOBJHDR)+Size
- * \param Magic - object magic (see GDI Magic)
+ * \param ObjectType - type of object \ref GDI object types
*
* \return Handle of the allocated object.
*
* \note Use GDIOBJ_Lock() to obtain pointer to the new object.
+ * \todo return the object pointer and lock it by default.
*/
-HGDIOBJ GDIOBJ_AllocObj(WORD Size, WORD Magic)
+HGDIOBJ INTERNAL_CALL
+#ifdef GDI_DEBUG
+GDIOBJ_AllocObjDbg(const char* file, int line, ULONG ObjectType)
+#else /* !GDI_DEBUG */
+GDIOBJ_AllocObj(ULONG ObjectType)
+#endif /* GDI_DEBUG */
{
- PGDIOBJHDR newObject;
- PGDI_HANDLE_ENTRY handleEntry;
-
- DPRINT("GDIOBJ_AllocObj: size: %d, magic: %x\n", Size, Magic);
- newObject = ExAllocatePool (PagedPool, Size + sizeof (GDIOBJHDR));
- if (newObject == NULL)
- {
- DPRINT("GDIOBJ_AllocObj: failed\n");
- return NULL;
- }
- RtlZeroMemory (newObject, Size + sizeof (GDIOBJHDR));
-
- newObject->wTableIndex = GDIOBJ_iGetNextOpenHandleIndex ();
- newObject->dwCount = 0;
- handleEntry = GDIOBJ_iGetHandleEntryForIndex (newObject->wTableIndex);
- handleEntry->wMagic = Magic;
- handleEntry->hProcessId = PsGetCurrentProcessId ();
- handleEntry->pObject = newObject;
- DPRINT("GDIOBJ_AllocObj: object handle %d\n", newObject->wTableIndex );
- return (HGDIOBJ) newObject->wTableIndex;
+ PW32PROCESS W32Process;
+ PGDIOBJHDR newObject;
+ PPAGED_LOOKASIDE_LIST LookasideList;
+ LONG CurrentProcessId, LockedProcessId;
+#ifdef GDI_DEBUG
+ ULONG Attempts = 0;
+#endif
+
+ ASSERT(ObjectType != GDI_OBJECT_TYPE_DONTCARE);
+
+ LookasideList = FindLookasideList(ObjectType);
+ if(LookasideList != NULL)
+ {
+ newObject = ExAllocateFromPagedLookasideList(LookasideList);
+ if(newObject != NULL)
+ {
+ PSLIST_ENTRY FreeEntry;
+ PGDI_TABLE_ENTRY Entry;
+ PGDIOBJ ObjectBody;
+ LONG TypeInfo;
+
+ /* shift the process id to the left so we can use the first bit to lock
+ the object.
+ FIXME - don't shift once ROS' PIDs match with nt! */
+ CurrentProcessId = (LONG)PsGetCurrentProcessId() << 1;
+ LockedProcessId = CurrentProcessId | 0x1;
+ W32Process = PsGetWin32Process();
+
+ newObject->LockingThread = NULL;
+ newObject->Locks = 0;
+
+#ifdef GDI_DEBUG
+ newObject->createdfile = file;
+ newObject->createdline = line;
+ newObject->lockfile = NULL;
+ newObject->lockline = 0;
+#endif
+
+ ObjectBody = GDIHdrToBdy(newObject);
+
+ RtlZeroMemory(ObjectBody, GetObjectSize(ObjectType));
+
+ TypeInfo = (ObjectType & 0xFFFF0000) | (ObjectType >> 16);
+
+ FreeEntry = InterlockedPopEntrySList(&HandleTable->FreeEntriesHead);
+ if(FreeEntry != NULL)
+ {
+ LONG PrevProcId;
+ UINT Index;
+ HGDIOBJ Handle;
+
+ /* calculate the entry from the address of the entry in the free slot array */
+ Index = ((ULONG_PTR)FreeEntry - (ULONG_PTR)&HandleTable->FreeEntries[0]) /
+ sizeof(HandleTable->FreeEntries[0]);
+ Entry = &HandleTable->Entries[Index];
+ Handle = (HGDIOBJ)((Index & 0xFFFF) | (ObjectType & 0xFFFF0000));
+
+LockHandle:
+ PrevProcId = InterlockedCompareExchange(&Entry->ProcessId, LockedProcessId, 0);
+ if(PrevProcId == 0)
+ {
+ ASSERT(Entry->KernelData == NULL);
+
+ Entry->KernelData = ObjectBody;
+
+ /* we found a free entry, no need to exchange this field atomically
+ since we're holding the lock */
+ Entry->Type = TypeInfo;
+
+ /* unlock the entry */
+ InterlockedExchange(&Entry->ProcessId, CurrentProcessId);
+
+#ifdef DBG
+ {
+ PULONG Frame;
+ int which;
+#if defined __GNUC__
+ __asm__("mov %%ebp, %%ebx" : "=b" (Frame) : );
+#elif defined(_MSC_VER)
+ __asm mov [Frame], ebp
+#endif
+ Frame = (PULONG)Frame[0]; // step out of AllocObj()
+ for ( which = 0; which < GDI_STACK_LEVELS && Frame[1] != 0 && Frame[1] != 0xDEADBEEF; which++ )
+ {
+ GDIHandleAllocator[which][Index] = Frame[1]; // step out of AllocObj()
+ Frame = ((PULONG)Frame[0]);
+ }
+ for ( ; which < GDI_STACK_LEVELS; which++ )
+ GDIHandleAllocator[which][Index] = 0xDEADBEEF;
+ }
+#endif//DBG
+ //ExAllocatePool ( PagedPool, (ULONG)newObject ); // initiate red-zone verification of object we allocated
+
+ if(W32Process != NULL)
+ {
+ InterlockedIncrement(&W32Process->GDIObjects);
+ }
+
+ DPRINT("GDIOBJ_AllocObj: 0x%x ob: 0x%x\n", Handle, ObjectBody);
+ return Handle;
+ }
+ else
+ {
+#ifdef GDI_DEBUG
+ if(++Attempts > 20)
+ {
+ DPRINT1("[%d]Waiting on 0x%x\n", Attempts, Handle);
+ }
+#endif
+ /* damn, someone is trying to lock the object even though it doesn't
+ eve nexist anymore, wait a little and try again!
+ FIXME - we shouldn't loop forever! Give up after some time! */
+ DelayExecution();
+ /* try again */
+ goto LockHandle;
+ }
+ }
+
+ ExFreeToPagedLookasideList(LookasideList, newObject);
+ DPRINT1("Failed to insert gdi object into the handle table, no handles left!\n");
+#ifdef DBG
+ if ( !leak_reported )
+ {
+ DPRINT1("reporting gdi handle abusers:\n");
+ int which;
+ for ( which = 0; which < GDI_STACK_LEVELS; which++ )
+ IntDumpHandleTable(which);
+ leak_reported = 1;
+ }
+ else
+ {
+ DPRINT1("gdi handle abusers already reported!\n");
+ }
+#endif//DBG
+ }
+ else
+ {
+ DPRINT1("Not enough memory to allocate gdi object!\n");
+ }
+ }
+ else
+ {
+ DPRINT1("Failed to find lookaside list for object type 0x%x\n", ObjectType);
+ }
+ return NULL;
}
/*!
* 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.
- * \param Magic - object magic or GO_MAGIC_DONTCARE.
- * \param Flag - if set to GDIOBJFLAG_IGNOREPID then the routine doesn't check if the process that
- * tries to delete the object is the same one that created it.
+ * \param hObj - handle of the object to be deleted.
*
* \return Returns TRUE if succesful.
- *
- * \note You should only use GDIOBJFLAG_IGNOREPID if you are cleaning up after the process that terminated.
- * \note This function deferres object deletion if it is still in use.
+ * \return Returns FALSE if the cleanup routine returned FALSE or the object doesn't belong
+ * to the calling process.
*/
-BOOL GDIOBJ_FreeObj(HGDIOBJ hObj, WORD Magic, DWORD Flag)
+BOOL INTERNAL_CALL
+#ifdef GDI_DEBUG
+GDIOBJ_FreeObjDbg(const char* file, int line, HGDIOBJ hObj, DWORD ObjectType)
+#else /* !GDI_DEBUG */
+GDIOBJ_FreeObj(HGDIOBJ hObj, DWORD ObjectType)
+#endif /* GDI_DEBUG */
{
- PGDIOBJHDR objectHeader;
- PGDI_HANDLE_ENTRY handleEntry;
- PGDIOBJ Obj;
- BOOL bRet = TRUE;
+ PGDI_TABLE_ENTRY Entry;
+ PPAGED_LOOKASIDE_LIST LookasideList;
+ LONG ProcessId, LockedProcessId, PrevProcId, ExpectedType;
+#ifdef GDI_DEBUG
+ ULONG Attempts = 0;
+#endif
- handleEntry = GDIOBJ_iGetHandleEntryForIndex ((WORD)hObj & 0xffff);
- DPRINT("GDIOBJ_FreeObj: hObj: %d, magic: %x, handleEntry: %x\n", (WORD)hObj & 0xffff, Magic, handleEntry );
+ DPRINT("GDIOBJ_FreeObj: hObj: 0x%08x\n", hObj);
- if (handleEntry == 0 || (handleEntry->wMagic != Magic && Magic != GO_MAGIC_DONTCARE )
- || ((handleEntry->hProcessId != PsGetCurrentProcessId()) && !(Flag & GDIOBJFLAG_IGNOREPID))){
+ if(GDI_HANDLE_IS_STOCKOBJ(hObj))
+ {
+ DPRINT1("GDIOBJ_FreeObj() failed, can't delete stock object handle: 0x%x !!!\n", hObj);
+#ifdef GDI_DEBUG
+ DPRINT1("-> called from %s:%i\n", file, line);
+#endif
+ return FALSE;
+ }
- DPRINT("Can't Delete hObj: %d, magic: %x, pid:%d\n currpid:%d, flag:%d, hmm:%d\n",(WORD)hObj & 0xffff, handleEntry->wMagic, handleEntry->hProcessId, PsGetCurrentProcessId(), (Flag&GDIOBJFLAG_IGNOREPID), ((handleEntry->hProcessId != PsGetCurrentProcessId()) && !(Flag&GDIOBJFLAG_IGNOREPID)) );
- return FALSE;
- }
+ /* shift the process id to the left so we can use the first bit to lock the object.
+ FIXME - don't shift once ROS' PIDs match with nt! */
+ ProcessId = (LONG)PsGetCurrentProcessId() << 1;
+ LockedProcessId = ProcessId | 0x1;
- objectHeader = (PGDIOBJHDR) handleEntry->pObject;
- ASSERT(objectHeader);
- DPRINT("FreeObj: locks: %x\n", objectHeader->dwCount );
- if( !(Flag & GDIOBJFLAG_IGNORELOCK) ){
- // check that the reference count is zero. if not then set flag
- // and delete object when releaseobj is called
- ExAcquireFastMutex(&RefCountHandling);
- if( ( objectHeader->dwCount & ~0x80000000 ) > 0 ){
- DPRINT("GDIOBJ_FreeObj: delayed object deletion: count %d\n", objectHeader->dwCount);
- objectHeader->dwCount |= 0x80000000;
- ExReleaseFastMutex(&RefCountHandling);
- return TRUE;
- }
- ExReleaseFastMutex(&RefCountHandling);
- }
+ ExpectedType = ((ObjectType != GDI_OBJECT_TYPE_DONTCARE) ? ObjectType : 0);
- //allow object to delete internal data
- Obj = (PGDIOBJ)((PCHAR)handleEntry->pObject + sizeof(GDIOBJHDR));
- switch( handleEntry->wMagic ){
- case GO_REGION_MAGIC:
- bRet = RGNDATA_InternalDelete( (PROSRGNDATA) Obj );
- break;
- case GO_BITMAP_MAGIC:
- bRet = Bitmap_InternalDelete( (PBITMAPOBJ) Obj );
- break;
- case GO_DC_MAGIC:
- bRet = DC_InternalDeleteDC( (PDC) Obj );
- break;
- case GO_PEN_MAGIC:
- case GO_PALETTE_MAGIC:
- case GO_DISABLED_DC_MAGIC:
- case GO_META_DC_MAGIC:
- case GO_METAFILE_MAGIC:
- case GO_METAFILE_DC_MAGIC:
- case GO_ENHMETAFILE_MAGIC:
- case GO_ENHMETAFILE_DC_MAGIC:
-
- case GO_BRUSH_MAGIC:
- case GO_FONT_MAGIC:
- break;
- }
- handleEntry->hProcessId = 0;
- ExFreePool (handleEntry->pObject);
- handleEntry->pObject = 0;
- handleEntry->wMagic = 0;
+ Entry = GDI_HANDLE_GET_ENTRY(HandleTable, hObj);
- return TRUE;
-}
-
-/*!
- * Return pointer to the object by handle.
- *
- * \param hObj Object handle
- * \param Magic one of the magic numbers defined in \ref GDI Magic
- * \return Pointer to the object.
- *
- * \note Process can only get pointer to the objects it created or global objects.
- *
- * \todo Don't allow to lock the objects twice! Synchronization!
-*/
-PGDIOBJ GDIOBJ_LockObj( HGDIOBJ hObj, WORD Magic )
-{
- PGDI_HANDLE_ENTRY handleEntry = GDIOBJ_iGetHandleEntryForIndex ((WORD) hObj & 0xffff);
- PGDIOBJHDR objectHeader;
-
- DPRINT("GDIOBJ_LockObj: hObj: %d, magic: %x, \n handleEntry: %x, mag %x\n", hObj, Magic, handleEntry, handleEntry->wMagic);
- if (handleEntry == 0 || (handleEntry->wMagic != Magic && Magic != GO_MAGIC_DONTCARE )
- || (handleEntry->hProcessId != (HANDLE)0xFFFFFFFF &&
- handleEntry->hProcessId != PsGetCurrentProcessId ())){
- DPRINT("GDIBOJ_LockObj failed for %d, magic: %d, reqMagic\n",(WORD) hObj & 0xffff, handleEntry->wMagic, Magic);
- return NULL;
- }
-
- objectHeader = (PGDIOBJHDR) handleEntry->pObject;
- ASSERT(objectHeader);
- if( objectHeader->dwCount > 0 ){
- DbgPrint("Caution! GDIOBJ_LockObj trying to lock object second time\n" );
- DbgPrint("\t called from: %x\n", __builtin_return_address(0));
- }
+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 = InterlockedCompareExchange(&Entry->ProcessId, LockedProcessId, ProcessId);
+ if(PrevProcId == ProcessId)
+ {
+ if(Entry->Type != 0 && Entry->KernelData != NULL && (ExpectedType == 0 || ((Entry->Type << 16) == ExpectedType)))
+ {
+ PGDIOBJHDR GdiHdr;
+
+ GdiHdr = GDIBdyToHdr(Entry->KernelData);
+
+ if(GdiHdr->LockingThread == NULL)
+ {
+ BOOL Ret;
+ PW32PROCESS W32Process = PsGetWin32Process();
+ ULONG Type = Entry->Type << 16;
+
+ /* Clear the type field so when unlocking the handle it gets finally deleted */
+ Entry->Type = 0;
+ Entry->KernelData = NULL;
+
+ /* unlock the handle slot */
+ InterlockedExchange(&Entry->ProcessId, 0);
+
+ /* push this entry to the free list */
+ InterlockedPushEntrySList(&HandleTable->FreeEntriesHead,
+ &HandleTable->FreeEntries[GDI_ENTRY_TO_INDEX(HandleTable, Entry)]);
+
+ if(W32Process != NULL)
+ {
+ InterlockedDecrement(&W32Process->GDIObjects);
+ }
+
+ /* call the cleanup routine. */
+ Ret = RunCleanupCallback(GDIHdrToBdy(GdiHdr), Type);
+
+ /* Now it's time to free the memory */
+ LookasideList = FindLookasideList(Type);
+ if(LookasideList != NULL)
+ {
+ ExFreeToPagedLookasideList(LookasideList, GdiHdr);
+ }
+
+ return Ret;
+ }
+ else
+ {
+ /* the object is currently locked. just clear the type field so when the
+ object gets unlocked it will be finally deleted from the table. */
+ Entry->Type = 0;
+
+ /* unlock the handle slot */
+ InterlockedExchange(&Entry->ProcessId, 0);
+
+ /* report a successful deletion as the object is actually removed from the table */
+ return TRUE;
+ }
+ }
+ else
+ {
+ if(Entry->Type != 0)
+ {
+ DPRINT1("Attempted to delete object 0x%x, type mismatch (0x%x : 0x%x)\n", hObj, ObjectType, ExpectedType);
+ }
+ else
+ {
+ DPRINT1("Attempted to delete object 0x%x which was already deleted!\n", hObj);
+ }
+ InterlockedExchange(&Entry->ProcessId, PrevProcId);
+ }
+ }
+ else if(PrevProcId == LockedProcessId)
+ {
+#ifdef GDI_DEBUG
+ if(++Attempts > 20)
+ {
+ DPRINT1("[%d]Waiting on 0x%x\n", Attempts, hObj);
+ }
+#endif
+ /* 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((PrevProcId >> 1) == 0)
+ {
+ DPRINT1("Attempted to free global gdi handle 0x%x, caller needs to get ownership first!!!", hObj);
+ }
+ else
+ {
+ DPRINT1("Attempted to free foreign handle: 0x%x Owner: 0x%x from Caller: 0x%x\n", hObj, PrevProcId >> 1, ProcessId >> 1);
+ }
+#ifdef GDI_DEBUG
+ DPRINT1("-> called from %s:%i\n", file, line);
+#endif
+ }
- ExAcquireFastMutex(&RefCountHandling);
- objectHeader->dwCount++;
- ExReleaseFastMutex(&RefCountHandling);
- return (PGDIOBJ)((PCHAR)objectHeader + sizeof(GDIOBJHDR));
+ return FALSE;
}
/*!
* Lock multiple objects. Use this function when you need to lock multiple objects and some of them may be
* duplicates. You should use this function to avoid trying to lock the same object twice!
*
- * \param pList pointer to the list that contains handles to the objects. You should set hObj and Magic fields.
+ * \param pList pointer to the list that contains handles to the objects. You should set hObj and ObjectType fields.
* \param nObj number of objects to lock
* \return for each entry in pList this function sets pObj field to point to the object.
*
* \note this function uses an O(n^2) algoritm because we shouldn't need to call it with more than 3 or 4 objects.
*/
-BOOL GDIOBJ_LockMultipleObj( PGDIMULTILOCK pList, INT nObj )
-{
- INT i, j;
- ASSERT( pList );
- //go through the list checking for duplicate objects
- for( i = 0; i < nObj; i++ ){
- (pList+i)->pObj = NULL;
- for( j = 0; j < i; j++ ){
- if( ((pList+i)->hObj == (pList+j)->hObj)
- && ((pList+i)->Magic == (pList+j)->Magic) ){
- //already locked, so just copy the pointer to the object
- (pList+i)->pObj = (pList+j)->pObj;
- break;
- }
- }
- if( (pList+i)->pObj == NULL ){
- //object hasn't been locked, so lock it.
- (pList+i)->pObj = GDIOBJ_LockObj( (pList+i)->hObj, (pList+i)->Magic );
- }
- }
- return TRUE;
-}
-
-/*!
- * Release GDI object. Every object locked by GDIOBJ_LockObj() must be unlocked. You should unlock the object
- * as soon as you don't need to have access to it's data.
-
- * \param hObj Object handle
- * \param Magic one of the magic numbers defined in \ref GDI Magic
- *
- * \note This function performs delayed cleanup. If the object is locked when GDI_FreeObj() is called
- * then \em this function frees the object when reference count is zero.
- *
- * \todo Change synchronization algorithm.
-*/
-BOOL GDIOBJ_UnlockObj( HGDIOBJ hObj, WORD Magic )
+BOOL INTERNAL_CALL
+GDIOBJ_LockMultipleObj(PGDIMULTILOCK pList, INT nObj)
{
- PGDI_HANDLE_ENTRY handleEntry = GDIOBJ_iGetHandleEntryForIndex ((WORD) hObj & 0xffff);
- PGDIOBJHDR objectHeader;
-
- DPRINT("GDIOBJ_UnlockObj: hObj: %d, magic: %x, \n handleEntry: %x\n", hObj, Magic, handleEntry);
- if (handleEntry == 0 || (handleEntry->wMagic != Magic && Magic != GO_MAGIC_DONTCARE )
- || (handleEntry->hProcessId != (HANDLE)0xFFFFFFFF &&
- handleEntry->hProcessId != PsGetCurrentProcessId ())){
- DPRINT( "GDIOBJ_UnLockObj: failed\n");
- return FALSE;
+ INT i, j;
+ ASSERT( pList );
+ /* FIXME - check for "invalid" handles */
+ /* go through the list checking for duplicate objects */
+ for (i = 0; i < nObj; i++)
+ {
+ pList[i].pObj = NULL;
+ for (j = 0; j < i; j++)
+ {
+ if (pList[i].hObj == pList[j].hObj)
+ {
+ /* already locked, so just copy the pointer to the object */
+ pList[i].pObj = pList[j].pObj;
+ break;
+ }
}
- objectHeader = (PGDIOBJHDR) handleEntry->pObject;
- ASSERT(objectHeader);
-
- ExAcquireFastMutex(&RefCountHandling);
- if( ( objectHeader->dwCount & ~0x80000000 ) == 0 ){
- ExReleaseFastMutex(&RefCountHandling);
- DPRINT( "GDIOBJ_UnLockObj: unlock object that is not locked\n" );
- return FALSE;
+ if (NULL == pList[i].pObj)
+ {
+ /* object hasn't been locked, so lock it. */
+ if (NULL != pList[i].hObj)
+ {
+ pList[i].pObj = GDIOBJ_LockObj(pList[i].hObj, pList[i].ObjectType);
+ }
}
+ }
- objectHeader = (PGDIOBJHDR) handleEntry->pObject;
- ASSERT(objectHeader);
- objectHeader->dwCount--;
-
- if( objectHeader->dwCount == 0x80000000 ){
- //delayed object release
- objectHeader->dwCount = 0;
- ExReleaseFastMutex(&RefCountHandling);
- DPRINT("GDIOBJ_UnlockObj: delayed delete\n");
- return GDIOBJ_FreeObj( hObj, Magic, GDIOBJFLAG_DEFAULT );
- }
- ExReleaseFastMutex(&RefCountHandling);
- return TRUE;
+ return TRUE;
}
-
/*!
* Unlock multiple objects. Use this function when you need to unlock multiple objects and some of them may be
* duplicates.
*
- * \param pList pointer to the list that contains handles to the objects. You should set hObj and Magic fields.
+ * \param pList pointer to the list that contains handles to the objects. You should set hObj and ObjectType fields.
* \param nObj number of objects to lock
*
* \note this function uses O(n^2) algoritm because we shouldn't need to call it with more than 3 or 4 objects.
*/
-BOOL GDIOBJ_UnlockMultipleObj( PGDIMULTILOCK pList, INT nObj )
+BOOL INTERNAL_CALL
+GDIOBJ_UnlockMultipleObj(PGDIMULTILOCK pList, INT nObj)
{
- INT i, j;
- ASSERT( pList );
- //go through the list checking for duplicate objects
- for( i = 0; i < nObj; i++ ){
- if( (pList+i)->pObj != NULL ){
- for( j = i+1; j < nObj; j++ ){
- if( ((pList+i)->pObj == (pList+j)->pObj) ){
- //set the pointer to zero for all duplicates
- (pList+j)->pObj = NULL;
- }
- }
- GDIOBJ_UnlockObj( (pList+i)->hObj, (pList+i)->Magic );
- (pList+i)->pObj = NULL;
+ INT i, j;
+ ASSERT(pList);
+
+ /* go through the list checking for duplicate objects */
+ for (i = 0; i < nObj; i++)
+ {
+ if (NULL != pList[i].pObj)
+ {
+ for (j = i + 1; j < nObj; j++)
+ {
+ if ((pList[i].pObj == pList[j].pObj))
+ {
+ /* set the pointer to zero for all duplicates */
+ pList[j].pObj = NULL;
}
+ }
+ GDIOBJ_UnlockObj(pList[i].hObj);
+ pList[i].pObj = NULL;
}
- return TRUE;
+ }
+
+ return TRUE;
}
/*!
- * Marks the object as global. (Creator process ID is set to 0xFFFFFFFF). Global objects may be
- * accessed by any process.
- * \param ObjectHandle - handle of the object to make global.
- *
- * \note Only stock objects should be marked global.
+ * Initialization of the GDI object engine.
*/
-VOID GDIOBJ_MarkObjectGlobal(HGDIOBJ ObjectHandle)
+VOID INTERNAL_CALL
+InitGdiObjectHandleTable (VOID)
{
- PGDI_HANDLE_ENTRY handleEntry;
+ DPRINT("InitGdiObjectHandleTable\n");
- if (ObjectHandle == NULL)
- return;
+ HandleTable = GDIOBJ_iAllocHandleTable();
+ DPRINT("HandleTable: %x\n", HandleTable);
+}
- handleEntry = GDIOBJ_iGetHandleEntryForIndex ((WORD)ObjectHandle & 0xffff);
- if (handleEntry == 0)
- return;
+/*!
+ * Delete GDI object
+ * \param hObject object handle
+ * \return if the function fails the returned value is FALSE.
+*/
+BOOL STDCALL
+NtGdiDeleteObject(HGDIOBJ hObject)
+{
+ DPRINT("NtGdiDeleteObject handle 0x%08x\n", hObject);
- handleEntry->hProcessId = (HANDLE)0xFFFFFFFF;
+ return NULL != hObject
+ ? GDIOBJ_FreeObj(hObject, GDI_OBJECT_TYPE_DONTCARE) : FALSE;
}
/*!
- * Get the type (magic value) of the object.
- * \param ObjectHandle - handle of the object.
- * \return GDI Magic value.
+ * Internal function. Called when the process is destroyed to free the remaining GDI handles.
+ * \param Process - PID of the process that will be destroyed.
*/
-WORD GDIOBJ_GetHandleMagic (HGDIOBJ ObjectHandle)
+BOOL INTERNAL_CALL
+GDI_CleanupForProcess (struct _EPROCESS *Process)
{
- PGDI_HANDLE_ENTRY handleEntry;
+ PGDI_TABLE_ENTRY Entry, End;
+ PEPROCESS CurrentProcess;
+ PW32PROCESS W32Process;
+ LONG ProcId;
+ ULONG Index = RESERVE_ENTRIES_COUNT;
+
+ DPRINT("Starting CleanupForProcess prochandle %x Pid %d\n", Process, Pid);
+ CurrentProcess = PsGetCurrentProcess();
+ if (CurrentProcess != Process)
+ {
+ KeAttachProcess(Process);
+ }
+ W32Process = Process->Win32Process;
+ ASSERT(W32Process);
- if (ObjectHandle == NULL)
- return 0;
+ if(W32Process->GDIObjects > 0)
+ {
+ /* FIXME - Instead of building the handle here and delete it using GDIOBJ_FreeObj
+ we should delete it directly here! */
+ ProcId = ((LONG)Process->UniqueProcessId << 1);
+
+ End = &HandleTable->Entries[GDI_HANDLE_COUNT];
+ for(Entry = &HandleTable->Entries[RESERVE_ENTRIES_COUNT];
+ Entry < End;
+ Entry++, Index++)
+ {
+ /* ignore the lock bit */
+ if((Entry->ProcessId & ~0x1) == ProcId && Entry->Type != 0)
+ {
+ HGDIOBJ ObjectHandle;
+
+ /* Create the object handle for the entry, the upper 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 & 0xFFFF0000));
+
+ if(GDIOBJ_FreeObj(ObjectHandle, GDI_OBJECT_TYPE_DONTCARE) &&
+ W32Process->GDIObjects == 0)
+ {
+ /* there are no more gdi handles for this process, bail */
+ break;
+ }
+ }
+ }
+ }
- handleEntry = GDIOBJ_iGetHandleEntryForIndex ((WORD)ObjectHandle & 0xffff);
- if (handleEntry == 0 ||
- (handleEntry->hProcessId != (HANDLE)0xFFFFFFFF &&
- handleEntry->hProcessId != PsGetCurrentProcessId ()))
- return 0;
+ if (CurrentProcess != Process)
+ {
+ KeDetachProcess();
+ }
- return handleEntry->wMagic;
+ DPRINT("Completed cleanup for process %d\n", Pid);
+
+ return TRUE;
}
/*!
- * Initialization of the GDI object engine.
+ * 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 ObjectType parameter!
*/
-VOID
-InitGdiObjectHandleTable (void)
+PGDIOBJ INTERNAL_CALL
+#ifdef GDI_DEBUG
+GDIOBJ_LockObjDbg (const char* file, int line, HGDIOBJ hObj, DWORD ObjectType)
+#else /* !GDI_DEBUG */
+GDIOBJ_LockObj (HGDIOBJ hObj, DWORD ObjectType)
+#endif /* GDI_DEBUG */
{
- DPRINT ("InitGdiObjectHandleTable\n");
- ExInitializeFastMutex (&HandleTableMutex);
- ExInitializeFastMutex (&RefCountHandling);
+ PGDI_TABLE_ENTRY Entry;
+ PETHREAD Thread;
+ LONG ProcessId, LockedProcessId, PrevProcId, ExpectedType;
+#ifdef GDI_DEBUG
+ ULONG Attempts = 0;
+#endif
+
+ DPRINT("GDIOBJ_LockObj: hObj: 0x%08x\n", hObj);
+
+ Thread = PsGetCurrentThread();
+
+ /* shift the process id to the left so we can use the first bit to lock the object.
+ FIXME - don't shift once ROS' PIDs match with nt! */
+ ProcessId = (LONG)PsGetCurrentProcessId() << 1;
+ LockedProcessId = ProcessId | 0x1;
+
+ ExpectedType = ((ObjectType != GDI_OBJECT_TYPE_DONTCARE) ? ObjectType : 0);
+
+ Entry = GDI_HANDLE_GET_ENTRY(HandleTable, hObj);
+
+LockHandle:
+ /* lock the object, we must not delete stock objects, so don't check!!! */
+ PrevProcId = InterlockedCompareExchange(&Entry->ProcessId, LockedProcessId, ProcessId);
+ if(PrevProcId == ProcessId)
+ {
+ LONG EntryType = Entry->Type << 16;
+
+ /* we're locking an object that belongs to our process or it's a global
+ object if ProcessId == 0 here. ProcessId can only be 0 here if it previously
+ failed to lock the object and it turned out to be a global object. */
+ if(EntryType != 0 && Entry->KernelData != NULL && (ExpectedType == 0 || (EntryType == ExpectedType)))
+ {
+ PETHREAD PrevThread;
+ PGDIOBJHDR GdiHdr;
+
+ GdiHdr = GDIBdyToHdr(Entry->KernelData);
+
+ /* save the pointer to the calling thread so we know it was this thread
+ that locked the object. There's no need to do this atomically as we're
+ holding the lock of the handle slot, but this way it's easier ;) */
+ PrevThread = InterlockedCompareExchangePointer(&GdiHdr->LockingThread, Thread, NULL);
+
+ if(PrevThread == NULL || PrevThread == Thread)
+ {
+ if(++GdiHdr->Locks == 1)
+ {
+#ifdef GDI_DEBUG
+ GdiHdr->lockfile = file;
+ GdiHdr->lockline = line;
+#endif
+ }
+
+ InterlockedExchange(&Entry->ProcessId, PrevProcId);
+
+ /* we're done, return the object body */
+ return GDIHdrToBdy(GdiHdr);
+ }
+ else
+ {
+ InterlockedExchange(&Entry->ProcessId, PrevProcId);
+
+#ifdef GDI_DEBUG
+ if(++Attempts > 20)
+ {
+ DPRINT1("[%d]Waiting at %s:%i as 0x%x on 0x%x\n", Attempts, file, line, Thread, PrevThread);
+ }
+#endif
+
+ DelayExecution();
+ goto LockHandle;
+ }
+ }
+ else
+ {
+ InterlockedExchange(&Entry->ProcessId, PrevProcId);
+
+ if(EntryType == 0)
+ {
+ DPRINT1("Attempted to lock object 0x%x that is deleted!\n", hObj);
+ KeRosDumpStackFrames ( NULL, 20 );
+ }
+ else
+ {
+ DPRINT1("Attempted to lock object 0x%x, type mismatch (0x%x : 0x%x)\n", hObj, EntryType, ExpectedType);
+ KeRosDumpStackFrames ( NULL, 20 );
+ }
+#ifdef GDI_DEBUG
+ DPRINT1("-> called from %s:%i\n", file, line);
+#endif
+ }
+ }
+ else if(PrevProcId == LockedProcessId)
+ {
+#ifdef GDI_DEBUG
+ if(++Attempts > 20)
+ {
+ DPRINT1("[%d]Waiting from %s:%i on 0x%x\n", Attempts, file, line, hObj);
+ }
+#endif
+ /* the handle 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((PrevProcId & ~0x1) == 0)
+ {
+ /* we're trying to lock a global object, change the ProcessId to 0 and try again */
+ ProcessId = 0x0;
+ LockedProcessId = ProcessId |0x1;
- HandleTable = GDIOBJ_iAllocHandleTable (GDI_HANDLE_NUMBER);
- DPRINT("HandleTable: %x\n", HandleTable );
+ goto LockHandle;
+ }
+ else
+ {
+ DPRINT1("Attempted to lock foreign handle: 0x%x, Owner: 0x%x locked: 0x%x Caller: 0x%x, stockobj: 0x%x\n", hObj, PrevProcId >> 1, PrevProcId & 0x1, PsGetCurrentProcessId(), GDI_HANDLE_IS_STOCKOBJ(hObj));
+ KeRosDumpStackFrames ( NULL, 20 );
+#ifdef GDI_DEBUG
+ DPRINT1("-> called from %s:%i\n", file, line);
+#endif
+ }
- InitEngHandleTable();
+ return NULL;
}
+
/*!
- * Creates a bunch of stock objects: brushes, pens, fonts.
+ * Release GDI object. Every object locked by GDIOBJ_LockObj() must be unlocked. You should unlock the object
+ * as soon as you don't need to have access to it's data.
+
+ * \param hObj Object handle
+ *
+ * \note This function performs delayed cleanup. If the object is locked when GDI_FreeObj() is called
+ * then \em this function frees the object when reference count is zero.
*/
-VOID CreateStockObjects(void)
+BOOL INTERNAL_CALL
+#ifdef GDI_DEBUG
+GDIOBJ_UnlockObjDbg (const char* file, int line, HGDIOBJ hObj)
+#else /* !GDI_DEBUG */
+GDIOBJ_UnlockObj (HGDIOBJ hObj)
+#endif /* GDI_DEBUG */
{
- // Create GDI Stock Objects from the logical structures we've defined
-
- StockObjects[WHITE_BRUSH] = W32kCreateBrushIndirect(&WhiteBrush);
- GDIOBJ_MarkObjectGlobal(StockObjects[WHITE_BRUSH]);
- StockObjects[LTGRAY_BRUSH] = W32kCreateBrushIndirect(&LtGrayBrush);
- GDIOBJ_MarkObjectGlobal(StockObjects[LTGRAY_BRUSH]);
- StockObjects[GRAY_BRUSH] = W32kCreateBrushIndirect(&GrayBrush);
- GDIOBJ_MarkObjectGlobal(StockObjects[GRAY_BRUSH]);
- StockObjects[DKGRAY_BRUSH] = W32kCreateBrushIndirect(&DkGrayBrush);
- GDIOBJ_MarkObjectGlobal(StockObjects[DKGRAY_BRUSH]);
- StockObjects[BLACK_BRUSH] = W32kCreateBrushIndirect(&BlackBrush);
- GDIOBJ_MarkObjectGlobal(StockObjects[BLACK_BRUSH]);
- StockObjects[NULL_BRUSH] = W32kCreateBrushIndirect(&NullBrush);
- GDIOBJ_MarkObjectGlobal(StockObjects[NULL_BRUSH]);
-
- StockObjects[WHITE_PEN] = W32kCreatePenIndirect(&WhitePen);
- GDIOBJ_MarkObjectGlobal(StockObjects[WHITE_PEN]);
- StockObjects[BLACK_PEN] = W32kCreatePenIndirect(&BlackPen);
- GDIOBJ_MarkObjectGlobal(StockObjects[BLACK_PEN]);
- StockObjects[NULL_PEN] = W32kCreatePenIndirect(&NullPen);
- GDIOBJ_MarkObjectGlobal(StockObjects[NULL_PEN]);
-
- StockObjects[OEM_FIXED_FONT] = W32kCreateFontIndirect(&OEMFixedFont);
- GDIOBJ_MarkObjectGlobal(StockObjects[OEM_FIXED_FONT]);
- StockObjects[ANSI_FIXED_FONT] = W32kCreateFontIndirect(&AnsiFixedFont);
- GDIOBJ_MarkObjectGlobal(StockObjects[ANSI_FIXED_FONT]);
- StockObjects[SYSTEM_FONT] = W32kCreateFontIndirect(&SystemFont);
- GDIOBJ_MarkObjectGlobal(StockObjects[SYSTEM_FONT]);
- StockObjects[DEVICE_DEFAULT_FONT] =
- W32kCreateFontIndirect(&DeviceDefaultFont);
- GDIOBJ_MarkObjectGlobal(StockObjects[DEVICE_DEFAULT_FONT]);
- StockObjects[SYSTEM_FIXED_FONT] = W32kCreateFontIndirect(&SystemFixedFont);
- GDIOBJ_MarkObjectGlobal(StockObjects[SYSTEM_FIXED_FONT]);
- StockObjects[DEFAULT_GUI_FONT] = W32kCreateFontIndirect(&DefaultGuiFont);
- GDIOBJ_MarkObjectGlobal(StockObjects[DEFAULT_GUI_FONT]);
-
- StockObjects[DEFAULT_PALETTE] = (HGDIOBJ*)PALETTE_Init();
+ PGDI_TABLE_ENTRY Entry;
+ PETHREAD Thread;
+ LONG ProcessId, LockedProcessId, PrevProcId;
+#ifdef GDI_DEBUG
+ ULONG Attempts = 0;
+#endif
+
+ DPRINT("GDIOBJ_UnlockObj: hObj: 0x%08x\n", hObj);
+ Thread = PsGetCurrentThread();
+
+ /* shift the process id to the left so we can use the first bit to lock the object.
+ FIXME - don't shift once ROS' PIDs match with nt! */
+ ProcessId = (LONG)PsGetCurrentProcessId() << 1;
+ LockedProcessId = ProcessId | 0x1;
+
+ Entry = GDI_HANDLE_GET_ENTRY(HandleTable, hObj);
+
+LockHandle:
+ /* lock the handle, we must not delete stock objects, so don't check!!! */
+ PrevProcId = InterlockedCompareExchange(&Entry->ProcessId, LockedProcessId, ProcessId);
+ if(PrevProcId == ProcessId)
+ {
+ /* we're unlocking an object that belongs to our process or it's a global
+ object if ProcessId == 0 here. ProcessId can only be 0 here if it previously
+ failed to lock the object and it turned out to be a global object. */
+ if(Entry->KernelData != NULL)
+ {
+ PETHREAD PrevThread;
+ PGDIOBJHDR GdiHdr;
+
+ GdiHdr = GDIBdyToHdr(Entry->KernelData);
+ //ExAllocatePool ( PagedPool, (ULONG)GdiHdr ); // initiate red-zone validation on this block
+
+ PrevThread = GdiHdr->LockingThread;
+ if(PrevThread == Thread)
+ {
+ BOOL Ret;
+
+ if(--GdiHdr->Locks == 0)
+ {
+ GdiHdr->LockingThread = NULL;
+
+#ifdef GDI_DEBUG
+ GdiHdr->lockfile = NULL;
+ GdiHdr->lockline = 0;
+#endif
+ }
+
+ if(Entry->Type == 0 && GdiHdr->Locks == 0)
+ {
+ PPAGED_LOOKASIDE_LIST LookasideList;
+ PW32PROCESS W32Process = PsGetWin32Process();
+ DWORD Type = GDI_HANDLE_GET_TYPE(hObj);
+
+ ASSERT(ProcessId != 0); /* must not delete a global handle!!!! */
+
+ /* we should delete the handle */
+ Entry->KernelData = NULL;
+ InterlockedExchange(&Entry->ProcessId, 0);
+
+ InterlockedPushEntrySList(&HandleTable->FreeEntriesHead,
+ &HandleTable->FreeEntries[GDI_ENTRY_TO_INDEX(HandleTable, Entry)]);
+
+ if(W32Process != NULL)
+ {
+ InterlockedDecrement(&W32Process->GDIObjects);
+ }
+
+ /* call the cleanup routine. */
+ Ret = RunCleanupCallback(GDIHdrToBdy(GdiHdr), Type);
+
+ /* Now it's time to free the memory */
+ LookasideList = FindLookasideList(Type);
+ if(LookasideList != NULL)
+ {
+ ExFreeToPagedLookasideList(LookasideList, GdiHdr);
+ }
+ }
+ else
+ {
+ /* remove the handle slot lock */
+ InterlockedExchange(&Entry->ProcessId, PrevProcId);
+ Ret = TRUE;
+ }
+
+ /* we're done*/
+ return Ret;
+ }
+#ifdef GDI_DEBUG
+ else if(PrevThread != NULL)
+ {
+ DPRINT1("Attempted to unlock object 0x%x, previously locked by other thread (0x%x) from %s:%i (called from %s:%i)\n",
+ hObj, PrevThread, GdiHdr->lockfile, GdiHdr->lockline, file, line);
+ InterlockedExchange(&Entry->ProcessId, PrevProcId);
+ }
+#endif
+ else
+ {
+#ifdef GDI_DEBUG
+ if(++Attempts > 20)
+ {
+ DPRINT1("[%d]Waiting at %s:%i as 0x%x on 0x%x\n", Attempts, file, line, Thread, PrevThread);
+ }
+#endif
+ /* FIXME - we should give up after some time unless we want to wait forever! */
+ InterlockedExchange(&Entry->ProcessId, PrevProcId);
+
+ DelayExecution();
+ goto LockHandle;
+ }
+ }
+ else
+ {
+ InterlockedExchange(&Entry->ProcessId, PrevProcId);
+ DPRINT1("Attempted to unlock object 0x%x that is deleted!\n", hObj);
+ }
+ }
+ else if(PrevProcId == LockedProcessId)
+ {
+#ifdef GDI_DEBUG
+ if(++Attempts > 20)
+ {
+ DPRINT1("[%d]Waiting from %s:%i on 0x%x\n", Attempts, file, line, hObj);
+ }
+#endif
+ /* the handle 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((PrevProcId & ~0x1) == 0)
+ {
+ /* we're trying to unlock a global object, change the ProcessId to 0 and try again */
+ ProcessId = 0x0;
+ LockedProcessId = ProcessId |0x1;
+
+ goto LockHandle;
+ }
+ else
+ {
+ DPRINT1("Attempted to unlock foreign handle: 0x%x, Owner: 0x%x locked: 0x%x Caller: 0x%x, stockobj: 0x%x\n", hObj, PrevProcId >> 1, PrevProcId & 0x1, PsGetCurrentProcessId(), GDI_HANDLE_IS_STOCKOBJ(hObj));
+ }
+
+ return FALSE;
}
-/*!
- * Return stock object.
- * \param Object - stock object id.
- * \return Handle to the object.
-*/
-HGDIOBJ STDCALL W32kGetStockObject(INT Object)
+BOOL INTERNAL_CALL
+GDIOBJ_OwnedByCurrentProcess(HGDIOBJ ObjectHandle)
{
- // check when adding new objects
- if( (Object < 0) || (Object >= NB_STOCK_OBJECTS) )
- return NULL;
- return StockObjects[Object];
+ PGDI_TABLE_ENTRY Entry;
+ LONG ProcessId;
+ BOOL Ret;
+
+ DPRINT("GDIOBJ_OwnedByCurrentProcess: ObjectHandle: 0x%08x\n", ObjectHandle);
+
+ if(!GDI_HANDLE_IS_STOCKOBJ(ObjectHandle))
+ {
+ ProcessId = (LONG)PsGetCurrentProcessId() << 1;
+
+ Entry = GDI_HANDLE_GET_ENTRY(HandleTable, ObjectHandle);
+ Ret = Entry->KernelData != NULL &&
+ Entry->Type != 0 &&
+ (Entry->ProcessId & ~0x1) == ProcessId;
+
+ return Ret;
+ }
+
+ return FALSE;
}
-/*!
- * Delete GDI object
- * \param hObject object handle
- * \return if the function fails the returned value is NULL.
-*/
-BOOL STDCALL W32kDeleteObject(HGDIOBJ hObject)
+BOOL INTERNAL_CALL
+GDIOBJ_ConvertToStockObj(HGDIOBJ *hObj)
{
- return GDIOBJ_FreeObj( hObject, GO_MAGIC_DONTCARE, GDIOBJFLAG_DEFAULT );
+/*
+ * 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;
+ LONG ProcessId, LockedProcessId, PrevProcId;
+ PETHREAD Thread;
+#ifdef GDI_DEBUG
+ ULONG Attempts = 0;
+#endif
+
+ ASSERT(hObj);
+
+ DPRINT("GDIOBJ_ConvertToStockObj: hObj: 0x%08x\n", *hObj);
+
+ Thread = PsGetCurrentThread();
+
+ if(!GDI_HANDLE_IS_STOCKOBJ(*hObj))
+ {
+ /* shift the process id to the left so we can use the first bit to lock the object.
+ FIXME - don't shift once ROS' PIDs match with nt! */
+ ProcessId = (LONG)PsGetCurrentProcessId() << 1;
+ LockedProcessId = ProcessId | 0x1;
+
+ Entry = GDI_HANDLE_GET_ENTRY(HandleTable, *hObj);
+
+LockHandle:
+ /* lock the object, we must not convert stock objects, so don't check!!! */
+ PrevProcId = InterlockedCompareExchange(&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.*/
+ NewType = GDI_HANDLE_GET_TYPE(*hObj);
+ NewType |= NewType >> 16;
+ /* This is the type that the object should have right now, save it */
+ OldType = NewType;
+ /* As the object should be a stock object, set it's flag, but only in the upper 16 bits */
+ NewType |= GDI_HANDLE_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)
+ {
+ PETHREAD PrevThread;
+ PGDIOBJHDR GdiHdr;
+
+ /* We successfully set the stock object flag.
+ KernelData should never be NULL here!!! */
+ ASSERT(Entry->KernelData);
+
+ GdiHdr = GDIBdyToHdr(Entry->KernelData);
+
+ PrevThread = GdiHdr->LockingThread;
+ if(PrevThread == NULL || PrevThread == Thread)
+ {
+ /* dereference the process' object counter */
+ if(PrevProcId != GDI_GLOBAL_PROCESS)
+ {
+ PEPROCESS OldProcess;
+ PW32PROCESS W32Process;
+ NTSTATUS Status;
+
+ /* FIXME */
+ Status = PsLookupProcessByProcessId((PVOID)(PrevProcId >> 1), &OldProcess);
+ if(NT_SUCCESS(Status))
+ {
+ W32Process = OldProcess->Win32Process;
+ if(W32Process != NULL)
+ {
+ InterlockedDecrement(&W32Process->GDIObjects);
+ }
+ ObDereferenceObject(OldProcess);
+ }
+ }
+
+ /* remove the process id lock and make it global */
+ InterlockedExchange(&Entry->ProcessId, GDI_GLOBAL_PROCESS);
+
+ *hObj = (HGDIOBJ)((ULONG)(*hObj) | GDI_HANDLE_STOCK_MASK);
+
+ /* we're done, successfully converted the object */
+ return TRUE;
+ }
+ else
+ {
+#ifdef GDI_DEBUG
+ if(++Attempts > 20)
+ {
+ if(GdiHdr->lockfile != NULL)
+ {
+ DPRINT1("[%d]Locked %s:%i by 0x%x (we're 0x%x)\n", Attempts, GdiHdr->lockfile, GdiHdr->lockline, PrevThread, Thread);
+ }
+ }
+#endif
+ /* 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! */
+ InterlockedExchange(&Entry->ProcessId, PrevProcId);
+
+ DelayExecution();
+ goto LockHandle;
+ }
+ }
+ else
+ {
+ DPRINT1("Attempted to convert object 0x%x that is deleted! Should never get here!!!\n", hObj);
+ }
+ }
+ else if(PrevProcId == LockedProcessId)
+ {
+#ifdef GDI_DEBUG
+ if(++Attempts > 20)
+ {
+ DPRINT1("[%d]Waiting on 0x%x\n", Attempts, hObj);
+ }
+#endif
+ /* 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;
}
-/*!
- * Internal function. Called when the process is destroyed to free the remaining GDI handles.
- * \param Process - PID of the process that was destroyed.
-*/
-BOOL STDCALL W32kCleanupForProcess( INT Process )
+void INTERNAL_CALL
+GDIOBJ_SetOwnership(HGDIOBJ ObjectHandle, PEPROCESS NewOwner)
{
- DWORD i;
- PGDI_HANDLE_ENTRY handleEntry;
- PGDIOBJHDR objectHeader;
-
- for( i=1; i < GDI_HANDLE_NUMBER; i++ ){
- handleEntry = GDIOBJ_iGetHandleEntryForIndex ((WORD) i & 0xffff);
- if( handleEntry && handleEntry->wMagic != 0 && handleEntry->hProcessId == Process){
- objectHeader = (PGDIOBJHDR) handleEntry->pObject;
- DPRINT("\nW32kCleanup: %d, magic: %x \n process: %d, locks: %d", i, handleEntry->wMagic, handleEntry->hProcessId, objectHeader->dwCount);
- GDIOBJ_FreeObj( (WORD) i & 0xffff, GO_MAGIC_DONTCARE, GDIOBJFLAG_IGNOREPID|GDIOBJFLAG_IGNORELOCK );
- }
- }
- return TRUE;
+ PGDI_TABLE_ENTRY Entry;
+ LONG ProcessId, LockedProcessId, PrevProcId;
+ PETHREAD Thread;
+#ifdef GDI_DEBUG
+ ULONG Attempts = 0;
+#endif
+
+ DPRINT("GDIOBJ_SetOwnership: hObj: 0x%x, NewProcess: 0x%x\n", ObjectHandle, (NewOwner ? PsGetProcessId(NewOwner) : 0));
+
+ Thread = PsGetCurrentThread();
+
+ if(!GDI_HANDLE_IS_STOCKOBJ(ObjectHandle))
+ {
+ /* shift the process id to the left so we can use the first bit to lock the object.
+ FIXME - don't shift once ROS' PIDs match with nt! */
+ ProcessId = (LONG)PsGetCurrentProcessId() << 1;
+ LockedProcessId = ProcessId | 0x1;
+
+ Entry = GDI_HANDLE_GET_ENTRY(HandleTable, ObjectHandle);
+
+LockHandle:
+ /* lock the object, we must not convert stock objects, so don't check!!! */
+ PrevProcId = InterlockedCompareExchange(&Entry->ProcessId, ProcessId, LockedProcessId);
+ if(PrevProcId == ProcessId)
+ {
+ PETHREAD PrevThread;
+
+ if(Entry->Type != 0 && Entry->KernelData != NULL)
+ {
+ PGDIOBJHDR GdiHdr = GDIBdyToHdr(Entry->KernelData);
+
+ PrevThread = GdiHdr->LockingThread;
+ if(PrevThread == NULL || PrevThread == Thread)
+ {
+ PEPROCESS OldProcess;
+ PW32PROCESS W32Process;
+ NTSTATUS Status;
+
+ /* dereference the process' object counter */
+ /* FIXME */
+ Status = PsLookupProcessByProcessId((PVOID)(PrevProcId >> 1), &OldProcess);
+ if(NT_SUCCESS(Status))
+ {
+ W32Process = OldProcess->Win32Process;
+ if(W32Process != NULL)
+ {
+ InterlockedDecrement(&W32Process->GDIObjects);
+ }
+ ObDereferenceObject(OldProcess);
+ }
+
+ if(NewOwner != NULL)
+ {
+ /* FIXME */
+ ProcessId = (LONG)PsGetProcessId(NewOwner) << 1;
+
+ /* Increase the new process' object counter */
+ W32Process = NewOwner->Win32Process;
+ if(W32Process != NULL)
+ {
+ InterlockedIncrement(&W32Process->GDIObjects);
+ }
+ }
+ else
+ ProcessId = 0;
+
+ /* remove the process id lock and change it to the new process id */
+ InterlockedExchange(&Entry->ProcessId, ProcessId);
+
+ /* we're done! */
+ return;
+ }
+ else
+ {
+#ifdef GDI_DEBUG
+ if(++Attempts > 20)
+ {
+ if(GdiHdr->lockfile != NULL)
+ {
+ DPRINT1("[%d]Locked from %s:%i by 0x%x (we're 0x%x)\n", Attempts, GdiHdr->lockfile, GdiHdr->lockline, PrevThread, Thread);
+ }
+ }
+#endif
+ /* 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! */
+ InterlockedExchange(&Entry->ProcessId, PrevProcId);
+
+ DelayExecution();
+ goto LockHandle;
+ }
+ }
+ else
+ {
+ DPRINT1("Attempted to change ownership of an object 0x%x currently being destroyed!!!\n", ObjectHandle);
+ }
+ }
+ else if(PrevProcId == LockedProcessId)
+ {
+#ifdef GDI_DEBUG
+ if(++Attempts > 20)
+ {
+ DPRINT1("[%d]Waiting on 0x%x\n", Attempts, ObjectHandle);
+ }
+#endif
+ /* 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((PrevProcId >> 1) == 0)
+ {
+ /* allow changing ownership of global objects */
+ ProcessId = 0;
+ LockedProcessId = ProcessId | 0x1;
+ goto LockHandle;
+ }
+ else if((PrevProcId >> 1) != (LONG)PsGetCurrentProcessId())
+ {
+ DPRINT1("Attempted to change ownership of object 0x%x (pid: 0x%x) from pid 0x%x!!!\n", ObjectHandle, PrevProcId >> 1, PsGetCurrentProcessId());
+ }
+ else
+ {
+ DPRINT1("Attempted to change owner of invalid handle: 0x%x\n", ObjectHandle);
+ }
+ }
}
-/*!
- * Internal function. Dumps all the objects for the given process.
- * \param If process == 0 dump all the objects.
- *
-*/
-VOID STDCALL W32kDumpGdiObjects( INT Process )
+void INTERNAL_CALL
+GDIOBJ_CopyOwnership(HGDIOBJ CopyFrom, HGDIOBJ CopyTo)
{
- DWORD i;
- PGDI_HANDLE_ENTRY handleEntry;
- PGDIOBJHDR objectHeader;
-
- for( i=1; i < GDI_HANDLE_NUMBER; i++ ){
- handleEntry = GDIOBJ_iGetHandleEntryForIndex ((WORD) i & 0xffff);
- if( handleEntry && handleEntry->wMagic != 0 ){
- objectHeader = (PGDIOBJHDR) handleEntry->pObject;
- DPRINT("\nHandle: %d, magic: %x \n process: %d, locks: %d", i, handleEntry->wMagic, handleEntry->hProcessId, objectHeader->dwCount);
- }
- }
+ PGDI_TABLE_ENTRY FromEntry;
+ PETHREAD Thread;
+ LONG FromProcessId, FromLockedProcessId, FromPrevProcId;
+#ifdef GDI_DEBUG
+ ULONG Attempts = 0;
+#endif
+
+ DPRINT("GDIOBJ_CopyOwnership: from: 0x%x, to: 0x%x\n", CopyFrom, CopyTo);
+
+ Thread = PsGetCurrentThread();
+ if(!GDI_HANDLE_IS_STOCKOBJ(CopyFrom) && !GDI_HANDLE_IS_STOCKOBJ(CopyTo))
+ {
+ FromEntry = GDI_HANDLE_GET_ENTRY(HandleTable, CopyFrom);
+
+ FromProcessId = FromEntry->ProcessId & ~0x1;
+ FromLockedProcessId = FromProcessId | 0x1;
+
+LockHandleFrom:
+ /* lock the object, we must not convert stock objects, so don't check!!! */
+ FromPrevProcId = InterlockedCompareExchange(&FromEntry->ProcessId, FromProcessId, FromLockedProcessId);
+ if(FromPrevProcId == FromProcessId)
+ {
+ PETHREAD PrevThread;
+ PGDIOBJHDR GdiHdr;
+
+ if(FromEntry->Type != 0 && FromEntry->KernelData != NULL)
+ {
+ GdiHdr = GDIBdyToHdr(FromEntry->KernelData);
+
+ /* save the pointer to the calling thread so we know it was this thread
+ that locked the object */
+ PrevThread = GdiHdr->LockingThread;
+ if(PrevThread == NULL || PrevThread == Thread)
+ {
+ /* now let's change the ownership of the target object */
+
+ if((FromPrevProcId & ~0x1) != 0)
+ {
+ PEPROCESS ProcessTo;
+ /* FIXME */
+ if(NT_SUCCESS(PsLookupProcessByProcessId((PVOID)(FromPrevProcId >> 1), &ProcessTo)))
+ {
+ GDIOBJ_SetOwnership(CopyTo, ProcessTo);
+ ObDereferenceObject(ProcessTo);
+ }
+ }
+ else
+ {
+ /* mark the object as global */
+ GDIOBJ_SetOwnership(CopyTo, NULL);
+ }
+
+ InterlockedExchange(&FromEntry->ProcessId, FromPrevProcId);
+ }
+ else
+ {
+#ifdef GDI_DEBUG
+ if(++Attempts > 20)
+ {
+ if(GdiHdr->lockfile != NULL)
+ {
+ DPRINT1("[%d]Locked from %s:%i by 0x%x (we're 0x%x)\n", Attempts, GdiHdr->lockfile, GdiHdr->lockline, PrevThread, Thread);
+ }
+ }
+#endif
+ /* 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! */
+ InterlockedExchange(&FromEntry->ProcessId, FromPrevProcId);
+
+ DelayExecution();
+ goto LockHandleFrom;
+ }
+ }
+ else
+ {
+ DPRINT1("Attempted to copy ownership from an object 0x%x currently being destroyed!!!\n", CopyFrom);
+ }
+ }
+ else if(FromPrevProcId == FromLockedProcessId)
+ {
+#ifdef GDI_DEBUG
+ if(++Attempts > 20)
+ {
+ DPRINT1("[%d]Waiting on 0x%x\n", Attempts, CopyFrom);
+ }
+#endif
+ /* 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((FromPrevProcId >> 1) != (LONG)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, FromPrevProcId >> 1, PsGetCurrentProcessId());
+ FromProcessId = FromPrevProcId & ~0x1;
+ FromLockedProcessId = FromProcessId | 0x1;
+ goto LockHandleFrom;
+ }
+ else
+ {
+ DPRINT1("Attempted to copy ownership from invalid handle: 0x%x\n", CopyFrom);
+ }
+ }
}
+
+PVOID INTERNAL_CALL
+GDI_MapHandleTable(HANDLE hProcess)
+{
+ DPRINT("%s:%i: %s(): FIXME - Map handle table into the process memory space!\n",
+ __FILE__, __LINE__, __FUNCTION__);
+ /* FIXME - Map the entire gdi handle table read-only to userland into the
+ scope of hProcess and return the pointer */
+ return NULL;
+}
+
+/* EOF */