2 * PROJECT: ReactOS win32 kernel mode subsystem
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: subsystems/win32/win32k/objects/gdiobj.c
5 * PURPOSE: General GDI object manipulation routines
9 /** INCLUDES ******************************************************************/
15 #define GDI_ENTRY_TO_INDEX(ht, e) \
16 (((ULONG_PTR)(e) - (ULONG_PTR)&((ht)->Entries[0])) / sizeof(GDI_TABLE_ENTRY))
17 #define GDI_HANDLE_GET_ENTRY(HandleTable, h) \
18 (&(HandleTable)->Entries[GDI_HANDLE_GET_INDEX((h))])
20 /* apparently the first 10 entries are never used in windows as they are empty */
21 #define RESERVE_ENTRIES_COUNT 10
23 #define BASE_OBJTYPE_COUNT 32
25 #define DelayExecution() \
26 DPRINT("%s:%i: Delay\n", __FILE__, __LINE__); \
27 KeDelayExecutionThread(KernelMode, FALSE, &ShortDelay)
30 BOOL INTERNAL_CALL
GDI_CleanupDummy(PVOID ObjectBody
);
32 /** GLOBALS *******************************************************************/
39 GDICLEANUPPROC CleanupProc
;
40 } OBJ_TYPE_INFO
, *POBJ_TYPE_INFO
;
43 OBJ_TYPE_INFO ObjTypeInfo
[BASE_OBJTYPE_COUNT
] =
45 {0, 0, 0, NULL
}, /* 00 reserved entry */
46 {1, sizeof(DC
), TAG_DC
, DC_Cleanup
}, /* 01 DC */
47 {1, 0, 0, NULL
}, /* 02 UNUSED1 */
48 {1, 0, 0, NULL
}, /* 03 UNUSED2 */
49 {1, sizeof(ROSRGNDATA
), TAG_REGION
, REGION_Cleanup
}, /* 04 RGN */
50 {1, sizeof(SURFACE
), TAG_SURFACE
, SURFACE_Cleanup
}, /* 05 SURFACE */
51 {1, sizeof(CLIENTOBJ
), TAG_CLIENTOBJ
, GDI_CleanupDummy
}, /* 06 CLIENTOBJ: METADC,... */
52 {1, sizeof(PATH
), TAG_PATH
, GDI_CleanupDummy
}, /* 07 PATH */
53 {1, sizeof(PALETTE
), TAG_PALETTE
, PALETTE_Cleanup
}, /* 08 PAL */
54 {1, sizeof(COLORSPACE
), TAG_ICMLCS
, GDI_CleanupDummy
}, /* 09 ICMLCS, */
55 {1, sizeof(TEXTOBJ
), TAG_LFONT
, GDI_CleanupDummy
}, /* 0a LFONT */
56 {0, 0, TAG_RFONT
, NULL
}, /* 0b RFONT, unused */
57 {0, 0, TAG_PFE
, NULL
}, /* 0c PFE, unused */
58 {0, 0, TAG_PFT
, NULL
}, /* 0d PFT, unused */
59 {0, sizeof(GDICLRXFORM
), TAG_ICMCXF
, GDI_CleanupDummy
}, /* 0e ICMCXF, */
60 {0, 0, TAG_SPRITE
, NULL
}, /* 0f SPRITE, unused */
61 {1, sizeof(BRUSH
), TAG_BRUSH
, BRUSH_Cleanup
}, /* 10 BRUSH, PEN, EXTPEN */
62 {0, 0, TAG_UMPD
, NULL
}, /* 11 UMPD, unused */
63 {0, 0, 0, NULL
}, /* 12 UNUSED4 */
64 {0, 0, TAG_SPACE
, NULL
}, /* 13 SPACE, unused */
65 {0, 0, 0, NULL
}, /* 14 UNUSED5 */
66 {0, 0, TAG_META
, NULL
}, /* 15 META, unused */
67 {0, 0, TAG_EFSTATE
, NULL
}, /* 16 EFSTATE, unused */
68 {0, 0, TAG_BMFD
, NULL
}, /* 17 BMFD, unused */
69 {0, 0, TAG_VTFD
, NULL
}, /* 18 VTFD, unused */
70 {0, 0, TAG_TTFD
, NULL
}, /* 19 TTFD, unused */
71 {0, 0, TAG_RC
, NULL
}, /* 1a RC, unused */
72 {0, 0, TAG_TEMP
, NULL
}, /* 1b TEMP, unused */
73 {0, sizeof(EDRIVEROBJ
), TAG_DRVOBJ
, DRIVEROBJ_Cleanup
},/* 1c DRVOBJ */
74 {0, 0, TAG_DCIOBJ
, NULL
}, /* 1d DCIOBJ, unused */
75 {0, 0, TAG_SPOOL
, NULL
}, /* 1e SPOOL, unused */
76 {0, 0, 0, NULL
}, /* 1f reserved entry */
79 static LARGE_INTEGER ShortDelay
;
80 PGDI_HANDLE_TABLE GdiHandleTable
= NULL
;
81 PSECTION_OBJECT GdiTableSection
= NULL
;
83 /** INTERNAL FUNCTIONS ********************************************************/
93 AllocTypeDataDump(INT TypeInfo
)
95 switch( TypeInfo
& GDI_HANDLE_TYPE_MASK
)
97 case GDILoObjType_LO_BRUSH_TYPE
:
100 case GDILoObjType_LO_DC_TYPE
:
103 case GDILoObjType_LO_BITMAP_TYPE
:
106 case GDILoObjType_LO_FONT_TYPE
:
109 case GDILoObjType_LO_REGION_TYPE
:
116 DeAllocTypeDataDump(INT TypeInfo
)
118 switch( TypeInfo
& GDI_HANDLE_TYPE_MASK
)
120 case GDILoObjType_LO_BRUSH_TYPE
:
123 case GDILoObjType_LO_DC_TYPE
:
126 case GDILoObjType_LO_BITMAP_TYPE
:
129 case GDILoObjType_LO_FONT_TYPE
:
132 case GDILoObjType_LO_REGION_TYPE
:
139 * Dummy GDI Cleanup Callback
143 GDI_CleanupDummy(PVOID ObjectBody
)
149 * Allocate GDI object table.
150 * \param Size - number of entries in the object table.
155 GDIOBJ_iAllocHandleTable(OUT PSECTION_OBJECT
*SectionObject
)
157 PGDI_HANDLE_TABLE HandleTable
= NULL
;
158 LARGE_INTEGER htSize
;
163 ASSERT(SectionObject
!= NULL
);
165 htSize
.QuadPart
= sizeof(GDI_HANDLE_TABLE
);
167 Status
= MmCreateSection((PVOID
*)SectionObject
,
175 if (!NT_SUCCESS(Status
))
178 /* FIXME - use MmMapViewInSessionSpace once available! */
179 Status
= MmMapViewInSystemSpace(*SectionObject
,
180 (PVOID
*)&HandleTable
,
182 if (!NT_SUCCESS(Status
))
184 ObDereferenceObject(*SectionObject
);
185 *SectionObject
= NULL
;
189 RtlZeroMemory(HandleTable
, sizeof(GDI_HANDLE_TABLE
));
191 HandleTable
->LookasideLists
= ExAllocatePoolWithTag(NonPagedPool
,
192 BASE_OBJTYPE_COUNT
* sizeof(PAGED_LOOKASIDE_LIST
),
194 if (HandleTable
->LookasideLists
== NULL
)
196 MmUnmapViewInSystemSpace(HandleTable
);
197 ObDereferenceObject(*SectionObject
);
198 *SectionObject
= NULL
;
202 for (ObjType
= 0; ObjType
< BASE_OBJTYPE_COUNT
; ObjType
++)
204 if (ObjTypeInfo
[ObjType
].bUseLookaside
)
206 ExInitializePagedLookasideList(HandleTable
->LookasideLists
+ ObjType
,
210 ObjTypeInfo
[ObjType
].ulBodySize
,
211 ObjTypeInfo
[ObjType
].Tag
,
216 ShortDelay
.QuadPart
= -5000LL; /* FIXME - 0.5 ms? */
218 HandleTable
->FirstFree
= 0;
219 HandleTable
->FirstUnused
= RESERVE_ENTRIES_COUNT
;
229 /* Create the GDI handle table */
230 GdiHandleTable
= GDIOBJ_iAllocHandleTable(&GdiTableSection
);
231 if (GdiHandleTable
== NULL
)
233 DPRINT1("Failed to initialize the GDI handle table.\n");
234 return STATUS_UNSUCCESSFUL
;
237 return STATUS_SUCCESS
;
242 LockErrorDebugOutput(HGDIOBJ hObj
, PGDI_TABLE_ENTRY Entry
, LPSTR Function
)
244 if ((Entry
->Type
& GDI_ENTRY_BASETYPE_MASK
) == 0)
246 DPRINT1("%s: Attempted to lock object 0x%x that is deleted!\n", Function
, hObj
);
247 GDIDBG_TRACEDELETER(hObj
);
249 else if (GDI_HANDLE_GET_REUSECNT(hObj
) != GDI_ENTRY_GET_REUSECNT(Entry
->Type
))
251 DPRINT1("%s: Attempted to lock object 0x%x, wrong reuse counter (Handle: 0x%x, Entry: 0x%x)\n",
252 Function
, hObj
, GDI_HANDLE_GET_REUSECNT(hObj
), GDI_ENTRY_GET_REUSECNT(Entry
->Type
));
254 else if (GDI_HANDLE_GET_TYPE(hObj
) != ((Entry
->Type
<< GDI_ENTRY_UPPER_SHIFT
) & GDI_HANDLE_TYPE_MASK
))
256 DPRINT1("%s: Attempted to lock object 0x%x, type mismatch (Handle: 0x%x, Entry: 0x%x)\n",
257 Function
, hObj
, GDI_HANDLE_GET_TYPE(hObj
), (Entry
->Type
<< GDI_ENTRY_UPPER_SHIFT
) & GDI_HANDLE_TYPE_MASK
);
261 DPRINT1("%s: Attempted to lock object 0x%x, something went wrong, typeinfo = 0x%x\n",
262 Function
, hObj
, Entry
->Type
);
264 GDIDBG_TRACECALLER();
269 InterlockedPopFreeEntry(VOID
)
271 ULONG iFirst
, iNext
, iPrev
;
272 PGDI_TABLE_ENTRY pEntry
;
274 DPRINT("Enter InterLockedPopFreeEntry\n");
278 /* Get the index and sequence number of the first free entry */
279 iFirst
= GdiHandleTable
->FirstFree
;
281 /* Check if we have a free entry */
282 if (!(iFirst
& GDI_HANDLE_INDEX_MASK
))
284 /* Increment FirstUnused and get the new index */
285 iFirst
= InterlockedIncrement((LONG
*)&GdiHandleTable
->FirstUnused
) - 1;
287 /* Check if we have unused entries left */
288 if (iFirst
>= GDI_HANDLE_COUNT
)
290 DPRINT1("No more gdi handles left!\n");
294 /* Return the old index */
298 /* Get a pointer to the first free entry */
299 pEntry
= GdiHandleTable
->Entries
+ (iFirst
& GDI_HANDLE_INDEX_MASK
);
301 /* Create a new value with an increased sequence number */
302 iNext
= (USHORT
)(ULONG_PTR
)pEntry
->KernelData
;
303 iNext
|= (iFirst
& ~GDI_HANDLE_INDEX_MASK
) + 0x10000;
305 /* Try to exchange the FirstFree value */
306 iPrev
= InterlockedCompareExchange((LONG
*)&GdiHandleTable
->FirstFree
,
310 while (iPrev
!= iFirst
);
312 /* Sanity check: is entry really free? */
313 ASSERT(((ULONG_PTR
)pEntry
->KernelData
& ~GDI_HANDLE_INDEX_MASK
) == 0);
315 return iFirst
& GDI_HANDLE_INDEX_MASK
;
318 /* Pushes an entry of the handle table to the free list,
319 The entry must be unlocked and the base type field must be 0 */
322 InterlockedPushFreeEntry(ULONG idxToFree
)
324 ULONG iToFree
, iFirst
, iPrev
;
325 PGDI_TABLE_ENTRY pFreeEntry
;
327 DPRINT("Enter InterlockedPushFreeEntry\n");
329 pFreeEntry
= GdiHandleTable
->Entries
+ idxToFree
;
330 ASSERT((pFreeEntry
->Type
& GDI_ENTRY_BASETYPE_MASK
) == 0);
331 ASSERT(pFreeEntry
->ProcessId
== 0);
332 pFreeEntry
->UserData
= NULL
; // FIXME
333 ASSERT(pFreeEntry
->UserData
== NULL
);
337 /* Get the current first free index and sequence number */
338 iFirst
= GdiHandleTable
->FirstFree
;
340 /* Set the KernelData member to the index of the first free entry */
341 pFreeEntry
->KernelData
= UlongToPtr(iFirst
& GDI_HANDLE_INDEX_MASK
);
343 /* Combine new index and increased sequence number in iToFree */
344 iToFree
= idxToFree
| ((iFirst
& ~GDI_HANDLE_INDEX_MASK
) + 0x10000);
346 /* Try to atomically update the first free entry */
347 iPrev
= InterlockedCompareExchange((LONG
*)&GdiHandleTable
->FirstFree
,
351 while (iPrev
!= iFirst
);
357 GDIOBJ_ValidateHandle(HGDIOBJ hObj
, ULONG ObjectType
)
359 PGDI_TABLE_ENTRY Entry
= GDI_HANDLE_GET_ENTRY(GdiHandleTable
, hObj
);
360 if ((((ULONG_PTR
)hObj
& GDI_HANDLE_TYPE_MASK
) == ObjectType
) &&
361 (Entry
->Type
<< GDI_ENTRY_UPPER_SHIFT
) == GDI_HANDLE_GET_UPPER(hObj
))
363 HANDLE pid
= (HANDLE
)((ULONG_PTR
)Entry
->ProcessId
& ~0x1);
364 if (pid
== NULL
|| pid
== PsGetCurrentProcessId())
373 GDIOBJ_AllocObj(UCHAR BaseType
)
377 ASSERT((BaseType
& ~GDIObjTypeTotal
) == 0);
378 // BaseType &= GDI_HANDLE_BASETYPE_MASK;
380 if (ObjTypeInfo
[BaseType
].bUseLookaside
)
382 PPAGED_LOOKASIDE_LIST LookasideList
;
384 LookasideList
= GdiHandleTable
->LookasideLists
+ BaseType
;
385 pObject
= ExAllocateFromPagedLookasideList(LookasideList
);
389 pObject
= ExAllocatePoolWithTag(PagedPool
,
390 ObjTypeInfo
[BaseType
].ulBodySize
,
391 ObjTypeInfo
[BaseType
].Tag
);
396 RtlZeroMemory(pObject
, ObjTypeInfo
[BaseType
].ulBodySize
);
404 * Allocate memory for GDI object and return handle to it.
406 * \param ObjectType - type of object \ref GDI object types
408 * \return Pointer to the allocated object, which is locked.
411 GDIOBJ_AllocObjWithHandle(ULONG ObjectType
)
413 PPROCESSINFO W32Process
;
414 POBJ newObject
= NULL
;
415 HANDLE CurrentProcessId
, LockedProcessId
;
418 PGDI_TABLE_ENTRY Entry
;
421 GDIDBG_INITLOOPTRACE();
423 W32Process
= PsGetCurrentProcessWin32Process();
424 /* HACK HACK HACK: simplest-possible quota implementation - don't allow a process
425 to take too many GDI objects, itself. */
426 if (W32Process
&& W32Process
->GDIHandleCount
>= 0x2710)
428 DPRINT1("Too many objects for process!!!\n");
429 DPRINT1("DC %d BRUSH %d BITMAP %d FONT %d RGN %d\n",tDC
,tBRUSH
,tBITMAP
,tFONT
,tRGN
);
430 GDIDBG_DUMPHANDLETABLE();
434 ASSERT(ObjectType
!= GDI_OBJECT_TYPE_DONTCARE
);
436 TypeIndex
= GDI_OBJECT_GET_TYPE_INDEX(ObjectType
);
438 newObject
= GDIOBJ_AllocObj(TypeIndex
);
441 DPRINT1("Not enough memory to allocate gdi object!\n");
445 CurrentProcessId
= PsGetCurrentProcessId();
446 LockedProcessId
= (HANDLE
)((ULONG_PTR
)CurrentProcessId
| 0x1);
448 // RtlZeroMemory(newObject, ObjTypeInfo[TypeIndex].ulBodySize);
450 /* On Windows the higher 16 bit of the type field don't contain the
451 full type from the handle, but the base type.
452 (type = BRSUH, PEN, EXTPEN, basetype = BRUSH) */
453 TypeInfo
= (ObjectType
& GDI_HANDLE_BASETYPE_MASK
) | (ObjectType
>> GDI_ENTRY_UPPER_SHIFT
);
455 Index
= InterlockedPopFreeEntry();
460 Entry
= &GdiHandleTable
->Entries
[Index
];
463 PrevProcId
= InterlockedCompareExchangePointer((PVOID
*)&Entry
->ProcessId
, LockedProcessId
, 0);
464 if (PrevProcId
== NULL
)
466 PTHREADINFO Thread
= (PTHREADINFO
)PsGetCurrentThreadWin32Thread();
469 Entry
->KernelData
= newObject
;
471 /* copy the reuse-counter */
472 TypeInfo
|= Entry
->Type
& GDI_ENTRY_REUSE_MASK
;
474 /* we found a free entry, no need to exchange this field atomically
475 since we're holding the lock */
476 Entry
->Type
= TypeInfo
;
478 /* Create a handle */
479 Handle
= (HGDIOBJ
)((Index
& 0xFFFF) | (TypeInfo
<< GDI_ENTRY_UPPER_SHIFT
));
481 /* Initialize BaseObject fields */
482 newObject
->hHmgr
= Handle
;
483 newObject
->ulShareCount
= 0;
484 newObject
->cExclusiveLock
= 1;
485 newObject
->Tid
= Thread
;
487 AllocTypeDataDump(TypeInfo
);
489 /* unlock the entry */
490 (void)InterlockedExchangePointer((PVOID
*)&Entry
->ProcessId
, CurrentProcessId
);
492 GDIDBG_CAPTUREALLOCATOR(Index
);
494 if (W32Process
!= NULL
)
496 InterlockedIncrement(&W32Process
->GDIHandleCount
);
499 DPRINT("GDIOBJ_AllocObj: 0x%x ob: 0x%x\n", Handle
, newObject
);
504 GDIDBG_TRACELOOP(Index
, PrevProcId
, CurrentProcessId
);
505 /* damn, someone is trying to lock the object even though it doesn't
506 even exist anymore, wait a little and try again!
507 FIXME - we shouldn't loop forever! Give up after some time! */
514 GDIOBJ_FreeObj(newObject
, TypeIndex
);
516 DPRINT1("Failed to insert gdi object into the handle table, no handles left!\n");
517 GDIDBG_DUMPHANDLETABLE();
524 GDIOBJ_FreeObj(POBJ pObject
, UCHAR BaseType
)
526 /* Object must not have a handle! */
527 ASSERT(pObject
->hHmgr
== NULL
);
529 if (ObjTypeInfo
[BaseType
].bUseLookaside
)
531 PPAGED_LOOKASIDE_LIST LookasideList
;
533 LookasideList
= GdiHandleTable
->LookasideLists
+ BaseType
;
534 ExFreeToPagedLookasideList(LookasideList
, pObject
);
543 * Free memory allocated for the GDI object. For each object type this function calls the
544 * appropriate cleanup routine.
546 * \param hObj - handle of the object to be deleted.
548 * \return Returns TRUE if succesful.
549 * \return Returns FALSE if the cleanup routine returned FALSE or the object doesn't belong
550 * to the calling process.
552 * \bug This function should return VOID and kill the object no matter what...
555 GDIOBJ_FreeObjByHandle(HGDIOBJ hObj
, DWORD ExpectedType
)
557 PGDI_TABLE_ENTRY Entry
;
558 HANDLE ProcessId
, LockedProcessId
, PrevProcId
;
559 ULONG HandleType
, HandleUpper
, TypeIndex
;
562 GDIDBG_INITLOOPTRACE();
564 DPRINT("GDIOBJ_FreeObj: hObj: 0x%08x\n", hObj
);
566 if (GDI_HANDLE_IS_STOCKOBJ(hObj
))
568 DPRINT1("GDIOBJ_FreeObj() failed, can't delete stock object handle: 0x%x !!!\n", hObj
);
569 GDIDBG_TRACECALLER();
573 ProcessId
= PsGetCurrentProcessId();
574 LockedProcessId
= (HANDLE
)((ULONG_PTR
)ProcessId
| 0x1);
576 Silent
= (ExpectedType
& GDI_OBJECT_TYPE_SILENT
);
577 ExpectedType
&= ~GDI_OBJECT_TYPE_SILENT
;
579 HandleType
= GDI_HANDLE_GET_TYPE(hObj
);
580 HandleUpper
= GDI_HANDLE_GET_UPPER(hObj
);
582 /* Check if we have the requested type */
583 if ( (ExpectedType
!= GDI_OBJECT_TYPE_DONTCARE
&&
584 HandleType
!= ExpectedType
) ||
587 DPRINT1("Attempted to free object 0x%x of wrong type (Handle: 0x%x, expected: 0x%x)\n",
588 hObj
, HandleType
, ExpectedType
);
589 GDIDBG_TRACECALLER();
593 Entry
= GDI_HANDLE_GET_ENTRY(GdiHandleTable
, hObj
);
596 /* lock the object, we must not delete global objects, so don't exchange the locking
597 process ID to zero when attempting to lock a global object... */
598 PrevProcId
= InterlockedCompareExchangePointer((PVOID
*)&Entry
->ProcessId
, LockedProcessId
, ProcessId
);
599 if (PrevProcId
== ProcessId
)
601 if ( (Entry
->KernelData
!= NULL
) &&
602 ((Entry
->Type
<< GDI_ENTRY_UPPER_SHIFT
) == HandleUpper
) &&
603 ((Entry
->Type
& GDI_ENTRY_BASETYPE_MASK
) == (HandleUpper
& GDI_ENTRY_BASETYPE_MASK
)) )
607 Object
= Entry
->KernelData
;
609 if ((Object
->cExclusiveLock
== 0 ||
610 Object
->Tid
== (PTHREADINFO
)PsGetCurrentThreadWin32Thread()) &&
611 Object
->ulShareCount
== 0)
614 PPROCESSINFO W32Process
= PsGetCurrentProcessWin32Process();
616 /* Clear the basetype field so when unlocking the handle it gets finally deleted and increment reuse counter */
617 Entry
->Type
= (Entry
->Type
+ GDI_ENTRY_REUSE_INC
) & ~GDI_ENTRY_BASETYPE_MASK
;
619 /* unlock the handle slot */
620 (void)InterlockedExchangePointer((PVOID
*)&Entry
->ProcessId
, NULL
);
622 /* push this entry to the free list */
623 InterlockedPushFreeEntry(GDI_ENTRY_TO_INDEX(GdiHandleTable
, Entry
));
625 Object
->hHmgr
= NULL
;
627 if (W32Process
!= NULL
)
629 InterlockedDecrement(&W32Process
->GDIHandleCount
);
632 /* call the cleanup routine. */
633 TypeIndex
= GDI_OBJECT_GET_TYPE_INDEX(HandleType
);
634 Ret
= ObjTypeInfo
[TypeIndex
].CleanupProc(Object
);
636 DeAllocTypeDataDump(HandleType
);
638 /* Now it's time to free the memory */
639 GDIOBJ_FreeObj(Object
, TypeIndex
);
641 GDIDBG_CAPTUREDELETER(hObj
);
644 else if (Object
->ulShareCount
!= 0)
647 PEPROCESS OldProcess
;
648 Object
->BaseFlags
|= BASEFLAG_READY_TO_DIE
;
649 DPRINT("Object %p, ulShareCount = %d\n", Object
->hHmgr
, Object
->ulShareCount
);
650 /* Set NULL owner. Do the work here to avoid race conditions */
651 Status
= PsLookupProcessByProcessId((HANDLE
)((ULONG_PTR
)PrevProcId
& ~0x1), &OldProcess
);
652 if (NT_SUCCESS(Status
))
654 PPROCESSINFO W32Process
= (PPROCESSINFO
)OldProcess
->Win32Process
;
655 if (W32Process
!= NULL
)
657 InterlockedDecrement(&W32Process
->GDIHandleCount
);
659 ObDereferenceObject(OldProcess
);
661 (void)InterlockedExchangePointer((PVOID
*)&Entry
->ProcessId
, NULL
);
662 /* Don't wait on shared locks */
668 * The object is currently locked by another thread, so freeing is forbidden!
670 DPRINT1("Object->cExclusiveLock = %d\n", Object
->cExclusiveLock
);
671 GDIDBG_TRACECALLER();
672 GDIDBG_TRACELOCKER(hObj
);
673 (void)InterlockedExchangePointer((PVOID
*)&Entry
->ProcessId
, PrevProcId
);
674 /* do not assert here for it will call again from dxg.sys it being call twice */
682 LockErrorDebugOutput(hObj
, Entry
, "GDIOBJ_FreeObj");
683 (void)InterlockedExchangePointer((PVOID
*)&Entry
->ProcessId
, PrevProcId
);
686 else if (PrevProcId
== LockedProcessId
)
688 GDIDBG_TRACELOOP(hObj
, PrevProcId
, ProcessId
);
690 /* the object is currently locked, wait some time and try again.
691 FIXME - we shouldn't loop forever! Give up after some time! */
700 if ((Entry
->Type
& GDI_ENTRY_BASETYPE_MASK
) == 0)
702 DPRINT1("Attempted to free gdi handle 0x%x that is already deleted!\n", hObj
);
704 else if (((ULONG_PTR
)PrevProcId
& ~0x1) == 0)
706 DPRINT1("Attempted to free global gdi handle 0x%x, caller needs to get ownership first!!!\n", hObj
);
710 DPRINT1("Attempted to free foreign handle: 0x%x Owner: 0x%x from Caller: 0x%x\n", hObj
, (ULONG_PTR
)PrevProcId
& ~0x1, (ULONG_PTR
)ProcessId
& ~0x1);
712 DPRINT1("Type = 0x%lx, KernelData = 0x%p, ProcessId = 0x%p\n", Entry
->Type
, Entry
->KernelData
, Entry
->ProcessId
);
713 GDIDBG_TRACECALLER();
714 GDIDBG_TRACEALLOCATOR(hObj
);
723 IsObjectDead(HGDIOBJ hObject
)
725 INT Index
= GDI_HANDLE_GET_INDEX(hObject
);
726 PGDI_TABLE_ENTRY Entry
= &GdiHandleTable
->Entries
[Index
];
727 // We check to see if the objects are knocking on deaths door.
728 if ((Entry
->Type
& GDI_ENTRY_BASETYPE_MASK
) != 0)
732 DPRINT1("Object 0x%x currently being destroyed!!!\n",hObject
);
733 return TRUE
; // return true and move on.
740 bPEBCacheHandle(HGDIOBJ Handle
, int oType
, PVOID pAttr
)
742 PGDIHANDLECACHE GdiHandleCache
;
745 int Offset
= 0, Number
;
748 GdiHandleCache
= (PGDIHANDLECACHE
)NtCurrentTeb()->ProcessEnvironmentBlock
->GdiHandleBuffer
;
757 Offset
= CACHE_BRUSH_ENTRIES
;
760 case hctRegionHandle
:
761 Offset
= CACHE_BRUSH_ENTRIES
+CACHE_PEN_ENTRIES
;
768 Lock
= InterlockedCompareExchangePointer( (PVOID
*)&GdiHandleCache
->ulLock
,
771 if (Lock
) return FALSE
;
775 Number
= GdiHandleCache
->ulNumHandles
[oType
];
777 hPtr
= GdiHandleCache
->Handle
+ Offset
;
779 if ( pAttr
&& oType
== hctRegionHandle
)
781 if ( Number
< CACHE_REGION_ENTRIES
)
783 ((PRGN_ATTR
)pAttr
)->AttrFlags
|= ATTR_CACHED
;
784 hPtr
[Number
] = Handle
;
785 GdiHandleCache
->ulNumHandles
[oType
]++;
786 DPRINT("Put Handle Count %d PEB 0x%x\n", GdiHandleCache
->ulNumHandles
[oType
], NtCurrentTeb()->ProcessEnvironmentBlock
);
791 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
797 (void)InterlockedExchangePointer((PVOID
*)&GdiHandleCache
->ulLock
, Lock
);
803 * \param hObject object handle
804 * \return if the function fails the returned value is FALSE.
808 GreDeleteObject(HGDIOBJ hObject
)
811 PGDI_TABLE_ENTRY Entry
;
815 DPRINT("NtGdiDeleteObject handle 0x%08x\n", hObject
);
816 if (!IsObjectDead(hObject
))
818 dwObjectType
= GDIOBJ_GetObjectType(hObject
);
820 Index
= GDI_HANDLE_GET_INDEX(hObject
);
821 Entry
= &GdiHandleTable
->Entries
[Index
];
822 pAttr
= Entry
->UserData
;
824 switch (dwObjectType
)
826 case GDI_OBJECT_TYPE_BRUSH
:
829 case GDI_OBJECT_TYPE_REGION
:
830 /* If pAttr NULL, the probability is high for System Region. */
832 bPEBCacheHandle(hObject
, hctRegionHandle
, pAttr
))
834 /* User space handle only! */
839 FreeObjectAttr(pAttr
);
840 Entry
->UserData
= NULL
;
844 case GDI_OBJECT_TYPE_DC
:
845 // DC_FreeDcAttr(hObject);
849 return NULL
!= hObject
850 ? GDIOBJ_FreeObjByHandle(hObject
, dwObjectType
) : FALSE
;
854 DPRINT1("Attempt DeleteObject 0x%x currently being destroyed!!!\n",hObject
);
855 return TRUE
; // return true and move on.
861 IntDeleteHandlesForProcess(struct _EPROCESS
*Process
, ULONG ObjectType
)
863 PGDI_TABLE_ENTRY Entry
, End
;
864 ULONG Index
= RESERVE_ENTRIES_COUNT
;
866 PPROCESSINFO W32Process
;
868 W32Process
= (PPROCESSINFO
)Process
->Win32Process
;
871 if (W32Process
->GDIHandleCount
> 0)
873 ProcId
= Process
->UniqueProcessId
;
875 /* FIXME - Instead of building the handle here and delete it using GDIOBJ_FreeObj
876 we should delete it directly here! */
878 End
= &GdiHandleTable
->Entries
[GDI_HANDLE_COUNT
];
879 for (Entry
= &GdiHandleTable
->Entries
[RESERVE_ENTRIES_COUNT
];
883 /* ignore the lock bit */
884 if ( (HANDLE
)((ULONG_PTR
)Entry
->ProcessId
& ~0x1) == ProcId
)
886 if ( (Entry
->Type
& GDI_ENTRY_BASETYPE_MASK
) == ObjectType
||
887 ObjectType
== GDI_OBJECT_TYPE_DONTCARE
)
889 HGDIOBJ ObjectHandle
;
891 /* Create the object handle for the entry, the lower(!) 16 bit of the
892 Type field includes the type of the object including the stock
893 object flag - but since stock objects don't have a process id we can
894 simply ignore this fact here. */
895 ObjectHandle
= (HGDIOBJ
)(Index
| (Entry
->Type
<< GDI_ENTRY_UPPER_SHIFT
));
897 if (!GDIOBJ_FreeObjByHandle(ObjectHandle
, GDI_OBJECT_TYPE_DONTCARE
))
899 DPRINT1("Failed to delete object %p!\n", ObjectHandle
);
902 if (W32Process
->GDIHandleCount
== 0)
904 /* there are no more gdi handles for this process, bail */
915 * Internal function. Called when the process is destroyed to free the remaining GDI handles.
916 * \param Process - PID of the process that will be destroyed.
919 GDI_CleanupForProcess(struct _EPROCESS
*Process
)
921 PEPROCESS CurrentProcess
;
922 PPROCESSINFO W32Process
;
924 DPRINT("Starting CleanupForProcess prochandle %x Pid %d\n", Process
, Process
->UniqueProcessId
);
925 CurrentProcess
= PsGetCurrentProcess();
926 if (CurrentProcess
!= Process
)
928 KeAttachProcess(&Process
->Pcb
);
931 W32Process
= (PPROCESSINFO
)CurrentProcess
->Win32Process
;
933 /* Delete objects. Begin with types that are not referenced by other types */
934 IntDeleteHandlesForProcess(Process
, GDILoObjType_LO_DC_TYPE
);
935 IntDeleteHandlesForProcess(Process
, GDILoObjType_LO_BRUSH_TYPE
);
936 IntDeleteHandlesForProcess(Process
, GDILoObjType_LO_BITMAP_TYPE
);
938 /* Finally finish with what's left */
939 IntDeleteHandlesForProcess(Process
, GDI_OBJECT_TYPE_DONTCARE
);
941 if (CurrentProcess
!= Process
)
947 GdiDbgHTIntegrityCheck();
950 DPRINT("Completed cleanup for process %d\n", Process
->UniqueProcessId
);
951 if (W32Process
->GDIHandleCount
> 0)
953 DPRINT1("Leaking %d handles!\n", W32Process
->GDIHandleCount
);
960 * Return pointer to the object by handle.
962 * \param hObj Object handle
963 * \return Pointer to the object.
965 * \note Process can only get pointer to the objects it created or global objects.
967 * \todo Get rid of the ExpectedType parameter!
969 PGDIOBJ INTERNAL_CALL
970 GDIOBJ_LockObj(HGDIOBJ hObj
, DWORD ExpectedType
)
973 PGDI_TABLE_ENTRY Entry
;
974 HANDLE ProcessId
, HandleProcessId
, LockedProcessId
, PrevProcId
;
976 ULONG HandleType
, HandleUpper
;
978 /* Check for dummy call */
982 GDIDBG_INITLOOPTRACE();
984 HandleIndex
= GDI_HANDLE_GET_INDEX(hObj
);
985 HandleType
= GDI_HANDLE_GET_TYPE(hObj
);
986 HandleUpper
= GDI_HANDLE_GET_UPPER(hObj
);
988 /* Check that the handle index is valid. */
989 if (HandleIndex
>= GDI_HANDLE_COUNT
)
992 Entry
= &GdiHandleTable
->Entries
[HandleIndex
];
994 /* Check if we have the requested type */
995 if ( (ExpectedType
!= GDI_OBJECT_TYPE_DONTCARE
&&
996 HandleType
!= ExpectedType
) ||
999 DPRINT("Attempted to lock object 0x%x of wrong type (Handle: 0x%x, requested: 0x%x)\n",
1000 hObj
, HandleType
, ExpectedType
);
1001 GDIDBG_TRACECALLER();
1002 GDIDBG_TRACEALLOCATOR(hObj
);
1003 GDIDBG_TRACEDELETER(hObj
);
1007 ProcessId
= (HANDLE
)((ULONG_PTR
)PsGetCurrentProcessId() & ~1);
1008 HandleProcessId
= (HANDLE
)((ULONG_PTR
)Entry
->ProcessId
& ~1);
1010 /* Check for invalid owner. */
1011 if (ProcessId
!= HandleProcessId
&& HandleProcessId
!= NULL
)
1013 DPRINT1("Tried to lock object (0x%p) of wrong owner! ProcessId = %p, HandleProcessId = %p\n", hObj
, ProcessId
, HandleProcessId
);
1014 GDIDBG_TRACECALLER();
1015 GDIDBG_TRACEALLOCATOR(hObj
);
1020 * Prevent the thread from being terminated during the locking process.
1021 * It would result in undesired effects and inconsistency of the global
1025 KeEnterCriticalRegion();
1028 * Loop until we either successfully lock the handle entry & object or
1029 * fail some of the check.
1034 /* Lock the handle table entry. */
1035 LockedProcessId
= (HANDLE
)((ULONG_PTR
)HandleProcessId
| 0x1);
1036 PrevProcId
= InterlockedCompareExchangePointer((PVOID
*)&Entry
->ProcessId
,
1040 if (PrevProcId
== HandleProcessId
)
1043 * We're locking an object that belongs to our process or it's a
1044 * global object if HandleProcessId is 0 here.
1047 if ( (Entry
->KernelData
!= NULL
) &&
1048 ((Entry
->Type
<< GDI_ENTRY_UPPER_SHIFT
) == HandleUpper
) )
1050 PTHREADINFO Thread
= (PTHREADINFO
)PsGetCurrentThreadWin32Thread();
1051 Object
= Entry
->KernelData
;
1053 if (Object
->cExclusiveLock
== 0)
1055 Object
->Tid
= Thread
;
1056 Object
->cExclusiveLock
= 1;
1057 GDIDBG_CAPTURELOCKER(GDI_HANDLE_GET_INDEX(hObj
))
1061 if (Object
->Tid
!= Thread
)
1063 /* Unlock the handle table entry. */
1064 (void)InterlockedExchangePointer((PVOID
*)&Entry
->ProcessId
, PrevProcId
);
1069 InterlockedIncrement((PLONG
)&Object
->cExclusiveLock
);
1075 * Debugging code. Report attempts to lock deleted handles and
1076 * locking type mismatches.
1078 LockErrorDebugOutput(hObj
, Entry
, "GDIOBJ_LockObj");
1081 /* Unlock the handle table entry. */
1082 (void)InterlockedExchangePointer((PVOID
*)&Entry
->ProcessId
, PrevProcId
);
1089 * The handle is currently locked, wait some time and try again.
1091 GDIDBG_TRACELOOP(hObj
, PrevProcId
, NULL
);
1098 KeLeaveCriticalRegion();
1105 * Return pointer to the object by handle (and allow sharing of the handle
1108 * \param hObj Object handle
1109 * \return Pointer to the object.
1111 * \note Process can only get pointer to the objects it created or global objects.
1113 * \todo Get rid of the ExpectedType parameter!
1115 PGDIOBJ INTERNAL_CALL
1116 GDIOBJ_ShareLockObj(HGDIOBJ hObj
, DWORD ExpectedType
)
1119 PGDI_TABLE_ENTRY Entry
;
1120 HANDLE ProcessId
, HandleProcessId
, LockedProcessId
, PrevProcId
;
1122 ULONG_PTR HandleType
, HandleUpper
;
1124 /* Check for dummy call */
1128 HandleIndex
= GDI_HANDLE_GET_INDEX(hObj
);
1129 HandleType
= GDI_HANDLE_GET_TYPE(hObj
);
1130 HandleUpper
= GDI_HANDLE_GET_UPPER(hObj
);
1132 /* Check that the handle index is valid. */
1133 if (HandleIndex
>= GDI_HANDLE_COUNT
)
1136 /* Check if we have the requested type */
1137 if ( (ExpectedType
!= GDI_OBJECT_TYPE_DONTCARE
&&
1138 HandleType
!= ExpectedType
) ||
1141 DPRINT1("Attempted to lock object 0x%x of wrong type (Handle: 0x%x, requested: 0x%x)\n",
1142 hObj
, HandleType
, ExpectedType
);
1146 Entry
= &GdiHandleTable
->Entries
[HandleIndex
];
1148 ProcessId
= (HANDLE
)((ULONG_PTR
)PsGetCurrentProcessId() & ~1);
1149 HandleProcessId
= (HANDLE
)((ULONG_PTR
)Entry
->ProcessId
& ~1);
1151 /* Check for invalid owner. */
1152 if (ProcessId
!= HandleProcessId
&& HandleProcessId
!= NULL
)
1158 * Prevent the thread from being terminated during the locking process.
1159 * It would result in undesired effects and inconsistency of the global
1163 KeEnterCriticalRegion();
1166 * Loop until we either successfully lock the handle entry & object or
1167 * fail some of the check.
1172 /* Lock the handle table entry. */
1173 LockedProcessId
= (HANDLE
)((ULONG_PTR
)HandleProcessId
| 0x1);
1174 PrevProcId
= InterlockedCompareExchangePointer((PVOID
*)&Entry
->ProcessId
,
1178 if (PrevProcId
== HandleProcessId
)
1181 * We're locking an object that belongs to our process or it's a
1182 * global object if HandleProcessId is 0 here.
1185 if ( (Entry
->KernelData
!= NULL
) &&
1186 (HandleUpper
== (Entry
->Type
<< GDI_ENTRY_UPPER_SHIFT
)) )
1188 Object
= (POBJ
)Entry
->KernelData
;
1190 GDIDBG_CAPTURESHARELOCKER(HandleIndex
);
1192 if (InterlockedIncrement((PLONG
)&Object
->ulShareCount
) == 1)
1194 memset(GDIHandleLocker
[HandleIndex
], 0x00, GDI_STACK_LEVELS
* sizeof(ULONG
));
1195 RtlCaptureStackBackTrace(1, GDI_STACK_LEVELS
, (PVOID
*)GDIHandleShareLocker
[HandleIndex
], NULL
);
1198 InterlockedIncrement((PLONG
)&Object
->ulShareCount
);
1204 * Debugging code. Report attempts to lock deleted handles and
1205 * locking type mismatches.
1207 LockErrorDebugOutput(hObj
, Entry
, "GDIOBJ_ShareLockObj");
1210 /* Unlock the handle table entry. */
1211 (void)InterlockedExchangePointer((PVOID
*)&Entry
->ProcessId
, PrevProcId
);
1218 * The handle is currently locked, wait some time and try again.
1226 KeLeaveCriticalRegion();
1232 GDIOBJ_OwnedByCurrentProcess(HGDIOBJ ObjectHandle
)
1234 PGDI_TABLE_ENTRY Entry
;
1238 DPRINT("GDIOBJ_OwnedByCurrentProcess: ObjectHandle: 0x%08x\n", ObjectHandle
);
1240 if (!GDI_HANDLE_IS_STOCKOBJ(ObjectHandle
))
1242 ProcessId
= PsGetCurrentProcessId();
1244 Entry
= GDI_HANDLE_GET_ENTRY(GdiHandleTable
, ObjectHandle
);
1245 Ret
= Entry
->KernelData
!= NULL
&&
1246 (Entry
->Type
& GDI_ENTRY_BASETYPE_MASK
) != 0 &&
1247 (HANDLE
)((ULONG_PTR
)Entry
->ProcessId
& ~0x1) == ProcessId
;
1256 GDIOBJ_ConvertToStockObj(HGDIOBJ
*phObj
)
1259 * FIXME !!!!! THIS FUNCTION NEEDS TO BE FIXED - IT IS NOT SAFE WHEN OTHER THREADS
1260 * MIGHT ATTEMPT TO LOCK THE OBJECT DURING THIS CALL!!!
1262 PGDI_TABLE_ENTRY Entry
;
1263 HANDLE ProcessId
, LockedProcessId
, PrevProcId
;
1267 GDIDBG_INITLOOPTRACE();
1272 DPRINT("GDIOBJ_ConvertToStockObj: hObj: 0x%08x\n", hObj
);
1274 Thread
= (PTHREADINFO
)PsGetCurrentThreadWin32Thread();
1276 if (!GDI_HANDLE_IS_STOCKOBJ(hObj
))
1278 ProcessId
= PsGetCurrentProcessId();
1279 LockedProcessId
= (HANDLE
)((ULONG_PTR
)ProcessId
| 0x1);
1281 Entry
= GDI_HANDLE_GET_ENTRY(GdiHandleTable
, hObj
);
1284 /* lock the object, we must not convert stock objects, so don't check!!! */
1285 PrevProcId
= InterlockedCompareExchangePointer((PVOID
*)&Entry
->ProcessId
, LockedProcessId
, ProcessId
);
1286 if (PrevProcId
== ProcessId
)
1288 LONG NewType
, PrevType
, OldType
;
1290 /* we're locking an object that belongs to our process. First calculate
1291 the new object type including the stock object flag and then try to
1293 /* On Windows the higher 16 bit of the type field don't contain the
1294 full type from the handle, but the base type.
1295 (type = BRSUH, PEN, EXTPEN, basetype = BRUSH) */
1296 OldType
= ((ULONG
)hObj
& GDI_HANDLE_BASETYPE_MASK
) | ((ULONG
)hObj
>> GDI_ENTRY_UPPER_SHIFT
);
1297 /* We are currently not using bits 24..31 (flags) of the type field, but for compatibility
1298 we copy them as we can't get them from the handle */
1299 OldType
|= Entry
->Type
& GDI_ENTRY_FLAGS_MASK
;
1301 /* As the object should be a stock object, set it's flag, but only in the lower 16 bits */
1302 NewType
= OldType
| GDI_ENTRY_STOCK_MASK
;
1304 /* Try to exchange the type field - but only if the old (previous type) matches! */
1305 PrevType
= InterlockedCompareExchange(&Entry
->Type
, NewType
, OldType
);
1306 if (PrevType
== OldType
&& Entry
->KernelData
!= NULL
)
1308 PTHREADINFO PrevThread
;
1311 /* We successfully set the stock object flag.
1312 KernelData should never be NULL here!!! */
1313 ASSERT(Entry
->KernelData
);
1315 Object
= Entry
->KernelData
;
1317 PrevThread
= Object
->Tid
;
1318 if (Object
->cExclusiveLock
== 0 || PrevThread
== Thread
)
1320 /* dereference the process' object counter */
1321 if (PrevProcId
!= GDI_GLOBAL_PROCESS
)
1323 PEPROCESS OldProcess
;
1324 PPROCESSINFO W32Process
;
1328 Status
= PsLookupProcessByProcessId((HANDLE
)((ULONG_PTR
)PrevProcId
& ~0x1), &OldProcess
);
1329 if (NT_SUCCESS(Status
))
1331 W32Process
= (PPROCESSINFO
)OldProcess
->Win32Process
;
1332 if (W32Process
!= NULL
)
1334 InterlockedDecrement(&W32Process
->GDIHandleCount
);
1336 ObDereferenceObject(OldProcess
);
1340 hObj
= (HGDIOBJ
)((ULONG
)(hObj
) | GDI_HANDLE_STOCK_MASK
);
1342 Object
->hHmgr
= hObj
;
1344 /* remove the process id lock and make it global */
1345 (void)InterlockedExchangePointer((PVOID
*)&Entry
->ProcessId
, GDI_GLOBAL_PROCESS
);
1347 /* we're done, successfully converted the object */
1352 GDIDBG_TRACELOOP(hObj
, PrevThread
, Thread
);
1354 /* WTF?! The object is already locked by a different thread!
1355 Release the lock, wait a bit and try again!
1356 FIXME - we should give up after some time unless we want to wait forever! */
1357 (void)InterlockedExchangePointer((PVOID
*)&Entry
->ProcessId
, PrevProcId
);
1365 DPRINT1("Attempted to convert object 0x%x that is deleted! Should never get here!!!\n", hObj
);
1366 DPRINT1("OldType = 0x%x, Entry->Type = 0x%x, NewType = 0x%x, Entry->KernelData = 0x%x\n", OldType
, Entry
->Type
, NewType
, Entry
->KernelData
);
1369 else if (PrevProcId
== LockedProcessId
)
1371 GDIDBG_TRACELOOP(hObj
, PrevProcId
, ProcessId
);
1373 /* the object is currently locked, wait some time and try again.
1374 FIXME - we shouldn't loop forever! Give up after some time! */
1381 DPRINT1("Attempted to convert invalid handle: 0x%x\n", hObj
);
1389 GDIOBJ_SetOwnership(HGDIOBJ ObjectHandle
, PEPROCESS NewOwner
)
1391 PGDI_TABLE_ENTRY Entry
;
1392 HANDLE ProcessId
, LockedProcessId
, PrevProcId
;
1396 GDIDBG_INITLOOPTRACE();
1398 DPRINT("GDIOBJ_SetOwnership: hObj: 0x%x, NewProcess: 0x%x\n", ObjectHandle
, (NewOwner
? PsGetProcessId(NewOwner
) : 0));
1400 Thread
= (PTHREADINFO
)PsGetCurrentThreadWin32Thread();
1402 if (!GDI_HANDLE_IS_STOCKOBJ(ObjectHandle
))
1404 ProcessId
= PsGetCurrentProcessId();
1405 LockedProcessId
= (HANDLE
)((ULONG_PTR
)ProcessId
| 0x1);
1407 Entry
= GDI_HANDLE_GET_ENTRY(GdiHandleTable
, ObjectHandle
);
1410 /* lock the object, we must not convert stock objects, so don't check!!! */
1411 PrevProcId
= InterlockedCompareExchangePointer((PVOID
*)&Entry
->ProcessId
, ProcessId
, LockedProcessId
);
1412 if (PrevProcId
== ProcessId
)
1414 PTHREADINFO PrevThread
;
1416 if ((Entry
->Type
& GDI_ENTRY_BASETYPE_MASK
) != 0)
1418 POBJ Object
= Entry
->KernelData
;
1420 PrevThread
= Object
->Tid
;
1421 if (Object
->cExclusiveLock
== 0 || PrevThread
== Thread
)
1423 PEPROCESS OldProcess
;
1424 PPROCESSINFO W32Process
;
1427 if (NewOwner
!= NULL
)
1429 ProcessId
= PsGetProcessId(NewOwner
);
1434 if((ULONG_PTR
)ProcessId
== ((ULONG_PTR
)PrevProcId
& ~0x1))
1436 DPRINT("Setting same process than previous one, nothing to do\n");
1440 /* dereference the process' object counter */
1442 if ((ULONG_PTR
)PrevProcId
& ~0x1)
1444 Status
= PsLookupProcessByProcessId((HANDLE
)((ULONG_PTR
)PrevProcId
& ~0x1), &OldProcess
);
1445 if (NT_SUCCESS(Status
))
1447 W32Process
= (PPROCESSINFO
)OldProcess
->Win32Process
;
1448 if (W32Process
!= NULL
)
1450 InterlockedDecrement(&W32Process
->GDIHandleCount
);
1452 ObDereferenceObject(OldProcess
);
1456 if (NewOwner
!= NULL
)
1458 /* Increase the new process' object counter */
1459 W32Process
= (PPROCESSINFO
)NewOwner
->Win32Process
;
1460 if (W32Process
!= NULL
)
1462 InterlockedIncrement(&W32Process
->GDIHandleCount
);
1467 /* remove the process id lock and change it to the new process id */
1468 (void)InterlockedExchangePointer((PVOID
*)&Entry
->ProcessId
, ProcessId
);
1475 GDIDBG_TRACELOOP(ObjectHandle
, PrevThread
, Thread
);
1477 /* WTF?! The object is already locked by a different thread!
1478 Release the lock, wait a bit and try again! DO reset the pid lock
1479 so we make sure we don't access invalid memory in case the object is
1480 being deleted in the meantime (because we don't have aquired a reference
1482 FIXME - we should give up after some time unless we want to wait forever! */
1483 (void)InterlockedExchangePointer((PVOID
*)&Entry
->ProcessId
, PrevProcId
);
1491 DPRINT1("Attempted to change ownership of an object 0x%x currently being destroyed!!!\n", ObjectHandle
);
1492 DPRINT1("Entry->Type = 0x%lx, Entry->KernelData = 0x%p\n", Entry
->Type
, Entry
->KernelData
);
1496 else if (PrevProcId
== LockedProcessId
)
1498 GDIDBG_TRACELOOP(ObjectHandle
, PrevProcId
, ProcessId
);
1500 /* the object is currently locked, wait some time and try again.
1501 FIXME - we shouldn't loop forever! Give up after some time! */
1506 else if (((ULONG_PTR
)PrevProcId
& ~0x1) == 0)
1508 /* allow changing ownership of global objects */
1510 LockedProcessId
= (HANDLE
)((ULONG_PTR
)ProcessId
| 0x1);
1513 else if ((HANDLE
)((ULONG_PTR
)PrevProcId
& ~0x1) != PsGetCurrentProcessId())
1515 DPRINT1("Attempted to change ownership of object 0x%x (pid: 0x%x) from pid 0x%x!!!\n", ObjectHandle
, (ULONG_PTR
)PrevProcId
& ~0x1, PsGetCurrentProcessId());
1520 DPRINT1("Attempted to change owner of invalid handle: 0x%x\n", ObjectHandle
);
1528 GDIOBJ_CopyOwnership(HGDIOBJ CopyFrom
, HGDIOBJ CopyTo
)
1530 PGDI_TABLE_ENTRY FromEntry
;
1532 HANDLE FromProcessId
, FromLockedProcessId
, FromPrevProcId
;
1535 GDIDBG_INITLOOPTRACE();
1537 DPRINT("GDIOBJ_CopyOwnership: from: 0x%x, to: 0x%x\n", CopyFrom
, CopyTo
);
1539 Thread
= (PTHREADINFO
)PsGetCurrentThreadWin32Thread();
1541 if (!GDI_HANDLE_IS_STOCKOBJ(CopyFrom
) && !GDI_HANDLE_IS_STOCKOBJ(CopyTo
))
1543 FromEntry
= GDI_HANDLE_GET_ENTRY(GdiHandleTable
, CopyFrom
);
1545 FromProcessId
= (HANDLE
)((ULONG_PTR
)FromEntry
->ProcessId
& ~0x1);
1546 FromLockedProcessId
= (HANDLE
)((ULONG_PTR
)FromProcessId
| 0x1);
1549 /* lock the object, we must not convert stock objects, so don't check!!! */
1550 FromPrevProcId
= InterlockedCompareExchangePointer((PVOID
*)&FromEntry
->ProcessId
, FromProcessId
, FromLockedProcessId
);
1551 if (FromPrevProcId
== FromProcessId
)
1553 PTHREADINFO PrevThread
;
1556 if ((FromEntry
->Type
& GDI_ENTRY_BASETYPE_MASK
) != 0)
1558 Object
= FromEntry
->KernelData
;
1560 /* save the pointer to the calling thread so we know it was this thread
1561 that locked the object */
1562 PrevThread
= Object
->Tid
;
1563 if (Object
->cExclusiveLock
== 0 || PrevThread
== Thread
)
1565 /* now let's change the ownership of the target object */
1567 if (((ULONG_PTR
)FromPrevProcId
& ~0x1) != 0)
1569 PEPROCESS ProcessTo
;
1571 if (NT_SUCCESS(PsLookupProcessByProcessId((HANDLE
)((ULONG_PTR
)FromPrevProcId
& ~0x1), &ProcessTo
)))
1573 GDIOBJ_SetOwnership(CopyTo
, ProcessTo
);
1574 ObDereferenceObject(ProcessTo
);
1579 /* mark the object as global */
1580 GDIOBJ_SetOwnership(CopyTo
, NULL
);
1583 (void)InterlockedExchangePointer((PVOID
*)&FromEntry
->ProcessId
, FromPrevProcId
);
1587 GDIDBG_TRACELOOP(CopyFrom
, PrevThread
, Thread
);
1589 /* WTF?! The object is already locked by a different thread!
1590 Release the lock, wait a bit and try again! DO reset the pid lock
1591 so we make sure we don't access invalid memory in case the object is
1592 being deleted in the meantime (because we don't have aquired a reference
1594 FIXME - we should give up after some time unless we want to wait forever! */
1595 (void)InterlockedExchangePointer((PVOID
*)&FromEntry
->ProcessId
, FromPrevProcId
);
1598 goto LockHandleFrom
;
1603 DPRINT1("Attempted to copy ownership from an object 0x%x currently being destroyed!!!\n", CopyFrom
);
1607 else if (FromPrevProcId
== FromLockedProcessId
)
1609 GDIDBG_TRACELOOP(CopyFrom
, FromPrevProcId
, FromProcessId
);
1611 /* the object is currently locked, wait some time and try again.
1612 FIXME - we shouldn't loop forever! Give up after some time! */
1615 goto LockHandleFrom
;
1617 else if ((HANDLE
)((ULONG_PTR
)FromPrevProcId
& ~0x1) != PsGetCurrentProcessId())
1619 /* FIXME - should we really allow copying ownership from objects that we don't even own? */
1620 DPRINT1("WARNING! Changing copying ownership of object 0x%x (pid: 0x%x) to pid 0x%x!!!\n", CopyFrom
, (ULONG_PTR
)FromPrevProcId
& ~0x1, PsGetCurrentProcessId());
1621 FromProcessId
= (HANDLE
)((ULONG_PTR
)FromPrevProcId
& ~0x1);
1622 FromLockedProcessId
= (HANDLE
)((ULONG_PTR
)FromProcessId
| 0x1);
1623 goto LockHandleFrom
;
1627 DPRINT1("Attempted to copy ownership from invalid handle: 0x%x\n", CopyFrom
);
1635 GDI_MapHandleTable(PSECTION_OBJECT SectionObject
, PEPROCESS Process
)
1637 PVOID MappedView
= NULL
;
1639 LARGE_INTEGER Offset
;
1640 ULONG ViewSize
= sizeof(GDI_HANDLE_TABLE
);
1642 Offset
.QuadPart
= 0;
1644 ASSERT(SectionObject
!= NULL
);
1645 ASSERT(Process
!= NULL
);
1647 Status
= MmMapViewOfSection(SectionObject
,
1658 if (!NT_SUCCESS(Status
))
1664 /* Locks 2 or 3 objects at a time */
1667 GDIOBJ_LockMultipleObjs(ULONG ulCount
,
1671 UINT auiIndices
[3] = {0,1,2};
1673 BOOL bUnsorted
= TRUE
;
1675 /* First is greatest */
1679 for(i
=1; i
<ulCount
; i
++)
1681 if((ULONG_PTR
)ahObj
[auiIndices
[i
-1]] < (ULONG_PTR
)ahObj
[auiIndices
[i
]])
1683 tmp
= auiIndices
[i
-1];
1684 auiIndices
[i
-1] = auiIndices
[i
];
1685 auiIndices
[i
] = tmp
;
1691 for(i
=0;i
<ulCount
;i
++)
1692 apObj
[auiIndices
[i
]] = GDIOBJ_LockObj(ahObj
[auiIndices
[i
]], GDI_OBJECT_TYPE_DONTCARE
);
1696 /** PUBLIC FUNCTIONS **********************************************************/
1700 IntGdiSetRegionOwner(HRGN hRgn
, DWORD OwnerMask
)
1703 PGDI_TABLE_ENTRY Entry
;
1706 These regions do not use attribute sections and when allocated, use gdiobj
1709 // FIXME! HAX!!! Remove this once we get everything right!
1710 Index
= GDI_HANDLE_GET_INDEX(hRgn
);
1711 Entry
= &GdiHandleTable
->Entries
[Index
];
1712 if (Entry
->UserData
) FreeObjectAttr(Entry
->UserData
);
1713 Entry
->UserData
= NULL
;
1715 if ((OwnerMask
== GDI_OBJ_HMGR_PUBLIC
) || OwnerMask
== GDI_OBJ_HMGR_NONE
)
1717 return GDIOBJ_SetOwnership(hRgn
, NULL
);
1719 if (OwnerMask
== GDI_OBJ_HMGR_POWNED
)
1721 return GDIOBJ_SetOwnership((HGDIOBJ
) hRgn
, PsGetCurrentProcess() );
1728 IntGdiSetBrushOwner(PBRUSH pbr
, DWORD OwnerMask
)
1731 PEPROCESS Owner
= NULL
;
1732 PGDI_TABLE_ENTRY pEntry
= NULL
;
1734 if (!pbr
) return FALSE
;
1736 hBR
= pbr
->BaseObject
.hHmgr
;
1738 if (!hBR
|| (GDI_HANDLE_GET_TYPE(hBR
) != GDI_OBJECT_TYPE_BRUSH
))
1742 INT Index
= GDI_HANDLE_GET_INDEX((HGDIOBJ
)hBR
);
1743 pEntry
= &GdiHandleTable
->Entries
[Index
];
1746 if (pbr
->flAttrs
& GDIBRUSH_IS_GLOBAL
)
1748 GDIOBJ_ShareUnlockObjByPtr((POBJ
)pbr
);
1752 if ((OwnerMask
== GDI_OBJ_HMGR_PUBLIC
) || OwnerMask
== GDI_OBJ_HMGR_NONE
)
1754 // Set this Brush to inaccessible mode and to an Owner of NONE.
1755 // if (OwnerMask == GDI_OBJ_HMGR_NONE) Owner = OwnerMask;
1757 if (!GDIOBJ_SetOwnership((HGDIOBJ
) hBR
, Owner
))
1760 // Deny user access to User Data.
1761 pEntry
->UserData
= NULL
; // This hBR is inaccessible!
1764 if (OwnerMask
== GDI_OBJ_HMGR_POWNED
)
1766 if (!GDIOBJ_SetOwnership((HGDIOBJ
) hBR
, PsGetCurrentProcess() ))
1769 // Allow user access to User Data.
1770 pEntry
->UserData
= pbr
->pBrushAttr
;
1777 IntGdiSetDCOwnerEx( HDC hDC
, DWORD OwnerMask
, BOOL NoSetBrush
)
1782 if (!hDC
|| (GDI_HANDLE_GET_TYPE(hDC
) != GDI_OBJECT_TYPE_DC
)) return FALSE
;
1784 if ((OwnerMask
== GDI_OBJ_HMGR_PUBLIC
) || OwnerMask
== GDI_OBJ_HMGR_NONE
)
1786 pDC
= DC_LockDc ( hDC
);
1787 MmCopyFromCaller(&pDC
->dcattr
, pDC
->pdcattr
, sizeof(DC_ATTR
));
1788 DC_vFreeDcAttr(pDC
);
1791 if (!DC_SetOwnership( hDC
, NULL
)) // This hDC is inaccessible!
1795 if (OwnerMask
== GDI_OBJ_HMGR_POWNED
)
1797 pDC
= DC_LockDc ( hDC
);
1798 ASSERT(pDC
->pdcattr
== &pDC
->dcattr
);
1801 if (!DC_SetOwnership( hDC
, PsGetCurrentProcess() )) return Ret
;
1803 DC_AllocateDcAttr( hDC
); // Allocate new dcattr
1805 DCU_SynchDcAttrtoUser( hDC
); // Copy data from dc to dcattr
1808 if ((OwnerMask
!= GDI_OBJ_HMGR_NONE
) && !NoSetBrush
)
1810 pDC
= DC_LockDc ( hDC
);
1811 if (IntGdiSetBrushOwner((PBRUSH
)pDC
->dclevel
.pbrFill
, OwnerMask
))
1812 IntGdiSetBrushOwner((PBRUSH
)pDC
->dclevel
.pbrLine
, OwnerMask
);
1820 GreGetObjectOwner(HGDIOBJ Handle
, GDIOBJTYPE ObjType
)
1822 INT Ret
= GDI_OBJ_HMGR_RESTRICTED
;
1824 if ( GDI_HANDLE_GET_INDEX(Handle
) < GDI_HANDLE_COUNT
)
1826 PGDI_TABLE_ENTRY pEntry
= &GdiHandleTable
->Entries
[GDI_HANDLE_GET_INDEX(Handle
)];
1828 if (pEntry
->ObjectType
== ObjType
)
1830 if (pEntry
->FullUnique
== (GDI_HANDLE_GET_UPPER(Handle
) >> GDI_ENTRY_UPPER_SHIFT
))
1831 Ret
= pEntry
->ProcessId
& ~1;
1840 NtGdiCreateClientObj(
1847 /* Mask out everything that would change the type in a wrong manner */
1848 ulType
&= (GDI_HANDLE_TYPE_MASK
& ~GDI_HANDLE_BASETYPE_MASK
);
1850 /* Allocate a new object */
1851 pObject
= GDIOBJ_AllocObjWithHandle(GDI_OBJECT_TYPE_CLIOBJ
| ulType
);
1857 /* get the handle */
1858 handle
= pObject
->hHmgr
;
1861 GDIOBJ_UnlockObjByPtr(pObject
);
1869 NtGdiDeleteClientObj(
1873 /* We first need to get the real type from the handle */
1874 ULONG type
= GDI_HANDLE_GET_TYPE(h
);
1876 /* Check if it's really a CLIENTOBJ */
1877 if ((type
& GDI_HANDLE_BASETYPE_MASK
) != GDILoObjType_LO_CLIENTOBJ_TYPE
)
1879 /* FIXME: SetLastError? */
1882 return GDIOBJ_FreeObjByHandle(h
, type
);
1887 IntGdiGetObject(IN HANDLE Handle
,
1895 pGdiObject
= GDIOBJ_LockObj(Handle
, GDI_OBJECT_TYPE_DONTCARE
);
1898 EngSetLastError(ERROR_INVALID_HANDLE
);
1902 dwObjectType
= GDIOBJ_GetObjectType(Handle
);
1903 switch (dwObjectType
)
1905 case GDI_OBJECT_TYPE_PEN
:
1906 case GDI_OBJECT_TYPE_EXTPEN
:
1907 Result
= PEN_GetObject((PBRUSH
) pGdiObject
, cbCount
, (PLOGPEN
) lpBuffer
); // IntGdiCreatePenIndirect
1910 case GDI_OBJECT_TYPE_BRUSH
:
1911 Result
= BRUSH_GetObject((PBRUSH
) pGdiObject
, cbCount
, (LPLOGBRUSH
)lpBuffer
);
1914 case GDI_OBJECT_TYPE_BITMAP
:
1915 Result
= BITMAP_GetObject((SURFACE
*) pGdiObject
, cbCount
, lpBuffer
);
1917 case GDI_OBJECT_TYPE_FONT
:
1918 Result
= FontGetObject((PTEXTOBJ
) pGdiObject
, cbCount
, lpBuffer
);
1920 // Fix the LOGFONT structure for the stock fonts
1921 if (FIRST_STOCK_HANDLE
<= Handle
&& Handle
<= LAST_STOCK_HANDLE
)
1923 FixStockFontSizeW(Handle
, cbCount
, lpBuffer
);
1928 case GDI_OBJECT_TYPE_PALETTE
:
1929 Result
= PALETTE_GetObject((PPALETTE
) pGdiObject
, cbCount
, lpBuffer
);
1933 DPRINT1("GDI object type 0x%08x not implemented\n", dwObjectType
);
1937 GDIOBJ_UnlockObjByPtr(pGdiObject
);
1947 NtGdiExtGetObjectW(IN HANDLE hGdiObj
,
1949 OUT LPVOID lpBuffer
)
1956 DIBSECTION dibsection
;
1960 EXTLOGFONTW extlogfontw
;
1961 ENUMLOGFONTEXDVW enumlogfontexdvw
;
1964 // Normalize to the largest supported object size
1965 cbCount
= min((UINT
)cbCount
, sizeof(Object
));
1967 // Now do the actual call
1968 iRetCount
= IntGdiGetObject(hGdiObj
, cbCount
, lpBuffer
? &Object
: NULL
);
1969 cbCopyCount
= min((UINT
)cbCount
, (UINT
)iRetCount
);
1971 // Make sure we have a buffer and a copy size
1972 if ((cbCopyCount
) && (lpBuffer
))
1974 // Enter SEH for buffer transfer
1977 // Probe the buffer and copy it
1978 ProbeForWrite(lpBuffer
, cbCopyCount
, sizeof(WORD
));
1979 RtlCopyMemory(lpBuffer
, &Object
, cbCopyCount
);
1981 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1983 // Clear the return value.
1984 // Do *NOT* set last error here!