xref: /reactos/win32ss/gdi/ntgdi/gdiobj.c (revision 48cc7814)
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 
484     /* Get pointer to the entry */
485     pentry = &gpentHmgr[ulIndex];
486 
487     /* Get the current reference count */
488     cOldRefs = gpaulRefCount[ulIndex];
489 
490     do
491     {
492         /* Check if the slot is deleted */
493         if ((cOldRefs & REF_MASK_VALID) == 0)
494         {
495             DPRINT("GDIOBJ: Slot is not valid: 0x%lx, hobh=%p\n", cOldRefs, hobj);
496             return NULL;
497         }
498 
499         /* Check if the unique value matches */
500         if (pentry->FullUnique != (USHORT)((ULONG_PTR)hobj >> 16))
501         {
502             DPRINT("GDIOBJ: Wrong unique value. Handle: 0x%4x, entry: 0x%4x\n",
503                    (USHORT)((ULONG_PTR)hobj >> 16), pentry->FullUnique);
504             return NULL;
505         }
506 
507         /* Check if the object owner is this process or public */
508         if (!(fl & GDIOBJFLAG_IGNOREPID) &&
509             pentry->ObjectOwner.ulObj != GDI_OBJ_HMGR_PUBLIC &&
510             pentry->ObjectOwner.ulObj != PtrToUlong(PsGetCurrentProcessId()))
511         {
512             DPRINT("GDIOBJ: Cannot reference foreign handle %p, pentry=%p:%lx.\n",
513                     hobj, pentry, pentry->ObjectOwner.ulObj);
514             return NULL;
515         }
516 
517         /* Try to atomically increment the reference count */
518         cNewRefs = cOldRefs + 1;
519         cOldRefs = InterlockedCompareExchange((PLONG)&gpaulRefCount[ulIndex],
520                                               cNewRefs,
521                                               cOldRefs);
522     }
523     while (cNewRefs != cOldRefs + 1);
524 
525     /* Integrity checks */
526     ASSERT((pentry->FullUnique & 0x1f) == pentry->Objt);
527     ASSERT(pentry->einfo.pobj && pentry->einfo.pobj->hHmgr == hobj);
528 
529     return pentry;
530 }
531 
532 static
533 HGDIOBJ
534 ENTRY_hInsertObject(PENTRY pentry, POBJ pobj, UCHAR objt, ULONG ulOwner)
535 {
536     ULONG ulIndex;
537 
538     /* Calculate the handle index */
539     ulIndex = pentry - gpentHmgr;
540 
541     /* Update the fields in the ENTRY */
542     pentry->einfo.pobj = pobj;
543     pentry->Objt = objt & 0x1f;
544     pentry->FullUnique = (pentry->FullUnique & 0xff00) | objt;
545     pentry->ObjectOwner.ulObj = ulOwner;
546 
547     /* Make the handle valid with 1 reference */
548     ASSERT((gpaulRefCount[ulIndex] & REF_MASK_INUSE) == 0);
549     InterlockedOr((LONG*)&gpaulRefCount[ulIndex], REF_MASK_VALID | 1);
550 
551     /* Return the handle */
552     return (HGDIOBJ)(((ULONG_PTR)pentry->FullUnique << 16) | ulIndex);
553 }
554 
555 POBJ
556 NTAPI
557 GDIOBJ_AllocateObject(UCHAR objt, ULONG cjSize, FLONG fl)
558 {
559     POBJ pobj;
560 
561     if (fl & BASEFLAG_LOOKASIDE)
562     {
563         /* Allocate the object from a lookaside list */
564         pobj = ExAllocateFromPagedLookasideList(&gpaLookasideList[objt & 0x1f]);
565     }
566     else
567     {
568         /* Allocate the object from paged pool */
569         pobj = ExAllocatePoolWithTag(PagedPool, cjSize, GDIOBJ_POOL_TAG(objt));
570     }
571 
572     if (!pobj) return NULL;
573 
574     /* Initialize the object */
575     RtlZeroMemory(pobj, cjSize);
576     pobj->hHmgr = (HGDIOBJ)((ULONG_PTR)objt << 16);
577     pobj->cExclusiveLock = 0;
578     pobj->ulShareCount = 1;
579     pobj->BaseFlags = fl & 0xffff;
580     DBG_INITLOG(&pobj->slhLog);
581     DBG_LOGEVENT(&pobj->slhLog, EVENT_ALLOCATE, 0);
582 #if DBG_ENABLE_GDIOBJ_BACKTRACES
583     DbgCaptureStackBackTace(pobj->apvBackTrace, 1, GDI_OBJECT_STACK_LEVELS);
584 #endif /* GDI_DEBUG */
585 
586     return pobj;
587 }
588 
589 VOID
590 NTAPI
591 GDIOBJ_vFreeObject(POBJ pobj)
592 {
593     UCHAR objt;
594 
595     DBG_CLEANUP_EVENT_LIST(&pobj->slhLog);
596 
597     /* Get the object type */
598     objt = ((ULONG_PTR)pobj->hHmgr >> 16) & 0x1f;
599 
600     /* Check if we have a delete procedure (for C++ based objects) */
601     if (apfnDelete[objt] != NULL)
602     {
603         /* Invoke the delete procedure */
604         apfnDelete[objt](pobj);
605     }
606     else
607     {
608         /* Call the cleanup procedure */
609         NT_ASSERT(apfnCleanup[objt]);
610         apfnCleanup[objt](pobj);
611 
612         /* Check if the object is allocated from a lookaside list */
613         if (pobj->BaseFlags & BASEFLAG_LOOKASIDE)
614         {
615             ExFreeToPagedLookasideList(&gpaLookasideList[objt], pobj);
616         }
617         else
618         {
619             ExFreePoolWithTag(pobj, GDIOBJ_POOL_TAG(objt));
620         }
621     }
622 }
623 
624 VOID
625 NTAPI
626 GDIOBJ_vDereferenceObject(POBJ pobj)
627 {
628     ULONG cRefs, ulIndex;
629 
630     /* Calculate the index */
631     ulIndex = GDI_HANDLE_GET_INDEX(pobj->hHmgr);
632 
633     /* Check if the object has a handle */
634     if (ulIndex)
635     {
636         /* Decrement reference count */
637         if ((gpaulRefCount[ulIndex] & REF_MASK_COUNT) == 0)
638         {
639             DBG_DUMP_EVENT_LIST(&pobj->slhLog);
640         }
641         ASSERT((gpaulRefCount[ulIndex] & REF_MASK_COUNT) > 0);
642         cRefs = InterlockedDecrement((LONG*)&gpaulRefCount[ulIndex]);
643         DBG_LOGEVENT(&pobj->slhLog, EVENT_DEREFERENCE, cRefs);
644 
645         /* Check if we reached 0 and handle bit is not set */
646         if ((cRefs & REF_MASK_INUSE) == 0)
647         {
648             /* Make sure it's ok to delete the object */
649             ASSERT(pobj->BaseFlags & BASEFLAG_READY_TO_DIE);
650 
651             /* Check if the handle was process owned */
652             if (gpentHmgr[ulIndex].ObjectOwner.ulObj != GDI_OBJ_HMGR_PUBLIC &&
653                 gpentHmgr[ulIndex].ObjectOwner.ulObj != GDI_OBJ_HMGR_NONE)
654             {
655                 /* Decrement the process handle count */
656                 ASSERT(gpentHmgr[ulIndex].ObjectOwner.ulObj ==
657                        HandleToUlong(PsGetCurrentProcessId()));
658                 DecrementCurrentProcessGdiHandleCount();
659             }
660 
661             /* Push entry to the free list */
662             ENTRY_vPushFreeEntry(&gpentHmgr[ulIndex]);
663 
664             /* Free the object */
665             GDIOBJ_vFreeObject(pobj);
666         }
667     }
668     else
669     {
670         /* Decrement the objects reference count */
671         ASSERT(pobj->ulShareCount > 0);
672         cRefs = InterlockedDecrement((LONG*)&pobj->ulShareCount);
673         DBG_LOGEVENT(&pobj->slhLog, EVENT_DEREFERENCE, cRefs);
674 
675         /* Check if we reached 0 */
676         if (cRefs == 0)
677         {
678             /* Free the object */
679             GDIOBJ_vFreeObject(pobj);
680         }
681     }
682 }
683 
684 POBJ
685 NTAPI
686 GDIOBJ_ReferenceObjectByHandle(
687     HGDIOBJ hobj,
688     UCHAR objt)
689 {
690     PENTRY pentry;
691     POBJ pobj;
692 
693     /* Check if the handle type matches */
694     ASSERT_SHARED_OBJECT_TYPE(objt);
695     if ((((ULONG_PTR)hobj >> 16) & 0x1f) != objt)
696     {
697         DPRINT("GDIOBJ: Wrong type. handle=%p, type=%x\n", hobj, objt);
698         return NULL;
699     }
700 
701     /* Reference the handle entry */
702     pentry = ENTRY_ReferenceEntryByHandle(hobj, 0);
703     if (!pentry)
704     {
705         DPRINT("GDIOBJ: Requested handle 0x%p is not valid.\n", hobj);
706         return NULL;
707     }
708 
709     /* Get the pointer to the BASEOBJECT */
710     pobj = pentry->einfo.pobj;
711 
712     /* Check if the object is exclusively locked */
713     if (pobj->cExclusiveLock != 0)
714     {
715         DPRINT1("GDIOBJ: Cannot reference object %p with exclusive lock.\n", hobj);
716         GDIOBJ_vDereferenceObject(pobj);
717         DBG_DUMP_EVENT_LIST(&pobj->slhLog);
718         return NULL;
719     }
720 
721     DBG_LOGEVENT(&pobj->slhLog, EVENT_REFERENCE, gpaulRefCount[pentry - gpentHmgr]);
722 
723     /* All is well, return the object */
724     return pobj;
725 }
726 
727 VOID
728 NTAPI
729 GDIOBJ_vReferenceObjectByPointer(POBJ pobj)
730 {
731     ULONG cRefs;
732 
733     /* Check if the object has a handle */
734     if (GDI_HANDLE_GET_INDEX(pobj->hHmgr))
735     {
736         /* Increase the handle's reference count */
737         ULONG ulIndex = GDI_HANDLE_GET_INDEX(pobj->hHmgr);
738         ASSERT((gpaulRefCount[ulIndex] & REF_MASK_COUNT) > 0);
739         cRefs = InterlockedIncrement((LONG*)&gpaulRefCount[ulIndex]);
740     }
741     else
742     {
743         /* Increase the object's reference count */
744         cRefs = InterlockedIncrement((LONG*)&pobj->ulShareCount);
745     }
746 
747     DBG_LOGEVENT(&pobj->slhLog, EVENT_REFERENCE, cRefs);
748 }
749 
750 PGDIOBJ
751 NTAPI
752 GDIOBJ_TryLockObject(
753     HGDIOBJ hobj,
754     UCHAR objt)
755 {
756     PENTRY pentry;
757     POBJ pobj;
758     DWORD dwThreadId;
759 
760     /* Check if the handle type matches */
761     ASSERT_TRYLOCK_OBJECT_TYPE(objt);
762     if ((((ULONG_PTR)hobj >> 16) & 0x1f) != objt)
763     {
764         DPRINT("Wrong object type: hobj=0x%p, objt=0x%x\n", hobj, objt);
765         return NULL;
766     }
767 
768     /* Make sure lock order is correct */
769     ASSERT_LOCK_ORDER(objt);
770 
771     /* Reference the handle entry */
772     pentry = ENTRY_ReferenceEntryByHandle(hobj, 0);
773     if (!pentry)
774     {
775         DPRINT("GDIOBJ: Requested handle 0x%p is not valid.\n", hobj);
776         return NULL;
777     }
778 
779     /* Get the pointer to the BASEOBJECT */
780     pobj = pentry->einfo.pobj;
781 
782     /* Check if we already own the lock */
783     dwThreadId = PtrToUlong(PsGetCurrentThreadId());
784     if (pobj->dwThreadId != dwThreadId)
785     {
786         /* Disable APCs and try acquiring the push lock */
787         KeEnterCriticalRegion();
788         if(!ExTryAcquirePushLockExclusive(&pobj->pushlock))
789         {
790             ULONG cRefs, ulIndex;
791             /* Already owned. Clean up and leave. */
792             KeLeaveCriticalRegion();
793 
794             /* Calculate the index */
795             ulIndex = GDI_HANDLE_GET_INDEX(pobj->hHmgr);
796 
797             /* Decrement reference count */
798             ASSERT((gpaulRefCount[ulIndex] & REF_MASK_COUNT) > 0);
799             cRefs = InterlockedDecrement((LONG*)&gpaulRefCount[ulIndex]);
800             ASSERT(cRefs & REF_MASK_VALID);
801 
802             return NULL;
803         }
804 
805         /* Set us as lock owner */
806         ASSERT(pobj->dwThreadId == 0);
807         pobj->dwThreadId = dwThreadId;
808     }
809 
810     /* Increase lock count */
811     pobj->cExclusiveLock++;
812     INCREASE_THREAD_LOCK_COUNT(hobj);
813     DBG_LOGEVENT(&pobj->slhLog, EVENT_LOCK, 0);
814 
815     /* Return the object */
816     return pobj;
817 }
818 
819 PGDIOBJ
820 NTAPI
821 GDIOBJ_LockObject(
822     HGDIOBJ hobj,
823     UCHAR objt)
824 {
825     PENTRY pentry;
826     POBJ pobj;
827     DWORD dwThreadId;
828 
829     /* Check if the handle type matches */
830     ASSERT_EXCLUSIVE_OBJECT_TYPE(objt);
831     if ((((ULONG_PTR)hobj >> 16) & 0x1f) != objt)
832     {
833         DPRINT("Wrong object type: hobj=0x%p, objt=0x%x\n", hobj, objt);
834         return NULL;
835     }
836 
837     /* Make sure lock order is correct */
838     ASSERT_LOCK_ORDER(objt);
839 
840     /* Reference the handle entry */
841     pentry = ENTRY_ReferenceEntryByHandle(hobj, 0);
842     if (!pentry)
843     {
844         DPRINT("GDIOBJ: Requested handle 0x%p is not valid.\n", hobj);
845         return NULL;
846     }
847 
848     /* Get the pointer to the BASEOBJECT */
849     pobj = pentry->einfo.pobj;
850 
851     /* Check if we already own the lock */
852     dwThreadId = PtrToUlong(PsGetCurrentThreadId());
853     if (pobj->dwThreadId != dwThreadId)
854     {
855         /* Disable APCs and acquire the push lock */
856         KeEnterCriticalRegion();
857         ExAcquirePushLockExclusive(&pobj->pushlock);
858 
859         /* Set us as lock owner */
860         ASSERT(pobj->dwThreadId == 0);
861         pobj->dwThreadId = dwThreadId;
862     }
863 
864     /* Increase lock count */
865     pobj->cExclusiveLock++;
866     INCREASE_THREAD_LOCK_COUNT(hobj);
867     DBG_LOGEVENT(&pobj->slhLog, EVENT_LOCK, 0);
868 
869     /* Return the object */
870     return pobj;
871 }
872 
873 VOID
874 NTAPI
875 GDIOBJ_vUnlockObject(POBJ pobj)
876 {
877     ULONG cRefs, ulIndex;
878     ASSERT(pobj->cExclusiveLock > 0);
879 
880     /* Decrease lock count */
881     pobj->cExclusiveLock--;
882     DECREASE_THREAD_LOCK_COUNT(pobj->hHmgr);
883     DBG_LOGEVENT(&pobj->slhLog, EVENT_UNLOCK, 0);
884 
885     /* Check if this was the last lock */
886     if (pobj->cExclusiveLock == 0)
887     {
888         /* Reset lock owner */
889         pobj->dwThreadId = 0;
890 
891         /* Release the pushlock and reenable APCs */
892         ExReleasePushLockExclusive(&pobj->pushlock);
893         KeLeaveCriticalRegion();
894     }
895 
896     /* Calculate the index */
897     ulIndex = GDI_HANDLE_GET_INDEX(pobj->hHmgr);
898 
899     /* Decrement reference count */
900     ASSERT((gpaulRefCount[ulIndex] & REF_MASK_COUNT) > 0);
901     cRefs = InterlockedDecrement((LONG*)&gpaulRefCount[ulIndex]);
902     ASSERT(cRefs & REF_MASK_VALID);
903 }
904 
905 HGDIOBJ
906 NTAPI
907 GDIOBJ_hInsertObject(
908     POBJ pobj,
909     ULONG ulOwner)
910 {
911     PENTRY pentry;
912     UCHAR objt;
913 
914     /* Must have no handle and only one reference */
915     ASSERT(GDI_HANDLE_GET_INDEX(pobj->hHmgr) == 0);
916     ASSERT(pobj->cExclusiveLock == 0);
917     ASSERT(pobj->ulShareCount == 1);
918 
919     /* Get a free handle entry */
920     pentry = ENTRY_pentPopFreeEntry();
921     if (!pentry)
922     {
923         DPRINT1("GDIOBJ: Could not get a free entry.\n");
924         return NULL;
925     }
926 
927     /* Make the object exclusively locked */
928     ExInitializePushLock(&pobj->pushlock);
929     KeEnterCriticalRegion();
930     ExAcquirePushLockExclusive(&pobj->pushlock);
931     pobj->cExclusiveLock = 1;
932     pobj->dwThreadId = PtrToUlong(PsGetCurrentThreadId());
933     INCREASE_THREAD_LOCK_COUNT(pobj->hHmgr);
934 
935     /* Get object type from the hHmgr field */
936     objt = ((ULONG_PTR)pobj->hHmgr >> 16) & 0xff;
937     ASSERT(objt != GDIObjType_DEF_TYPE);
938 
939     /* Check if current process is requested owner */
940     if (ulOwner == GDI_OBJ_HMGR_POWNED)
941     {
942         /* Increment the process handle count */
943         IncrementCurrentProcessGdiHandleCount();
944 
945         /* Use Process id */
946         ulOwner = HandleToUlong(PsGetCurrentProcessId());
947     }
948 
949     /* Insert the object into the handle table */
950     pobj->hHmgr = ENTRY_hInsertObject(pentry, pobj, objt, ulOwner);
951 
952     /* Return the handle */
953     DPRINT("GDIOBJ: Created handle: %p\n", pobj->hHmgr);
954     DBG_LOGEVENT(&pobj->slhLog, EVENT_CREATE_HANDLE, 0);
955     return pobj->hHmgr;
956 }
957 
958 VOID
959 NTAPI
960 GDIOBJ_vSetObjectOwner(
961     POBJ pobj,
962     ULONG ulNewOwner)
963 {
964     PENTRY pentry;
965     ULONG ulOldOwner;
966 
967     /* This is a ugly HACK, needed to fix IntGdiSetDCOwnerEx */
968     if (GDI_HANDLE_IS_STOCKOBJ(pobj->hHmgr))
969     {
970         DPRINT("Trying to set ownership of stock object %p to %lx\n", pobj->hHmgr, ulNewOwner);
971         return;
972     }
973 
974     /* Get the handle entry */
975     NT_ASSERT(GDI_HANDLE_GET_INDEX(pobj->hHmgr));
976     pentry = &gpentHmgr[GDI_HANDLE_GET_INDEX(pobj->hHmgr)];
977 
978     /* Check if the new owner is the same as the old one */
979     ulOldOwner = pentry->ObjectOwner.ulObj;
980     if (ulOldOwner == ulNewOwner)
981     {
982         /* Nothing to do */
983         return;
984     }
985 
986     /* Is the current process requested? */
987     if (ulNewOwner == GDI_OBJ_HMGR_POWNED)
988     {
989         /* Use process id */
990         ulNewOwner = HandleToUlong(PsGetCurrentProcessId());
991     }
992 
993     // HACK
994     if (ulNewOwner == GDI_OBJ_HMGR_NONE)
995         ulNewOwner = GDI_OBJ_HMGR_PUBLIC;
996 
997     /* Was the object process owned? */
998     if ((ulOldOwner != GDI_OBJ_HMGR_PUBLIC) &&
999         (ulOldOwner != GDI_OBJ_HMGR_NONE))
1000     {
1001         /* Decrement the previous owners handle count */
1002         DecrementGdiHandleCount(ulOldOwner);
1003     }
1004 
1005     /* Is the new owner a process? */
1006     if ((ulNewOwner != GDI_OBJ_HMGR_PUBLIC) &&
1007         (ulNewOwner != GDI_OBJ_HMGR_NONE))
1008     {
1009         /* Increment the new owners handle count */
1010         IncrementGdiHandleCount(ulNewOwner);
1011     }
1012     else
1013     {
1014         /* Make sure we don't leak user mode memory */
1015         NT_ASSERT(pentry->pUser == NULL);
1016     }
1017 
1018     /* Set new owner */
1019     pentry->ObjectOwner.ulObj = ulNewOwner;
1020     DBG_LOGEVENT(&pobj->slhLog, EVENT_SET_OWNER, 0);
1021 }
1022 
1023 /* Locks 2 or 3 objects at a time */
1024 BOOL
1025 NTAPI
1026 GDIOBJ_bLockMultipleObjects(
1027     IN ULONG ulCount,
1028     IN HGDIOBJ* ahObj,
1029     OUT PGDIOBJ* apObj,
1030     IN UCHAR objt)
1031 {
1032     UINT auiIndices[3] = {0, 1, 2};
1033     UINT i, j, tmp;
1034 
1035     ASSERT(ulCount <= 3);
1036 
1037     /* Sort the handles */
1038     for (i = 0; i < ulCount - 1; i++)
1039     {
1040         for (j = i + 1; j < ulCount; j++)
1041         {
1042             if ((ULONG_PTR)ahObj[auiIndices[i]] <
1043                 (ULONG_PTR)ahObj[auiIndices[j]])
1044             {
1045                 tmp = auiIndices[i];
1046                 auiIndices[i] = auiIndices[j];
1047                 auiIndices[j] = tmp;
1048             }
1049         }
1050     }
1051 
1052     /* Lock the objects in safe order */
1053     for (i = 0; i < ulCount; i++)
1054     {
1055         /* Skip NULL handles */
1056         if (ahObj[auiIndices[i]] == NULL)
1057         {
1058             apObj[auiIndices[i]] = NULL;
1059             continue;
1060         }
1061 
1062         /* Lock the object */
1063         apObj[auiIndices[i]] = GDIOBJ_LockObject(ahObj[auiIndices[i]], objt);
1064 
1065         /* Check for failure */
1066         if (apObj[auiIndices[i]] == NULL)
1067         {
1068             /* Cleanup */
1069             while (i--)
1070             {
1071                 if (apObj[auiIndices[i]])
1072                     GDIOBJ_vUnlockObject(apObj[auiIndices[i]]);
1073             }
1074             return FALSE;
1075         }
1076     }
1077 
1078     return TRUE;
1079 }
1080 
1081 PVOID
1082 NTAPI
1083 GDIOBJ_pvGetObjectAttr(POBJ pobj)
1084 {
1085     ULONG ulIndex = GDI_HANDLE_GET_INDEX(pobj->hHmgr);
1086     return gpentHmgr[ulIndex].pUser;
1087 }
1088 
1089 VOID
1090 NTAPI
1091 GDIOBJ_vSetObjectAttr(POBJ pobj, PVOID pvObjAttr)
1092 {
1093     ULONG ulIndex;
1094 
1095     ASSERT(pobj->hHmgr);
1096 
1097     /* Get the handle index */
1098     ulIndex = GDI_HANDLE_GET_INDEX(pobj->hHmgr);
1099 
1100     /* Set pointer to the usermode attribute */
1101     gpentHmgr[ulIndex].pUser = pvObjAttr;
1102 }
1103 
1104 VOID
1105 NTAPI
1106 GDIOBJ_vDeleteObject(POBJ pobj)
1107 {
1108     ULONG ulIndex;
1109 
1110     /* Set the object's delete flag */
1111     InterlockedOr16((SHORT*)&pobj->BaseFlags, BASEFLAG_READY_TO_DIE);
1112     DBG_LOGEVENT(&pobj->slhLog, EVENT_DELETE, 0);
1113 
1114     /* Get the handle index */
1115     ulIndex = GDI_HANDLE_GET_INDEX(pobj->hHmgr);
1116     if (ulIndex)
1117     {
1118         /* Reset the handle valid bit */
1119         InterlockedAnd((LONG*)&gpaulRefCount[ulIndex], ~REF_MASK_VALID);
1120 
1121         /* Check if the object is exclusively locked */
1122         if (pobj->cExclusiveLock != 0)
1123         {
1124             /* Reset lock owner and lock count */
1125             pobj->dwThreadId = 0;
1126             pobj->cExclusiveLock = 0;
1127 
1128             /* Release the pushlock and reenable APCs */
1129             ExReleasePushLockExclusive(&pobj->pushlock);
1130             KeLeaveCriticalRegion();
1131             DECREASE_THREAD_LOCK_COUNT(pobj->hHmgr);
1132         }
1133     }
1134 
1135     /* Dereference the object (will take care of deletion) */
1136     GDIOBJ_vDereferenceObject(pobj);
1137 }
1138 
1139 BOOL
1140 NTAPI
1141 GreIsHandleValid(HGDIOBJ hobj)
1142 {
1143     PENTRY pentry;
1144 
1145     pentry = ENTRY_ReferenceEntryByHandle(hobj, 0);
1146     if (!pentry) return FALSE;
1147     GDIOBJ_vDereferenceObject(pentry->einfo.pobj);
1148     return TRUE;
1149 }
1150 
1151 BOOL
1152 NTAPI
1153 GreDeleteObject(HGDIOBJ hobj)
1154 {
1155     PENTRY pentry;
1156 
1157     /* Check for stock objects */
1158     if (GDI_HANDLE_IS_STOCKOBJ(hobj))
1159     {
1160         DPRINT1("GreDeleteObject: Cannot delete stock object %p.\n", hobj);
1161         return FALSE;
1162     }
1163 
1164     /* Reference the handle entry */
1165     pentry = ENTRY_ReferenceEntryByHandle(hobj, 0);
1166     if (!pentry)
1167     {
1168         DPRINT1("GreDeleteObject: Trying to delete invalid object %p\n", hobj);
1169         return FALSE;
1170     }
1171 
1172     /* Check for public owner */
1173     if (pentry->ObjectOwner.ulObj == GDI_OBJ_HMGR_PUBLIC)
1174     {
1175         DPRINT1("GreDeleteObject: Trying to delete global object %p\n", hobj);
1176         GDIOBJ_vDereferenceObject(pentry->einfo.pobj);
1177         return FALSE;
1178     }
1179 
1180     /* Delete the object */
1181     GDIOBJ_vDeleteObject(pentry->einfo.pobj);
1182     return TRUE;
1183 }
1184 
1185 ULONG
1186 NTAPI
1187 GreGetObjectOwner(HGDIOBJ hobj)
1188 {
1189     ULONG ulIndex, ulOwner;
1190 
1191     /* Get the handle index */
1192     ulIndex = GDI_HANDLE_GET_INDEX(hobj);
1193 
1194     /* Check if the handle is valid */
1195     if (ulIndex >= GDI_HANDLE_COUNT ||
1196         gpentHmgr[ulIndex].Objt == GDIObjType_DEF_TYPE ||
1197         ((ULONG_PTR)hobj >> 16) != gpentHmgr[ulIndex].FullUnique)
1198     {
1199         DPRINT1("GreGetObjectOwner: invalid handle 0x%p.\n", hobj);
1200         return GDI_OBJ_HMGR_RESTRICTED;
1201     }
1202 
1203     /* Get the object owner */
1204     ulOwner = gpentHmgr[ulIndex].ObjectOwner.ulObj;
1205 
1206     if (ulOwner == HandleToUlong(PsGetCurrentProcessId()))
1207         return GDI_OBJ_HMGR_POWNED;
1208 
1209     if (ulOwner == GDI_OBJ_HMGR_PUBLIC)
1210         return GDI_OBJ_HMGR_PUBLIC;
1211 
1212     return GDI_OBJ_HMGR_RESTRICTED;
1213 }
1214 
1215 BOOL
1216 NTAPI
1217 GreSetObjectOwnerEx(
1218     HGDIOBJ hobj,
1219     ULONG ulOwner,
1220     ULONG Flags)
1221 {
1222     PENTRY pentry;
1223 
1224     /* Check for stock objects */
1225     if (GDI_HANDLE_IS_STOCKOBJ(hobj))
1226     {
1227         DPRINT("GreSetObjectOwner: Got stock object %p\n", hobj);
1228         return FALSE;
1229     }
1230 
1231     /* Reference the handle entry */
1232     pentry = ENTRY_ReferenceEntryByHandle(hobj, Flags);
1233     if (!pentry)
1234     {
1235         DPRINT("GreSetObjectOwner: Invalid handle 0x%p.\n", hobj);
1236         return FALSE;
1237     }
1238 
1239     /* Call internal function */
1240     GDIOBJ_vSetObjectOwner(pentry->einfo.pobj, ulOwner);
1241 
1242     /* Dereference the object */
1243     GDIOBJ_vDereferenceObject(pentry->einfo.pobj);
1244 
1245     return TRUE;
1246 }
1247 
1248 BOOL
1249 NTAPI
1250 GreSetObjectOwner(
1251     HGDIOBJ hobj,
1252     ULONG ulOwner)
1253 {
1254     return GreSetObjectOwnerEx(hobj, ulOwner, 0);
1255 }
1256 
1257 INT
1258 NTAPI
1259 GreGetObject(
1260     IN HGDIOBJ hobj,
1261     IN INT cbCount,
1262     IN PVOID pvBuffer)
1263 {
1264     PVOID pvObj;
1265     UCHAR objt;
1266     INT iResult = 0;
1267 
1268     /* Verify object type */
1269     objt = ((ULONG_PTR)hobj >> 16) & 0x1f;
1270     if (objt != GDIObjType_BRUSH_TYPE &&
1271         objt != GDIObjType_SURF_TYPE &&
1272         objt != GDIObjType_LFONT_TYPE &&
1273         objt != GDIObjType_PAL_TYPE)
1274     {
1275         DPRINT1("GreGetObject: Invalid object type\n");
1276         return 0;
1277     }
1278 
1279     pvObj = GDIOBJ_ReferenceObjectByHandle(hobj, objt);
1280     if (!pvObj)
1281     {
1282         DPRINT("GreGetObject: Could not lock object\n");
1283         return 0;
1284     }
1285 
1286     switch (GDI_HANDLE_GET_TYPE(hobj))
1287     {
1288         case GDILoObjType_LO_PEN_TYPE:
1289         case GDILoObjType_LO_EXTPEN_TYPE:
1290             iResult = PEN_GetObject(pvObj, cbCount, pvBuffer);
1291             break;
1292 
1293         case GDILoObjType_LO_BRUSH_TYPE:
1294             iResult = BRUSH_GetObject(pvObj, cbCount, pvBuffer);
1295             break;
1296 
1297         case GDILoObjType_LO_BITMAP_TYPE:
1298             iResult = BITMAP_GetObject(pvObj, cbCount, pvBuffer);
1299             break;
1300 
1301         case GDILoObjType_LO_FONT_TYPE:
1302             iResult = FontGetObject(pvObj, cbCount, pvBuffer);
1303             break;
1304 
1305         case GDILoObjType_LO_PALETTE_TYPE:
1306             iResult = PALETTE_GetObject(pvObj, cbCount, pvBuffer);
1307             break;
1308 
1309         default:
1310             DPRINT1("GDI object type of 0x%p not implemented\n", hobj);
1311             break;
1312     }
1313 
1314     GDIOBJ_vDereferenceObject(pvObj);
1315     return iResult;
1316 }
1317 
1318 W32KAPI
1319 INT
1320 APIENTRY
1321 NtGdiExtGetObjectW(
1322     IN HANDLE hobj,
1323     IN INT cjBufferSize,
1324     OUT LPVOID lpBuffer)
1325 {
1326     UINT iResult, cjMaxSize;
1327     union
1328     {
1329         BITMAP bitmap;
1330         DIBSECTION dibsection;
1331         LOGPEN logpen;
1332         LOGBRUSH logbrush;
1333         LOGFONTW logfontw;
1334         EXTLOGFONTW extlogfontw;
1335         ENUMLOGFONTEXDVW enumlogfontexdvw;
1336     } object;
1337 
1338     /* Normalize to the largest supported object size */
1339     cjMaxSize = min((UINT)cjBufferSize, sizeof(object));
1340 
1341     /* Now do the actual call */
1342     iResult = GreGetObject(hobj, cjMaxSize, lpBuffer ? &object : NULL);
1343 
1344     /* Check if we have a buffer and data */
1345     if ((lpBuffer != NULL) && (iResult != 0))
1346     {
1347         /* Enter SEH for buffer transfer */
1348         _SEH2_TRY
1349         {
1350             /* Probe the buffer and copy it */
1351             cjMaxSize = min(cjMaxSize, iResult);
1352             ProbeForWrite(lpBuffer, cjMaxSize, sizeof(WORD));
1353             RtlCopyMemory(lpBuffer, &object, cjMaxSize);
1354         }
1355         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1356         {
1357             /* Clear the return value.
1358              * Do *NOT* set last error here! */
1359             iResult = 0;
1360         }
1361         _SEH2_END;
1362     }
1363 
1364     /* Return the count */
1365     return iResult;
1366 }
1367 
1368 W32KAPI
1369 HANDLE
1370 APIENTRY
1371 NtGdiCreateClientObj(
1372     IN ULONG ulType)
1373 {
1374     POBJ pObject;
1375     HANDLE handle;
1376 
1377     /* Check if ulType is valid */
1378     if ((ulType != GDILoObjType_LO_METAFILE16_TYPE) &&
1379         (ulType != GDILoObjType_LO_METAFILE_TYPE) &&
1380         (ulType != GDILoObjType_LO_METADC16_TYPE))
1381     {
1382         DPRINT1("NtGdiCreateClientObj: Invalid object type 0x%lx.\n", ulType);
1383         return NULL;
1384     }
1385 
1386     /* Allocate a new object */
1387     pObject = GDIOBJ_AllocateObject(GDIObjType_CLIENTOBJ_TYPE,
1388                                     sizeof(CLIENTOBJ),
1389                                     BASEFLAG_LOOKASIDE);
1390     if (!pObject)
1391     {
1392         DPRINT1("NtGdiCreateClientObj: Could not allocate a clientobj.\n");
1393         return NULL;
1394     }
1395 
1396     /* Set the real object type */
1397     pObject->hHmgr = UlongToHandle(ulType | GDILoObjType_LO_CLIENTOBJ_TYPE);
1398 
1399     /* Create a handle */
1400     handle = GDIOBJ_hInsertObject(pObject, GDI_OBJ_HMGR_POWNED);
1401     if (!handle)
1402     {
1403         DPRINT1("NtGdiCreateClientObj: Could not create a handle.\n");
1404         GDIOBJ_vFreeObject(pObject);
1405         return NULL;
1406     }
1407 
1408     /* Unlock it */
1409     GDIOBJ_vUnlockObject(pObject);
1410 
1411     return handle;
1412 }
1413 
1414 W32KAPI
1415 BOOL
1416 APIENTRY
1417 NtGdiDeleteClientObj(
1418     IN HANDLE hobj)
1419 {
1420     /* We first need to get the real type from the handle */
1421     ULONG ulType = GDI_HANDLE_GET_TYPE(hobj);
1422 
1423     /* Check if it's really a CLIENTOBJ */
1424     if ((ulType & GDI_HANDLE_BASETYPE_MASK) != GDILoObjType_LO_CLIENTOBJ_TYPE)
1425     {
1426         /* FIXME: SetLastError? */
1427         return FALSE;
1428     }
1429 
1430     return GreDeleteObject(hobj);
1431 }
1432 
1433 
1434 
1435 PGDI_HANDLE_TABLE GdiHandleTable = NULL;
1436 
1437 PGDIOBJ NTAPI
1438 GDIOBJ_ShareLockObj(HGDIOBJ hObj, DWORD ExpectedType)
1439 {
1440     if (ExpectedType == GDI_OBJECT_TYPE_DONTCARE)
1441         ExpectedType = GDI_HANDLE_GET_TYPE(hObj);
1442     return GDIOBJ_ReferenceObjectByHandle(hObj, (ExpectedType >> 16) & 0x1f);
1443 }
1444 
1445 // This function is not safe to use with concurrent deleting attempts
1446 // That shouldn't be a problem, since we don't have any processes yet,
1447 // that could delete the handle
1448 BOOL
1449 NTAPI
1450 GDIOBJ_ConvertToStockObj(HGDIOBJ *phObj)
1451 {
1452     PENTRY pentry;
1453     POBJ pobj;
1454 
1455     /* Reference the handle entry */
1456     pentry = ENTRY_ReferenceEntryByHandle(*phObj, 0);
1457     if (!pentry)
1458     {
1459         DPRINT1("GDIOBJ: Requested handle 0x%p is not valid.\n", *phObj);
1460         return FALSE;
1461     }
1462 
1463     /* Update the entry */
1464     pentry->FullUnique |= GDI_ENTRY_STOCK_MASK;
1465     pentry->ObjectOwner.ulObj = 0;
1466 
1467     /* Get the pointer to the BASEOBJECT */
1468     pobj = pentry->einfo.pobj;
1469 
1470     /* Calculate the new handle */
1471     pobj->hHmgr = (HGDIOBJ)((ULONG_PTR)pobj->hHmgr | GDI_HANDLE_STOCK_MASK);
1472 
1473     /* Return the new handle */
1474     *phObj = pobj->hHmgr;
1475 
1476     /* Dereference the handle */
1477     GDIOBJ_vDereferenceObject(pobj);
1478 
1479     return TRUE;
1480 }
1481 
1482 POBJ NTAPI
1483 GDIOBJ_AllocObjWithHandle(ULONG ObjectType, ULONG cjSize)
1484 {
1485     POBJ pobj;
1486     FLONG fl = 0;
1487     UCHAR objt = (ObjectType >> 16) & 0xFF;
1488 
1489     if ((objt == GDIObjType_DC_TYPE && cjSize == sizeof(DC)) ||
1490         (objt == GDIObjType_PAL_TYPE && cjSize == sizeof(PALETTE)) ||
1491         (objt == GDIObjType_RGN_TYPE && cjSize == sizeof(REGION)) ||
1492         (objt == GDIObjType_SURF_TYPE && cjSize == sizeof(SURFACE)) ||
1493         (objt == GDIObjType_PATH_TYPE && cjSize == sizeof(PATH)))
1494     {
1495         fl |= BASEFLAG_LOOKASIDE;
1496     }
1497 
1498     pobj = GDIOBJ_AllocateObject(objt, cjSize, fl);
1499     if (!pobj)
1500     {
1501         return NULL;
1502     }
1503 
1504     if (!GDIOBJ_hInsertObject(pobj, GDI_OBJ_HMGR_POWNED))
1505     {
1506         GDIOBJ_vFreeObject(pobj);
1507         return NULL;
1508     }
1509     return pobj;
1510 }
1511 
1512 PVOID NTAPI
1513 GDI_MapHandleTable(PEPROCESS pProcess)
1514 {
1515     PVOID pvMappedView = NULL;
1516     NTSTATUS Status;
1517     LARGE_INTEGER liOffset;
1518     SIZE_T cjViewSize = sizeof(GDI_HANDLE_TABLE);
1519 
1520     liOffset.QuadPart = 0;
1521 
1522     ASSERT(gpvGdiHdlTblSection != NULL);
1523     ASSERT(pProcess != NULL);
1524 
1525     Status = MmMapViewOfSection(gpvGdiHdlTblSection,
1526                                 pProcess,
1527                                 &pvMappedView,
1528                                 0,
1529                                 0,
1530                                 &liOffset,
1531                                 &cjViewSize,
1532                                 ViewUnmap,
1533                                 SEC_NO_CHANGE,
1534                                 PAGE_READONLY);
1535 
1536     if (!NT_SUCCESS(Status))
1537         return NULL;
1538 
1539     return pvMappedView;
1540 }
1541 
1542 BOOL NTAPI
1543 GDI_CleanupForProcess(struct _EPROCESS *Process)
1544 {
1545     PENTRY pentry;
1546     ULONG ulIndex;
1547     DWORD dwProcessId;
1548     PPROCESSINFO ppi;
1549 
1550     DPRINT("CleanupForProcess prochandle %p Pid %p\n",
1551            Process, Process->UniqueProcessId);
1552 
1553     ASSERT(Process == PsGetCurrentProcess());
1554 
1555     /* Get the current process Id */
1556     dwProcessId = PtrToUlong(PsGetCurrentProcessId());
1557 
1558     /* Loop all handles in the handle table */
1559     for (ulIndex = RESERVE_ENTRIES_COUNT; ulIndex < gulFirstUnused; ulIndex++)
1560     {
1561         pentry = &gpentHmgr[ulIndex];
1562 
1563         /* Check if the object is owned by the process */
1564         if (pentry->ObjectOwner.ulObj == dwProcessId)
1565         {
1566             ASSERT(pentry->einfo.pobj->cExclusiveLock == 0);
1567 
1568             /* Reference the object and delete it */
1569             InterlockedIncrement((LONG*)&gpaulRefCount[ulIndex]);
1570             GDIOBJ_vDeleteObject(pentry->einfo.pobj);
1571         }
1572     }
1573 
1574 #if DBG
1575     DbgGdiHTIntegrityCheck();
1576 #endif
1577 
1578     ppi = PsGetCurrentProcessWin32Process();
1579     DPRINT("Completed cleanup for process %p\n", Process->UniqueProcessId);
1580     if (ppi->GDIHandleCount != 0)
1581     {
1582         DPRINT1("Leaking %d handles!\n", ppi->GDIHandleCount);
1583         ASSERT(FALSE);
1584     }
1585 
1586     /* Loop all handles in the handle table */
1587     for (ulIndex = RESERVE_ENTRIES_COUNT; ulIndex < gulFirstUnused; ulIndex++)
1588     {
1589         pentry = &gpentHmgr[ulIndex];
1590 
1591         /* Check if the object is owned by the process */
1592         if (pentry->ObjectOwner.ulObj == dwProcessId)
1593         {
1594             DPRINT1("Leaking object. Index=%lx, type=0x%x, refcount=%lx\n",
1595                     ulIndex, pentry->Objt, gpaulRefCount[ulIndex]);
1596             DBG_DUMP_EVENT_LIST(&pentry->einfo.pobj->slhLog);
1597             //DBG_CLEANUP_EVENT_LIST(&pentry->einfo.pobj->slhLog);
1598             ASSERT(FALSE);
1599         }
1600     }
1601 
1602     return TRUE;
1603 }
1604 
1605 /// HACK!
1606 PGDI_POOL
1607 GetBrushAttrPool(VOID)
1608 {
1609     PPROCESSINFO ppi;
1610 
1611     ppi = PsGetCurrentProcessWin32Process();
1612     NT_ASSERT(ppi != NULL);
1613 
1614     return ppi->pPoolBrushAttr;
1615 }
1616 
1617 /* EOF */
1618