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