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 (objt) == GDIObjType_LFONT_TYPE)
57 #define DBG_INCREASE_LOCK_COUNT(ppi, hobj)
58 #define DBG_DECREASE_LOCK_COUNT(x, y)
59 #define ASSERT_SHARED_OBJECT_TYPE(objt)
60 #define ASSERT_EXCLUSIVE_OBJECT_TYPE(objt)
63 #if defined(_M_IX86) || defined(_M_AMD64)
64 #define InterlockedOr16 _InterlockedOr16
67 #define GDIOBJ_POOL_TAG(type) ('00hG' + (((type) & 0x1f) << 24))
71 REF_MASK_REUSE
= 0xff000000,
72 REF_INC_REUSE
= 0x01000000,
73 REF_MASK_VALID
= 0x00800000,
74 REF_MASK_COUNT
= 0x007fffff,
75 REF_MASK_INUSE
= 0x00ffffff,
78 /* GLOBALS *******************************************************************/
80 /* Per session handle table globals */
81 static PVOID gpvGdiHdlTblSection
= NULL
;
82 static PENTRY gpentHmgr
;
83 static PULONG gpaulRefCount
;
86 static PPAGED_LOOKASIDE_LIST gpaLookasideList
;
88 static BOOL NTAPI
GDIOBJ_Cleanup(PVOID ObjectBody
);
94 NULL
, /* 00 GDIObjType_DEF_TYPE */
95 DC_Cleanup
, /* 01 GDIObjType_DC_TYPE */
96 NULL
, /* 02 GDIObjType_UNUSED1_TYPE */
97 NULL
, /* 03 GDIObjType_UNUSED2_TYPE */
98 REGION_Cleanup
, /* 04 GDIObjType_RGN_TYPE */
99 SURFACE_Cleanup
, /* 05 GDIObjType_SURF_TYPE */
100 GDIOBJ_Cleanup
, /* 06 GDIObjType_CLIENTOBJ_TYPE */
101 GDIOBJ_Cleanup
, /* 07 GDIObjType_PATH_TYPE */
102 PALETTE_Cleanup
, /* 08 GDIObjType_PAL_TYPE */
103 GDIOBJ_Cleanup
, /* 09 GDIObjType_ICMLCS_TYPE */
104 GDIOBJ_Cleanup
, /* 0a GDIObjType_LFONT_TYPE */
105 NULL
, /* 0b GDIObjType_RFONT_TYPE, unused */
106 NULL
, /* 0c GDIObjType_PFE_TYPE, unused */
107 NULL
, /* 0d GDIObjType_PFT_TYPE, unused */
108 GDIOBJ_Cleanup
, /* 0e GDIObjType_ICMCXF_TYPE */
109 NULL
, /* 0f GDIObjType_SPRITE_TYPE, unused */
110 BRUSH_Cleanup
, /* 10 GDIObjType_BRUSH_TYPE, BRUSH, PEN, EXTPEN */
111 NULL
, /* 11 GDIObjType_UMPD_TYPE, unused */
112 NULL
, /* 12 GDIObjType_UNUSED4_TYPE */
113 NULL
, /* 13 GDIObjType_SPACE_TYPE, unused */
114 NULL
, /* 14 GDIObjType_UNUSED5_TYPE */
115 NULL
, /* 15 GDIObjType_META_TYPE, unused */
116 NULL
, /* 16 GDIObjType_EFSTATE_TYPE, unused */
117 NULL
, /* 17 GDIObjType_BMFD_TYPE, unused */
118 NULL
, /* 18 GDIObjType_VTFD_TYPE, unused */
119 NULL
, /* 19 GDIObjType_TTFD_TYPE, unused */
120 NULL
, /* 1a GDIObjType_RC_TYPE, unused */
121 NULL
, /* 1b GDIObjType_TEMP_TYPE, unused */
122 DRIVEROBJ_Cleanup
,/* 1c GDIObjType_DRVOBJ_TYPE */
123 NULL
, /* 1d GDIObjType_DCIOBJ_TYPE, unused */
124 NULL
, /* 1e GDIObjType_SPOOL_TYPE, unused */
125 NULL
, /* 1f reserved entry */
128 /* INTERNAL FUNCTIONS ********************************************************/
132 GDIOBJ_Cleanup(PVOID ObjectBody
)
139 InitLookasideList(UCHAR objt
, ULONG cjSize
)
141 ExInitializePagedLookasideList(&gpaLookasideList
[objt
],
146 GDITAG_HMGR_LOOKASIDE_START
+ (objt
<< 24),
153 InitGdiHandleTable(void)
156 LARGE_INTEGER liSize
;
158 SIZE_T cjViewSize
= 0;
160 /* Create a section for the shared handle table */
161 liSize
.QuadPart
= sizeof(GDI_HANDLE_TABLE
); // GDI_HANDLE_COUNT * sizeof(ENTRY);
162 status
= MmCreateSection(&gpvGdiHdlTblSection
,
170 if (!NT_SUCCESS(status
))
172 DPRINT1("INITGDI: Could not allocate a GDI handle table.\n");
176 /* Map the section in session space */
177 status
= MmMapViewInSessionSpace(gpvGdiHdlTblSection
,
180 if (!NT_SUCCESS(status
))
182 DPRINT1("INITGDI: Failed to map handle table section\n");
183 ObDereferenceObject(gpvGdiHdlTblSection
);
187 /* Allocate memory for the reference counter table */
188 gpaulRefCount
= EngAllocSectionMem(&pvSection
,
190 GDI_HANDLE_COUNT
* sizeof(ULONG
),
194 DPRINT1("INITGDI: Failed to allocate reference table.\n");
195 ObDereferenceObject(gpvGdiHdlTblSection
);
196 return STATUS_INSUFFICIENT_RESOURCES
;
200 gulFirstUnused
= RESERVE_ENTRIES_COUNT
;
202 GdiHandleTable
= (PVOID
)gpentHmgr
;
204 /* Initialize the lookaside lists */
205 gpaLookasideList
= ExAllocatePoolWithTag(NonPagedPool
,
206 GDIObjTypeTotal
* sizeof(PAGED_LOOKASIDE_LIST
),
208 if(!gpaLookasideList
)
209 return STATUS_NO_MEMORY
;
211 InitLookasideList(GDIObjType_DC_TYPE
, sizeof(DC
));
212 InitLookasideList(GDIObjType_RGN_TYPE
, sizeof(REGION
));
213 InitLookasideList(GDIObjType_SURF_TYPE
, sizeof(SURFACE
));
214 InitLookasideList(GDIObjType_CLIENTOBJ_TYPE
, sizeof(CLIENTOBJ
));
215 InitLookasideList(GDIObjType_PATH_TYPE
, sizeof(PATH
));
216 InitLookasideList(GDIObjType_PAL_TYPE
, sizeof(PALETTE
));
217 InitLookasideList(GDIObjType_ICMLCS_TYPE
, sizeof(COLORSPACE
));
218 InitLookasideList(GDIObjType_LFONT_TYPE
, sizeof(TEXTOBJ
));
219 InitLookasideList(GDIObjType_BRUSH_TYPE
, sizeof(BRUSH
));
221 return STATUS_SUCCESS
;
226 IncrementGdiHandleCount(void)
228 PPROCESSINFO ppi
= PsGetCurrentProcessWin32Process();
229 if (ppi
) InterlockedIncrement((LONG
*)&ppi
->GDIHandleCount
);
234 DecrementGdiHandleCount(void)
236 PPROCESSINFO ppi
= PsGetCurrentProcessWin32Process();
237 if (ppi
) InterlockedDecrement((LONG
*)&ppi
->GDIHandleCount
);
242 ENTRY_pentPopFreeEntry(VOID
)
244 ULONG iFirst
, iNext
, iPrev
;
247 DPRINT("Enter InterLockedPopFreeEntry\n");
251 /* Get the index and sequence number of the first free entry */
252 iFirst
= gulFirstFree
;
254 /* Check if we have a free entry */
255 if (!(iFirst
& GDI_HANDLE_INDEX_MASK
))
257 /* Increment FirstUnused and get the new index */
258 iFirst
= InterlockedIncrement((LONG
*)&gulFirstUnused
) - 1;
260 /* Check if we have unused entries left */
261 if (iFirst
>= GDI_HANDLE_COUNT
)
263 DPRINT1("No more GDI handles left!\n");
267 /* Return the old entry */
268 return &gpentHmgr
[iFirst
];
271 /* Get a pointer to the first free entry */
272 pentFree
= &gpentHmgr
[iFirst
& GDI_HANDLE_INDEX_MASK
];
274 /* Create a new value with an increased sequence number */
275 iNext
= (USHORT
)(ULONG_PTR
)pentFree
->einfo
.pobj
;
276 iNext
|= (iFirst
& ~GDI_HANDLE_INDEX_MASK
) + 0x10000;
278 /* Try to exchange the FirstFree value */
279 iPrev
= InterlockedCompareExchange((LONG
*)&gulFirstFree
,
283 while (iPrev
!= iFirst
);
285 /* Sanity check: is entry really free? */
286 ASSERT(((ULONG_PTR
)pentFree
->einfo
.pobj
& ~GDI_HANDLE_INDEX_MASK
) == 0);
291 /* Pushes an entry of the handle table to the free list,
292 The entry must not have any references left */
295 ENTRY_vPushFreeEntry(PENTRY pentFree
)
297 ULONG iToFree
, iFirst
, iPrev
, idxToFree
;
299 DPRINT("Enter ENTRY_vPushFreeEntry\n");
301 idxToFree
= pentFree
- gpentHmgr
;
302 ASSERT((gpaulRefCount
[idxToFree
] & REF_MASK_INUSE
) == 0);
304 /* Initialize entry */
305 pentFree
->Objt
= GDIObjType_DEF_TYPE
;
306 pentFree
->ObjectOwner
.ulObj
= 0;
307 pentFree
->pUser
= NULL
;
309 /* Increase reuse counter in entry and reference counter */
310 InterlockedExchangeAdd((LONG
*)&gpaulRefCount
[idxToFree
], REF_INC_REUSE
);
311 pentFree
->FullUnique
+= 0x0100;
315 /* Get the current first free index and sequence number */
316 iFirst
= gulFirstFree
;
318 /* Set the einfo.pobj member to the index of the first free entry */
319 pentFree
->einfo
.pobj
= UlongToPtr(iFirst
& GDI_HANDLE_INDEX_MASK
);
321 /* Combine new index and increased sequence number in iToFree */
322 iToFree
= idxToFree
| ((iFirst
& ~GDI_HANDLE_INDEX_MASK
) + 0x10000);
324 /* Try to atomically update the first free entry */
325 iPrev
= InterlockedCompareExchange((LONG
*)&gulFirstFree
,
329 while (iPrev
!= iFirst
);
334 ENTRY_ReferenceEntryByHandle(HGDIOBJ hobj
, FLONG fl
)
336 ULONG ulIndex
, cNewRefs
, cOldRefs
;
339 /* Get the handle index and check if its too big */
340 ulIndex
= GDI_HANDLE_GET_INDEX(hobj
);
341 if (ulIndex
>= GDI_HANDLE_COUNT
) return NULL
;
343 /* Get pointer to the entry */
344 pentry
= &gpentHmgr
[ulIndex
];
346 /* Get the current reference count */
347 cOldRefs
= gpaulRefCount
[ulIndex
];
351 /* Check if the slot is deleted */
352 if ((cOldRefs
& REF_MASK_VALID
) == 0)
354 DPRINT("GDIOBJ: Slot is not valid: 0x%lx, hobh=%p\n", cOldRefs
, hobj
);
358 /* Check if the unique value matches */
359 if (pentry
->FullUnique
!= (USHORT
)((ULONG_PTR
)hobj
>> 16))
361 DPRINT("GDIOBJ: Wrong unique value. Handle: 0x%4x, entry: 0x%4x\n",
362 (USHORT
)((ULONG_PTR
)hobj
>> 16), pentry
->FullUnique
);
366 /* Check if the object owner is this process or public */
367 if (!(fl
& GDIOBJFLAG_IGNOREPID
) &&
368 pentry
->ObjectOwner
.ulObj
!= GDI_OBJ_HMGR_PUBLIC
&&
369 pentry
->ObjectOwner
.ulObj
!= PtrToUlong(PsGetCurrentProcessId()))
371 DPRINT("GDIOBJ: Cannot reference foreign handle %p, pentry=%p:%lx.\n",
372 hobj
, pentry
, pentry
->ObjectOwner
.ulObj
);
376 /* Try to atomically increment the reference count */
377 cNewRefs
= cOldRefs
+ 1;
378 cOldRefs
= InterlockedCompareExchange((PLONG
)&gpaulRefCount
[ulIndex
],
382 while (cNewRefs
!= cOldRefs
+ 1);
384 /* Integrity checks */
385 ASSERT((pentry
->FullUnique
& 0x1f) == pentry
->Objt
);
386 ASSERT(pentry
->einfo
.pobj
&& pentry
->einfo
.pobj
->hHmgr
== hobj
);
393 ENTRY_hInsertObject(PENTRY pentry
, POBJ pobj
, UCHAR objt
, ULONG ulOwner
)
397 /* Calculate the handle index */
398 ulIndex
= pentry
- gpentHmgr
;
400 /* Update the fields in the ENTRY */
401 pentry
->einfo
.pobj
= pobj
;
402 pentry
->Objt
= objt
& 0x1f;
403 pentry
->FullUnique
= (pentry
->FullUnique
& 0xff00) | objt
;
404 pentry
->ObjectOwner
.ulObj
= ulOwner
;
406 /* Make the handle valid with 1 reference */
407 ASSERT((gpaulRefCount
[ulIndex
] & REF_MASK_INUSE
) == 0);
408 InterlockedOr((LONG
*)&gpaulRefCount
[ulIndex
], REF_MASK_VALID
| 1);
410 /* Return the handle */
411 return (HGDIOBJ
)(((ULONG_PTR
)pentry
->FullUnique
<< 16) | ulIndex
);
416 GDIOBJ_AllocateObject(UCHAR objt
, ULONG cjSize
, FLONG fl
)
420 if (fl
& BASEFLAG_LOOKASIDE
)
422 /* Allocate the object from a lookaside list */
423 pobj
= ExAllocateFromPagedLookasideList(&gpaLookasideList
[objt
& 0x1f]);
427 /* Allocate the object from paged pool */
428 pobj
= ExAllocatePoolWithTag(PagedPool
, cjSize
, GDIOBJ_POOL_TAG(objt
));
431 if (!pobj
) return NULL
;
433 /* Initialize the object */
434 RtlZeroMemory(pobj
, cjSize
);
435 pobj
->hHmgr
= (HGDIOBJ
)((ULONG_PTR
)objt
<< 16);
436 pobj
->cExclusiveLock
= 0;
437 pobj
->ulShareCount
= 1;
438 pobj
->BaseFlags
= fl
& 0xffff;
439 DBG_INITLOG(&pobj
->slhLog
);
440 DBG_LOGEVENT(&pobj
->slhLog
, EVENT_ALLOCATE
, 0);
447 GDIOBJ_vFreeObject(POBJ pobj
)
451 DBG_CLEANUP_EVENT_LIST(&pobj
->slhLog
);
453 /* Get the object type */
454 objt
= ((ULONG_PTR
)pobj
->hHmgr
>> 16) & 0x1f;
456 /* Call the cleanup procedure */
457 ASSERT(apfnCleanup
[objt
]);
458 apfnCleanup
[objt
](pobj
);
460 /* Check if the object is allocated from a lookaside list */
461 if (pobj
->BaseFlags
& BASEFLAG_LOOKASIDE
)
463 ExFreeToPagedLookasideList(&gpaLookasideList
[objt
], pobj
);
467 ExFreePoolWithTag(pobj
, GDIOBJ_POOL_TAG(objt
));
473 GDIOBJ_vDereferenceObject(POBJ pobj
)
475 ULONG cRefs
, ulIndex
;
477 /* Calculate the index */
478 ulIndex
= GDI_HANDLE_GET_INDEX(pobj
->hHmgr
);
480 /* Check if the object has a handle */
483 /* Decrement reference count */
484 ASSERT((gpaulRefCount
[ulIndex
] & REF_MASK_COUNT
) > 0);
485 cRefs
= InterlockedDecrement((LONG
*)&gpaulRefCount
[ulIndex
]);
486 DBG_LOGEVENT(&pobj
->slhLog
, EVENT_DEREFERENCE
, cRefs
);
488 /* Check if we reached 0 and handle bit is not set */
489 if ((cRefs
& REF_MASK_INUSE
) == 0)
491 /* Make sure it's ok to delete the object */
492 ASSERT(pobj
->BaseFlags
& BASEFLAG_READY_TO_DIE
);
494 /* Check if the handle was process owned */
495 if (gpentHmgr
[ulIndex
].ObjectOwner
.ulObj
!= GDI_OBJ_HMGR_PUBLIC
&&
496 gpentHmgr
[ulIndex
].ObjectOwner
.ulObj
!= GDI_OBJ_HMGR_NONE
)
498 /* Decrement the process handle count */
499 ASSERT(gpentHmgr
[ulIndex
].ObjectOwner
.ulObj
==
500 HandleToUlong(PsGetCurrentProcessId()));
501 DecrementGdiHandleCount();
504 /* Push entry to the free list */
505 ENTRY_vPushFreeEntry(&gpentHmgr
[ulIndex
]);
507 /* Free the object */
508 GDIOBJ_vFreeObject(pobj
);
513 /* Decrement the objects reference count */
514 ASSERT(pobj
->ulShareCount
> 0);
515 cRefs
= InterlockedDecrement((LONG
*)&pobj
->ulShareCount
);
516 DBG_LOGEVENT(&pobj
->slhLog
, EVENT_DEREFERENCE
, cRefs
);
518 /* Check if we reached 0 */
521 /* Free the object */
522 GDIOBJ_vFreeObject(pobj
);
529 GDIOBJ_ReferenceObjectByHandle(
536 /* Check if the handle type matches */
537 ASSERT_SHARED_OBJECT_TYPE(objt
);
538 if ((((ULONG_PTR
)hobj
>> 16) & 0x1f) != objt
)
540 DPRINT("GDIOBJ: Wrong type. handle=%p, type=%x\n", hobj
, objt
);
544 /* Reference the handle entry */
545 pentry
= ENTRY_ReferenceEntryByHandle(hobj
, 0);
548 DPRINT("GDIOBJ: Requested handle 0x%p is not valid.\n", hobj
);
552 /* Get the pointer to the BASEOBJECT */
553 pobj
= pentry
->einfo
.pobj
;
555 /* Check if the object is exclusively locked */
556 if (pobj
->cExclusiveLock
!= 0)
558 DPRINT1("GDIOBJ: Cannot reference oject %p with exclusive lock.\n", hobj
);
559 GDIOBJ_vDereferenceObject(pobj
);
560 DBG_DUMP_EVENT_LIST(&pobj
->slhLog
);
564 DBG_LOGEVENT(&pobj
->slhLog
, EVENT_REFERENCE
, gpaulRefCount
[pentry
- gpentHmgr
]);
566 /* All is well, return the object */
572 GDIOBJ_vReferenceObjectByPointer(POBJ pobj
)
576 /* Check if the object has a handle */
577 if (GDI_HANDLE_GET_INDEX(pobj
->hHmgr
))
579 /* Increase the handle's reference count */
580 ULONG ulIndex
= GDI_HANDLE_GET_INDEX(pobj
->hHmgr
);
581 ASSERT((gpaulRefCount
[ulIndex
] & REF_MASK_COUNT
) > 0);
582 cRefs
= InterlockedIncrement((LONG
*)&gpaulRefCount
[ulIndex
]);
586 /* Increase the object's reference count */
587 cRefs
= InterlockedIncrement((LONG
*)&pobj
->ulShareCount
);
590 DBG_LOGEVENT(&pobj
->slhLog
, EVENT_REFERENCE
, cRefs
);
603 /* Check if the handle type matches */
604 ASSERT_EXCLUSIVE_OBJECT_TYPE(objt
);
605 if ((((ULONG_PTR
)hobj
>> 16) & 0x1f) != objt
)
607 DPRINT("Wrong object type: hobj=0x%p, objt=0x%x\n", hobj
, objt
);
611 /* Reference the handle entry */
612 pentry
= ENTRY_ReferenceEntryByHandle(hobj
, 0);
615 DPRINT("GDIOBJ: Requested handle 0x%p is not valid.\n", hobj
);
619 /* Get the pointer to the BASEOBJECT */
620 pobj
= pentry
->einfo
.pobj
;
622 /* Check if we already own the lock */
623 dwThreadId
= PtrToUlong(PsGetCurrentThreadId());
624 if (pobj
->dwThreadId
!= dwThreadId
)
626 /* Disable APCs and acquire the push lock */
627 KeEnterCriticalRegion();
628 ExAcquirePushLockExclusive(&pobj
->pushlock
);
630 /* Set us as lock owner */
631 ASSERT(pobj
->dwThreadId
== 0);
632 pobj
->dwThreadId
= dwThreadId
;
635 /* Increase lock count */
636 pobj
->cExclusiveLock
++;
637 DBG_INCREASE_LOCK_COUNT(PsGetCurrentProcessWin32Process(), hobj
);
638 DBG_LOGEVENT(&pobj
->slhLog
, EVENT_LOCK
, 0);
640 /* Return the object */
646 GDIOBJ_vUnlockObject(POBJ pobj
)
648 ULONG cRefs
, ulIndex
;
649 ASSERT(pobj
->cExclusiveLock
> 0);
651 /* Decrease lock count */
652 pobj
->cExclusiveLock
--;
653 DBG_DECREASE_LOCK_COUNT(PsGetCurrentProcessWin32Process(), pobj
->hHmgr
);
654 DBG_LOGEVENT(&pobj
->slhLog
, EVENT_UNLOCK
, 0);
656 /* Check if this was the last lock */
657 if (pobj
->cExclusiveLock
== 0)
659 /* Reset lock owner */
660 pobj
->dwThreadId
= 0;
662 /* Release the pushlock and reenable APCs */
663 ExReleasePushLockExclusive(&pobj
->pushlock
);
664 KeLeaveCriticalRegion();
667 /* Calculate the index */
668 ulIndex
= GDI_HANDLE_GET_INDEX(pobj
->hHmgr
);
670 /* Decrement reference count */
671 ASSERT((gpaulRefCount
[ulIndex
] & REF_MASK_COUNT
) > 0);
672 cRefs
= InterlockedDecrement((LONG
*)&gpaulRefCount
[ulIndex
]);
673 ASSERT(cRefs
& REF_MASK_VALID
);
678 GDIOBJ_hInsertObject(
685 /* Must have no handle and only one reference */
686 ASSERT(GDI_HANDLE_GET_INDEX(pobj
->hHmgr
) == 0);
687 ASSERT(pobj
->cExclusiveLock
== 0);
688 ASSERT(pobj
->ulShareCount
== 1);
690 /* Get a free handle entry */
691 pentry
= ENTRY_pentPopFreeEntry();
694 DPRINT1("GDIOBJ: Could not get a free entry.\n");
698 /* Make the object exclusively locked */
699 ExInitializePushLock(&pobj
->pushlock
);
700 KeEnterCriticalRegion();
701 ExAcquirePushLockExclusive(&pobj
->pushlock
);
702 pobj
->cExclusiveLock
= 1;
703 pobj
->dwThreadId
= PtrToUlong(PsGetCurrentThreadId());
704 DBG_INCREASE_LOCK_COUNT(PsGetCurrentProcessWin32Process(), pobj
->hHmgr
);
706 /* Get object type from the hHmgr field */
707 objt
= ((ULONG_PTR
)pobj
->hHmgr
>> 16) & 0xff;
708 ASSERT(objt
!= GDIObjType_DEF_TYPE
);
710 /* Check if current process is requested owner */
711 if (ulOwner
== GDI_OBJ_HMGR_POWNED
)
713 /* Increment the process handle count */
714 IncrementGdiHandleCount();
717 ulOwner
= HandleToUlong(PsGetCurrentProcessId());
720 /* Insert the object into the handle table */
721 pobj
->hHmgr
= ENTRY_hInsertObject(pentry
, pobj
, objt
, ulOwner
);
723 /* Return the handle */
724 DPRINT("GDIOBJ: Created handle: %p\n", pobj
->hHmgr
);
725 DBG_LOGEVENT(&pobj
->slhLog
, EVENT_CREATE_HANDLE
, 0);
731 GDIOBJ_vSetObjectOwner(
737 /* This is a ugly HACK, needed to fix IntGdiSetDCOwnerEx */
738 if (GDI_HANDLE_IS_STOCKOBJ(pobj
->hHmgr
))
740 DPRINT("Trying to set ownership of stock object %p to %lx\n", pobj
->hHmgr
, ulOwner
);
744 /* Get the handle entry */
745 ASSERT(GDI_HANDLE_GET_INDEX(pobj
->hHmgr
));
746 pentry
= &gpentHmgr
[GDI_HANDLE_GET_INDEX(pobj
->hHmgr
)];
748 /* Is the current process requested? */
749 if (ulOwner
== GDI_OBJ_HMGR_POWNED
)
752 ulOwner
= HandleToUlong(PsGetCurrentProcessId());
753 if (pentry
->ObjectOwner
.ulObj
!= ulOwner
)
755 IncrementGdiHandleCount();
760 if (ulOwner
== GDI_OBJ_HMGR_NONE
)
761 ulOwner
= GDI_OBJ_HMGR_PUBLIC
;
763 if (ulOwner
== GDI_OBJ_HMGR_PUBLIC
||
764 ulOwner
== GDI_OBJ_HMGR_NONE
)
766 /* Make sure we don't leak user mode memory */
767 ASSERT(pentry
->pUser
== NULL
);
768 if (pentry
->ObjectOwner
.ulObj
!= GDI_OBJ_HMGR_PUBLIC
&&
769 pentry
->ObjectOwner
.ulObj
!= GDI_OBJ_HMGR_NONE
)
771 DecrementGdiHandleCount();
776 pentry
->ObjectOwner
.ulObj
= ulOwner
;
777 DBG_LOGEVENT(&pobj
->slhLog
, EVENT_SET_OWNER
, 0);
780 /* Locks 2 or 3 objects at a time */
783 GDIOBJ_bLockMultipleObjects(
789 UINT auiIndices
[3] = {0, 1, 2};
792 ASSERT(ulCount
<= 3);
794 /* Sort the handles */
795 for (i
= 0; i
< ulCount
- 1; i
++)
797 for (j
= i
+ 1; j
< ulCount
; j
++)
799 if ((ULONG_PTR
)ahObj
[auiIndices
[i
]] <
800 (ULONG_PTR
)ahObj
[auiIndices
[j
]])
803 auiIndices
[i
] = auiIndices
[j
];
809 /* Lock the objects in safe order */
810 for (i
= 0; i
< ulCount
; i
++)
812 /* Skip NULL handles */
813 if (ahObj
[auiIndices
[i
]] == NULL
)
815 apObj
[auiIndices
[i
]] = NULL
;
819 /* Lock the object */
820 apObj
[auiIndices
[i
]] = GDIOBJ_LockObject(ahObj
[auiIndices
[i
]], objt
);
822 /* Check for failure */
823 if (apObj
[auiIndices
[i
]] == NULL
)
828 if (apObj
[auiIndices
[i
]])
829 GDIOBJ_vUnlockObject(apObj
[auiIndices
[i
]]);
840 GDIOBJ_pvGetObjectAttr(POBJ pobj
)
842 ULONG ulIndex
= GDI_HANDLE_GET_INDEX(pobj
->hHmgr
);
843 return gpentHmgr
[ulIndex
].pUser
;
848 GDIOBJ_vSetObjectAttr(POBJ pobj
, PVOID pvObjAttr
)
854 /* Get the handle index */
855 ulIndex
= GDI_HANDLE_GET_INDEX(pobj
->hHmgr
);
857 /* Set pointer to the usermode attribute */
858 gpentHmgr
[ulIndex
].pUser
= pvObjAttr
;
863 GDIOBJ_vDeleteObject(POBJ pobj
)
867 /* Set the object's delete flag */
868 InterlockedOr16((SHORT
*)&pobj
->BaseFlags
, BASEFLAG_READY_TO_DIE
);
869 DBG_LOGEVENT(&pobj
->slhLog
, EVENT_DELETE
, 0);
871 /* Get the handle index */
872 ulIndex
= GDI_HANDLE_GET_INDEX(pobj
->hHmgr
);
875 /* Reset the handle valid bit */
876 InterlockedAnd((LONG
*)&gpaulRefCount
[ulIndex
], ~REF_MASK_VALID
);
878 /* Check if the object is exclusively locked */
879 if (pobj
->cExclusiveLock
!= 0)
881 /* Reset lock owner and lock count */
882 pobj
->dwThreadId
= 0;
883 pobj
->cExclusiveLock
= 0;
885 /* Release the pushlock and reenable APCs */
886 ExReleasePushLockExclusive(&pobj
->pushlock
);
887 KeLeaveCriticalRegion();
888 DBG_DECREASE_LOCK_COUNT(PsGetCurrentProcessWin32Process(), pobj
->hHmgr
);
892 /* Dereference the object (will take care of deletion) */
893 GDIOBJ_vDereferenceObject(pobj
);
898 GreIsHandleValid(HGDIOBJ hobj
)
902 pentry
= ENTRY_ReferenceEntryByHandle(hobj
, 0);
903 if (!pentry
) return FALSE
;
904 GDIOBJ_vDereferenceObject(pentry
->einfo
.pobj
);
910 GreDeleteObject(HGDIOBJ hobj
)
914 /* Check for stock objects */
915 if (GDI_HANDLE_IS_STOCKOBJ(hobj
))
917 DPRINT1("GreDeleteObject: Cannot delete stock object %p.\n", hobj
);
921 /* Reference the handle entry */
922 pentry
= ENTRY_ReferenceEntryByHandle(hobj
, 0);
925 DPRINT1("GreDeleteObject: Trying to delete invalid object %p\n", hobj
);
929 /* Check for public owner */
930 if (pentry
->ObjectOwner
.ulObj
== GDI_OBJ_HMGR_PUBLIC
)
932 DPRINT1("GreDeleteObject: Trying to delete global object %p\n", hobj
);
933 GDIOBJ_vDereferenceObject(pentry
->einfo
.pobj
);
937 /* Delete the object */
938 GDIOBJ_vDeleteObject(pentry
->einfo
.pobj
);
944 GreGetObjectOwner(HGDIOBJ hobj
)
946 ULONG ulIndex
, ulOwner
;
948 /* Get the handle index */
949 ulIndex
= GDI_HANDLE_GET_INDEX(hobj
);
951 /* Check if the handle is valid */
952 if (ulIndex
>= GDI_HANDLE_COUNT
||
953 gpentHmgr
[ulIndex
].Objt
== GDIObjType_DEF_TYPE
||
954 ((ULONG_PTR
)hobj
>> 16) != gpentHmgr
[ulIndex
].FullUnique
)
956 DPRINT1("GreGetObjectOwner: invalid handle 0x%p.\n", hobj
);
957 return GDI_OBJ_HMGR_RESTRICTED
;
960 /* Get the object owner */
961 ulOwner
= gpentHmgr
[ulIndex
].ObjectOwner
.ulObj
;
963 if (ulOwner
== HandleToUlong(PsGetCurrentProcessId()))
964 return GDI_OBJ_HMGR_POWNED
;
966 if (ulOwner
== GDI_OBJ_HMGR_PUBLIC
)
967 return GDI_OBJ_HMGR_PUBLIC
;
969 return GDI_OBJ_HMGR_RESTRICTED
;
980 /* Check for stock objects */
981 if (GDI_HANDLE_IS_STOCKOBJ(hobj
))
983 DPRINT("GreSetObjectOwner: Got stock object %p\n", hobj
);
987 /* Reference the handle entry */
988 pentry
= ENTRY_ReferenceEntryByHandle(hobj
, 0);
991 DPRINT("GreSetObjectOwner: Invalid handle 0x%p.\n", hobj
);
995 /* Call internal function */
996 GDIOBJ_vSetObjectOwner(pentry
->einfo
.pobj
, ulOwner
);
998 /* Dereference the object */
999 GDIOBJ_vDereferenceObject(pentry
->einfo
.pobj
);
1015 /* Verify object type */
1016 objt
= ((ULONG_PTR
)hobj
>> 16) & 0x1f;
1017 if (objt
!= GDIObjType_BRUSH_TYPE
&&
1018 objt
!= GDIObjType_SURF_TYPE
&&
1019 objt
!= GDIObjType_LFONT_TYPE
&&
1020 objt
!= GDIObjType_PAL_TYPE
)
1022 DPRINT1("GreGetObject: Invalid object type\n");
1026 pvObj
= GDIOBJ_ReferenceObjectByHandle(hobj
, objt
);
1029 DPRINT("GreGetObject: Could not lock object\n");
1033 switch (GDI_HANDLE_GET_TYPE(hobj
))
1035 case GDILoObjType_LO_PEN_TYPE
:
1036 case GDILoObjType_LO_EXTPEN_TYPE
:
1037 iResult
= PEN_GetObject(pvObj
, cbCount
, pvBuffer
);
1040 case GDILoObjType_LO_BRUSH_TYPE
:
1041 iResult
= BRUSH_GetObject(pvObj
, cbCount
, pvBuffer
);
1044 case GDILoObjType_LO_BITMAP_TYPE
:
1045 iResult
= BITMAP_GetObject(pvObj
, cbCount
, pvBuffer
);
1048 case GDILoObjType_LO_FONT_TYPE
:
1049 iResult
= FontGetObject(pvObj
, cbCount
, pvBuffer
);
1052 case GDILoObjType_LO_PALETTE_TYPE
:
1053 iResult
= PALETTE_GetObject(pvObj
, cbCount
, pvBuffer
);
1057 DPRINT1("GDI object type of 0x%p not implemented\n", hobj
);
1061 GDIOBJ_vDereferenceObject(pvObj
);
1071 OUT LPVOID lpBuffer
)
1078 DIBSECTION dibsection
;
1082 EXTLOGFONTW extlogfontw
;
1083 ENUMLOGFONTEXDVW enumlogfontexdvw
;
1086 /* Normalize to the largest supported object size */
1087 cbCount
= min((UINT
)cbCount
, sizeof(object
));
1089 /* Now do the actual call */
1090 iRetCount
= GreGetObject(hobj
, cbCount
, lpBuffer
? &object
: NULL
);
1091 cbCopyCount
= min((UINT
)cbCount
, (UINT
)iRetCount
);
1093 /* Make sure we have a buffer and a copy size */
1094 if ((cbCopyCount
) && (lpBuffer
))
1096 /* Enter SEH for buffer transfer */
1099 /* Probe the buffer and copy it */
1100 ProbeForWrite(lpBuffer
, cbCopyCount
, sizeof(WORD
));
1101 RtlCopyMemory(lpBuffer
, &object
, cbCopyCount
);
1103 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1105 /* Clear the return value.
1106 * Do *NOT* set last error here! */
1112 /* Return the count */
1119 NtGdiCreateClientObj(
1125 /* Allocate a new object */
1126 pObject
= GDIOBJ_AllocateObject(GDIObjType_CLIENTOBJ_TYPE
,
1128 BASEFLAG_LOOKASIDE
);
1131 DPRINT1("NtGdiCreateClientObj: Could not allocate a clientobj.\n");
1135 /* Mask out everything that would change the type in a wrong manner */
1136 ulType
&= (GDI_HANDLE_TYPE_MASK
& ~GDI_HANDLE_BASETYPE_MASK
);
1138 /* Set the real object type */
1139 pObject
->hHmgr
= UlongToHandle(ulType
| GDILoObjType_LO_CLIENTOBJ_TYPE
);
1141 /* Create a handle */
1142 handle
= GDIOBJ_hInsertObject(pObject
, GDI_OBJ_HMGR_POWNED
);
1145 DPRINT1("NtGdiCreateClientObj: Could not create a handle.\n");
1146 GDIOBJ_vFreeObject(pObject
);
1151 GDIOBJ_vUnlockObject(pObject
);
1159 NtGdiDeleteClientObj(
1162 /* We first need to get the real type from the handle */
1163 ULONG ulType
= GDI_HANDLE_GET_TYPE(hobj
);
1165 /* Check if it's really a CLIENTOBJ */
1166 if ((ulType
& GDI_HANDLE_BASETYPE_MASK
) != GDILoObjType_LO_CLIENTOBJ_TYPE
)
1168 /* FIXME: SetLastError? */
1172 return GreDeleteObject(hobj
);
1177 PGDI_HANDLE_TABLE GdiHandleTable
= NULL
;
1180 GDIOBJ_ShareLockObj(HGDIOBJ hObj
, DWORD ExpectedType
)
1182 if (ExpectedType
== GDI_OBJECT_TYPE_DONTCARE
)
1183 ExpectedType
= GDI_HANDLE_GET_TYPE(hObj
);
1184 return GDIOBJ_ReferenceObjectByHandle(hObj
, (ExpectedType
>> 16) & 0x1f);
1187 // This function is not safe to use with concurrent deleting attempts
1188 // That shouldn't be a problem, since we don't have any processes yet,
1189 // that could delete the handle
1192 GDIOBJ_ConvertToStockObj(HGDIOBJ
*phObj
)
1197 /* Reference the handle entry */
1198 pentry
= ENTRY_ReferenceEntryByHandle(*phObj
, 0);
1201 DPRINT1("GDIOBJ: Requested handle 0x%p is not valid.\n", *phObj
);
1205 /* Update the entry */
1206 pentry
->FullUnique
|= GDI_ENTRY_STOCK_MASK
;
1207 pentry
->ObjectOwner
.ulObj
= 0;
1209 /* Get the pointer to the BASEOBJECT */
1210 pobj
= pentry
->einfo
.pobj
;
1212 /* Calculate the new handle */
1213 pobj
->hHmgr
= (HGDIOBJ
)((ULONG_PTR
)pobj
->hHmgr
| GDI_HANDLE_STOCK_MASK
);
1215 /* Return the new handle */
1216 *phObj
= pobj
->hHmgr
;
1218 /* Dereference the handle */
1219 GDIOBJ_vDereferenceObject(pobj
);
1225 GDIOBJ_AllocObjWithHandle(ULONG ObjectType
, ULONG cjSize
)
1229 UCHAR objt
= (ObjectType
>> 16) & 0xFF;
1231 if ((objt
== GDIObjType_DC_TYPE
&& cjSize
== sizeof(DC
)) ||
1232 (objt
== GDIObjType_PAL_TYPE
&& cjSize
== sizeof(PALETTE
)) ||
1233 (objt
== GDIObjType_RGN_TYPE
&& cjSize
== sizeof(REGION
)) ||
1234 (objt
== GDIObjType_SURF_TYPE
&& cjSize
== sizeof(SURFACE
)) ||
1235 (objt
== GDIObjType_PATH_TYPE
&& cjSize
== sizeof(PATH
)))
1237 fl
|= BASEFLAG_LOOKASIDE
;
1240 pobj
= GDIOBJ_AllocateObject(objt
, cjSize
, fl
);
1241 if (!GDIOBJ_hInsertObject(pobj
, GDI_OBJ_HMGR_POWNED
))
1243 GDIOBJ_vFreeObject(pobj
);
1250 GDI_MapHandleTable(PEPROCESS pProcess
)
1252 PVOID pvMappedView
= NULL
;
1254 LARGE_INTEGER liOffset
;
1255 ULONG cjViewSize
= sizeof(GDI_HANDLE_TABLE
);
1257 liOffset
.QuadPart
= 0;
1259 ASSERT(gpvGdiHdlTblSection
!= NULL
);
1260 ASSERT(pProcess
!= NULL
);
1262 Status
= MmMapViewOfSection(gpvGdiHdlTblSection
,
1273 if (!NT_SUCCESS(Status
))
1276 return pvMappedView
;
1280 GDI_CleanupForProcess(struct _EPROCESS
*Process
)
1287 DPRINT("CleanupForProcess prochandle %p Pid %p\n",
1288 Process
, Process
->UniqueProcessId
);
1290 ASSERT(Process
== PsGetCurrentProcess());
1292 /* Get the current process Id */
1293 dwProcessId
= PtrToUlong(PsGetCurrentProcessId());
1295 /* Loop all handles in the handle table */
1296 for (ulIndex
= RESERVE_ENTRIES_COUNT
; ulIndex
< gulFirstUnused
; ulIndex
++)
1298 pentry
= &gpentHmgr
[ulIndex
];
1300 /* Check if the object is owned by the process */
1301 if (pentry
->ObjectOwner
.ulObj
== dwProcessId
)
1303 ASSERT(pentry
->einfo
.pobj
->cExclusiveLock
== 0);
1305 /* Reference the object and delete it */
1306 InterlockedIncrement((LONG
*)&gpaulRefCount
[ulIndex
]);
1307 GDIOBJ_vDeleteObject(pentry
->einfo
.pobj
);
1313 DbgGdiHTIntegrityCheck();
1317 ppi
= PsGetCurrentProcessWin32Process();
1318 DPRINT("Completed cleanup for process %p\n", Process
->UniqueProcessId
);
1319 if (ppi
->GDIHandleCount
!= 0)
1321 DPRINT1("Leaking %d handles!\n", ppi
->GDIHandleCount
);
1325 /* Loop all handles in the handle table */
1326 for (ulIndex
= RESERVE_ENTRIES_COUNT
; ulIndex
< gulFirstUnused
; ulIndex
++)
1328 pentry
= &gpentHmgr
[ulIndex
];
1330 /* Check if the object is owned by the process */
1331 if (pentry
->ObjectOwner
.ulObj
== dwProcessId
)
1333 DPRINT1("Leaking object. Index=%lx, type=0x%x, refcount=%lx\n",
1334 ulIndex
, pentry
->Objt
, gpaulRefCount
[ulIndex
]);
1335 DBG_DUMP_EVENT_LIST(&pentry
->einfo
.pobj
->slhLog
);
1336 //DBG_CLEANUP_EVENT_LIST(&pentry->einfo.pobj->slhLog);
1345 static const char * gpszObjectTypes
[] =
1347 "FREE", "DC", "UNUSED1", "UNUSED2", "RGN", "SURF", "CLIENTOBJ", "PATH",
1348 "PAL", "ICMLCS", "LFONT", "RFONT", "PFE", "PFT", "ICMCXF", "SPRITE",
1349 "BRUSH", "UMPD", "UNUSED4", "SPACE", "UNUSED5", "META", "EFSTATE",
1350 "BMFD", "VTFD", "TTFD", "RC", "TEMP", "DRVOBJ", "DCIOBJ", "SPOOL",
1354 extern PEPROCESS gpepCSRSS
;;
1358 DbgDumpGdiHandleTable(ULONG argc
, char *argv
[])
1361 UCHAR Objt
, jReqestedType
;
1364 KAPC_STATE ApcState
;
1366 /* No CSRSS, no handle table */
1367 if (!gpepCSRSS
) return;
1368 KeStackAttachProcess(&gpepCSRSS
->Pcb
, &ApcState
);
1372 USHORT Counts
[GDIObjType_MAX_TYPE
+ 2] = {0};
1374 /* Loop all possibly used entries in the handle table */
1375 for (i
= RESERVE_ENTRIES_COUNT
; i
< gulFirstUnused
; i
++)
1377 if (MmIsAddressValid(&gpentHmgr
[i
]))
1379 Objt
= gpentHmgr
[i
].Objt
& 0x1F;
1384 DbgPrint("Type Count\n");
1385 DbgPrint("-------------------\n");
1386 for (i
= 0; i
<= GDIObjType_MAX_TYPE
; i
++)
1388 DbgPrint("%02x %-9s %d\n",
1389 i
, gpszObjectTypes
[i
], Counts
[i
]);
1395 /* Loop all object types */
1396 for (i
= 0; i
<= GDIObjType_MAX_TYPE
+ 1; i
++)
1398 /* Check if this object type was requested */
1399 if (stricmp(argv
[0], gpszObjectTypes
[i
]) == 0)
1406 /* Check if we didn't find it yet */
1407 if (i
> GDIObjType_MAX_TYPE
)
1409 /* Try if it's a number */
1413 if ((i
> GDIObjType_MAX_TYPE
) ||
1414 ((i
== 0) && (stricmp(argv
[0], "0") == 0)))
1416 DbgPrint("Unknown object type: %s\n", argv
[0]);
1424 DbgPrint("Index Handle Type ThreadId cLocks ulRefCount\n");
1425 DbgPrint("----------------------------------------------------\n");
1427 /* Loop all possibly used entries in the handle table */
1428 for (i
= RESERVE_ENTRIES_COUNT
; i
< gulFirstUnused
; i
++)
1430 /* Get the entry and the object */
1431 pentry
= &gpentHmgr
[i
];
1433 if (!MmIsAddressValid(pentry
)) continue;
1435 pobj
= pentry
->einfo
.pobj
;
1436 Objt
= pentry
->Objt
& 0x1F;
1438 if ((jReqestedType
== GDIObjType_MAX_TYPE
+ 1) ||
1439 (Objt
== jReqestedType
))
1441 DbgPrint("%04lx %p %-9s 0x%06lx %-7ld ",
1442 i
, pobj
->hHmgr
, gpszObjectTypes
[Objt
],
1443 pobj
->dwThreadId
, pobj
->cExclusiveLock
);
1444 if (MmIsAddressValid(&gpaulRefCount
[i
]))
1445 DbgPrint("0x%06lx\n", gpaulRefCount
[i
]);
1447 DbgPrint("????????\n");
1453 KeUnstackDetachProcess(&ApcState
);
1458 DbgDumpHandleInfo(char *argv
)
1465 KAPC_STATE ApcState
;
1467 /* Skip optional '0x' prefix */
1468 if ((argv
[0] == '0') && ((argv
[1] == 'x') || (argv
[1] == 'X')))
1471 /* Make a number from the string (hex) */
1472 ulObject
= strtol(argv
, &endptr
, 16);
1473 if (*endptr
!= '\0')
1476 /* No CSRSS, no handle table */
1477 if (!gpepCSRSS
) return;
1478 KeStackAttachProcess(&gpepCSRSS
->Pcb
, &ApcState
);
1480 usIndex
= ulObject
& 0xFFFF;
1481 pentry
= &gpentHmgr
[usIndex
];
1483 if (MmIsAddressValid(pentry
))
1485 pobj
= pentry
->einfo
.pobj
;
1487 DbgPrint("GDI handle=%p, type=%s, index=0x%lx, pentry=%p.\n",
1488 ulObject
, gpszObjectTypes
[(ulObject
>> 16) & 0x1f],
1490 DbgPrint(" ENTRY = {.pobj = %p, ObjectOwner = 0x%lx, FullUnique = 0x%04x,\n"
1491 " Objt=0x%02x, Flags = 0x%02x, pUser = 0x%p}\n",
1492 pentry
->einfo
.pobj
, pentry
->ObjectOwner
.ulObj
, pentry
->FullUnique
,
1493 pentry
->Objt
, pentry
->Flags
, pentry
->pUser
);
1494 DbgPrint(" BASEOBJECT = {hHmgr = %p, dwThreadId = 0x%lx,\n"
1495 " cExclusiveLock = %ld, BaseFlags = 0x%lx}\n",
1496 pobj
->hHmgr
, pobj
->dwThreadId
,
1497 pobj
->cExclusiveLock
, pobj
->BaseFlags
);
1498 if (MmIsAddressValid(&gpaulRefCount
[usIndex
]))
1499 DbgPrint(" gpaulRefCount[idx] = %ld\n", gpaulRefCount
[usIndex
]);
1503 DbgPrint("Coudn't access ENTRY. Probably paged out.\n");
1506 KeUnstackDetachProcess(&ApcState
);
1508 #endif // DBG && KDBG