[WIN32SS][FONT] Fix GetTextFace function and related (#829)
[reactos.git] / win32ss / gdi / ntgdi / gdiobj.c
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 */