📄 gdiobj.c
字号:
/*
* 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.
*
* 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 28405 2007-08-18 16:32:20Z cfinck $
*/
#include <w32k.h>
#define NDEBUG
#include <debug.h>
/* FIXME include right header for KeRosDumpStackFrames */
VOID
NTAPI
KeRosDumpStackFrames(
PULONG Frame,
ULONG FrameCount
);
#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))])
#define GDIBdyToHdr(body) \
((PGDIOBJHDR)(body) - 1)
#define GDIHdrToBdy(hdr) \
(PGDIOBJ)((PGDIOBJHDR)(hdr) + 1)
/* apparently the first 10 entries are never used in windows as they are empty */
#define RESERVE_ENTRIES_COUNT 10
typedef struct
{
ULONG Type;
ULONG Size;
GDICLEANUPPROC CleanupProc;
} GDI_OBJ_INFO, *PGDI_OBJ_INFO;
/*
* Dummy GDI Cleanup Callback
*/
static BOOL INTERNAL_CALL
GDI_CleanupDummy(PVOID ObjectBody)
{
return TRUE;
}
/* Testing shows that regions are the most used GDIObj type,
so put that one first for performance */
static 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_DIRECTDRAW, sizeof(DD_DIRECTDRAW), DD_Cleanup},
{GDI_OBJECT_TYPE_DD_SURFACE, sizeof(DD_SURFACE), DDSURF_Cleanup},
{GDI_OBJECT_TYPE_EXTPEN, sizeof(GDIBRUSHOBJ), EXTPEN_Cleanup},
/* FIXME do not use normal DC struct for this */
{GDI_OBJECT_TYPE_METADC, sizeof(DC), GDI_CleanupDummy},
{GDI_OBJECT_TYPE_METAFILE, sizeof(DC), GDI_CleanupDummy},
{GDI_OBJECT_TYPE_ENHMETAFILE, 0, GDI_CleanupDummy},
{GDI_OBJECT_TYPE_EMF, 0, GDI_CleanupDummy}
};
#define OBJTYPE_COUNT (sizeof(ObjInfo) / sizeof(ObjInfo[0]))
static LARGE_INTEGER ShortDelay;
#define DelayExecution() \
DPRINT("%s:%i: Delay\n", __FILE__, __LINE__); \
KeDelayExecutionThread(KernelMode, FALSE, &ShortDelay)
#ifdef GDI_DEBUG
BOOLEAN STDCALL KiRosPrintAddress(PVOID Address);
VOID STDCALL KeRosDumpStackFrames(PULONG Frame, ULONG FrameCount);
ULONG STDCALL KeRosGetStackFrames(PULONG Frames, ULONG FrameCount);
#endif
/*!
* 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;
UINT i;
ULONG ViewSize = 0;
PGDI_TABLE_ENTRY Entry;
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));
/*
* 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]);
}
HandleTable->LookasideLists = ExAllocatePoolWithTag(NonPagedPool,
OBJTYPE_COUNT * sizeof(PAGED_LOOKASIDE_LIST),
TAG_GDIHNDTBLE);
if(HandleTable->LookasideLists == NULL)
{
MmUnmapViewInSystemSpace(HandleTable);
ObDereferenceObject(*SectionObject);
*SectionObject = NULL;
return NULL;
}
for(ObjType = 0; ObjType < OBJTYPE_COUNT; ObjType++)
{
ExInitializePagedLookasideList(HandleTable->LookasideLists + ObjType, NULL, NULL, 0,
ObjInfo[ObjType].Size + sizeof(GDIOBJHDR), TAG_GDIOBJ, 0);
}
ShortDelay.QuadPart = -5000LL; /* FIXME - 0.5 ms? */
return HandleTable;
}
static __inline PPAGED_LOOKASIDE_LIST
FindLookasideList(PGDI_HANDLE_TABLE HandleTable,
DWORD ObjectType)
{
int Index;
for (Index = 0; Index < OBJTYPE_COUNT; Index++)
{
if (ObjInfo[Index].Type == ObjectType)
{
return HandleTable->LookasideLists + Index;
}
}
DPRINT1("Can't find lookaside list for object type 0x%08x\n", ObjectType);
return NULL;
}
static __inline BOOL
RunCleanupCallback(PGDIOBJ pObj, DWORD ObjectType)
{
int Index;
for (Index = 0; Index < OBJTYPE_COUNT; Index++)
{
if (ObjInfo[Index].Type == ObjectType)
{
return ((GDICLEANUPPROC)ObjInfo[Index].CleanupProc)(pObj);
}
}
DPRINT1("Can't find cleanup callback for object type 0x%08x\n", ObjectType);
return TRUE;
}
static __inline ULONG
GetObjectSize(DWORD ObjectType)
{
int Index;
for (Index = 0; Index < OBJTYPE_COUNT; Index++)
{
if (ObjInfo[Index].Type == ObjectType)
{
return ObjInfo[Index].Size;
}
}
DPRINT1("Can't find size for object type 0x%08x\n", ObjectType);
return 0;
}
#ifdef GDI_DEBUG
static int leak_reported = 0;
#define GDI_STACK_LEVELS 12
static ULONG GDIHandleAllocator[GDI_HANDLE_COUNT][GDI_STACK_LEVELS];
struct DbgOpenGDIHandle
{
ULONG idx;
int count;
};
#define H 1024
static struct DbgOpenGDIHandle h[H];
void IntDumpHandleTable(PGDI_HANDLE_TABLE HandleTable)
{
int i, n = 0, j, k, J;
if ( leak_reported )
{
DPRINT1("gdi handle abusers already reported!\n");
return;
}
leak_reported = 1;
DPRINT1("reporting gdi handle abusers:\n");
/* 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++ )
{
next:
J = h[j].idx;
for ( k = 0; k < GDI_STACK_LEVELS; k++ )
{
if ( GDIHandleAllocator[i][k]
!= GDIHandleAllocator[J][k] )
{
if ( ++j == n )
goto done;
else
goto next;
}
}
goto done;
}
done:
if ( j < H )
{
if ( j == n )
{
h[j].idx = 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 = h[i+1];
h[i+1] = h[i];
j = i;
while ( j > 0 && h[j-1].count < t.count )
j--;
h[j] = t;
}
}
/* print the worst offenders... */
DbgPrint ( "Worst GDI Handle leak offenders (out of %i unique locations):\n", n );
for ( i = 0; i < n && h[i].count > 1; i++ )
{
int j;
DbgPrint ( " %i allocs: ", h[i].count );
for ( j = 0; j < GDI_STACK_LEVELS; j++ )
{
ULONG Addr = GDIHandleAllocator[h[i].idx][j];
if ( !KiRosPrintAddress ( (PVOID)Addr ) )
DbgPrint ( "<%X>", Addr );
}
DbgPrint ( "\n" );
}
if ( i < n && h[i].count == 1 )
DbgPrint ( "(list terminated - the remaining entries have 1 allocation only)\n" );
}
#endif /* GDI_DEBUG */
static void FASTCALL
LockErrorDebugOutput(HGDIOBJ hObj, PGDI_TABLE_ENTRY Entry, LPSTR Function)
{
if (Entry->KernelData == NULL)
{
DPRINT1("%s: Attempted to lock object 0x%x that is deleted!\n", Function, 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) != GDI_HANDLE_GET_TYPE(Entry->Type))
{
DPRINT1("%s: Attempted to lock object 0x%x, type mismatch (Handle: 0x%x, Entry: 0x%x)\n",
Function, hObj, GDI_HANDLE_GET_TYPE(hObj), GDI_HANDLE_GET_TYPE(Entry->Type));
}
else
{
DPRINT1("%s: Attempted to lock object 0x%x, something went wrong, typeinfo = 0x%x\n",
Function, hObj, Entry->Type);
}
KeRosDumpStackFrames(NULL, 20);
}
/*!
* Allocate memory for GDI object and return handle to it.
*
* \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 INTERNAL_CALL
#ifdef GDI_DEBUG
GDIOBJ_AllocObjDbg(PGDI_HANDLE_TABLE HandleTable, const char* file, int line, ULONG ObjectType)
#else /* !GDI_DEBUG */
GDIOBJ_AllocObj(PGDI_HANDLE_TABLE HandleTable, ULONG ObjectType)
#endif /* GDI_DEBUG */
{
PW32PROCESS W32Process;
PGDIOBJHDR newObject;
PPAGED_LOOKASIDE_LIST LookasideList;
HANDLE CurrentProcessId, LockedProcessId;
#ifdef GDI_DEBUG
ULONG Attempts = 0;
#endif
W32Process = PsGetCurrentProcessWin32Process();
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -