2 * PROJECT: ReactOS win32 kernel mode subsystem
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: subsystems/win32/win32k/objects/gdiobj.c
5 * PURPOSE: General GDI object manipulation routines
6 * PROGRAMMERS: Timo Kreuzer
10 * If you want to understand this code, you need to start thinking in portals.
11 * - gpaulRefCount is a global pointer to an allocated array of ULONG values,
12 * one for each handle. Bits 0 - 22 contain a reference count for the handle.
13 * It gets increased for each handle lock / reference. Bit 23 contains a valid
14 * bit. If this bit is 0, the handle got deleted and will be pushed to the free
15 * list, once all references are gone. Bits 24 - 31 contain the reuse value of
16 * the handle, which allows to check if the entry was changed before atomically
17 * exchanging the reference count.
18 * - Objects can exist with or without a handle
19 * - Objects with a handle can be locked either exclusively or shared.
20 * Both locks increase the handle reference count in gpaulRefCount.
21 * Exclusive locks also increase the BASEOBJECT's cExclusiveLock field
22 * and the first lock (can be acquired recursively) acquires a pushlock
23 * that is also stored in the BASEOBJECT.
24 * - Objects without a handle cannot have exclusive locks. Their reference
25 * count is tracked in the BASEOBJECT's ulShareCount field.
26 * - An object that is inserted in the handle table automatically has an
27 * exclusive lock. For objects that are "shared objects" (BRUSH, PALETTE, ...)
28 * this is the only way it can ever be exclusively locked. It prevents the
29 * object from being locked by another thread. A shared lock will simply fail,
30 * while an exclusive lock will succeed after the object was unlocked.
34 /* INCLUDES ******************************************************************/
43 INCREASE_THREAD_LOCK_COUNT(
46 PTHREADINFO pti
= PsGetCurrentThreadWin32Thread();
47 DBG_UNREFERENCED_PARAMETER(hobj
);
51 pti
->acExclusiveLockCount
[((ULONG_PTR
)hobj
>> 16) & 0x1f]++;
53 pti
->cExclusiveLocks
++;
59 DECREASE_THREAD_LOCK_COUNT(
62 PTHREADINFO pti
= PsGetCurrentThreadWin32Thread();
63 DBG_UNREFERENCED_PARAMETER(hobj
);
67 pti
->acExclusiveLockCount
[((ULONG_PTR
)hobj
>> 16) & 0x1f]--;
69 pti
->cExclusiveLocks
--;
74 #define ASSERT_SHARED_OBJECT_TYPE(objt) \
75 ASSERT((objt) == GDIObjType_SURF_TYPE || \
76 (objt) == GDIObjType_PAL_TYPE || \
77 (objt) == GDIObjType_LFONT_TYPE || \
78 (objt) == GDIObjType_PATH_TYPE || \
79 (objt) == GDIObjType_BRUSH_TYPE)
80 #define ASSERT_EXCLUSIVE_OBJECT_TYPE(objt) \
81 ASSERT((objt) == GDIObjType_DC_TYPE || \
82 (objt) == GDIObjType_RGN_TYPE)
83 #define ASSERT_TRYLOCK_OBJECT_TYPE(objt) \
84 ASSERT((objt) == GDIObjType_DRVOBJ_TYPE)
86 #define ASSERT_SHARED_OBJECT_TYPE(objt)
87 #define ASSERT_EXCLUSIVE_OBJECT_TYPE(objt)
88 #define ASSERT_TRYLOCK_OBJECT_TYPE(objt)
91 #if defined(_M_IX86) || defined(_M_AMD64)
92 #define InterlockedOr16 _InterlockedOr16
95 #define GDIOBJ_POOL_TAG(type) ('00hG' + (((type) & 0x1f) << 24))
99 REF_MASK_REUSE
= 0xff000000,
100 REF_INC_REUSE
= 0x01000000,
101 REF_MASK_VALID
= 0x00800000,
102 REF_MASK_COUNT
= 0x007fffff,
103 REF_MASK_INUSE
= 0x00ffffff,
106 /* GLOBALS *******************************************************************/
108 /* Per session handle table globals */
109 static PVOID gpvGdiHdlTblSection
= NULL
;
111 PULONG gpaulRefCount
;
113 ULONG gulFirstUnused
;
114 static PPAGED_LOOKASIDE_LIST gpaLookasideList
;
116 static VOID NTAPI
GDIOBJ_vCleanup(PVOID ObjectBody
);
122 NULL
, /* 00 GDIObjType_DEF_TYPE */
123 DC_vCleanup
, /* 01 GDIObjType_DC_TYPE */
124 NULL
, /* 02 GDIObjType_UNUSED1_TYPE */
125 NULL
, /* 03 GDIObjType_UNUSED2_TYPE */
126 REGION_vCleanup
, /* 04 GDIObjType_RGN_TYPE */
127 SURFACE_vCleanup
, /* 05 GDIObjType_SURF_TYPE */
128 GDIOBJ_vCleanup
, /* 06 GDIObjType_CLIENTOBJ_TYPE */
129 GDIOBJ_vCleanup
, /* 07 GDIObjType_PATH_TYPE */
130 PALETTE_vCleanup
, /* 08 GDIObjType_PAL_TYPE */
131 GDIOBJ_vCleanup
, /* 09 GDIObjType_ICMLCS_TYPE */
132 GDIOBJ_vCleanup
, /* 0a GDIObjType_LFONT_TYPE */
133 NULL
, /* 0b GDIObjType_RFONT_TYPE, unused */
134 NULL
, /* 0c GDIObjType_PFE_TYPE, unused */
135 NULL
, /* 0d GDIObjType_PFT_TYPE, unused */
136 GDIOBJ_vCleanup
, /* 0e GDIObjType_ICMCXF_TYPE */
137 NULL
, /* 0f GDIObjType_SPRITE_TYPE, unused */
138 BRUSH_vCleanup
, /* 10 GDIObjType_BRUSH_TYPE, BRUSH, PEN, EXTPEN */
139 NULL
, /* 11 GDIObjType_UMPD_TYPE, unused */
140 NULL
, /* 12 GDIObjType_UNUSED4_TYPE */
141 NULL
, /* 13 GDIObjType_SPACE_TYPE, unused */
142 NULL
, /* 14 GDIObjType_UNUSED5_TYPE */
143 NULL
, /* 15 GDIObjType_META_TYPE, unused */
144 NULL
, /* 16 GDIObjType_EFSTATE_TYPE, unused */
145 NULL
, /* 17 GDIObjType_BMFD_TYPE, unused */
146 NULL
, /* 18 GDIObjType_VTFD_TYPE, unused */
147 NULL
, /* 19 GDIObjType_TTFD_TYPE, unused */
148 NULL
, /* 1a GDIObjType_RC_TYPE, unused */
149 NULL
, /* 1b GDIObjType_TEMP_TYPE, unused */
150 DRIVEROBJ_vCleanup
,/* 1c GDIObjType_DRVOBJ_TYPE */
151 NULL
, /* 1d GDIObjType_DCIOBJ_TYPE, unused */
152 NULL
, /* 1e GDIObjType_SPOOL_TYPE, unused */
153 NULL
, /* 1f reserved entry */
156 /* INTERNAL FUNCTIONS ********************************************************/
161 GDIOBJ_vCleanup(PVOID ObjectBody
)
168 InitLookasideList(UCHAR objt
, ULONG cjSize
)
170 ExInitializePagedLookasideList(&gpaLookasideList
[objt
],
175 GDITAG_HMGR_LOOKASIDE_START
+ (objt
<< 24),
182 InitGdiHandleTable(void)
185 LARGE_INTEGER liSize
;
187 SIZE_T cjViewSize
= 0;
189 /* Create a section for the shared handle table */
190 liSize
.QuadPart
= sizeof(GDI_HANDLE_TABLE
); // GDI_HANDLE_COUNT * sizeof(ENTRY);
191 status
= MmCreateSection(&gpvGdiHdlTblSection
,
199 if (!NT_SUCCESS(status
))
201 DPRINT1("INITGDI: Could not allocate a GDI handle table.\n");
205 /* Map the section in session space */
206 status
= MmMapViewInSessionSpace(gpvGdiHdlTblSection
,
209 if (!NT_SUCCESS(status
))
211 DPRINT1("INITGDI: Failed to map handle table section\n");
212 ObDereferenceObject(gpvGdiHdlTblSection
);
216 /* Allocate memory for the reference counter table */
217 gpaulRefCount
= EngAllocSectionMem(&pvSection
,
219 GDI_HANDLE_COUNT
* sizeof(ULONG
),
223 DPRINT1("INITGDI: Failed to allocate reference table.\n");
224 ObDereferenceObject(gpvGdiHdlTblSection
);
225 return STATUS_INSUFFICIENT_RESOURCES
;
229 gulFirstUnused
= RESERVE_ENTRIES_COUNT
;
231 GdiHandleTable
= (PVOID
)gpentHmgr
;
233 /* Initialize the lookaside lists */
234 gpaLookasideList
= ExAllocatePoolWithTag(NonPagedPool
,
235 GDIObjTypeTotal
* sizeof(PAGED_LOOKASIDE_LIST
),
237 if(!gpaLookasideList
)
238 return STATUS_NO_MEMORY
;
240 InitLookasideList(GDIObjType_DC_TYPE
, sizeof(DC
));
241 InitLookasideList(GDIObjType_RGN_TYPE
, sizeof(REGION
));
242 InitLookasideList(GDIObjType_SURF_TYPE
, sizeof(SURFACE
));
243 InitLookasideList(GDIObjType_CLIENTOBJ_TYPE
, sizeof(CLIENTOBJ
));
244 InitLookasideList(GDIObjType_PATH_TYPE
, sizeof(PATH
));
245 InitLookasideList(GDIObjType_PAL_TYPE
, sizeof(PALETTE
));
246 InitLookasideList(GDIObjType_ICMLCS_TYPE
, sizeof(COLORSPACE
));
247 InitLookasideList(GDIObjType_LFONT_TYPE
, sizeof(TEXTOBJ
));
248 InitLookasideList(GDIObjType_BRUSH_TYPE
, sizeof(BRUSH
));
250 return STATUS_SUCCESS
;
255 IncrementCurrentProcessGdiHandleCount(void)
257 PPROCESSINFO ppi
= PsGetCurrentProcessWin32Process();
258 if (ppi
) InterlockedIncrement((LONG
*)&ppi
->GDIHandleCount
);
263 DecrementCurrentProcessGdiHandleCount(void)
265 PPROCESSINFO ppi
= PsGetCurrentProcessWin32Process();
266 if (ppi
) InterlockedDecrement((LONG
*)&ppi
->GDIHandleCount
);
271 IncrementGdiHandleCount(ULONG ulProcessId
)
277 Status
= PsLookupProcessByProcessId(ULongToHandle(ulProcessId
), &pep
);
278 NT_ASSERT(NT_SUCCESS(Status
));
280 ppi
= PsGetProcessWin32Process(pep
);
281 if (ppi
) InterlockedIncrement((LONG
*)&ppi
->GDIHandleCount
);
282 if (NT_SUCCESS(Status
)) ObDereferenceObject(pep
);
287 DecrementGdiHandleCount(ULONG ulProcessId
)
293 Status
= PsLookupProcessByProcessId(ULongToHandle(ulProcessId
), &pep
);
294 NT_ASSERT(NT_SUCCESS(Status
));
296 ppi
= PsGetProcessWin32Process(pep
);
297 if (ppi
) InterlockedDecrement((LONG
*)&ppi
->GDIHandleCount
);
298 if (NT_SUCCESS(Status
)) ObDereferenceObject(pep
);
303 ENTRY_pentPopFreeEntry(VOID
)
305 ULONG iFirst
, iNext
, iPrev
;
308 DPRINT("Enter InterLockedPopFreeEntry\n");
312 /* Get the index and sequence number of the first free entry */
313 iFirst
= gulFirstFree
;
315 /* Check if we have a free entry */
316 if (!(iFirst
& GDI_HANDLE_INDEX_MASK
))
318 /* Increment FirstUnused and get the new index */
319 iFirst
= InterlockedIncrement((LONG
*)&gulFirstUnused
) - 1;
321 /* Check if we have unused entries left */
322 if (iFirst
>= GDI_HANDLE_COUNT
)
324 DPRINT1("No more GDI handles left!\n");
328 /* Return the old entry */
329 return &gpentHmgr
[iFirst
];
332 /* Get a pointer to the first free entry */
333 pentFree
= &gpentHmgr
[iFirst
& GDI_HANDLE_INDEX_MASK
];
335 /* Create a new value with an increased sequence number */
336 iNext
= (USHORT
)(ULONG_PTR
)pentFree
->einfo
.pobj
;
337 iNext
|= (iFirst
& ~GDI_HANDLE_INDEX_MASK
) + 0x10000;
339 /* Try to exchange the FirstFree value */
340 iPrev
= InterlockedCompareExchange((LONG
*)&gulFirstFree
,
344 while (iPrev
!= iFirst
);
346 /* Sanity check: is entry really free? */
347 ASSERT(((ULONG_PTR
)pentFree
->einfo
.pobj
& ~GDI_HANDLE_INDEX_MASK
) == 0);
352 /* Pushes an entry of the handle table to the free list,
353 The entry must not have any references left */
356 ENTRY_vPushFreeEntry(PENTRY pentFree
)
358 ULONG iToFree
, iFirst
, iPrev
, idxToFree
;
360 DPRINT("Enter ENTRY_vPushFreeEntry\n");
362 idxToFree
= pentFree
- gpentHmgr
;
363 ASSERT((gpaulRefCount
[idxToFree
] & REF_MASK_INUSE
) == 0);
365 /* Initialize entry */
366 pentFree
->Objt
= GDIObjType_DEF_TYPE
;
367 pentFree
->ObjectOwner
.ulObj
= 0;
368 pentFree
->pUser
= NULL
;
370 /* Increase reuse counter in entry and reference counter */
371 InterlockedExchangeAdd((LONG
*)&gpaulRefCount
[idxToFree
], REF_INC_REUSE
);
372 pentFree
->FullUnique
+= 0x0100;
376 /* Get the current first free index and sequence number */
377 iFirst
= gulFirstFree
;
379 /* Set the einfo.pobj member to the index of the first free entry */
380 pentFree
->einfo
.pobj
= UlongToPtr(iFirst
& GDI_HANDLE_INDEX_MASK
);
382 /* Combine new index and increased sequence number in iToFree */
383 iToFree
= idxToFree
| ((iFirst
& ~GDI_HANDLE_INDEX_MASK
) + 0x10000);
385 /* Try to atomically update the first free entry */
386 iPrev
= InterlockedCompareExchange((LONG
*)&gulFirstFree
,
390 while (iPrev
!= iFirst
);
395 ENTRY_ReferenceEntryByHandle(HGDIOBJ hobj
, FLONG fl
)
397 ULONG ulIndex
, cNewRefs
, cOldRefs
;
400 /* Get the handle index and check if its too big */
401 ulIndex
= GDI_HANDLE_GET_INDEX(hobj
);
402 if (ulIndex
>= GDI_HANDLE_COUNT
) return NULL
;
404 /* Get pointer to the entry */
405 pentry
= &gpentHmgr
[ulIndex
];
407 /* Get the current reference count */
408 cOldRefs
= gpaulRefCount
[ulIndex
];
412 /* Check if the slot is deleted */
413 if ((cOldRefs
& REF_MASK_VALID
) == 0)
415 DPRINT("GDIOBJ: Slot is not valid: 0x%lx, hobh=%p\n", cOldRefs
, hobj
);
419 /* Check if the unique value matches */
420 if (pentry
->FullUnique
!= (USHORT
)((ULONG_PTR
)hobj
>> 16))
422 DPRINT("GDIOBJ: Wrong unique value. Handle: 0x%4x, entry: 0x%4x\n",
423 (USHORT
)((ULONG_PTR
)hobj
>> 16), pentry
->FullUnique
);
427 /* Check if the object owner is this process or public */
428 if (!(fl
& GDIOBJFLAG_IGNOREPID
) &&
429 pentry
->ObjectOwner
.ulObj
!= GDI_OBJ_HMGR_PUBLIC
&&
430 pentry
->ObjectOwner
.ulObj
!= PtrToUlong(PsGetCurrentProcessId()))
432 DPRINT("GDIOBJ: Cannot reference foreign handle %p, pentry=%p:%lx.\n",
433 hobj
, pentry
, pentry
->ObjectOwner
.ulObj
);
437 /* Try to atomically increment the reference count */
438 cNewRefs
= cOldRefs
+ 1;
439 cOldRefs
= InterlockedCompareExchange((PLONG
)&gpaulRefCount
[ulIndex
],
443 while (cNewRefs
!= cOldRefs
+ 1);
445 /* Integrity checks */
446 ASSERT((pentry
->FullUnique
& 0x1f) == pentry
->Objt
);
447 ASSERT(pentry
->einfo
.pobj
&& pentry
->einfo
.pobj
->hHmgr
== hobj
);
454 ENTRY_hInsertObject(PENTRY pentry
, POBJ pobj
, UCHAR objt
, ULONG ulOwner
)
458 /* Calculate the handle index */
459 ulIndex
= pentry
- gpentHmgr
;
461 /* Update the fields in the ENTRY */
462 pentry
->einfo
.pobj
= pobj
;
463 pentry
->Objt
= objt
& 0x1f;
464 pentry
->FullUnique
= (pentry
->FullUnique
& 0xff00) | objt
;
465 pentry
->ObjectOwner
.ulObj
= ulOwner
;
467 /* Make the handle valid with 1 reference */
468 ASSERT((gpaulRefCount
[ulIndex
] & REF_MASK_INUSE
) == 0);
469 InterlockedOr((LONG
*)&gpaulRefCount
[ulIndex
], REF_MASK_VALID
| 1);
471 /* Return the handle */
472 return (HGDIOBJ
)(((ULONG_PTR
)pentry
->FullUnique
<< 16) | ulIndex
);
477 GDIOBJ_AllocateObject(UCHAR objt
, ULONG cjSize
, FLONG fl
)
481 if (fl
& BASEFLAG_LOOKASIDE
)
483 /* Allocate the object from a lookaside list */
484 pobj
= ExAllocateFromPagedLookasideList(&gpaLookasideList
[objt
& 0x1f]);
488 /* Allocate the object from paged pool */
489 pobj
= ExAllocatePoolWithTag(PagedPool
, cjSize
, GDIOBJ_POOL_TAG(objt
));
492 if (!pobj
) return NULL
;
494 /* Initialize the object */
495 RtlZeroMemory(pobj
, cjSize
);
496 pobj
->hHmgr
= (HGDIOBJ
)((ULONG_PTR
)objt
<< 16);
497 pobj
->cExclusiveLock
= 0;
498 pobj
->ulShareCount
= 1;
499 pobj
->BaseFlags
= fl
& 0xffff;
500 DBG_INITLOG(&pobj
->slhLog
);
501 DBG_LOGEVENT(&pobj
->slhLog
, EVENT_ALLOCATE
, 0);
508 GDIOBJ_vFreeObject(POBJ pobj
)
512 DBG_CLEANUP_EVENT_LIST(&pobj
->slhLog
);
514 /* Get the object type */
515 objt
= ((ULONG_PTR
)pobj
->hHmgr
>> 16) & 0x1f;
517 /* Call the cleanup procedure */
518 ASSERT(apfnCleanup
[objt
]);
519 apfnCleanup
[objt
](pobj
);
521 /* Check if the object is allocated from a lookaside list */
522 if (pobj
->BaseFlags
& BASEFLAG_LOOKASIDE
)
524 ExFreeToPagedLookasideList(&gpaLookasideList
[objt
], pobj
);
528 ExFreePoolWithTag(pobj
, GDIOBJ_POOL_TAG(objt
));
534 GDIOBJ_vDereferenceObject(POBJ pobj
)
536 ULONG cRefs
, ulIndex
;
538 /* Calculate the index */
539 ulIndex
= GDI_HANDLE_GET_INDEX(pobj
->hHmgr
);
541 /* Check if the object has a handle */
544 /* Decrement reference count */
545 ASSERT((gpaulRefCount
[ulIndex
] & REF_MASK_COUNT
) > 0);
546 cRefs
= InterlockedDecrement((LONG
*)&gpaulRefCount
[ulIndex
]);
547 DBG_LOGEVENT(&pobj
->slhLog
, EVENT_DEREFERENCE
, cRefs
);
549 /* Check if we reached 0 and handle bit is not set */
550 if ((cRefs
& REF_MASK_INUSE
) == 0)
552 /* Make sure it's ok to delete the object */
553 ASSERT(pobj
->BaseFlags
& BASEFLAG_READY_TO_DIE
);
555 /* Check if the handle was process owned */
556 if (gpentHmgr
[ulIndex
].ObjectOwner
.ulObj
!= GDI_OBJ_HMGR_PUBLIC
&&
557 gpentHmgr
[ulIndex
].ObjectOwner
.ulObj
!= GDI_OBJ_HMGR_NONE
)
559 /* Decrement the process handle count */
560 ASSERT(gpentHmgr
[ulIndex
].ObjectOwner
.ulObj
==
561 HandleToUlong(PsGetCurrentProcessId()));
562 DecrementCurrentProcessGdiHandleCount();
565 /* Push entry to the free list */
566 ENTRY_vPushFreeEntry(&gpentHmgr
[ulIndex
]);
568 /* Free the object */
569 GDIOBJ_vFreeObject(pobj
);
574 /* Decrement the objects reference count */
575 ASSERT(pobj
->ulShareCount
> 0);
576 cRefs
= InterlockedDecrement((LONG
*)&pobj
->ulShareCount
);
577 DBG_LOGEVENT(&pobj
->slhLog
, EVENT_DEREFERENCE
, cRefs
);
579 /* Check if we reached 0 */
582 /* Free the object */
583 GDIOBJ_vFreeObject(pobj
);
590 GDIOBJ_ReferenceObjectByHandle(
597 /* Check if the handle type matches */
598 ASSERT_SHARED_OBJECT_TYPE(objt
);
599 if ((((ULONG_PTR
)hobj
>> 16) & 0x1f) != objt
)
601 DPRINT("GDIOBJ: Wrong type. handle=%p, type=%x\n", hobj
, objt
);
605 /* Reference the handle entry */
606 pentry
= ENTRY_ReferenceEntryByHandle(hobj
, 0);
609 DPRINT("GDIOBJ: Requested handle 0x%p is not valid.\n", hobj
);
613 /* Get the pointer to the BASEOBJECT */
614 pobj
= pentry
->einfo
.pobj
;
616 /* Check if the object is exclusively locked */
617 if (pobj
->cExclusiveLock
!= 0)
619 DPRINT1("GDIOBJ: Cannot reference oject %p with exclusive lock.\n", hobj
);
620 GDIOBJ_vDereferenceObject(pobj
);
621 DBG_DUMP_EVENT_LIST(&pobj
->slhLog
);
625 DBG_LOGEVENT(&pobj
->slhLog
, EVENT_REFERENCE
, gpaulRefCount
[pentry
- gpentHmgr
]);
627 /* All is well, return the object */
633 GDIOBJ_vReferenceObjectByPointer(POBJ pobj
)
637 /* Check if the object has a handle */
638 if (GDI_HANDLE_GET_INDEX(pobj
->hHmgr
))
640 /* Increase the handle's reference count */
641 ULONG ulIndex
= GDI_HANDLE_GET_INDEX(pobj
->hHmgr
);
642 ASSERT((gpaulRefCount
[ulIndex
] & REF_MASK_COUNT
) > 0);
643 cRefs
= InterlockedIncrement((LONG
*)&gpaulRefCount
[ulIndex
]);
647 /* Increase the object's reference count */
648 cRefs
= InterlockedIncrement((LONG
*)&pobj
->ulShareCount
);
651 DBG_LOGEVENT(&pobj
->slhLog
, EVENT_REFERENCE
, cRefs
);
656 GDIOBJ_TryLockObject(
664 /* Check if the handle type matches */
665 ASSERT_TRYLOCK_OBJECT_TYPE(objt
);
666 if ((((ULONG_PTR
)hobj
>> 16) & 0x1f) != objt
)
668 DPRINT("Wrong object type: hobj=0x%p, objt=0x%x\n", hobj
, objt
);
672 /* Reference the handle entry */
673 pentry
= ENTRY_ReferenceEntryByHandle(hobj
, 0);
676 DPRINT("GDIOBJ: Requested handle 0x%p is not valid.\n", hobj
);
680 /* Get the pointer to the BASEOBJECT */
681 pobj
= pentry
->einfo
.pobj
;
683 /* Check if we already own the lock */
684 dwThreadId
= PtrToUlong(PsGetCurrentThreadId());
685 if (pobj
->dwThreadId
!= dwThreadId
)
687 /* Disable APCs and try acquiring the push lock */
688 KeEnterCriticalRegion();
689 if(!ExTryAcquirePushLockExclusive(&pobj
->pushlock
))
691 ULONG cRefs
, ulIndex
;
692 /* Already owned. Clean up and leave. */
693 KeLeaveCriticalRegion();
695 /* Calculate the index */
696 ulIndex
= GDI_HANDLE_GET_INDEX(pobj
->hHmgr
);
698 /* Decrement reference count */
699 ASSERT((gpaulRefCount
[ulIndex
] & REF_MASK_COUNT
) > 0);
700 cRefs
= InterlockedDecrement((LONG
*)&gpaulRefCount
[ulIndex
]);
701 ASSERT(cRefs
& REF_MASK_VALID
);
706 /* Set us as lock owner */
707 ASSERT(pobj
->dwThreadId
== 0);
708 pobj
->dwThreadId
= dwThreadId
;
711 /* Increase lock count */
712 pobj
->cExclusiveLock
++;
713 INCREASE_THREAD_LOCK_COUNT(hobj
);
714 DBG_LOGEVENT(&pobj
->slhLog
, EVENT_LOCK
, 0);
716 /* Return the object */
730 /* Check if the handle type matches */
731 ASSERT_EXCLUSIVE_OBJECT_TYPE(objt
);
732 if ((((ULONG_PTR
)hobj
>> 16) & 0x1f) != objt
)
734 DPRINT("Wrong object type: hobj=0x%p, objt=0x%x\n", hobj
, objt
);
738 /* Reference the handle entry */
739 pentry
= ENTRY_ReferenceEntryByHandle(hobj
, 0);
742 DPRINT("GDIOBJ: Requested handle 0x%p is not valid.\n", hobj
);
746 /* Get the pointer to the BASEOBJECT */
747 pobj
= pentry
->einfo
.pobj
;
749 /* Check if we already own the lock */
750 dwThreadId
= PtrToUlong(PsGetCurrentThreadId());
751 if (pobj
->dwThreadId
!= dwThreadId
)
753 /* Disable APCs and acquire the push lock */
754 KeEnterCriticalRegion();
755 ExAcquirePushLockExclusive(&pobj
->pushlock
);
757 /* Set us as lock owner */
758 ASSERT(pobj
->dwThreadId
== 0);
759 pobj
->dwThreadId
= dwThreadId
;
762 /* Increase lock count */
763 pobj
->cExclusiveLock
++;
764 INCREASE_THREAD_LOCK_COUNT(hobj
);
765 DBG_LOGEVENT(&pobj
->slhLog
, EVENT_LOCK
, 0);
767 /* Return the object */
773 GDIOBJ_vUnlockObject(POBJ pobj
)
775 ULONG cRefs
, ulIndex
;
776 ASSERT(pobj
->cExclusiveLock
> 0);
778 /* Decrease lock count */
779 pobj
->cExclusiveLock
--;
780 DECREASE_THREAD_LOCK_COUNT(pobj
->hHmgr
);
781 DBG_LOGEVENT(&pobj
->slhLog
, EVENT_UNLOCK
, 0);
783 /* Check if this was the last lock */
784 if (pobj
->cExclusiveLock
== 0)
786 /* Reset lock owner */
787 pobj
->dwThreadId
= 0;
789 /* Release the pushlock and reenable APCs */
790 ExReleasePushLockExclusive(&pobj
->pushlock
);
791 KeLeaveCriticalRegion();
794 /* Calculate the index */
795 ulIndex
= GDI_HANDLE_GET_INDEX(pobj
->hHmgr
);
797 /* Decrement reference count */
798 ASSERT((gpaulRefCount
[ulIndex
] & REF_MASK_COUNT
) > 0);
799 cRefs
= InterlockedDecrement((LONG
*)&gpaulRefCount
[ulIndex
]);
800 ASSERT(cRefs
& REF_MASK_VALID
);
805 GDIOBJ_hInsertObject(
812 /* Must have no handle and only one reference */
813 ASSERT(GDI_HANDLE_GET_INDEX(pobj
->hHmgr
) == 0);
814 ASSERT(pobj
->cExclusiveLock
== 0);
815 ASSERT(pobj
->ulShareCount
== 1);
817 /* Get a free handle entry */
818 pentry
= ENTRY_pentPopFreeEntry();
821 DPRINT1("GDIOBJ: Could not get a free entry.\n");
825 /* Make the object exclusively locked */
826 ExInitializePushLock(&pobj
->pushlock
);
827 KeEnterCriticalRegion();
828 ExAcquirePushLockExclusive(&pobj
->pushlock
);
829 pobj
->cExclusiveLock
= 1;
830 pobj
->dwThreadId
= PtrToUlong(PsGetCurrentThreadId());
831 INCREASE_THREAD_LOCK_COUNT(pobj
->hHmgr
);
833 /* Get object type from the hHmgr field */
834 objt
= ((ULONG_PTR
)pobj
->hHmgr
>> 16) & 0xff;
835 ASSERT(objt
!= GDIObjType_DEF_TYPE
);
837 /* Check if current process is requested owner */
838 if (ulOwner
== GDI_OBJ_HMGR_POWNED
)
840 /* Increment the process handle count */
841 IncrementCurrentProcessGdiHandleCount();
844 ulOwner
= HandleToUlong(PsGetCurrentProcessId());
847 /* Insert the object into the handle table */
848 pobj
->hHmgr
= ENTRY_hInsertObject(pentry
, pobj
, objt
, ulOwner
);
850 /* Return the handle */
851 DPRINT("GDIOBJ: Created handle: %p\n", pobj
->hHmgr
);
852 DBG_LOGEVENT(&pobj
->slhLog
, EVENT_CREATE_HANDLE
, 0);
858 GDIOBJ_vSetObjectOwner(
865 /* This is a ugly HACK, needed to fix IntGdiSetDCOwnerEx */
866 if (GDI_HANDLE_IS_STOCKOBJ(pobj
->hHmgr
))
868 DPRINT("Trying to set ownership of stock object %p to %lx\n", pobj
->hHmgr
, ulNewOwner
);
872 /* Get the handle entry */
873 NT_ASSERT(GDI_HANDLE_GET_INDEX(pobj
->hHmgr
));
874 pentry
= &gpentHmgr
[GDI_HANDLE_GET_INDEX(pobj
->hHmgr
)];
876 /* Check if the new owner is the same as the old one */
877 ulOldOwner
= pentry
->ObjectOwner
.ulObj
;
878 if (ulOldOwner
== ulNewOwner
)
884 /* Is the current process requested? */
885 if (ulNewOwner
== GDI_OBJ_HMGR_POWNED
)
888 ulNewOwner
= HandleToUlong(PsGetCurrentProcessId());
892 if (ulNewOwner
== GDI_OBJ_HMGR_NONE
)
893 ulNewOwner
= GDI_OBJ_HMGR_PUBLIC
;
895 /* Was the object process owned? */
896 if ((ulOldOwner
!= GDI_OBJ_HMGR_PUBLIC
) &&
897 (ulOldOwner
!= GDI_OBJ_HMGR_NONE
))
899 /* Decrement the previous owners handle count */
900 DecrementGdiHandleCount(ulOldOwner
);
903 /* Is the new owner a process? */
904 if ((ulNewOwner
!= GDI_OBJ_HMGR_PUBLIC
) &&
905 (ulNewOwner
!= GDI_OBJ_HMGR_NONE
))
907 /* Increment the new owners handle count */
908 IncrementGdiHandleCount(ulNewOwner
);
912 /* Make sure we don't leak user mode memory */
913 NT_ASSERT(pentry
->pUser
== NULL
);
917 pentry
->ObjectOwner
.ulObj
= ulNewOwner
;
918 DBG_LOGEVENT(&pobj
->slhLog
, EVENT_SET_OWNER
, 0);
921 /* Locks 2 or 3 objects at a time */
924 GDIOBJ_bLockMultipleObjects(
930 UINT auiIndices
[3] = {0, 1, 2};
933 ASSERT(ulCount
<= 3);
935 /* Sort the handles */
936 for (i
= 0; i
< ulCount
- 1; i
++)
938 for (j
= i
+ 1; j
< ulCount
; j
++)
940 if ((ULONG_PTR
)ahObj
[auiIndices
[i
]] <
941 (ULONG_PTR
)ahObj
[auiIndices
[j
]])
944 auiIndices
[i
] = auiIndices
[j
];
950 /* Lock the objects in safe order */
951 for (i
= 0; i
< ulCount
; i
++)
953 /* Skip NULL handles */
954 if (ahObj
[auiIndices
[i
]] == NULL
)
956 apObj
[auiIndices
[i
]] = NULL
;
960 /* Lock the object */
961 apObj
[auiIndices
[i
]] = GDIOBJ_LockObject(ahObj
[auiIndices
[i
]], objt
);
963 /* Check for failure */
964 if (apObj
[auiIndices
[i
]] == NULL
)
969 if (apObj
[auiIndices
[i
]])
970 GDIOBJ_vUnlockObject(apObj
[auiIndices
[i
]]);
981 GDIOBJ_pvGetObjectAttr(POBJ pobj
)
983 ULONG ulIndex
= GDI_HANDLE_GET_INDEX(pobj
->hHmgr
);
984 return gpentHmgr
[ulIndex
].pUser
;
989 GDIOBJ_vSetObjectAttr(POBJ pobj
, PVOID pvObjAttr
)
995 /* Get the handle index */
996 ulIndex
= GDI_HANDLE_GET_INDEX(pobj
->hHmgr
);
998 /* Set pointer to the usermode attribute */
999 gpentHmgr
[ulIndex
].pUser
= pvObjAttr
;
1004 GDIOBJ_vDeleteObject(POBJ pobj
)
1008 /* Set the object's delete flag */
1009 InterlockedOr16((SHORT
*)&pobj
->BaseFlags
, BASEFLAG_READY_TO_DIE
);
1010 DBG_LOGEVENT(&pobj
->slhLog
, EVENT_DELETE
, 0);
1012 /* Get the handle index */
1013 ulIndex
= GDI_HANDLE_GET_INDEX(pobj
->hHmgr
);
1016 /* Reset the handle valid bit */
1017 InterlockedAnd((LONG
*)&gpaulRefCount
[ulIndex
], ~REF_MASK_VALID
);
1019 /* Check if the object is exclusively locked */
1020 if (pobj
->cExclusiveLock
!= 0)
1022 /* Reset lock owner and lock count */
1023 pobj
->dwThreadId
= 0;
1024 pobj
->cExclusiveLock
= 0;
1026 /* Release the pushlock and reenable APCs */
1027 ExReleasePushLockExclusive(&pobj
->pushlock
);
1028 KeLeaveCriticalRegion();
1029 DECREASE_THREAD_LOCK_COUNT(pobj
->hHmgr
);
1033 /* Dereference the object (will take care of deletion) */
1034 GDIOBJ_vDereferenceObject(pobj
);
1039 GreIsHandleValid(HGDIOBJ hobj
)
1043 pentry
= ENTRY_ReferenceEntryByHandle(hobj
, 0);
1044 if (!pentry
) return FALSE
;
1045 GDIOBJ_vDereferenceObject(pentry
->einfo
.pobj
);
1051 GreDeleteObject(HGDIOBJ hobj
)
1055 /* Check for stock objects */
1056 if (GDI_HANDLE_IS_STOCKOBJ(hobj
))
1058 DPRINT1("GreDeleteObject: Cannot delete stock object %p.\n", hobj
);
1062 /* Reference the handle entry */
1063 pentry
= ENTRY_ReferenceEntryByHandle(hobj
, 0);
1066 DPRINT1("GreDeleteObject: Trying to delete invalid object %p\n", hobj
);
1070 /* Check for public owner */
1071 if (pentry
->ObjectOwner
.ulObj
== GDI_OBJ_HMGR_PUBLIC
)
1073 DPRINT1("GreDeleteObject: Trying to delete global object %p\n", hobj
);
1074 GDIOBJ_vDereferenceObject(pentry
->einfo
.pobj
);
1078 /* Delete the object */
1079 GDIOBJ_vDeleteObject(pentry
->einfo
.pobj
);
1085 GreGetObjectOwner(HGDIOBJ hobj
)
1087 ULONG ulIndex
, ulOwner
;
1089 /* Get the handle index */
1090 ulIndex
= GDI_HANDLE_GET_INDEX(hobj
);
1092 /* Check if the handle is valid */
1093 if (ulIndex
>= GDI_HANDLE_COUNT
||
1094 gpentHmgr
[ulIndex
].Objt
== GDIObjType_DEF_TYPE
||
1095 ((ULONG_PTR
)hobj
>> 16) != gpentHmgr
[ulIndex
].FullUnique
)
1097 DPRINT1("GreGetObjectOwner: invalid handle 0x%p.\n", hobj
);
1098 return GDI_OBJ_HMGR_RESTRICTED
;
1101 /* Get the object owner */
1102 ulOwner
= gpentHmgr
[ulIndex
].ObjectOwner
.ulObj
;
1104 if (ulOwner
== HandleToUlong(PsGetCurrentProcessId()))
1105 return GDI_OBJ_HMGR_POWNED
;
1107 if (ulOwner
== GDI_OBJ_HMGR_PUBLIC
)
1108 return GDI_OBJ_HMGR_PUBLIC
;
1110 return GDI_OBJ_HMGR_RESTRICTED
;
1115 GreSetObjectOwnerEx(
1122 /* Check for stock objects */
1123 if (GDI_HANDLE_IS_STOCKOBJ(hobj
))
1125 DPRINT("GreSetObjectOwner: Got stock object %p\n", hobj
);
1129 /* Reference the handle entry */
1130 pentry
= ENTRY_ReferenceEntryByHandle(hobj
, Flags
);
1133 DPRINT("GreSetObjectOwner: Invalid handle 0x%p.\n", hobj
);
1137 /* Call internal function */
1138 GDIOBJ_vSetObjectOwner(pentry
->einfo
.pobj
, ulOwner
);
1140 /* Dereference the object */
1141 GDIOBJ_vDereferenceObject(pentry
->einfo
.pobj
);
1152 return GreSetObjectOwnerEx(hobj
, ulOwner
, 0);
1166 /* Verify object type */
1167 objt
= ((ULONG_PTR
)hobj
>> 16) & 0x1f;
1168 if (objt
!= GDIObjType_BRUSH_TYPE
&&
1169 objt
!= GDIObjType_SURF_TYPE
&&
1170 objt
!= GDIObjType_LFONT_TYPE
&&
1171 objt
!= GDIObjType_PAL_TYPE
)
1173 DPRINT1("GreGetObject: Invalid object type\n");
1177 pvObj
= GDIOBJ_ReferenceObjectByHandle(hobj
, objt
);
1180 DPRINT("GreGetObject: Could not lock object\n");
1184 switch (GDI_HANDLE_GET_TYPE(hobj
))
1186 case GDILoObjType_LO_PEN_TYPE
:
1187 case GDILoObjType_LO_EXTPEN_TYPE
:
1188 iResult
= PEN_GetObject(pvObj
, cbCount
, pvBuffer
);
1191 case GDILoObjType_LO_BRUSH_TYPE
:
1192 iResult
= BRUSH_GetObject(pvObj
, cbCount
, pvBuffer
);
1195 case GDILoObjType_LO_BITMAP_TYPE
:
1196 iResult
= BITMAP_GetObject(pvObj
, cbCount
, pvBuffer
);
1199 case GDILoObjType_LO_FONT_TYPE
:
1200 iResult
= FontGetObject(pvObj
, cbCount
, pvBuffer
);
1203 case GDILoObjType_LO_PALETTE_TYPE
:
1204 iResult
= PALETTE_GetObject(pvObj
, cbCount
, pvBuffer
);
1208 DPRINT1("GDI object type of 0x%p not implemented\n", hobj
);
1212 GDIOBJ_vDereferenceObject(pvObj
);
1221 IN INT cjBufferSize
,
1222 OUT LPVOID lpBuffer
)
1224 UINT iResult
, cjMaxSize
;
1228 DIBSECTION dibsection
;
1232 EXTLOGFONTW extlogfontw
;
1233 ENUMLOGFONTEXDVW enumlogfontexdvw
;
1236 /* Normalize to the largest supported object size */
1237 cjMaxSize
= min((UINT
)cjBufferSize
, sizeof(object
));
1239 /* Now do the actual call */
1240 iResult
= GreGetObject(hobj
, cjMaxSize
, lpBuffer
? &object
: NULL
);
1242 /* Check if we have a buffer and data */
1243 if ((lpBuffer
!= NULL
) && (iResult
!= 0))
1245 /* Enter SEH for buffer transfer */
1248 /* Probe the buffer and copy it */
1249 cjMaxSize
= min(cjMaxSize
, iResult
);
1250 ProbeForWrite(lpBuffer
, cjMaxSize
, sizeof(WORD
));
1251 RtlCopyMemory(lpBuffer
, &object
, cjMaxSize
);
1253 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1255 /* Clear the return value.
1256 * Do *NOT* set last error here! */
1262 /* Return the count */
1269 NtGdiCreateClientObj(
1275 /* Allocate a new object */
1276 pObject
= GDIOBJ_AllocateObject(GDIObjType_CLIENTOBJ_TYPE
,
1278 BASEFLAG_LOOKASIDE
);
1281 DPRINT1("NtGdiCreateClientObj: Could not allocate a clientobj.\n");
1285 /* Mask out everything that would change the type in a wrong manner */
1286 ulType
&= (GDI_HANDLE_TYPE_MASK
& ~GDI_HANDLE_BASETYPE_MASK
);
1288 /* Set the real object type */
1289 pObject
->hHmgr
= UlongToHandle(ulType
| GDILoObjType_LO_CLIENTOBJ_TYPE
);
1291 /* Create a handle */
1292 handle
= GDIOBJ_hInsertObject(pObject
, GDI_OBJ_HMGR_POWNED
);
1295 DPRINT1("NtGdiCreateClientObj: Could not create a handle.\n");
1296 GDIOBJ_vFreeObject(pObject
);
1301 GDIOBJ_vUnlockObject(pObject
);
1309 NtGdiDeleteClientObj(
1312 /* We first need to get the real type from the handle */
1313 ULONG ulType
= GDI_HANDLE_GET_TYPE(hobj
);
1315 /* Check if it's really a CLIENTOBJ */
1316 if ((ulType
& GDI_HANDLE_BASETYPE_MASK
) != GDILoObjType_LO_CLIENTOBJ_TYPE
)
1318 /* FIXME: SetLastError? */
1322 return GreDeleteObject(hobj
);
1327 PGDI_HANDLE_TABLE GdiHandleTable
= NULL
;
1330 GDIOBJ_ShareLockObj(HGDIOBJ hObj
, DWORD ExpectedType
)
1332 if (ExpectedType
== GDI_OBJECT_TYPE_DONTCARE
)
1333 ExpectedType
= GDI_HANDLE_GET_TYPE(hObj
);
1334 return GDIOBJ_ReferenceObjectByHandle(hObj
, (ExpectedType
>> 16) & 0x1f);
1337 // This function is not safe to use with concurrent deleting attempts
1338 // That shouldn't be a problem, since we don't have any processes yet,
1339 // that could delete the handle
1342 GDIOBJ_ConvertToStockObj(HGDIOBJ
*phObj
)
1347 /* Reference the handle entry */
1348 pentry
= ENTRY_ReferenceEntryByHandle(*phObj
, 0);
1351 DPRINT1("GDIOBJ: Requested handle 0x%p is not valid.\n", *phObj
);
1355 /* Update the entry */
1356 pentry
->FullUnique
|= GDI_ENTRY_STOCK_MASK
;
1357 pentry
->ObjectOwner
.ulObj
= 0;
1359 /* Get the pointer to the BASEOBJECT */
1360 pobj
= pentry
->einfo
.pobj
;
1362 /* Calculate the new handle */
1363 pobj
->hHmgr
= (HGDIOBJ
)((ULONG_PTR
)pobj
->hHmgr
| GDI_HANDLE_STOCK_MASK
);
1365 /* Return the new handle */
1366 *phObj
= pobj
->hHmgr
;
1368 /* Dereference the handle */
1369 GDIOBJ_vDereferenceObject(pobj
);
1375 GDIOBJ_AllocObjWithHandle(ULONG ObjectType
, ULONG cjSize
)
1379 UCHAR objt
= (ObjectType
>> 16) & 0xFF;
1381 if ((objt
== GDIObjType_DC_TYPE
&& cjSize
== sizeof(DC
)) ||
1382 (objt
== GDIObjType_PAL_TYPE
&& cjSize
== sizeof(PALETTE
)) ||
1383 (objt
== GDIObjType_RGN_TYPE
&& cjSize
== sizeof(REGION
)) ||
1384 (objt
== GDIObjType_SURF_TYPE
&& cjSize
== sizeof(SURFACE
)) ||
1385 (objt
== GDIObjType_PATH_TYPE
&& cjSize
== sizeof(PATH
)))
1387 fl
|= BASEFLAG_LOOKASIDE
;
1390 pobj
= GDIOBJ_AllocateObject(objt
, cjSize
, fl
);
1396 if (!GDIOBJ_hInsertObject(pobj
, GDI_OBJ_HMGR_POWNED
))
1398 GDIOBJ_vFreeObject(pobj
);
1405 GDI_MapHandleTable(PEPROCESS pProcess
)
1407 PVOID pvMappedView
= NULL
;
1409 LARGE_INTEGER liOffset
;
1410 ULONG cjViewSize
= sizeof(GDI_HANDLE_TABLE
);
1412 liOffset
.QuadPart
= 0;
1414 ASSERT(gpvGdiHdlTblSection
!= NULL
);
1415 ASSERT(pProcess
!= NULL
);
1417 Status
= MmMapViewOfSection(gpvGdiHdlTblSection
,
1428 if (!NT_SUCCESS(Status
))
1431 return pvMappedView
;
1435 GDI_CleanupForProcess(struct _EPROCESS
*Process
)
1442 DPRINT("CleanupForProcess prochandle %p Pid %p\n",
1443 Process
, Process
->UniqueProcessId
);
1445 ASSERT(Process
== PsGetCurrentProcess());
1447 /* Get the current process Id */
1448 dwProcessId
= PtrToUlong(PsGetCurrentProcessId());
1450 /* Loop all handles in the handle table */
1451 for (ulIndex
= RESERVE_ENTRIES_COUNT
; ulIndex
< gulFirstUnused
; ulIndex
++)
1453 pentry
= &gpentHmgr
[ulIndex
];
1455 /* Check if the object is owned by the process */
1456 if (pentry
->ObjectOwner
.ulObj
== dwProcessId
)
1458 ASSERT(pentry
->einfo
.pobj
->cExclusiveLock
== 0);
1460 /* Reference the object and delete it */
1461 InterlockedIncrement((LONG
*)&gpaulRefCount
[ulIndex
]);
1462 GDIOBJ_vDeleteObject(pentry
->einfo
.pobj
);
1468 DbgGdiHTIntegrityCheck();
1472 ppi
= PsGetCurrentProcessWin32Process();
1473 DPRINT("Completed cleanup for process %p\n", Process
->UniqueProcessId
);
1474 if (ppi
->GDIHandleCount
!= 0)
1476 DPRINT1("Leaking %d handles!\n", ppi
->GDIHandleCount
);
1480 /* Loop all handles in the handle table */
1481 for (ulIndex
= RESERVE_ENTRIES_COUNT
; ulIndex
< gulFirstUnused
; ulIndex
++)
1483 pentry
= &gpentHmgr
[ulIndex
];
1485 /* Check if the object is owned by the process */
1486 if (pentry
->ObjectOwner
.ulObj
== dwProcessId
)
1488 DPRINT1("Leaking object. Index=%lx, type=0x%x, refcount=%lx\n",
1489 ulIndex
, pentry
->Objt
, gpaulRefCount
[ulIndex
]);
1490 DBG_DUMP_EVENT_LIST(&pentry
->einfo
.pobj
->slhLog
);
1491 //DBG_CLEANUP_EVENT_LIST(&pentry->einfo.pobj->slhLog);