⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 gdiobj.c

📁 winNT技术操作系统,国外开放的原代码和LIUX一样
💻 C
📖 第 1 页 / 共 3 页
字号:
/*
 *  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 25199 2006-12-21 17:38:36Z greatlrd $
 */

#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_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 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 */

/*!
 * 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();
  /* HACK HACK HACK: simplest-possible quota implementation - don't allow a process
     to take too many GDI objects, itself. */
  if ( W32Process && W32Process->GDIObjects >= 0x2710 )
    return NULL;

  ASSERT(ObjectType != GDI_OBJECT_TYPE_DONTCARE);

  LookasideList = FindLookasideList(HandleTable, ObjectType);
  if(LookasideList != NULL)
  {
    newObject = ExAllocateFromPagedLookasideList(LookasideList);
    if(newObject != NULL)
    {
      PSLIST_ENTRY FreeEntry;
      PGDI_TABLE_ENTRY Entry;
      PGDIOBJ ObjectBody;
      LONG TypeInfo;

      CurrentProcessId = PsGetCurrentProcessId();
      LockedProcessId = (HANDLE)((ULONG_PTR)CurrentProcessId | 0x1);

      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 & GDI_HANDLE_TYPE_MASK) | (ObjectType >> 16);

      FreeEntry = InterlockedPopEntrySList(&HandleTable->FreeEntriesHead);
      if(FreeEntry != NULL)
      {
        HANDLE PrevProcId;
        UINT Index;

        /* 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];

LockHandle:
        PrevProcId = InterlockedCompareExchangePointer(&Entry->ProcessId, LockedProcessId, 0);
        if(PrevProcId == NULL)
        {
          HGDIOBJ Handle;

          ASSERT(Entry->KernelData == NULL);

          Entry->KernelData = ObjectBody;

          /* copy the reuse-counter */
          TypeInfo |= Entry->Type & GDI_HANDLE_REUSE_MASK;

          /* we found a free entry, no need to exchange this field atomically
             since we're holding the lock */
          Entry->Type = TypeInfo;

          /* unlock the entry */
          (void)InterlockedExchangePointer(&Entry->ProcessId, CurrentProcessId);

#ifdef GDI_DEBUG
          memset ( GDIHandleAllocator[Index], 0xcd, GDI_STACK_LEVELS * sizeof(ULONG) );
          KeRosGetStackFrames ( GDIHandleAllocator[Index], GDI_STACK_LEVELS );
#endif /* GDI_DEBUG */

          if(W32Process != NULL)
          {
            InterlockedIncrement(&W32Process->GDIObjects);
          }
          Handle = (HGDIOBJ)((Index & 0xFFFF) | (TypeInfo & (GDI_HANDLE_TYPE_MASK | GDI_HANDLE_REUSE_MASK)));

          DPRINT("GDIOBJ_AllocObj: 0x%x ob: 0x%x\n", Handle, ObjectBody);
          return Handle;
        }
        else
        {
#ifdef GDI_DEBUG
          if(++Attempts > 20)
          {
            DPRINT1("[%d]Waiting on handle in index 0x%x\n", Attempts, Index);
          }
#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 GDI_DEBUG
      IntDumpHandleTable(HandleTable);
#endif /* GDI_DEBUG */
    }
    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.
 *
 * \return Returns TRUE if succesful.
 * \return Returns FALSE if the cleanup routine returned FALSE or the object doesn't belong
 * to the calling process.
*/
BOOL INTERNAL_CALL
#ifdef GDI_DEBUG
GDIOBJ_FreeObjDbg(PGDI_HANDLE_TABLE HandleTable, const char* file, int line, HGDIOBJ hObj, DWORD ObjectType)
#else /* !GDI_DEBUG */
GDIOBJ_FreeObj(PGDI_HANDLE_TABLE HandleTable, HGDIOBJ hObj, DWORD ObjectType)
#endif /* GDI_DEBUG */
{

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -