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 ******************************************************************/
42 #define DBG_INCREASE_LOCK_COUNT(pti, hobj) \
43 if (pti) ((PTHREADINFO)pti)->acExclusiveLockCount[((ULONG_PTR)hobj >> 16) & 0x1f]++;
44 #define DBG_DECREASE_LOCK_COUNT(pti, hobj) \
45 if (pti) ((PTHREADINFO)pti)->acExclusiveLockCount[((ULONG_PTR)hobj >> 16) & 0x1f]--;
46 #define ASSERT_SHARED_OBJECT_TYPE(objt) \
47 ASSERT((objt) == GDIObjType_SURF_TYPE || \
48 (objt) == GDIObjType_PAL_TYPE || \
49 (objt) == GDIObjType_LFONT_TYPE || \
50 (objt) == GDIObjType_PATH_TYPE || \
51 (objt) == GDIObjType_BRUSH_TYPE)
52 #define ASSERT_EXCLUSIVE_OBJECT_TYPE(objt) \
53 ASSERT((objt) == GDIObjType_DC_TYPE || \
54 (objt) == GDIObjType_RGN_TYPE)
55 #define ASSERT_TRYLOCK_OBJECT_TYPE(objt) \
56 ASSERT((objt) == GDIObjType_DRVOBJ_TYPE)
58 #define DBG_INCREASE_LOCK_COUNT(ppi, hobj)
59 #define DBG_DECREASE_LOCK_COUNT(x, y)
60 #define ASSERT_SHARED_OBJECT_TYPE(objt)
61 #define ASSERT_EXCLUSIVE_OBJECT_TYPE(objt)
62 #define ASSERT_TRYLOCK_OBJECT_TYPE(objt)
65 #if defined(_M_IX86) || defined(_M_AMD64)
66 #define InterlockedOr16 _InterlockedOr16
69 #define GDIOBJ_POOL_TAG(type) ('00hG' + (((type) & 0x1f) << 24))
73 REF_MASK_REUSE
= 0xff000000,
74 REF_INC_REUSE
= 0x01000000,
75 REF_MASK_VALID
= 0x00800000,
76 REF_MASK_COUNT
= 0x007fffff,
77 REF_MASK_INUSE
= 0x00ffffff,
80 /* GLOBALS *******************************************************************/
82 /* Per session handle table globals */
83 static PVOID gpvGdiHdlTblSection
= NULL
;
88 static PPAGED_LOOKASIDE_LIST gpaLookasideList
;
90 static BOOL NTAPI
GDIOBJ_Cleanup(PVOID ObjectBody
);
96 NULL
, /* 00 GDIObjType_DEF_TYPE */
97 DC_Cleanup
, /* 01 GDIObjType_DC_TYPE */
98 NULL
, /* 02 GDIObjType_UNUSED1_TYPE */
99 NULL
, /* 03 GDIObjType_UNUSED2_TYPE */
100 REGION_Cleanup
, /* 04 GDIObjType_RGN_TYPE */
101 SURFACE_Cleanup
, /* 05 GDIObjType_SURF_TYPE */
102 GDIOBJ_Cleanup
, /* 06 GDIObjType_CLIENTOBJ_TYPE */
103 GDIOBJ_Cleanup
, /* 07 GDIObjType_PATH_TYPE */
104 PALETTE_Cleanup
, /* 08 GDIObjType_PAL_TYPE */
105 GDIOBJ_Cleanup
, /* 09 GDIObjType_ICMLCS_TYPE */
106 GDIOBJ_Cleanup
, /* 0a GDIObjType_LFONT_TYPE */
107 NULL
, /* 0b GDIObjType_RFONT_TYPE, unused */
108 NULL
, /* 0c GDIObjType_PFE_TYPE, unused */
109 NULL
, /* 0d GDIObjType_PFT_TYPE, unused */
110 GDIOBJ_Cleanup
, /* 0e GDIObjType_ICMCXF_TYPE */
111 NULL
, /* 0f GDIObjType_SPRITE_TYPE, unused */
112 BRUSH_Cleanup
, /* 10 GDIObjType_BRUSH_TYPE, BRUSH, PEN, EXTPEN */
113 NULL
, /* 11 GDIObjType_UMPD_TYPE, unused */
114 NULL
, /* 12 GDIObjType_UNUSED4_TYPE */
115 NULL
, /* 13 GDIObjType_SPACE_TYPE, unused */
116 NULL
, /* 14 GDIObjType_UNUSED5_TYPE */
117 NULL
, /* 15 GDIObjType_META_TYPE, unused */
118 NULL
, /* 16 GDIObjType_EFSTATE_TYPE, unused */
119 NULL
, /* 17 GDIObjType_BMFD_TYPE, unused */
120 NULL
, /* 18 GDIObjType_VTFD_TYPE, unused */
121 NULL
, /* 19 GDIObjType_TTFD_TYPE, unused */
122 NULL
, /* 1a GDIObjType_RC_TYPE, unused */
123 NULL
, /* 1b GDIObjType_TEMP_TYPE, unused */
124 DRIVEROBJ_Cleanup
,/* 1c GDIObjType_DRVOBJ_TYPE */
125 NULL
, /* 1d GDIObjType_DCIOBJ_TYPE, unused */
126 NULL
, /* 1e GDIObjType_SPOOL_TYPE, unused */
127 NULL
, /* 1f reserved entry */
130 /* INTERNAL FUNCTIONS ********************************************************/
134 GDIOBJ_Cleanup(PVOID ObjectBody
)
141 InitLookasideList(UCHAR objt
, ULONG cjSize
)
143 ExInitializePagedLookasideList(&gpaLookasideList
[objt
],
148 GDITAG_HMGR_LOOKASIDE_START
+ (objt
<< 24),
155 InitGdiHandleTable(void)
158 LARGE_INTEGER liSize
;
160 SIZE_T cjViewSize
= 0;
162 /* Create a section for the shared handle table */
163 liSize
.QuadPart
= sizeof(GDI_HANDLE_TABLE
); // GDI_HANDLE_COUNT * sizeof(ENTRY);
164 status
= MmCreateSection(&gpvGdiHdlTblSection
,
172 if (!NT_SUCCESS(status
))
174 DPRINT1("INITGDI: Could not allocate a GDI handle table.\n");
178 /* Map the section in session space */
179 status
= MmMapViewInSessionSpace(gpvGdiHdlTblSection
,
182 if (!NT_SUCCESS(status
))
184 DPRINT1("INITGDI: Failed to map handle table section\n");
185 ObDereferenceObject(gpvGdiHdlTblSection
);
189 /* Allocate memory for the reference counter table */
190 gpaulRefCount
= EngAllocSectionMem(&pvSection
,
192 GDI_HANDLE_COUNT
* sizeof(ULONG
),
196 DPRINT1("INITGDI: Failed to allocate reference table.\n");
197 ObDereferenceObject(gpvGdiHdlTblSection
);
198 return STATUS_INSUFFICIENT_RESOURCES
;
202 gulFirstUnused
= RESERVE_ENTRIES_COUNT
;
204 GdiHandleTable
= (PVOID
)gpentHmgr
;
206 /* Initialize the lookaside lists */
207 gpaLookasideList
= ExAllocatePoolWithTag(NonPagedPool
,
208 GDIObjTypeTotal
* sizeof(PAGED_LOOKASIDE_LIST
),
210 if(!gpaLookasideList
)
211 return STATUS_NO_MEMORY
;
213 InitLookasideList(GDIObjType_DC_TYPE
, sizeof(DC
));
214 InitLookasideList(GDIObjType_RGN_TYPE
, sizeof(REGION
));
215 InitLookasideList(GDIObjType_SURF_TYPE
, sizeof(SURFACE
));
216 InitLookasideList(GDIObjType_CLIENTOBJ_TYPE
, sizeof(CLIENTOBJ
));
217 InitLookasideList(GDIObjType_PATH_TYPE
, sizeof(PATH
));
218 InitLookasideList(GDIObjType_PAL_TYPE
, sizeof(PALETTE
));
219 InitLookasideList(GDIObjType_ICMLCS_TYPE
, sizeof(COLORSPACE
));
220 InitLookasideList(GDIObjType_LFONT_TYPE
, sizeof(TEXTOBJ
));
221 InitLookasideList(GDIObjType_BRUSH_TYPE
, sizeof(BRUSH
));
223 return STATUS_SUCCESS
;
228 IncrementCurrentProcessGdiHandleCount(void)
230 PPROCESSINFO ppi
= PsGetCurrentProcessWin32Process();
231 if (ppi
) InterlockedIncrement((LONG
*)&ppi
->GDIHandleCount
);
236 DecrementCurrentProcessGdiHandleCount(void)
238 PPROCESSINFO ppi
= PsGetCurrentProcessWin32Process();
239 if (ppi
) InterlockedDecrement((LONG
*)&ppi
->GDIHandleCount
);
244 IncrementGdiHandleCount(ULONG ulProcessId
)
250 Status
= PsLookupProcessByProcessId(ULongToHandle(ulProcessId
), &pep
);
251 NT_ASSERT(NT_SUCCESS(Status
));
253 ppi
= PsGetProcessWin32Process(pep
);
254 if (ppi
) InterlockedIncrement((LONG
*)&ppi
->GDIHandleCount
);
255 if (NT_SUCCESS(Status
)) ObDereferenceObject(pep
);
260 DecrementGdiHandleCount(ULONG ulProcessId
)
266 Status
= PsLookupProcessByProcessId(ULongToHandle(ulProcessId
), &pep
);
267 NT_ASSERT(NT_SUCCESS(Status
));
269 ppi
= PsGetProcessWin32Process(pep
);
270 if (ppi
) InterlockedDecrement((LONG
*)&ppi
->GDIHandleCount
);
271 if (NT_SUCCESS(Status
)) ObDereferenceObject(pep
);
276 ENTRY_pentPopFreeEntry(VOID
)
278 ULONG iFirst
, iNext
, iPrev
;
281 DPRINT("Enter InterLockedPopFreeEntry\n");
285 /* Get the index and sequence number of the first free entry */
286 iFirst
= gulFirstFree
;
288 /* Check if we have a free entry */
289 if (!(iFirst
& GDI_HANDLE_INDEX_MASK
))
291 /* Increment FirstUnused and get the new index */
292 iFirst
= InterlockedIncrement((LONG
*)&gulFirstUnused
) - 1;
294 /* Check if we have unused entries left */
295 if (iFirst
>= GDI_HANDLE_COUNT
)
297 DPRINT1("No more GDI handles left!\n");
301 /* Return the old entry */
302 return &gpentHmgr
[iFirst
];
305 /* Get a pointer to the first free entry */
306 pentFree
= &gpentHmgr
[iFirst
& GDI_HANDLE_INDEX_MASK
];
308 /* Create a new value with an increased sequence number */
309 iNext
= (USHORT
)(ULONG_PTR
)pentFree
->einfo
.pobj
;
310 iNext
|= (iFirst
& ~GDI_HANDLE_INDEX_MASK
) + 0x10000;
312 /* Try to exchange the FirstFree value */
313 iPrev
= InterlockedCompareExchange((LONG
*)&gulFirstFree
,
317 while (iPrev
!= iFirst
);
319 /* Sanity check: is entry really free? */
320 ASSERT(((ULONG_PTR
)pentFree
->einfo
.pobj
& ~GDI_HANDLE_INDEX_MASK
) == 0);
325 /* Pushes an entry of the handle table to the free list,
326 The entry must not have any references left */
329 ENTRY_vPushFreeEntry(PENTRY pentFree
)
331 ULONG iToFree
, iFirst
, iPrev
, idxToFree
;
333 DPRINT("Enter ENTRY_vPushFreeEntry\n");
335 idxToFree
= pentFree
- gpentHmgr
;
336 ASSERT((gpaulRefCount
[idxToFree
] & REF_MASK_INUSE
) == 0);
338 /* Initialize entry */
339 pentFree
->Objt
= GDIObjType_DEF_TYPE
;
340 pentFree
->ObjectOwner
.ulObj
= 0;
341 pentFree
->pUser
= NULL
;
343 /* Increase reuse counter in entry and reference counter */
344 InterlockedExchangeAdd((LONG
*)&gpaulRefCount
[idxToFree
], REF_INC_REUSE
);
345 pentFree
->FullUnique
+= 0x0100;
349 /* Get the current first free index and sequence number */
350 iFirst
= gulFirstFree
;
352 /* Set the einfo.pobj member to the index of the first free entry */
353 pentFree
->einfo
.pobj
= UlongToPtr(iFirst
& GDI_HANDLE_INDEX_MASK
);
355 /* Combine new index and increased sequence number in iToFree */
356 iToFree
= idxToFree
| ((iFirst
& ~GDI_HANDLE_INDEX_MASK
) + 0x10000);
358 /* Try to atomically update the first free entry */
359 iPrev
= InterlockedCompareExchange((LONG
*)&gulFirstFree
,
363 while (iPrev
!= iFirst
);
368 ENTRY_ReferenceEntryByHandle(HGDIOBJ hobj
, FLONG fl
)
370 ULONG ulIndex
, cNewRefs
, cOldRefs
;
373 /* Get the handle index and check if its too big */
374 ulIndex
= GDI_HANDLE_GET_INDEX(hobj
);
375 if (ulIndex
>= GDI_HANDLE_COUNT
) return NULL
;
377 /* Get pointer to the entry */
378 pentry
= &gpentHmgr
[ulIndex
];
380 /* Get the current reference count */
381 cOldRefs
= gpaulRefCount
[ulIndex
];
385 /* Check if the slot is deleted */
386 if ((cOldRefs
& REF_MASK_VALID
) == 0)
388 DPRINT("GDIOBJ: Slot is not valid: 0x%lx, hobh=%p\n", cOldRefs
, hobj
);
392 /* Check if the unique value matches */
393 if (pentry
->FullUnique
!= (USHORT
)((ULONG_PTR
)hobj
>> 16))
395 DPRINT("GDIOBJ: Wrong unique value. Handle: 0x%4x, entry: 0x%4x\n",
396 (USHORT
)((ULONG_PTR
)hobj
>> 16), pentry
->FullUnique
);
400 /* Check if the object owner is this process or public */
401 if (!(fl
& GDIOBJFLAG_IGNOREPID
) &&
402 pentry
->ObjectOwner
.ulObj
!= GDI_OBJ_HMGR_PUBLIC
&&
403 pentry
->ObjectOwner
.ulObj
!= PtrToUlong(PsGetCurrentProcessId()))
405 DPRINT("GDIOBJ: Cannot reference foreign handle %p, pentry=%p:%lx.\n",
406 hobj
, pentry
, pentry
->ObjectOwner
.ulObj
);
410 /* Try to atomically increment the reference count */
411 cNewRefs
= cOldRefs
+ 1;
412 cOldRefs
= InterlockedCompareExchange((PLONG
)&gpaulRefCount
[ulIndex
],
416 while (cNewRefs
!= cOldRefs
+ 1);
418 /* Integrity checks */
419 ASSERT((pentry
->FullUnique
& 0x1f) == pentry
->Objt
);
420 ASSERT(pentry
->einfo
.pobj
&& pentry
->einfo
.pobj
->hHmgr
== hobj
);
427 ENTRY_hInsertObject(PENTRY pentry
, POBJ pobj
, UCHAR objt
, ULONG ulOwner
)
431 /* Calculate the handle index */
432 ulIndex
= pentry
- gpentHmgr
;
434 /* Update the fields in the ENTRY */
435 pentry
->einfo
.pobj
= pobj
;
436 pentry
->Objt
= objt
& 0x1f;
437 pentry
->FullUnique
= (pentry
->FullUnique
& 0xff00) | objt
;
438 pentry
->ObjectOwner
.ulObj
= ulOwner
;
440 /* Make the handle valid with 1 reference */
441 ASSERT((gpaulRefCount
[ulIndex
] & REF_MASK_INUSE
) == 0);
442 InterlockedOr((LONG
*)&gpaulRefCount
[ulIndex
], REF_MASK_VALID
| 1);
444 /* Return the handle */
445 return (HGDIOBJ
)(((ULONG_PTR
)pentry
->FullUnique
<< 16) | ulIndex
);
450 GDIOBJ_AllocateObject(UCHAR objt
, ULONG cjSize
, FLONG fl
)
454 if (fl
& BASEFLAG_LOOKASIDE
)
456 /* Allocate the object from a lookaside list */
457 pobj
= ExAllocateFromPagedLookasideList(&gpaLookasideList
[objt
& 0x1f]);
461 /* Allocate the object from paged pool */
462 pobj
= ExAllocatePoolWithTag(PagedPool
, cjSize
, GDIOBJ_POOL_TAG(objt
));
465 if (!pobj
) return NULL
;
467 /* Initialize the object */
468 RtlZeroMemory(pobj
, cjSize
);
469 pobj
->hHmgr
= (HGDIOBJ
)((ULONG_PTR
)objt
<< 16);
470 pobj
->cExclusiveLock
= 0;
471 pobj
->ulShareCount
= 1;
472 pobj
->BaseFlags
= fl
& 0xffff;
473 DBG_INITLOG(&pobj
->slhLog
);
474 DBG_LOGEVENT(&pobj
->slhLog
, EVENT_ALLOCATE
, 0);
481 GDIOBJ_vFreeObject(POBJ pobj
)
485 DBG_CLEANUP_EVENT_LIST(&pobj
->slhLog
);
487 /* Get the object type */
488 objt
= ((ULONG_PTR
)pobj
->hHmgr
>> 16) & 0x1f;
490 /* Call the cleanup procedure */
491 ASSERT(apfnCleanup
[objt
]);
492 apfnCleanup
[objt
](pobj
);
494 /* Check if the object is allocated from a lookaside list */
495 if (pobj
->BaseFlags
& BASEFLAG_LOOKASIDE
)
497 ExFreeToPagedLookasideList(&gpaLookasideList
[objt
], pobj
);
501 ExFreePoolWithTag(pobj
, GDIOBJ_POOL_TAG(objt
));
507 GDIOBJ_vDereferenceObject(POBJ pobj
)
509 ULONG cRefs
, ulIndex
;
511 /* Calculate the index */
512 ulIndex
= GDI_HANDLE_GET_INDEX(pobj
->hHmgr
);
514 /* Check if the object has a handle */
517 /* Decrement reference count */
518 ASSERT((gpaulRefCount
[ulIndex
] & REF_MASK_COUNT
) > 0);
519 cRefs
= InterlockedDecrement((LONG
*)&gpaulRefCount
[ulIndex
]);
520 DBG_LOGEVENT(&pobj
->slhLog
, EVENT_DEREFERENCE
, cRefs
);
522 /* Check if we reached 0 and handle bit is not set */
523 if ((cRefs
& REF_MASK_INUSE
) == 0)
525 /* Make sure it's ok to delete the object */
526 ASSERT(pobj
->BaseFlags
& BASEFLAG_READY_TO_DIE
);
528 /* Check if the handle was process owned */
529 if (gpentHmgr
[ulIndex
].ObjectOwner
.ulObj
!= GDI_OBJ_HMGR_PUBLIC
&&
530 gpentHmgr
[ulIndex
].ObjectOwner
.ulObj
!= GDI_OBJ_HMGR_NONE
)
532 /* Decrement the process handle count */
533 ASSERT(gpentHmgr
[ulIndex
].ObjectOwner
.ulObj
==
534 HandleToUlong(PsGetCurrentProcessId()));
535 DecrementCurrentProcessGdiHandleCount();
538 /* Push entry to the free list */
539 ENTRY_vPushFreeEntry(&gpentHmgr
[ulIndex
]);
541 /* Free the object */
542 GDIOBJ_vFreeObject(pobj
);
547 /* Decrement the objects reference count */
548 ASSERT(pobj
->ulShareCount
> 0);
549 cRefs
= InterlockedDecrement((LONG
*)&pobj
->ulShareCount
);
550 DBG_LOGEVENT(&pobj
->slhLog
, EVENT_DEREFERENCE
, cRefs
);
552 /* Check if we reached 0 */
555 /* Free the object */
556 GDIOBJ_vFreeObject(pobj
);
563 GDIOBJ_ReferenceObjectByHandle(
570 /* Check if the handle type matches */
571 ASSERT_SHARED_OBJECT_TYPE(objt
);
572 if ((((ULONG_PTR
)hobj
>> 16) & 0x1f) != objt
)
574 DPRINT("GDIOBJ: Wrong type. handle=%p, type=%x\n", hobj
, objt
);
578 /* Reference the handle entry */
579 pentry
= ENTRY_ReferenceEntryByHandle(hobj
, 0);
582 DPRINT("GDIOBJ: Requested handle 0x%p is not valid.\n", hobj
);
586 /* Get the pointer to the BASEOBJECT */
587 pobj
= pentry
->einfo
.pobj
;
589 /* Check if the object is exclusively locked */
590 if (pobj
->cExclusiveLock
!= 0)
592 DPRINT1("GDIOBJ: Cannot reference oject %p with exclusive lock.\n", hobj
);
593 GDIOBJ_vDereferenceObject(pobj
);
594 DBG_DUMP_EVENT_LIST(&pobj
->slhLog
);
598 DBG_LOGEVENT(&pobj
->slhLog
, EVENT_REFERENCE
, gpaulRefCount
[pentry
- gpentHmgr
]);
600 /* All is well, return the object */
606 GDIOBJ_vReferenceObjectByPointer(POBJ pobj
)
610 /* Check if the object has a handle */
611 if (GDI_HANDLE_GET_INDEX(pobj
->hHmgr
))
613 /* Increase the handle's reference count */
614 ULONG ulIndex
= GDI_HANDLE_GET_INDEX(pobj
->hHmgr
);
615 ASSERT((gpaulRefCount
[ulIndex
] & REF_MASK_COUNT
) > 0);
616 cRefs
= InterlockedIncrement((LONG
*)&gpaulRefCount
[ulIndex
]);
620 /* Increase the object's reference count */
621 cRefs
= InterlockedIncrement((LONG
*)&pobj
->ulShareCount
);
624 DBG_LOGEVENT(&pobj
->slhLog
, EVENT_REFERENCE
, cRefs
);
629 GDIOBJ_TryLockObject(
637 /* Check if the handle type matches */
638 ASSERT_TRYLOCK_OBJECT_TYPE(objt
);
639 if ((((ULONG_PTR
)hobj
>> 16) & 0x1f) != objt
)
641 DPRINT("Wrong object type: hobj=0x%p, objt=0x%x\n", hobj
, objt
);
645 /* Reference the handle entry */
646 pentry
= ENTRY_ReferenceEntryByHandle(hobj
, 0);
649 DPRINT("GDIOBJ: Requested handle 0x%p is not valid.\n", hobj
);
653 /* Get the pointer to the BASEOBJECT */
654 pobj
= pentry
->einfo
.pobj
;
656 /* Check if we already own the lock */
657 dwThreadId
= PtrToUlong(PsGetCurrentThreadId());
658 if (pobj
->dwThreadId
!= dwThreadId
)
660 /* Disable APCs and try acquiring the push lock */
661 KeEnterCriticalRegion();
662 if(!ExTryAcquirePushLockExclusive(&pobj
->pushlock
))
664 ULONG cRefs
, ulIndex
;
665 /* Already owned. Clean up and leave. */
666 KeLeaveCriticalRegion();
668 /* Calculate the index */
669 ulIndex
= GDI_HANDLE_GET_INDEX(pobj
->hHmgr
);
671 /* Decrement reference count */
672 ASSERT((gpaulRefCount
[ulIndex
] & REF_MASK_COUNT
) > 0);
673 cRefs
= InterlockedDecrement((LONG
*)&gpaulRefCount
[ulIndex
]);
674 ASSERT(cRefs
& REF_MASK_VALID
);
679 /* Set us as lock owner */
680 ASSERT(pobj
->dwThreadId
== 0);
681 pobj
->dwThreadId
= dwThreadId
;
684 /* Increase lock count */
685 pobj
->cExclusiveLock
++;
686 DBG_INCREASE_LOCK_COUNT(PsGetCurrentProcessWin32Process(), hobj
);
687 DBG_LOGEVENT(&pobj
->slhLog
, EVENT_LOCK
, 0);
689 /* Return the object */
703 /* Check if the handle type matches */
704 ASSERT_EXCLUSIVE_OBJECT_TYPE(objt
);
705 if ((((ULONG_PTR
)hobj
>> 16) & 0x1f) != objt
)
707 DPRINT("Wrong object type: hobj=0x%p, objt=0x%x\n", hobj
, objt
);
711 /* Reference the handle entry */
712 pentry
= ENTRY_ReferenceEntryByHandle(hobj
, 0);
715 DPRINT("GDIOBJ: Requested handle 0x%p is not valid.\n", hobj
);
719 /* Get the pointer to the BASEOBJECT */
720 pobj
= pentry
->einfo
.pobj
;
722 /* Check if we already own the lock */
723 dwThreadId
= PtrToUlong(PsGetCurrentThreadId());
724 if (pobj
->dwThreadId
!= dwThreadId
)
726 /* Disable APCs and acquire the push lock */
727 KeEnterCriticalRegion();
728 ExAcquirePushLockExclusive(&pobj
->pushlock
);
730 /* Set us as lock owner */
731 ASSERT(pobj
->dwThreadId
== 0);
732 pobj
->dwThreadId
= dwThreadId
;
735 /* Increase lock count */
736 pobj
->cExclusiveLock
++;
737 DBG_INCREASE_LOCK_COUNT(PsGetCurrentProcessWin32Process(), hobj
);
738 DBG_LOGEVENT(&pobj
->slhLog
, EVENT_LOCK
, 0);
740 /* Return the object */
746 GDIOBJ_vUnlockObject(POBJ pobj
)
748 ULONG cRefs
, ulIndex
;
749 ASSERT(pobj
->cExclusiveLock
> 0);
751 /* Decrease lock count */
752 pobj
->cExclusiveLock
--;
753 DBG_DECREASE_LOCK_COUNT(PsGetCurrentProcessWin32Process(), pobj
->hHmgr
);
754 DBG_LOGEVENT(&pobj
->slhLog
, EVENT_UNLOCK
, 0);
756 /* Check if this was the last lock */
757 if (pobj
->cExclusiveLock
== 0)
759 /* Reset lock owner */
760 pobj
->dwThreadId
= 0;
762 /* Release the pushlock and reenable APCs */
763 ExReleasePushLockExclusive(&pobj
->pushlock
);
764 KeLeaveCriticalRegion();
767 /* Calculate the index */
768 ulIndex
= GDI_HANDLE_GET_INDEX(pobj
->hHmgr
);
770 /* Decrement reference count */
771 ASSERT((gpaulRefCount
[ulIndex
] & REF_MASK_COUNT
) > 0);
772 cRefs
= InterlockedDecrement((LONG
*)&gpaulRefCount
[ulIndex
]);
773 ASSERT(cRefs
& REF_MASK_VALID
);
778 GDIOBJ_hInsertObject(
785 /* Must have no handle and only one reference */
786 ASSERT(GDI_HANDLE_GET_INDEX(pobj
->hHmgr
) == 0);
787 ASSERT(pobj
->cExclusiveLock
== 0);
788 ASSERT(pobj
->ulShareCount
== 1);
790 /* Get a free handle entry */
791 pentry
= ENTRY_pentPopFreeEntry();
794 DPRINT1("GDIOBJ: Could not get a free entry.\n");
798 /* Make the object exclusively locked */
799 ExInitializePushLock(&pobj
->pushlock
);
800 KeEnterCriticalRegion();
801 ExAcquirePushLockExclusive(&pobj
->pushlock
);
802 pobj
->cExclusiveLock
= 1;
803 pobj
->dwThreadId
= PtrToUlong(PsGetCurrentThreadId());
804 DBG_INCREASE_LOCK_COUNT(PsGetCurrentProcessWin32Process(), pobj
->hHmgr
);
806 /* Get object type from the hHmgr field */
807 objt
= ((ULONG_PTR
)pobj
->hHmgr
>> 16) & 0xff;
808 ASSERT(objt
!= GDIObjType_DEF_TYPE
);
810 /* Check if current process is requested owner */
811 if (ulOwner
== GDI_OBJ_HMGR_POWNED
)
813 /* Increment the process handle count */
814 IncrementCurrentProcessGdiHandleCount();
817 ulOwner
= HandleToUlong(PsGetCurrentProcessId());
820 /* Insert the object into the handle table */
821 pobj
->hHmgr
= ENTRY_hInsertObject(pentry
, pobj
, objt
, ulOwner
);
823 /* Return the handle */
824 DPRINT("GDIOBJ: Created handle: %p\n", pobj
->hHmgr
);
825 DBG_LOGEVENT(&pobj
->slhLog
, EVENT_CREATE_HANDLE
, 0);
831 GDIOBJ_vSetObjectOwner(
838 /* This is a ugly HACK, needed to fix IntGdiSetDCOwnerEx */
839 if (GDI_HANDLE_IS_STOCKOBJ(pobj
->hHmgr
))
841 DPRINT("Trying to set ownership of stock object %p to %lx\n", pobj
->hHmgr
, ulNewOwner
);
845 /* Get the handle entry */
846 NT_ASSERT(GDI_HANDLE_GET_INDEX(pobj
->hHmgr
));
847 pentry
= &gpentHmgr
[GDI_HANDLE_GET_INDEX(pobj
->hHmgr
)];
849 /* Check if the new owner is the same as the old one */
850 ulOldOwner
= pentry
->ObjectOwner
.ulObj
;
851 if (ulOldOwner
== ulNewOwner
)
857 /* Is the current process requested? */
858 if (ulNewOwner
== GDI_OBJ_HMGR_POWNED
)
861 ulNewOwner
= HandleToUlong(PsGetCurrentProcessId());
865 if (ulNewOwner
== GDI_OBJ_HMGR_NONE
)
866 ulNewOwner
= GDI_OBJ_HMGR_PUBLIC
;
868 /* Was the object process owned? */
869 if ((ulOldOwner
!= GDI_OBJ_HMGR_PUBLIC
) &&
870 (ulOldOwner
!= GDI_OBJ_HMGR_NONE
))
872 /* Decrement the previous owners handle count */
873 DecrementGdiHandleCount(ulOldOwner
);
876 /* Is the new owner a process? */
877 if ((ulNewOwner
!= GDI_OBJ_HMGR_PUBLIC
) &&
878 (ulNewOwner
!= GDI_OBJ_HMGR_NONE
))
880 /* Increment the new owners handle count */
881 IncrementGdiHandleCount(ulNewOwner
);
885 /* Make sure we don't leak user mode memory */
886 NT_ASSERT(pentry
->pUser
== NULL
);
890 pentry
->ObjectOwner
.ulObj
= ulNewOwner
;
891 DBG_LOGEVENT(&pobj
->slhLog
, EVENT_SET_OWNER
, 0);
894 /* Locks 2 or 3 objects at a time */
897 GDIOBJ_bLockMultipleObjects(
903 UINT auiIndices
[3] = {0, 1, 2};
906 ASSERT(ulCount
<= 3);
908 /* Sort the handles */
909 for (i
= 0; i
< ulCount
- 1; i
++)
911 for (j
= i
+ 1; j
< ulCount
; j
++)
913 if ((ULONG_PTR
)ahObj
[auiIndices
[i
]] <
914 (ULONG_PTR
)ahObj
[auiIndices
[j
]])
917 auiIndices
[i
] = auiIndices
[j
];
923 /* Lock the objects in safe order */
924 for (i
= 0; i
< ulCount
; i
++)
926 /* Skip NULL handles */
927 if (ahObj
[auiIndices
[i
]] == NULL
)
929 apObj
[auiIndices
[i
]] = NULL
;
933 /* Lock the object */
934 apObj
[auiIndices
[i
]] = GDIOBJ_LockObject(ahObj
[auiIndices
[i
]], objt
);
936 /* Check for failure */
937 if (apObj
[auiIndices
[i
]] == NULL
)
942 if (apObj
[auiIndices
[i
]])
943 GDIOBJ_vUnlockObject(apObj
[auiIndices
[i
]]);
954 GDIOBJ_pvGetObjectAttr(POBJ pobj
)
956 ULONG ulIndex
= GDI_HANDLE_GET_INDEX(pobj
->hHmgr
);
957 return gpentHmgr
[ulIndex
].pUser
;
962 GDIOBJ_vSetObjectAttr(POBJ pobj
, PVOID pvObjAttr
)
968 /* Get the handle index */
969 ulIndex
= GDI_HANDLE_GET_INDEX(pobj
->hHmgr
);
971 /* Set pointer to the usermode attribute */
972 gpentHmgr
[ulIndex
].pUser
= pvObjAttr
;
977 GDIOBJ_vDeleteObject(POBJ pobj
)
981 /* Set the object's delete flag */
982 InterlockedOr16((SHORT
*)&pobj
->BaseFlags
, BASEFLAG_READY_TO_DIE
);
983 DBG_LOGEVENT(&pobj
->slhLog
, EVENT_DELETE
, 0);
985 /* Get the handle index */
986 ulIndex
= GDI_HANDLE_GET_INDEX(pobj
->hHmgr
);
989 /* Reset the handle valid bit */
990 InterlockedAnd((LONG
*)&gpaulRefCount
[ulIndex
], ~REF_MASK_VALID
);
992 /* Check if the object is exclusively locked */
993 if (pobj
->cExclusiveLock
!= 0)
995 /* Reset lock owner and lock count */
996 pobj
->dwThreadId
= 0;
997 pobj
->cExclusiveLock
= 0;
999 /* Release the pushlock and reenable APCs */
1000 ExReleasePushLockExclusive(&pobj
->pushlock
);
1001 KeLeaveCriticalRegion();
1002 DBG_DECREASE_LOCK_COUNT(PsGetCurrentProcessWin32Process(), pobj
->hHmgr
);
1006 /* Dereference the object (will take care of deletion) */
1007 GDIOBJ_vDereferenceObject(pobj
);
1012 GreIsHandleValid(HGDIOBJ hobj
)
1016 pentry
= ENTRY_ReferenceEntryByHandle(hobj
, 0);
1017 if (!pentry
) return FALSE
;
1018 GDIOBJ_vDereferenceObject(pentry
->einfo
.pobj
);
1024 GreDeleteObject(HGDIOBJ hobj
)
1028 /* Check for stock objects */
1029 if (GDI_HANDLE_IS_STOCKOBJ(hobj
))
1031 DPRINT1("GreDeleteObject: Cannot delete stock object %p.\n", hobj
);
1035 /* Reference the handle entry */
1036 pentry
= ENTRY_ReferenceEntryByHandle(hobj
, 0);
1039 DPRINT1("GreDeleteObject: Trying to delete invalid object %p\n", hobj
);
1043 /* Check for public owner */
1044 if (pentry
->ObjectOwner
.ulObj
== GDI_OBJ_HMGR_PUBLIC
)
1046 DPRINT1("GreDeleteObject: Trying to delete global object %p\n", hobj
);
1047 GDIOBJ_vDereferenceObject(pentry
->einfo
.pobj
);
1051 /* Delete the object */
1052 GDIOBJ_vDeleteObject(pentry
->einfo
.pobj
);
1058 GreGetObjectOwner(HGDIOBJ hobj
)
1060 ULONG ulIndex
, ulOwner
;
1062 /* Get the handle index */
1063 ulIndex
= GDI_HANDLE_GET_INDEX(hobj
);
1065 /* Check if the handle is valid */
1066 if (ulIndex
>= GDI_HANDLE_COUNT
||
1067 gpentHmgr
[ulIndex
].Objt
== GDIObjType_DEF_TYPE
||
1068 ((ULONG_PTR
)hobj
>> 16) != gpentHmgr
[ulIndex
].FullUnique
)
1070 DPRINT1("GreGetObjectOwner: invalid handle 0x%p.\n", hobj
);
1071 return GDI_OBJ_HMGR_RESTRICTED
;
1074 /* Get the object owner */
1075 ulOwner
= gpentHmgr
[ulIndex
].ObjectOwner
.ulObj
;
1077 if (ulOwner
== HandleToUlong(PsGetCurrentProcessId()))
1078 return GDI_OBJ_HMGR_POWNED
;
1080 if (ulOwner
== GDI_OBJ_HMGR_PUBLIC
)
1081 return GDI_OBJ_HMGR_PUBLIC
;
1083 return GDI_OBJ_HMGR_RESTRICTED
;
1088 GreSetObjectOwnerEx(
1095 /* Check for stock objects */
1096 if (GDI_HANDLE_IS_STOCKOBJ(hobj
))
1098 DPRINT("GreSetObjectOwner: Got stock object %p\n", hobj
);
1102 /* Reference the handle entry */
1103 pentry
= ENTRY_ReferenceEntryByHandle(hobj
, Flags
);
1106 DPRINT("GreSetObjectOwner: Invalid handle 0x%p.\n", hobj
);
1110 /* Call internal function */
1111 GDIOBJ_vSetObjectOwner(pentry
->einfo
.pobj
, ulOwner
);
1113 /* Dereference the object */
1114 GDIOBJ_vDereferenceObject(pentry
->einfo
.pobj
);
1125 return GreSetObjectOwnerEx(hobj
, ulOwner
, 0);
1139 /* Verify object type */
1140 objt
= ((ULONG_PTR
)hobj
>> 16) & 0x1f;
1141 if (objt
!= GDIObjType_BRUSH_TYPE
&&
1142 objt
!= GDIObjType_SURF_TYPE
&&
1143 objt
!= GDIObjType_LFONT_TYPE
&&
1144 objt
!= GDIObjType_PAL_TYPE
)
1146 DPRINT1("GreGetObject: Invalid object type\n");
1150 pvObj
= GDIOBJ_ReferenceObjectByHandle(hobj
, objt
);
1153 DPRINT("GreGetObject: Could not lock object\n");
1157 switch (GDI_HANDLE_GET_TYPE(hobj
))
1159 case GDILoObjType_LO_PEN_TYPE
:
1160 case GDILoObjType_LO_EXTPEN_TYPE
:
1161 iResult
= PEN_GetObject(pvObj
, cbCount
, pvBuffer
);
1164 case GDILoObjType_LO_BRUSH_TYPE
:
1165 iResult
= BRUSH_GetObject(pvObj
, cbCount
, pvBuffer
);
1168 case GDILoObjType_LO_BITMAP_TYPE
:
1169 iResult
= BITMAP_GetObject(pvObj
, cbCount
, pvBuffer
);
1172 case GDILoObjType_LO_FONT_TYPE
:
1173 iResult
= FontGetObject(pvObj
, cbCount
, pvBuffer
);
1176 case GDILoObjType_LO_PALETTE_TYPE
:
1177 iResult
= PALETTE_GetObject(pvObj
, cbCount
, pvBuffer
);
1181 DPRINT1("GDI object type of 0x%p not implemented\n", hobj
);
1185 GDIOBJ_vDereferenceObject(pvObj
);
1194 IN INT cjBufferSize
,
1195 OUT LPVOID lpBuffer
)
1197 UINT iResult
, cjMaxSize
;
1201 DIBSECTION dibsection
;
1205 EXTLOGFONTW extlogfontw
;
1206 ENUMLOGFONTEXDVW enumlogfontexdvw
;
1209 /* Normalize to the largest supported object size */
1210 cjMaxSize
= min((UINT
)cjBufferSize
, sizeof(object
));
1212 /* Now do the actual call */
1213 iResult
= GreGetObject(hobj
, cjMaxSize
, lpBuffer
? &object
: NULL
);
1215 /* Check if we have a buffer and data */
1216 if ((lpBuffer
!= NULL
) && (iResult
!= 0))
1218 /* Enter SEH for buffer transfer */
1221 /* Probe the buffer and copy it */
1222 cjMaxSize
= min(cjMaxSize
, iResult
);
1223 ProbeForWrite(lpBuffer
, cjMaxSize
, sizeof(WORD
));
1224 RtlCopyMemory(lpBuffer
, &object
, cjMaxSize
);
1226 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1228 /* Clear the return value.
1229 * Do *NOT* set last error here! */
1235 /* Return the count */
1242 NtGdiCreateClientObj(
1248 /* Allocate a new object */
1249 pObject
= GDIOBJ_AllocateObject(GDIObjType_CLIENTOBJ_TYPE
,
1251 BASEFLAG_LOOKASIDE
);
1254 DPRINT1("NtGdiCreateClientObj: Could not allocate a clientobj.\n");
1258 /* Mask out everything that would change the type in a wrong manner */
1259 ulType
&= (GDI_HANDLE_TYPE_MASK
& ~GDI_HANDLE_BASETYPE_MASK
);
1261 /* Set the real object type */
1262 pObject
->hHmgr
= UlongToHandle(ulType
| GDILoObjType_LO_CLIENTOBJ_TYPE
);
1264 /* Create a handle */
1265 handle
= GDIOBJ_hInsertObject(pObject
, GDI_OBJ_HMGR_POWNED
);
1268 DPRINT1("NtGdiCreateClientObj: Could not create a handle.\n");
1269 GDIOBJ_vFreeObject(pObject
);
1274 GDIOBJ_vUnlockObject(pObject
);
1282 NtGdiDeleteClientObj(
1285 /* We first need to get the real type from the handle */
1286 ULONG ulType
= GDI_HANDLE_GET_TYPE(hobj
);
1288 /* Check if it's really a CLIENTOBJ */
1289 if ((ulType
& GDI_HANDLE_BASETYPE_MASK
) != GDILoObjType_LO_CLIENTOBJ_TYPE
)
1291 /* FIXME: SetLastError? */
1295 return GreDeleteObject(hobj
);
1300 PGDI_HANDLE_TABLE GdiHandleTable
= NULL
;
1303 GDIOBJ_ShareLockObj(HGDIOBJ hObj
, DWORD ExpectedType
)
1305 if (ExpectedType
== GDI_OBJECT_TYPE_DONTCARE
)
1306 ExpectedType
= GDI_HANDLE_GET_TYPE(hObj
);
1307 return GDIOBJ_ReferenceObjectByHandle(hObj
, (ExpectedType
>> 16) & 0x1f);
1310 // This function is not safe to use with concurrent deleting attempts
1311 // That shouldn't be a problem, since we don't have any processes yet,
1312 // that could delete the handle
1315 GDIOBJ_ConvertToStockObj(HGDIOBJ
*phObj
)
1320 /* Reference the handle entry */
1321 pentry
= ENTRY_ReferenceEntryByHandle(*phObj
, 0);
1324 DPRINT1("GDIOBJ: Requested handle 0x%p is not valid.\n", *phObj
);
1328 /* Update the entry */
1329 pentry
->FullUnique
|= GDI_ENTRY_STOCK_MASK
;
1330 pentry
->ObjectOwner
.ulObj
= 0;
1332 /* Get the pointer to the BASEOBJECT */
1333 pobj
= pentry
->einfo
.pobj
;
1335 /* Calculate the new handle */
1336 pobj
->hHmgr
= (HGDIOBJ
)((ULONG_PTR
)pobj
->hHmgr
| GDI_HANDLE_STOCK_MASK
);
1338 /* Return the new handle */
1339 *phObj
= pobj
->hHmgr
;
1341 /* Dereference the handle */
1342 GDIOBJ_vDereferenceObject(pobj
);
1348 GDIOBJ_AllocObjWithHandle(ULONG ObjectType
, ULONG cjSize
)
1352 UCHAR objt
= (ObjectType
>> 16) & 0xFF;
1354 if ((objt
== GDIObjType_DC_TYPE
&& cjSize
== sizeof(DC
)) ||
1355 (objt
== GDIObjType_PAL_TYPE
&& cjSize
== sizeof(PALETTE
)) ||
1356 (objt
== GDIObjType_RGN_TYPE
&& cjSize
== sizeof(REGION
)) ||
1357 (objt
== GDIObjType_SURF_TYPE
&& cjSize
== sizeof(SURFACE
)) ||
1358 (objt
== GDIObjType_PATH_TYPE
&& cjSize
== sizeof(PATH
)))
1360 fl
|= BASEFLAG_LOOKASIDE
;
1363 pobj
= GDIOBJ_AllocateObject(objt
, cjSize
, fl
);
1369 if (!GDIOBJ_hInsertObject(pobj
, GDI_OBJ_HMGR_POWNED
))
1371 GDIOBJ_vFreeObject(pobj
);
1378 GDI_MapHandleTable(PEPROCESS pProcess
)
1380 PVOID pvMappedView
= NULL
;
1382 LARGE_INTEGER liOffset
;
1383 ULONG cjViewSize
= sizeof(GDI_HANDLE_TABLE
);
1385 liOffset
.QuadPart
= 0;
1387 ASSERT(gpvGdiHdlTblSection
!= NULL
);
1388 ASSERT(pProcess
!= NULL
);
1390 Status
= MmMapViewOfSection(gpvGdiHdlTblSection
,
1401 if (!NT_SUCCESS(Status
))
1404 return pvMappedView
;
1408 GDI_CleanupForProcess(struct _EPROCESS
*Process
)
1415 DPRINT("CleanupForProcess prochandle %p Pid %p\n",
1416 Process
, Process
->UniqueProcessId
);
1418 ASSERT(Process
== PsGetCurrentProcess());
1420 /* Get the current process Id */
1421 dwProcessId
= PtrToUlong(PsGetCurrentProcessId());
1423 /* Loop all handles in the handle table */
1424 for (ulIndex
= RESERVE_ENTRIES_COUNT
; ulIndex
< gulFirstUnused
; ulIndex
++)
1426 pentry
= &gpentHmgr
[ulIndex
];
1428 /* Check if the object is owned by the process */
1429 if (pentry
->ObjectOwner
.ulObj
== dwProcessId
)
1431 ASSERT(pentry
->einfo
.pobj
->cExclusiveLock
== 0);
1433 /* Reference the object and delete it */
1434 InterlockedIncrement((LONG
*)&gpaulRefCount
[ulIndex
]);
1435 GDIOBJ_vDeleteObject(pentry
->einfo
.pobj
);
1441 DbgGdiHTIntegrityCheck();
1445 ppi
= PsGetCurrentProcessWin32Process();
1446 DPRINT("Completed cleanup for process %p\n", Process
->UniqueProcessId
);
1447 if (ppi
->GDIHandleCount
!= 0)
1449 DPRINT1("Leaking %d handles!\n", ppi
->GDIHandleCount
);
1453 /* Loop all handles in the handle table */
1454 for (ulIndex
= RESERVE_ENTRIES_COUNT
; ulIndex
< gulFirstUnused
; ulIndex
++)
1456 pentry
= &gpentHmgr
[ulIndex
];
1458 /* Check if the object is owned by the process */
1459 if (pentry
->ObjectOwner
.ulObj
== dwProcessId
)
1461 DPRINT1("Leaking object. Index=%lx, type=0x%x, refcount=%lx\n",
1462 ulIndex
, pentry
->Objt
, gpaulRefCount
[ulIndex
]);
1463 DBG_DUMP_EVENT_LIST(&pentry
->einfo
.pobj
->slhLog
);
1464 //DBG_CLEANUP_EVENT_LIST(&pentry->einfo.pobj->slhLog);