xref: /reactos/win32ss/gdi/ntgdi/gdiobj.c (revision 321bcc05)
1 /*
2  * PROJECT:         ReactOS win32 kernel mode subsystem
3  * LICENSE:         GPL - See COPYING in the top level directory
4  * FILE:            win32ss/gdi/ntgdi/gdiobj.c
5  * PURPOSE:         General GDI object manipulation routines
6  * PROGRAMMERS:     Timo Kreuzer
7  */
8 
9 /*
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.
31  *
32  * Ownership:
33  *
34  * Owner:               POWNED  PUBLIC  NONE    spec
35  * ---------------------------------------------------
36  * LockForRead          +       +       -       PUBLIC
37  * LockForWrite         +       -       -       POWNED
38  * LockAny              +       +       +       NONE
39  * NtGdiDeleteObjectApp +       -       -       PUBLIC
40  * GreDeleteObject      +       +       +       NONE
41  * GreSetOwner(POWNED)  -       -       +       -
42  * GreSetOwner(PUBLIC)  +       -       +       -
43  * GreSetOwner(NONE)    +       -       -       -
44  *
45  */
46 
47 /* INCLUDES ******************************************************************/
48 
49 #include <win32k.h>
50 #define NDEBUG
51 #include <debug.h>
52 
53 FORCEINLINE
54 ULONG
55 InterlockedReadUlong(
56     _In_ _Interlocked_operand_ ULONG volatile *Source)
57 {
58     return *Source;
59 }
60 
61 FORCEINLINE
62 void
63 INCREASE_THREAD_LOCK_COUNT(
64     _In_ HANDLE hobj)
65 {
66     PTHREADINFO pti = PsGetCurrentThreadWin32Thread();
67     DBG_UNREFERENCED_PARAMETER(hobj);
68     if (pti)
69     {
70 #if DBG
71         pti->acExclusiveLockCount[((ULONG_PTR)hobj >> 16) & 0x1f]++;
72 #endif
73         pti->cExclusiveLocks++;
74     }
75 }
76 
77 FORCEINLINE
78 void
79 DECREASE_THREAD_LOCK_COUNT(
80     _In_ HANDLE hobj)
81 {
82     PTHREADINFO pti = PsGetCurrentThreadWin32Thread();
83     DBG_UNREFERENCED_PARAMETER(hobj);
84     if (pti)
85     {
86 #if DBG
87         pti->acExclusiveLockCount[((ULONG_PTR)hobj >> 16) & 0x1f]--;
88 #endif
89         pti->cExclusiveLocks--;
90     }
91 }
92 
93 #if DBG
94 VOID
95 ASSERT_LOCK_ORDER(
96     _In_ UCHAR objt)
97 {
98     PTHREADINFO pti = PsGetCurrentThreadWin32Thread();
99     ULONG i;
100 
101     if (pti)
102     {
103         /* Ensure correct locking order! */
104         for (i = objt + 1; i < GDIObjTypeTotal; i++)
105         {
106             NT_ASSERT(pti->acExclusiveLockCount[i] == 0);
107         }
108     }
109 }
110 #define ASSERT_SHARED_OBJECT_TYPE(objt) \
111     ASSERT((objt) == GDIObjType_SURF_TYPE || \
112            (objt) == GDIObjType_PAL_TYPE || \
113            (objt) == GDIObjType_LFONT_TYPE || \
114            (objt) == GDIObjType_PATH_TYPE || \
115            (objt) == GDIObjType_BRUSH_TYPE)
116 #define ASSERT_EXCLUSIVE_OBJECT_TYPE(objt) \
117     ASSERT((objt) == GDIObjType_DC_TYPE || \
118            (objt) == GDIObjType_RGN_TYPE)
119 #define ASSERT_TRYLOCK_OBJECT_TYPE(objt) \
120     ASSERT((objt) == GDIObjType_DRVOBJ_TYPE)
121 #else
122 #define ASSERT_LOCK_ORDER(hobj)
123 #define ASSERT_SHARED_OBJECT_TYPE(objt)
124 #define ASSERT_EXCLUSIVE_OBJECT_TYPE(objt)
125 #define ASSERT_TRYLOCK_OBJECT_TYPE(objt)
126 #endif
127 
128 #if defined(_M_IX86) || defined(_M_AMD64)
129 #define InterlockedOr16 _InterlockedOr16
130 #endif
131 
132 #define GDIOBJ_POOL_TAG(type) ('00hG' + (((type) & 0x1f) << 24))
133 
134 enum
135 {
136     REF_MASK_REUSE = 0xff000000,
137     REF_INC_REUSE  = 0x01000000,
138     REF_MASK_VALID = 0x00800000,
139     REF_MASK_COUNT = 0x007fffff,
140     REF_MASK_INUSE = 0x00ffffff,
141 };
142 
143 /* GLOBALS *******************************************************************/
144 
145 /* Per session handle table globals */
146 static PVOID gpvGdiHdlTblSection = NULL;
147 PENTRY gpentHmgr;
148 PULONG gpaulRefCount;
149 volatile ULONG gulFirstFree;
150 volatile ULONG gulFirstUnused;
151 static PPAGED_LOOKASIDE_LIST gpaLookasideList;
152 
153 static VOID NTAPI GDIOBJ_vCleanup(PVOID ObjectBody);
154 
155 static const
156 GDICLEANUPPROC
157 apfnCleanup[] =
158 {
159     NULL,              /* 00 GDIObjType_DEF_TYPE */
160     DC_vCleanup,       /* 01 GDIObjType_DC_TYPE */
161     NULL,              /* 02 GDIObjType_UNUSED1_TYPE */
162     NULL,              /* 03 GDIObjType_UNUSED2_TYPE */
163     REGION_vCleanup,   /* 04 GDIObjType_RGN_TYPE */
164     SURFACE_vCleanup,  /* 05 GDIObjType_SURF_TYPE */
165     GDIOBJ_vCleanup,   /* 06 GDIObjType_CLIENTOBJ_TYPE */
166     GDIOBJ_vCleanup,   /* 07 GDIObjType_PATH_TYPE */
167     PALETTE_vCleanup,  /* 08 GDIObjType_PAL_TYPE */
168     GDIOBJ_vCleanup,   /* 09 GDIObjType_ICMLCS_TYPE */
169     GDIOBJ_vCleanup,   /* 0a GDIObjType_LFONT_TYPE */
170     NULL,              /* 0b GDIObjType_RFONT_TYPE, unused */
171     NULL,              /* 0c GDIObjType_PFE_TYPE, unused */
172     NULL,              /* 0d GDIObjType_PFT_TYPE, unused */
173     GDIOBJ_vCleanup,   /* 0e GDIObjType_ICMCXF_TYPE */
174     NULL,              /* 0f GDIObjType_SPRITE_TYPE, unused */
175     NULL,              /* 10 GDIObjType_BRUSH_TYPE, BRUSH, PEN, EXTPEN */
176     NULL,              /* 11 GDIObjType_UMPD_TYPE, unused */
177     NULL,              /* 12 GDIObjType_UNUSED4_TYPE */
178     NULL,              /* 13 GDIObjType_SPACE_TYPE, unused */
179     NULL,              /* 14 GDIObjType_UNUSED5_TYPE */
180     NULL,              /* 15 GDIObjType_META_TYPE, unused */
181     NULL,              /* 16 GDIObjType_EFSTATE_TYPE, unused */
182     NULL,              /* 17 GDIObjType_BMFD_TYPE, unused */
183     NULL,              /* 18 GDIObjType_VTFD_TYPE, unused */
184     NULL,              /* 19 GDIObjType_TTFD_TYPE, unused */
185     NULL,              /* 1a GDIObjType_RC_TYPE, unused */
186     NULL,              /* 1b GDIObjType_TEMP_TYPE, unused */
187     DRIVEROBJ_vCleanup,/* 1c GDIObjType_DRVOBJ_TYPE */
188     NULL,              /* 1d GDIObjType_DCIOBJ_TYPE, unused */
189     NULL,              /* 1e GDIObjType_SPOOL_TYPE, unused */
190     NULL,              /* 1f reserved entry */
191 };
192 
193 static const
194 GDIOBJDELETEPROC
195 apfnDelete[] =
196 {
197     NULL,              /* 00 GDIObjType_DEF_TYPE */
198     NULL,              /* 01 GDIObjType_DC_TYPE */
199     NULL,              /* 02 GDIObjType_UNUSED1_TYPE */
200     NULL,              /* 03 GDIObjType_UNUSED2_TYPE */
201     NULL,              /* 04 GDIObjType_RGN_TYPE */
202     NULL,              /* 05 GDIObjType_SURF_TYPE */
203     NULL,              /* 06 GDIObjType_CLIENTOBJ_TYPE */
204     NULL,              /* 07 GDIObjType_PATH_TYPE */
205     NULL,              /* 08 GDIObjType_PAL_TYPE */
206     NULL,              /* 09 GDIObjType_ICMLCS_TYPE */
207     NULL,              /* 0a GDIObjType_LFONT_TYPE */
208     NULL,              /* 0b GDIObjType_RFONT_TYPE, unused */
209     NULL,              /* 0c GDIObjType_PFE_TYPE, unused */
210     NULL,              /* 0d GDIObjType_PFT_TYPE, unused */
211     NULL,              /* 0e GDIObjType_ICMCXF_TYPE */
212     NULL,                 /* 0f GDIObjType_SPRITE_TYPE, unused */
213     BRUSH_vDeleteObject,  /* 10 GDIObjType_BRUSH_TYPE, BRUSH, PEN, EXTPEN */
214     NULL,              /* 11 GDIObjType_UMPD_TYPE, unused */
215     NULL,              /* 12 GDIObjType_UNUSED4_TYPE */
216     NULL,              /* 13 GDIObjType_SPACE_TYPE, unused */
217     NULL,              /* 14 GDIObjType_UNUSED5_TYPE */
218     NULL,              /* 15 GDIObjType_META_TYPE, unused */
219     NULL,              /* 16 GDIObjType_EFSTATE_TYPE, unused */
220     NULL,              /* 17 GDIObjType_BMFD_TYPE, unused */
221     NULL,              /* 18 GDIObjType_VTFD_TYPE, unused */
222     NULL,              /* 19 GDIObjType_TTFD_TYPE, unused */
223     NULL,              /* 1a GDIObjType_RC_TYPE, unused */
224     NULL,              /* 1b GDIObjType_TEMP_TYPE, unused */
225     NULL,              /* 1c GDIObjType_DRVOBJ_TYPE */
226     NULL,              /* 1d GDIObjType_DCIOBJ_TYPE, unused */
227     NULL,              /* 1e GDIObjType_SPOOL_TYPE, unused */
228     NULL,              /* 1f reserved entry */
229 };
230 
231 /* INTERNAL FUNCTIONS ********************************************************/
232 
233 static
234 VOID
235 NTAPI
236 GDIOBJ_vCleanup(PVOID ObjectBody)
237 {
238     /* Nothing to do */
239 }
240 
241 static
242 VOID
243 InitLookasideList(UCHAR objt, ULONG cjSize)
244 {
245     ExInitializePagedLookasideList(&gpaLookasideList[objt],
246                                    NULL,
247                                    NULL,
248                                    0,
249                                    cjSize,
250                                    GDITAG_HMGR_LOOKASIDE_START + (objt << 24),
251                                    0);
252 }
253 
254 INIT_FUNCTION
255 NTSTATUS
256 NTAPI
257 InitGdiHandleTable(void)
258 {
259     NTSTATUS status;
260     LARGE_INTEGER liSize;
261     PVOID pvSection;
262     SIZE_T cjViewSize = 0;
263 
264     /* Create a section for the shared handle table */
265     liSize.QuadPart = sizeof(GDI_HANDLE_TABLE); // GDI_HANDLE_COUNT * sizeof(ENTRY);
266     status = MmCreateSection(&gpvGdiHdlTblSection,
267                              SECTION_ALL_ACCESS,
268                              NULL,
269                              &liSize,
270                              PAGE_READWRITE,
271                              SEC_COMMIT | 0x1,
272                              NULL,
273                              NULL);
274     if (!NT_SUCCESS(status))
275     {
276         DPRINT1("INITGDI: Could not allocate a GDI handle table.\n");
277         return status;
278     }
279 
280     /* Map the section in session space */
281     status = MmMapViewInSessionSpace(gpvGdiHdlTblSection,
282                                      (PVOID*)&gpentHmgr,
283                                      &cjViewSize);
284     if (!NT_SUCCESS(status))
285     {
286         DPRINT1("INITGDI: Failed to map handle table section\n");
287         ObDereferenceObject(gpvGdiHdlTblSection);
288         return status;
289     }
290 
291     /* Allocate memory for the reference counter table */
292     gpaulRefCount = EngAllocSectionMem(&pvSection,
293                                      FL_ZERO_MEMORY,
294                                      GDI_HANDLE_COUNT * sizeof(ULONG),
295                                      'frHG');
296     if (!gpaulRefCount)
297     {
298         DPRINT1("INITGDI: Failed to allocate reference table.\n");
299         ObDereferenceObject(gpvGdiHdlTblSection);
300         return STATUS_INSUFFICIENT_RESOURCES;
301     }
302 
303     gulFirstFree = 0;
304     gulFirstUnused = RESERVE_ENTRIES_COUNT;
305 
306     GdiHandleTable = (PVOID)gpentHmgr;
307 
308     /* Initialize the lookaside lists */
309     gpaLookasideList = ExAllocatePoolWithTag(NonPagedPool,
310                            GDIObjTypeTotal * sizeof(PAGED_LOOKASIDE_LIST),
311                            TAG_GDIHNDTBLE);
312     if(!gpaLookasideList)
313         return STATUS_NO_MEMORY;
314 
315     InitLookasideList(GDIObjType_DC_TYPE, sizeof(DC));
316     InitLookasideList(GDIObjType_RGN_TYPE, sizeof(REGION));
317     InitLookasideList(GDIObjType_SURF_TYPE, sizeof(SURFACE));
318     InitLookasideList(GDIObjType_CLIENTOBJ_TYPE, sizeof(CLIENTOBJ));
319     InitLookasideList(GDIObjType_PATH_TYPE, sizeof(PATH));
320     InitLookasideList(GDIObjType_PAL_TYPE, sizeof(PALETTE));
321     InitLookasideList(GDIObjType_ICMLCS_TYPE, sizeof(COLORSPACE));
322     InitLookasideList(GDIObjType_LFONT_TYPE, sizeof(TEXTOBJ));
323     InitLookasideList(GDIObjType_BRUSH_TYPE, sizeof(BRUSH));
324 
325     return STATUS_SUCCESS;
326 }
327 
328 FORCEINLINE
329 VOID
330 IncrementCurrentProcessGdiHandleCount(void)
331 {
332     PPROCESSINFO ppi = PsGetCurrentProcessWin32Process();
333     if (ppi) InterlockedIncrement((LONG*)&ppi->GDIHandleCount);
334 }
335 
336 FORCEINLINE
337 VOID
338 DecrementCurrentProcessGdiHandleCount(void)
339 {
340     PPROCESSINFO ppi = PsGetCurrentProcessWin32Process();
341     if (ppi) InterlockedDecrement((LONG*)&ppi->GDIHandleCount);
342 }
343 
344 FORCEINLINE
345 VOID
346 IncrementGdiHandleCount(ULONG ulProcessId)
347 {
348     PEPROCESS pep;
349     PPROCESSINFO ppi;
350     NTSTATUS Status;
351 
352     Status = PsLookupProcessByProcessId(ULongToHandle(ulProcessId), &pep);
353     NT_ASSERT(NT_SUCCESS(Status));
354     __analysis_assume(NT_SUCCESS(Status));
355 
356     ppi = PsGetProcessWin32Process(pep);
357     if (ppi) InterlockedIncrement((LONG*)&ppi->GDIHandleCount);
358     if (NT_SUCCESS(Status)) ObDereferenceObject(pep);
359 }
360 
361 FORCEINLINE
362 VOID
363 DecrementGdiHandleCount(ULONG ulProcessId)
364 {
365     PEPROCESS pep;
366     PPROCESSINFO ppi;
367     NTSTATUS Status;
368 
369     Status = PsLookupProcessByProcessId(ULongToHandle(ulProcessId), &pep);
370     NT_ASSERT(NT_SUCCESS(Status));
371     __analysis_assume(NT_SUCCESS(Status));
372 
373     ppi = PsGetProcessWin32Process(pep);
374     if (ppi) InterlockedDecrement((LONG*)&ppi->GDIHandleCount);
375     if (NT_SUCCESS(Status)) ObDereferenceObject(pep);
376 }
377 
378 static
379 PENTRY
380 ENTRY_pentPopFreeEntry(VOID)
381 {
382     ULONG iFirst, iNext, iPrev;
383     PENTRY pentFree;
384 
385     DPRINT("Enter InterLockedPopFreeEntry\n");
386 
387     do
388     {
389         /* Get the index and sequence number of the first free entry */
390         iFirst = InterlockedReadUlong(&gulFirstFree);
391 
392         /* Check if we have a free entry */
393         if (!(iFirst & GDI_HANDLE_INDEX_MASK))
394         {
395             /* Increment FirstUnused and get the new index */
396             iFirst = InterlockedIncrement((LONG*)&gulFirstUnused) - 1;
397 
398             /* Check if we have unused entries left */
399             if (iFirst >= GDI_HANDLE_COUNT)
400             {
401                 DPRINT1("No more GDI handles left!\n");
402 #if DBG_ENABLE_GDIOBJ_BACKTRACES
403                 DbgDumpGdiHandleTableWithBT();
404 #endif
405                 InterlockedDecrement((LONG*)&gulFirstUnused);
406                 return 0;
407             }
408 
409             /* Return the old entry */
410             return &gpentHmgr[iFirst];
411         }
412 
413         /* Get a pointer to the first free entry */
414         pentFree = &gpentHmgr[iFirst & GDI_HANDLE_INDEX_MASK];
415 
416         /* Create a new value with an increased sequence number */
417         iNext = GDI_HANDLE_GET_INDEX(pentFree->einfo.hFree);
418         iNext |= (iFirst & ~GDI_HANDLE_INDEX_MASK) + 0x10000;
419 
420         /* Try to exchange the FirstFree value */
421         iPrev = InterlockedCompareExchange((LONG*)&gulFirstFree,
422                                            iNext,
423                                            iFirst);
424     }
425     while (iPrev != iFirst);
426 
427     /* Sanity check: is entry really free? */
428     ASSERT(((ULONG_PTR)pentFree->einfo.pobj & ~GDI_HANDLE_INDEX_MASK) == 0);
429 
430     return pentFree;
431 }
432 
433 /* Pushes an entry of the handle table to the free list,
434    The entry must not have any references left */
435 static
436 VOID
437 ENTRY_vPushFreeEntry(PENTRY pentFree)
438 {
439     ULONG iToFree, iFirst, iPrev, idxToFree;
440 
441     DPRINT("Enter ENTRY_vPushFreeEntry\n");
442 
443     idxToFree = pentFree - gpentHmgr;
444     ASSERT((gpaulRefCount[idxToFree] & REF_MASK_INUSE) == 0);
445 
446     /* Initialize entry */
447     pentFree->Objt = GDIObjType_DEF_TYPE;
448     pentFree->ObjectOwner.ulObj = 0;
449     pentFree->pUser = NULL;
450 
451     /* Increase reuse counter in entry and reference counter */
452     InterlockedExchangeAdd((LONG*)&gpaulRefCount[idxToFree], REF_INC_REUSE);
453     pentFree->FullUnique += 0x0100;
454 
455     do
456     {
457         /* Get the current first free index and sequence number */
458         iFirst = InterlockedReadUlong(&gulFirstFree);
459 
460         /* Set the einfo.pobj member to the index of the first free entry */
461         pentFree->einfo.pobj = UlongToPtr(iFirst & GDI_HANDLE_INDEX_MASK);
462 
463         /* Combine new index and increased sequence number in iToFree */
464         iToFree = idxToFree | ((iFirst & ~GDI_HANDLE_INDEX_MASK) + 0x10000);
465 
466         /* Try to atomically update the first free entry */
467         iPrev = InterlockedCompareExchange((LONG*)&gulFirstFree,
468                                            iToFree,
469                                            iFirst);
470     }
471     while (iPrev != iFirst);
472 }
473 
474 static
475 PENTRY
476 ENTRY_ReferenceEntryByHandle(HGDIOBJ hobj, FLONG fl)
477 {
478     ULONG ulIndex, cNewRefs, cOldRefs;
479     PENTRY pentry;
480 
481     /* Get the handle index and check if its too big */
482     ulIndex = GDI_HANDLE_GET_INDEX(hobj);
483     if (ulIndex >= GDI_HANDLE_COUNT) return NULL;
484 
485     /* Get pointer to the entry */
486     pentry = &gpentHmgr[ulIndex];
487 
488     /* Get the current reference count */
489     cOldRefs = gpaulRefCount[ulIndex];
490 
491     do
492     {
493         /* Check if the slot is deleted */
494         if ((cOldRefs & REF_MASK_VALID) == 0)
495         {
496             DPRINT("GDIOBJ: Slot is not valid: 0x%lx, hobh=%p\n", cOldRefs, hobj);
497             return NULL;
498         }
499 
500         /* Check if the unique value matches */
501         if (pentry->FullUnique != (USHORT)((ULONG_PTR)hobj >> 16))
502         {
503             DPRINT("GDIOBJ: Wrong unique value. Handle: 0x%4x, entry: 0x%4x\n",
504                    (USHORT)((ULONG_PTR)hobj >> 16), pentry->FullUnique);
505             return NULL;
506         }
507 
508         /* Check if the object owner is this process or public */
509         if (!(fl & GDIOBJFLAG_IGNOREPID) &&
510             pentry->ObjectOwner.ulObj != GDI_OBJ_HMGR_PUBLIC &&
511             pentry->ObjectOwner.ulObj != PtrToUlong(PsGetCurrentProcessId()))
512         {
513             DPRINT("GDIOBJ: Cannot reference foreign handle %p, pentry=%p:%lx.\n",
514                     hobj, pentry, pentry->ObjectOwner.ulObj);
515             return NULL;
516         }
517 
518         /* Try to atomically increment the reference count */
519         cNewRefs = cOldRefs + 1;
520         cOldRefs = InterlockedCompareExchange((PLONG)&gpaulRefCount[ulIndex],
521                                               cNewRefs,
522                                               cOldRefs);
523     }
524     while (cNewRefs != cOldRefs + 1);
525 
526     /* Integrity checks */
527     ASSERT((pentry->FullUnique & 0x1f) == pentry->Objt);
528     ASSERT(pentry->einfo.pobj && pentry->einfo.pobj->hHmgr == hobj);
529 
530     return pentry;
531 }
532 
533 static
534 HGDIOBJ
535 ENTRY_hInsertObject(PENTRY pentry, POBJ pobj, UCHAR objt, ULONG ulOwner)
536 {
537     ULONG ulIndex;
538 
539     /* Calculate the handle index */
540     ulIndex = pentry - gpentHmgr;
541 
542     /* Update the fields in the ENTRY */
543     pentry->einfo.pobj = pobj;
544     pentry->Objt = objt & 0x1f;
545     pentry->FullUnique = (pentry->FullUnique & 0xff00) | objt;
546     pentry->ObjectOwner.ulObj = ulOwner;
547 
548     /* Make the handle valid with 1 reference */
549     ASSERT((gpaulRefCount[ulIndex] & REF_MASK_INUSE) == 0);
550     InterlockedOr((LONG*)&gpaulRefCount[ulIndex], REF_MASK_VALID | 1);
551 
552     /* Return the handle */
553     return (HGDIOBJ)(((ULONG_PTR)pentry->FullUnique << 16) | ulIndex);
554 }
555 
556 POBJ
557 NTAPI
558 GDIOBJ_AllocateObject(UCHAR objt, ULONG cjSize, FLONG fl)
559 {
560     POBJ pobj;
561 
562     if (fl & BASEFLAG_LOOKASIDE)
563     {
564         /* Allocate the object from a lookaside list */
565         pobj = ExAllocateFromPagedLookasideList(&gpaLookasideList[objt & 0x1f]);
566     }
567     else
568     {
569         /* Allocate the object from paged pool */
570         pobj = ExAllocatePoolWithTag(PagedPool, cjSize, GDIOBJ_POOL_TAG(objt));
571     }
572 
573     if (!pobj) return NULL;
574 
575     /* Initialize the object */
576     RtlZeroMemory(pobj, cjSize);
577     pobj->hHmgr = (HGDIOBJ)((ULONG_PTR)objt << 16);
578     pobj->cExclusiveLock = 0;
579     pobj->ulShareCount = 1;
580     pobj->BaseFlags = fl & 0xffff;
581     DBG_INITLOG(&pobj->slhLog);
582     DBG_LOGEVENT(&pobj->slhLog, EVENT_ALLOCATE, 0);
583 #if DBG_ENABLE_GDIOBJ_BACKTRACES
584     DbgCaptureStackBackTace(pobj->apvBackTrace, 1, GDI_OBJECT_STACK_LEVELS);
585 #endif /* GDI_DEBUG */
586 
587     return pobj;
588 }
589 
590 VOID
591 NTAPI
592 GDIOBJ_vFreeObject(POBJ pobj)
593 {
594     UCHAR objt;
595 
596     DBG_CLEANUP_EVENT_LIST(&pobj->slhLog);
597 
598     /* Get the object type */
599     objt = ((ULONG_PTR)pobj->hHmgr >> 16) & 0x1f;
600 
601     /* Check if we have a delete procedure (for C++ based objects) */
602     if (apfnDelete[objt] != NULL)
603     {
604         /* Invoke the delete procedure */
605         apfnDelete[objt](pobj);
606     }
607     else
608     {
609         /* Call the cleanup procedure */
610         NT_ASSERT(apfnCleanup[objt]);
611         apfnCleanup[objt](pobj);
612 
613         /* Check if the object is allocated from a lookaside list */
614         if (pobj->BaseFlags & BASEFLAG_LOOKASIDE)
615         {
616             ExFreeToPagedLookasideList(&gpaLookasideList[objt], pobj);
617         }
618         else
619         {
620             ExFreePoolWithTag(pobj, GDIOBJ_POOL_TAG(objt));
621         }
622     }
623 }
624 
625 VOID
626 NTAPI
627 GDIOBJ_vDereferenceObject(POBJ pobj)
628 {
629     ULONG cRefs, ulIndex;
630 
631     /* Calculate the index */
632     ulIndex = GDI_HANDLE_GET_INDEX(pobj->hHmgr);
633 
634     /* Check if the object has a handle */
635     if (ulIndex)
636     {
637         /* Decrement reference count */
638         if ((gpaulRefCount[ulIndex] & REF_MASK_COUNT) == 0)
639         {
640             DBG_DUMP_EVENT_LIST(&pobj->slhLog);
641         }
642         ASSERT((gpaulRefCount[ulIndex] & REF_MASK_COUNT) > 0);
643         cRefs = InterlockedDecrement((LONG*)&gpaulRefCount[ulIndex]);
644         DBG_LOGEVENT(&pobj->slhLog, EVENT_DEREFERENCE, cRefs);
645 
646         /* Check if we reached 0 and handle bit is not set */
647         if ((cRefs & REF_MASK_INUSE) == 0)
648         {
649             /* Make sure it's ok to delete the object */
650             ASSERT(pobj->BaseFlags & BASEFLAG_READY_TO_DIE);
651 
652             /* Check if the handle was process owned */
653             if (gpentHmgr[ulIndex].ObjectOwner.ulObj != GDI_OBJ_HMGR_PUBLIC &&
654                 gpentHmgr[ulIndex].ObjectOwner.ulObj != GDI_OBJ_HMGR_NONE)
655             {
656                 /* Decrement the process handle count */
657                 ASSERT(gpentHmgr[ulIndex].ObjectOwner.ulObj ==
658                        HandleToUlong(PsGetCurrentProcessId()));
659                 DecrementCurrentProcessGdiHandleCount();
660             }
661 
662             /* Push entry to the free list */
663             ENTRY_vPushFreeEntry(&gpentHmgr[ulIndex]);
664 
665             /* Free the object */
666             GDIOBJ_vFreeObject(pobj);
667         }
668     }
669     else
670     {
671         /* Decrement the objects reference count */
672         ASSERT(pobj->ulShareCount > 0);
673         cRefs = InterlockedDecrement((LONG*)&pobj->ulShareCount);
674         DBG_LOGEVENT(&pobj->slhLog, EVENT_DEREFERENCE, cRefs);
675 
676         /* Check if we reached 0 */
677         if (cRefs == 0)
678         {
679             /* Free the object */
680             GDIOBJ_vFreeObject(pobj);
681         }
682     }
683 }
684 
685 POBJ
686 NTAPI
687 GDIOBJ_ReferenceObjectByHandle(
688     HGDIOBJ hobj,
689     UCHAR objt)
690 {
691     PENTRY pentry;
692     POBJ pobj;
693 
694     /* Check if the handle type matches */
695     ASSERT_SHARED_OBJECT_TYPE(objt);
696     if ((((ULONG_PTR)hobj >> 16) & 0x1f) != objt)
697     {
698         DPRINT("GDIOBJ: Wrong type. handle=%p, type=%x\n", hobj, objt);
699         return NULL;
700     }
701 
702     /* Reference the handle entry */
703     pentry = ENTRY_ReferenceEntryByHandle(hobj, 0);
704     if (!pentry)
705     {
706         DPRINT("GDIOBJ: Requested handle 0x%p is not valid.\n", hobj);
707         return NULL;
708     }
709 
710     /* Get the pointer to the BASEOBJECT */
711     pobj = pentry->einfo.pobj;
712 
713     /* Check if the object is exclusively locked */
714     if (pobj->cExclusiveLock != 0)
715     {
716         DPRINT1("GDIOBJ: Cannot reference object %p with exclusive lock.\n", hobj);
717         GDIOBJ_vDereferenceObject(pobj);
718         DBG_DUMP_EVENT_LIST(&pobj->slhLog);
719         return NULL;
720     }
721 
722     DBG_LOGEVENT(&pobj->slhLog, EVENT_REFERENCE, gpaulRefCount[pentry - gpentHmgr]);
723 
724     /* All is well, return the object */
725     return pobj;
726 }
727 
728 VOID
729 NTAPI
730 GDIOBJ_vReferenceObjectByPointer(POBJ pobj)
731 {
732     ULONG cRefs;
733 
734     /* Check if the object has a handle */
735     if (GDI_HANDLE_GET_INDEX(pobj->hHmgr))
736     {
737         /* Increase the handle's reference count */
738         ULONG ulIndex = GDI_HANDLE_GET_INDEX(pobj->hHmgr);
739         ASSERT((gpaulRefCount[ulIndex] & REF_MASK_COUNT) > 0);
740         cRefs = InterlockedIncrement((LONG*)&gpaulRefCount[ulIndex]);
741     }
742     else
743     {
744         /* Increase the object's reference count */
745         cRefs = InterlockedIncrement((LONG*)&pobj->ulShareCount);
746     }
747 
748     DBG_LOGEVENT(&pobj->slhLog, EVENT_REFERENCE, cRefs);
749 }
750 
751 PGDIOBJ
752 NTAPI
753 GDIOBJ_TryLockObject(
754     HGDIOBJ hobj,
755     UCHAR objt)
756 {
757     PENTRY pentry;
758     POBJ pobj;
759     DWORD dwThreadId;
760 
761     /* Check if the handle type matches */
762     ASSERT_TRYLOCK_OBJECT_TYPE(objt);
763     if ((((ULONG_PTR)hobj >> 16) & 0x1f) != objt)
764     {
765         DPRINT("Wrong object type: hobj=0x%p, objt=0x%x\n", hobj, objt);
766         return NULL;
767     }
768 
769     /* Make sure lock order is correct */
770     ASSERT_LOCK_ORDER(objt);
771 
772     /* Reference the handle entry */
773     pentry = ENTRY_ReferenceEntryByHandle(hobj, 0);
774     if (!pentry)
775     {
776         DPRINT("GDIOBJ: Requested handle 0x%p is not valid.\n", hobj);
777         return NULL;
778     }
779 
780     /* Get the pointer to the BASEOBJECT */
781     pobj = pentry->einfo.pobj;
782 
783     /* Check if we already own the lock */
784     dwThreadId = PtrToUlong(PsGetCurrentThreadId());
785     if (pobj->dwThreadId != dwThreadId)
786     {
787         /* Disable APCs and try acquiring the push lock */
788         KeEnterCriticalRegion();
789         if(!ExTryAcquirePushLockExclusive(&pobj->pushlock))
790         {
791             ULONG cRefs, ulIndex;
792             /* Already owned. Clean up and leave. */
793             KeLeaveCriticalRegion();
794 
795             /* Calculate the index */
796             ulIndex = GDI_HANDLE_GET_INDEX(pobj->hHmgr);
797 
798             /* Decrement reference count */
799             ASSERT((gpaulRefCount[ulIndex] & REF_MASK_COUNT) > 0);
800             cRefs = InterlockedDecrement((LONG*)&gpaulRefCount[ulIndex]);
801             ASSERT(cRefs & REF_MASK_VALID);
802 
803             return NULL;
804         }
805 
806         /* Set us as lock owner */
807         ASSERT(pobj->dwThreadId == 0);
808         pobj->dwThreadId = dwThreadId;
809     }
810 
811     /* Increase lock count */
812     pobj->cExclusiveLock++;
813     INCREASE_THREAD_LOCK_COUNT(hobj);
814     DBG_LOGEVENT(&pobj->slhLog, EVENT_LOCK, 0);
815 
816     /* Return the object */
817     return pobj;
818 }
819 
820 PGDIOBJ
821 NTAPI
822 GDIOBJ_LockObject(
823     HGDIOBJ hobj,
824     UCHAR objt)
825 {
826     PENTRY pentry;
827     POBJ pobj;
828     DWORD dwThreadId;
829 
830     /* Check if the handle type matches */
831     ASSERT_EXCLUSIVE_OBJECT_TYPE(objt);
832     if ((((ULONG_PTR)hobj >> 16) & 0x1f) != objt)
833     {
834         DPRINT("Wrong object type: hobj=0x%p, objt=0x%x\n", hobj, objt);
835         return NULL;
836     }
837 
838     /* Make sure lock order is correct */
839     ASSERT_LOCK_ORDER(objt);
840 
841     /* Reference the handle entry */
842     pentry = ENTRY_ReferenceEntryByHandle(hobj, 0);
843     if (!pentry)
844     {
845         DPRINT("GDIOBJ: Requested handle 0x%p is not valid.\n", hobj);
846         return NULL;
847     }
848 
849     /* Get the pointer to the BASEOBJECT */
850     pobj = pentry->einfo.pobj;
851 
852     /* Check if we already own the lock */
853     dwThreadId = PtrToUlong(PsGetCurrentThreadId());
854     if (pobj->dwThreadId != dwThreadId)
855     {
856         /* Disable APCs and acquire the push lock */
857         KeEnterCriticalRegion();
858         ExAcquirePushLockExclusive(&pobj->pushlock);
859 
860         /* Set us as lock owner */
861         ASSERT(pobj->dwThreadId == 0);
862         pobj->dwThreadId = dwThreadId;
863     }
864 
865     /* Increase lock count */
866     pobj->cExclusiveLock++;
867     INCREASE_THREAD_LOCK_COUNT(hobj);
868     DBG_LOGEVENT(&pobj->slhLog, EVENT_LOCK, 0);
869 
870     /* Return the object */
871     return pobj;
872 }
873 
874 VOID
875 NTAPI
876 GDIOBJ_vUnlockObject(POBJ pobj)
877 {
878     ULONG cRefs, ulIndex;
879     ASSERT(pobj->cExclusiveLock > 0);
880 
881     /* Decrease lock count */
882     pobj->cExclusiveLock--;
883     DECREASE_THREAD_LOCK_COUNT(pobj->hHmgr);
884     DBG_LOGEVENT(&pobj->slhLog, EVENT_UNLOCK, 0);
885 
886     /* Check if this was the last lock */
887     if (pobj->cExclusiveLock == 0)
888     {
889         /* Reset lock owner */
890         pobj->dwThreadId = 0;
891 
892         /* Release the pushlock and reenable APCs */
893         ExReleasePushLockExclusive(&pobj->pushlock);
894         KeLeaveCriticalRegion();
895     }
896 
897     /* Calculate the index */
898     ulIndex = GDI_HANDLE_GET_INDEX(pobj->hHmgr);
899 
900     /* Decrement reference count */
901     ASSERT((gpaulRefCount[ulIndex] & REF_MASK_COUNT) > 0);
902     cRefs = InterlockedDecrement((LONG*)&gpaulRefCount[ulIndex]);
903     ASSERT(cRefs & REF_MASK_VALID);
904 }
905 
906 HGDIOBJ
907 NTAPI
908 GDIOBJ_hInsertObject(
909     POBJ pobj,
910     ULONG ulOwner)
911 {
912     PENTRY pentry;
913     UCHAR objt;
914 
915     /* Must have no handle and only one reference */
916     ASSERT(GDI_HANDLE_GET_INDEX(pobj->hHmgr) == 0);
917     ASSERT(pobj->cExclusiveLock == 0);
918     ASSERT(pobj->ulShareCount == 1);
919 
920     /* Get a free handle entry */
921     pentry = ENTRY_pentPopFreeEntry();
922     if (!pentry)
923     {
924         DPRINT1("GDIOBJ: Could not get a free entry.\n");
925         return NULL;
926     }
927 
928     /* Make the object exclusively locked */
929     ExInitializePushLock(&pobj->pushlock);
930     KeEnterCriticalRegion();
931     ExAcquirePushLockExclusive(&pobj->pushlock);
932     pobj->cExclusiveLock = 1;
933     pobj->dwThreadId = PtrToUlong(PsGetCurrentThreadId());
934     INCREASE_THREAD_LOCK_COUNT(pobj->hHmgr);
935 
936     /* Get object type from the hHmgr field */
937     objt = ((ULONG_PTR)pobj->hHmgr >> 16) & 0xff;
938     ASSERT(objt != GDIObjType_DEF_TYPE);
939 
940     /* Check if current process is requested owner */
941     if (ulOwner == GDI_OBJ_HMGR_POWNED)
942     {
943         /* Increment the process handle count */
944         IncrementCurrentProcessGdiHandleCount();
945 
946         /* Use Process id */
947         ulOwner = HandleToUlong(PsGetCurrentProcessId());
948     }
949 
950     /* Insert the object into the handle table */
951     pobj->hHmgr = ENTRY_hInsertObject(pentry, pobj, objt, ulOwner);
952 
953     /* Return the handle */
954     DPRINT("GDIOBJ: Created handle: %p\n", pobj->hHmgr);
955     DBG_LOGEVENT(&pobj->slhLog, EVENT_CREATE_HANDLE, 0);
956     return pobj->hHmgr;
957 }
958 
959 VOID
960 NTAPI
961 GDIOBJ_vSetObjectOwner(
962     POBJ pobj,
963     ULONG ulNewOwner)
964 {
965     PENTRY pentry;
966     ULONG ulOldOwner;
967 
968     /* This is a ugly HACK, needed to fix IntGdiSetDCOwnerEx */
969     if (GDI_HANDLE_IS_STOCKOBJ(pobj->hHmgr))
970     {
971         DPRINT("Trying to set ownership of stock object %p to %lx\n", pobj->hHmgr, ulNewOwner);
972         return;
973     }
974 
975     /* Get the handle entry */
976     NT_ASSERT(GDI_HANDLE_GET_INDEX(pobj->hHmgr));
977     pentry = &gpentHmgr[GDI_HANDLE_GET_INDEX(pobj->hHmgr)];
978 
979     /* Check if the new owner is the same as the old one */
980     ulOldOwner = pentry->ObjectOwner.ulObj;
981     if (ulOldOwner == ulNewOwner)
982     {
983         /* Nothing to do */
984         return;
985     }
986 
987     /* Is the current process requested? */
988     if (ulNewOwner == GDI_OBJ_HMGR_POWNED)
989     {
990         /* Use process id */
991         ulNewOwner = HandleToUlong(PsGetCurrentProcessId());
992     }
993 
994     // HACK
995     if (ulNewOwner == GDI_OBJ_HMGR_NONE)
996         ulNewOwner = GDI_OBJ_HMGR_PUBLIC;
997 
998     /* Was the object process owned? */
999     if ((ulOldOwner != GDI_OBJ_HMGR_PUBLIC) &&
1000         (ulOldOwner != GDI_OBJ_HMGR_NONE))
1001     {
1002         /* Decrement the previous owners handle count */
1003         DecrementGdiHandleCount(ulOldOwner);
1004     }
1005 
1006     /* Is the new owner a process? */
1007     if ((ulNewOwner != GDI_OBJ_HMGR_PUBLIC) &&
1008         (ulNewOwner != GDI_OBJ_HMGR_NONE))
1009     {
1010         /* Increment the new owners handle count */
1011         IncrementGdiHandleCount(ulNewOwner);
1012     }
1013     else
1014     {
1015         /* Make sure we don't leak user mode memory */
1016         NT_ASSERT(pentry->pUser == NULL);
1017     }
1018 
1019     /* Set new owner */
1020     pentry->ObjectOwner.ulObj = ulNewOwner;
1021     DBG_LOGEVENT(&pobj->slhLog, EVENT_SET_OWNER, 0);
1022 }
1023 
1024 /* Locks 2 or 3 objects at a time */
1025 BOOL
1026 NTAPI
1027 GDIOBJ_bLockMultipleObjects(
1028     IN ULONG ulCount,
1029     IN HGDIOBJ* ahObj,
1030     OUT PGDIOBJ* apObj,
1031     IN UCHAR objt)
1032 {
1033     UINT auiIndices[3] = {0, 1, 2};
1034     UINT i, j, tmp;
1035 
1036     ASSERT(ulCount <= 3);
1037 
1038     /* Sort the handles */
1039     for (i = 0; i < ulCount - 1; i++)
1040     {
1041         for (j = i + 1; j < ulCount; j++)
1042         {
1043             if ((ULONG_PTR)ahObj[auiIndices[i]] <
1044                 (ULONG_PTR)ahObj[auiIndices[j]])
1045             {
1046                 tmp = auiIndices[i];
1047                 auiIndices[i] = auiIndices[j];
1048                 auiIndices[j] = tmp;
1049             }
1050         }
1051     }
1052 
1053     /* Lock the objects in safe order */
1054     for (i = 0; i < ulCount; i++)
1055     {
1056         /* Skip NULL handles */
1057         if (ahObj[auiIndices[i]] == NULL)
1058         {
1059             apObj[auiIndices[i]] = NULL;
1060             continue;
1061         }
1062 
1063         /* Lock the object */
1064         apObj[auiIndices[i]] = GDIOBJ_LockObject(ahObj[auiIndices[i]], objt);
1065 
1066         /* Check for failure */
1067         if (apObj[auiIndices[i]] == NULL)
1068         {
1069             /* Cleanup */
1070             while (i--)
1071             {
1072                 if (apObj[auiIndices[i]])
1073                     GDIOBJ_vUnlockObject(apObj[auiIndices[i]]);
1074             }
1075             return FALSE;
1076         }
1077     }
1078 
1079     return TRUE;
1080 }
1081 
1082 PVOID
1083 NTAPI
1084 GDIOBJ_pvGetObjectAttr(POBJ pobj)
1085 {
1086     ULONG ulIndex = GDI_HANDLE_GET_INDEX(pobj->hHmgr);
1087     return gpentHmgr[ulIndex].pUser;
1088 }
1089 
1090 VOID
1091 NTAPI
1092 GDIOBJ_vSetObjectAttr(POBJ pobj, PVOID pvObjAttr)
1093 {
1094     ULONG ulIndex;
1095 
1096     ASSERT(pobj->hHmgr);
1097 
1098     /* Get the handle index */
1099     ulIndex = GDI_HANDLE_GET_INDEX(pobj->hHmgr);
1100 
1101     /* Set pointer to the usermode attribute */
1102     gpentHmgr[ulIndex].pUser = pvObjAttr;
1103 }
1104 
1105 VOID
1106 NTAPI
1107 GDIOBJ_vDeleteObject(POBJ pobj)
1108 {
1109     ULONG ulIndex;
1110 
1111     /* Set the object's delete flag */
1112     InterlockedOr16((SHORT*)&pobj->BaseFlags, BASEFLAG_READY_TO_DIE);
1113     DBG_LOGEVENT(&pobj->slhLog, EVENT_DELETE, 0);
1114 
1115     /* Get the handle index */
1116     ulIndex = GDI_HANDLE_GET_INDEX(pobj->hHmgr);
1117     if (ulIndex)
1118     {
1119         /* Reset the handle valid bit */
1120         InterlockedAnd((LONG*)&gpaulRefCount[ulIndex], ~REF_MASK_VALID);
1121 
1122         /* Check if the object is exclusively locked */
1123         if (pobj->cExclusiveLock != 0)
1124         {
1125             /* Reset lock owner and lock count */
1126             pobj->dwThreadId = 0;
1127             pobj->cExclusiveLock = 0;
1128 
1129             /* Release the pushlock and reenable APCs */
1130             ExReleasePushLockExclusive(&pobj->pushlock);
1131             KeLeaveCriticalRegion();
1132             DECREASE_THREAD_LOCK_COUNT(pobj->hHmgr);
1133         }
1134     }
1135 
1136     /* Dereference the object (will take care of deletion) */
1137     GDIOBJ_vDereferenceObject(pobj);
1138 }
1139 
1140 BOOL
1141 NTAPI
1142 GreIsHandleValid(HGDIOBJ hobj)
1143 {
1144     PENTRY pentry;
1145 
1146     pentry = ENTRY_ReferenceEntryByHandle(hobj, 0);
1147     if (!pentry) return FALSE;
1148     GDIOBJ_vDereferenceObject(pentry->einfo.pobj);
1149     return TRUE;
1150 }
1151 
1152 BOOL
1153 NTAPI
1154 GreDeleteObject(HGDIOBJ hobj)
1155 {
1156     PENTRY pentry;
1157 
1158     /* Check for stock objects */
1159     if (GDI_HANDLE_IS_STOCKOBJ(hobj))
1160     {
1161         DPRINT1("GreDeleteObject: Cannot delete stock object %p.\n", hobj);
1162         return FALSE;
1163     }
1164 
1165     /* Reference the handle entry */
1166     pentry = ENTRY_ReferenceEntryByHandle(hobj, 0);
1167     if (!pentry)
1168     {
1169         DPRINT1("GreDeleteObject: Trying to delete invalid object %p\n", hobj);
1170         return FALSE;
1171     }
1172 
1173     /* Check for public owner */
1174     if (pentry->ObjectOwner.ulObj == GDI_OBJ_HMGR_PUBLIC)
1175     {
1176         DPRINT1("GreDeleteObject: Trying to delete global object %p\n", hobj);
1177         GDIOBJ_vDereferenceObject(pentry->einfo.pobj);
1178         return FALSE;
1179     }
1180 
1181     /* Delete the object */
1182     GDIOBJ_vDeleteObject(pentry->einfo.pobj);
1183     return TRUE;
1184 }
1185 
1186 ULONG
1187 NTAPI
1188 GreGetObjectOwner(HGDIOBJ hobj)
1189 {
1190     ULONG ulIndex, ulOwner;
1191 
1192     /* Get the handle index */
1193     ulIndex = GDI_HANDLE_GET_INDEX(hobj);
1194 
1195     /* Check if the handle is valid */
1196     if (ulIndex >= GDI_HANDLE_COUNT ||
1197         gpentHmgr[ulIndex].Objt == GDIObjType_DEF_TYPE ||
1198         ((ULONG_PTR)hobj >> 16) != gpentHmgr[ulIndex].FullUnique)
1199     {
1200         DPRINT1("GreGetObjectOwner: invalid handle 0x%p.\n", hobj);
1201         return GDI_OBJ_HMGR_RESTRICTED;
1202     }
1203 
1204     /* Get the object owner */
1205     ulOwner = gpentHmgr[ulIndex].ObjectOwner.ulObj;
1206 
1207     if (ulOwner == HandleToUlong(PsGetCurrentProcessId()))
1208         return GDI_OBJ_HMGR_POWNED;
1209 
1210     if (ulOwner == GDI_OBJ_HMGR_PUBLIC)
1211         return GDI_OBJ_HMGR_PUBLIC;
1212 
1213     return GDI_OBJ_HMGR_RESTRICTED;
1214 }
1215 
1216 BOOL
1217 NTAPI
1218 GreSetObjectOwnerEx(
1219     HGDIOBJ hobj,
1220     ULONG ulOwner,
1221     ULONG Flags)
1222 {
1223     PENTRY pentry;
1224 
1225     /* Check for stock objects */
1226     if (GDI_HANDLE_IS_STOCKOBJ(hobj))
1227     {
1228         DPRINT("GreSetObjectOwner: Got stock object %p\n", hobj);
1229         return FALSE;
1230     }
1231 
1232     /* Reference the handle entry */
1233     pentry = ENTRY_ReferenceEntryByHandle(hobj, Flags);
1234     if (!pentry)
1235     {
1236         DPRINT("GreSetObjectOwner: Invalid handle 0x%p.\n", hobj);
1237         return FALSE;
1238     }
1239 
1240     /* Call internal function */
1241     GDIOBJ_vSetObjectOwner(pentry->einfo.pobj, ulOwner);
1242 
1243     /* Dereference the object */
1244     GDIOBJ_vDereferenceObject(pentry->einfo.pobj);
1245 
1246     return TRUE;
1247 }
1248 
1249 BOOL
1250 NTAPI
1251 GreSetObjectOwner(
1252     HGDIOBJ hobj,
1253     ULONG ulOwner)
1254 {
1255     return GreSetObjectOwnerEx(hobj, ulOwner, 0);
1256 }
1257 
1258 INT
1259 NTAPI
1260 GreGetObject(
1261     IN HGDIOBJ hobj,
1262     IN INT cbCount,
1263     IN PVOID pvBuffer)
1264 {
1265     PVOID pvObj;
1266     UCHAR objt;
1267     INT iResult = 0;
1268 
1269     /* Verify object type */
1270     objt = ((ULONG_PTR)hobj >> 16) & 0x1f;
1271     if (objt != GDIObjType_BRUSH_TYPE &&
1272         objt != GDIObjType_SURF_TYPE &&
1273         objt != GDIObjType_LFONT_TYPE &&
1274         objt != GDIObjType_PAL_TYPE)
1275     {
1276         DPRINT1("GreGetObject: Invalid object type\n");
1277         return 0;
1278     }
1279 
1280     pvObj = GDIOBJ_ReferenceObjectByHandle(hobj, objt);
1281     if (!pvObj)
1282     {
1283         DPRINT("GreGetObject: Could not lock object\n");
1284         return 0;
1285     }
1286 
1287     switch (GDI_HANDLE_GET_TYPE(hobj))
1288     {
1289         case GDILoObjType_LO_PEN_TYPE:
1290         case GDILoObjType_LO_EXTPEN_TYPE:
1291             iResult = PEN_GetObject(pvObj, cbCount, pvBuffer);
1292             break;
1293 
1294         case GDILoObjType_LO_BRUSH_TYPE:
1295             iResult = BRUSH_GetObject(pvObj, cbCount, pvBuffer);
1296             break;
1297 
1298         case GDILoObjType_LO_BITMAP_TYPE:
1299             iResult = BITMAP_GetObject(pvObj, cbCount, pvBuffer);
1300             break;
1301 
1302         case GDILoObjType_LO_FONT_TYPE:
1303             iResult = FontGetObject(pvObj, cbCount, pvBuffer);
1304             break;
1305 
1306         case GDILoObjType_LO_PALETTE_TYPE:
1307             iResult = PALETTE_GetObject(pvObj, cbCount, pvBuffer);
1308             break;
1309 
1310         default:
1311             DPRINT1("GDI object type of 0x%p not implemented\n", hobj);
1312             break;
1313     }
1314 
1315     GDIOBJ_vDereferenceObject(pvObj);
1316     return iResult;
1317 }
1318 
1319 W32KAPI
1320 INT
1321 APIENTRY
1322 NtGdiExtGetObjectW(
1323     IN HANDLE hobj,
1324     IN INT cjBufferSize,
1325     OUT LPVOID lpBuffer)
1326 {
1327     UINT iResult, cjMaxSize;
1328     union
1329     {
1330         BITMAP bitmap;
1331         DIBSECTION dibsection;
1332         LOGPEN logpen;
1333         LOGBRUSH logbrush;
1334         LOGFONTW logfontw;
1335         EXTLOGFONTW extlogfontw;
1336         ENUMLOGFONTEXDVW enumlogfontexdvw;
1337     } object;
1338 
1339     /* Normalize to the largest supported object size */
1340     cjMaxSize = min((UINT)cjBufferSize, sizeof(object));
1341 
1342     /* Now do the actual call */
1343     iResult = GreGetObject(hobj, cjMaxSize, lpBuffer ? &object : NULL);
1344 
1345     /* Check if we have a buffer and data */
1346     if ((lpBuffer != NULL) && (iResult != 0))
1347     {
1348         /* Enter SEH for buffer transfer */
1349         _SEH2_TRY
1350         {
1351             /* Probe the buffer and copy it */
1352             cjMaxSize = min(cjMaxSize, iResult);
1353             ProbeForWrite(lpBuffer, cjMaxSize, sizeof(WORD));
1354             RtlCopyMemory(lpBuffer, &object, cjMaxSize);
1355         }
1356         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1357         {
1358             /* Clear the return value.
1359              * Do *NOT* set last error here! */
1360             iResult = 0;
1361         }
1362         _SEH2_END;
1363     }
1364 
1365     /* Return the count */
1366     return iResult;
1367 }
1368 
1369 W32KAPI
1370 HANDLE
1371 APIENTRY
1372 NtGdiCreateClientObj(
1373     IN ULONG ulType)
1374 {
1375     POBJ pObject;
1376     HANDLE handle;
1377 
1378     /* Check if ulType is valid */
1379     if ((ulType != GDILoObjType_LO_METAFILE16_TYPE) &&
1380         (ulType != GDILoObjType_LO_METAFILE_TYPE) &&
1381         (ulType != GDILoObjType_LO_METADC16_TYPE))
1382     {
1383         DPRINT1("NtGdiCreateClientObj: Invalid object type 0x%lx.\n", ulType);
1384         return NULL;
1385     }
1386 
1387     /* Allocate a new object */
1388     pObject = GDIOBJ_AllocateObject(GDIObjType_CLIENTOBJ_TYPE,
1389                                     sizeof(CLIENTOBJ),
1390                                     BASEFLAG_LOOKASIDE);
1391     if (!pObject)
1392     {
1393         DPRINT1("NtGdiCreateClientObj: Could not allocate a clientobj.\n");
1394         return NULL;
1395     }
1396 
1397     /* Set the real object type */
1398     pObject->hHmgr = UlongToHandle(ulType | GDILoObjType_LO_CLIENTOBJ_TYPE);
1399 
1400     /* Create a handle */
1401     handle = GDIOBJ_hInsertObject(pObject, GDI_OBJ_HMGR_POWNED);
1402     if (!handle)
1403     {
1404         DPRINT1("NtGdiCreateClientObj: Could not create a handle.\n");
1405         GDIOBJ_vFreeObject(pObject);
1406         return NULL;
1407     }
1408 
1409     /* Unlock it */
1410     GDIOBJ_vUnlockObject(pObject);
1411 
1412     return handle;
1413 }
1414 
1415 W32KAPI
1416 BOOL
1417 APIENTRY
1418 NtGdiDeleteClientObj(
1419     IN HANDLE hobj)
1420 {
1421     /* We first need to get the real type from the handle */
1422     ULONG ulType = GDI_HANDLE_GET_TYPE(hobj);
1423 
1424     /* Check if it's really a CLIENTOBJ */
1425     if ((ulType & GDI_HANDLE_BASETYPE_MASK) != GDILoObjType_LO_CLIENTOBJ_TYPE)
1426     {
1427         /* FIXME: SetLastError? */
1428         return FALSE;
1429     }
1430 
1431     return GreDeleteObject(hobj);
1432 }
1433 
1434 
1435 
1436 PGDI_HANDLE_TABLE GdiHandleTable = NULL;
1437 
1438 PGDIOBJ NTAPI
1439 GDIOBJ_ShareLockObj(HGDIOBJ hObj, DWORD ExpectedType)
1440 {
1441     if (ExpectedType == GDI_OBJECT_TYPE_DONTCARE)
1442         ExpectedType = GDI_HANDLE_GET_TYPE(hObj);
1443     return GDIOBJ_ReferenceObjectByHandle(hObj, (ExpectedType >> 16) & 0x1f);
1444 }
1445 
1446 // This function is not safe to use with concurrent deleting attempts
1447 // That shouldn't be a problem, since we don't have any processes yet,
1448 // that could delete the handle
1449 BOOL
1450 NTAPI
1451 GDIOBJ_ConvertToStockObj(HGDIOBJ *phObj)
1452 {
1453     PENTRY pentry;
1454     POBJ pobj;
1455 
1456     /* Reference the handle entry */
1457     pentry = ENTRY_ReferenceEntryByHandle(*phObj, 0);
1458     if (!pentry)
1459     {
1460         DPRINT1("GDIOBJ: Requested handle 0x%p is not valid.\n", *phObj);
1461         return FALSE;
1462     }
1463 
1464     /* Update the entry */
1465     pentry->FullUnique |= GDI_ENTRY_STOCK_MASK;
1466     pentry->ObjectOwner.ulObj = 0;
1467 
1468     /* Get the pointer to the BASEOBJECT */
1469     pobj = pentry->einfo.pobj;
1470 
1471     /* Calculate the new handle */
1472     pobj->hHmgr = (HGDIOBJ)((ULONG_PTR)pobj->hHmgr | GDI_HANDLE_STOCK_MASK);
1473 
1474     /* Return the new handle */
1475     *phObj = pobj->hHmgr;
1476 
1477     /* Dereference the handle */
1478     GDIOBJ_vDereferenceObject(pobj);
1479 
1480     return TRUE;
1481 }
1482 
1483 POBJ NTAPI
1484 GDIOBJ_AllocObjWithHandle(ULONG ObjectType, ULONG cjSize)
1485 {
1486     POBJ pobj;
1487     FLONG fl = 0;
1488     UCHAR objt = (ObjectType >> 16) & 0xFF;
1489 
1490     if ((objt == GDIObjType_DC_TYPE && cjSize == sizeof(DC)) ||
1491         (objt == GDIObjType_PAL_TYPE && cjSize == sizeof(PALETTE)) ||
1492         (objt == GDIObjType_RGN_TYPE && cjSize == sizeof(REGION)) ||
1493         (objt == GDIObjType_SURF_TYPE && cjSize == sizeof(SURFACE)) ||
1494         (objt == GDIObjType_PATH_TYPE && cjSize == sizeof(PATH)))
1495     {
1496         fl |= BASEFLAG_LOOKASIDE;
1497     }
1498 
1499     pobj = GDIOBJ_AllocateObject(objt, cjSize, fl);
1500     if (!pobj)
1501     {
1502         return NULL;
1503     }
1504 
1505     if (!GDIOBJ_hInsertObject(pobj, GDI_OBJ_HMGR_POWNED))
1506     {
1507         GDIOBJ_vFreeObject(pobj);
1508         return NULL;
1509     }
1510     return pobj;
1511 }
1512 
1513 PVOID NTAPI
1514 GDI_MapHandleTable(PEPROCESS pProcess)
1515 {
1516     PVOID pvMappedView = NULL;
1517     NTSTATUS Status;
1518     LARGE_INTEGER liOffset;
1519     ULONG cjViewSize = sizeof(GDI_HANDLE_TABLE);
1520 
1521     liOffset.QuadPart = 0;
1522 
1523     ASSERT(gpvGdiHdlTblSection != NULL);
1524     ASSERT(pProcess != NULL);
1525 
1526     Status = MmMapViewOfSection(gpvGdiHdlTblSection,
1527                                 pProcess,
1528                                 &pvMappedView,
1529                                 0,
1530                                 0,
1531                                 &liOffset,
1532                                 &cjViewSize,
1533                                 ViewUnmap,
1534                                 SEC_NO_CHANGE,
1535                                 PAGE_READONLY);
1536 
1537     if (!NT_SUCCESS(Status))
1538         return NULL;
1539 
1540     return pvMappedView;
1541 }
1542 
1543 BOOL NTAPI
1544 GDI_CleanupForProcess(struct _EPROCESS *Process)
1545 {
1546     PENTRY pentry;
1547     ULONG ulIndex;
1548     DWORD dwProcessId;
1549     PPROCESSINFO ppi;
1550 
1551     DPRINT("CleanupForProcess prochandle %p Pid %p\n",
1552            Process, Process->UniqueProcessId);
1553 
1554     ASSERT(Process == PsGetCurrentProcess());
1555 
1556     /* Get the current process Id */
1557     dwProcessId = PtrToUlong(PsGetCurrentProcessId());
1558 
1559     /* Loop all handles in the handle table */
1560     for (ulIndex = RESERVE_ENTRIES_COUNT; ulIndex < gulFirstUnused; ulIndex++)
1561     {
1562         pentry = &gpentHmgr[ulIndex];
1563 
1564         /* Check if the object is owned by the process */
1565         if (pentry->ObjectOwner.ulObj == dwProcessId)
1566         {
1567             ASSERT(pentry->einfo.pobj->cExclusiveLock == 0);
1568 
1569             /* Reference the object and delete it */
1570             InterlockedIncrement((LONG*)&gpaulRefCount[ulIndex]);
1571             GDIOBJ_vDeleteObject(pentry->einfo.pobj);
1572         }
1573     }
1574 
1575 #if DBG
1576     DbgGdiHTIntegrityCheck();
1577 #endif
1578 
1579     ppi = PsGetCurrentProcessWin32Process();
1580     DPRINT("Completed cleanup for process %p\n", Process->UniqueProcessId);
1581     if (ppi->GDIHandleCount != 0)
1582     {
1583         DPRINT1("Leaking %d handles!\n", ppi->GDIHandleCount);
1584         ASSERT(FALSE);
1585     }
1586 
1587     /* Loop all handles in the handle table */
1588     for (ulIndex = RESERVE_ENTRIES_COUNT; ulIndex < gulFirstUnused; ulIndex++)
1589     {
1590         pentry = &gpentHmgr[ulIndex];
1591 
1592         /* Check if the object is owned by the process */
1593         if (pentry->ObjectOwner.ulObj == dwProcessId)
1594         {
1595             DPRINT1("Leaking object. Index=%lx, type=0x%x, refcount=%lx\n",
1596                     ulIndex, pentry->Objt, gpaulRefCount[ulIndex]);
1597             DBG_DUMP_EVENT_LIST(&pentry->einfo.pobj->slhLog);
1598             //DBG_CLEANUP_EVENT_LIST(&pentry->einfo.pobj->slhLog);
1599             ASSERT(FALSE);
1600         }
1601     }
1602 
1603     return TRUE;
1604 }
1605 
1606 /// HACK!
1607 PGDI_POOL
1608 GetBrushAttrPool(VOID)
1609 {
1610     PPROCESSINFO ppi;
1611 
1612     ppi = PsGetCurrentProcessWin32Process();
1613     NT_ASSERT(ppi != NULL);
1614 
1615     return ppi->pPoolBrushAttr;
1616 }
1617 
1618 /* EOF */
1619