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);
1010 * Prevent the thread from being terminated during the locking process.
1011 * It would result in undesired effects and inconsistency of the global
1015 KeEnterCriticalRegion();
1018 * Loop until we either successfully lock the handle entry & object or
1019 * fail some of the check.
1024 HandleProcessId
= (HANDLE
)((ULONG_PTR
)Entry
->ProcessId
& ~1);
1026 /* Check for invalid owner. */
1027 if (ProcessId
!= HandleProcessId
&& HandleProcessId
!= NULL
)
1029 DPRINT1("Tried to lock object (0x%p) of wrong owner! ProcessId = %p, HandleProcessId = %p\n", hObj
, ProcessId
, HandleProcessId
);
1030 GDIDBG_TRACECALLER();
1031 GDIDBG_TRACEALLOCATOR(hObj
);
1035 /* Lock the handle table entry. */
1036 LockedProcessId
= (HANDLE
)((ULONG_PTR
)HandleProcessId
| 0x1);
1037 PrevProcId
= InterlockedCompareExchangePointer((PVOID
*)&Entry
->ProcessId
,
1041 if (PrevProcId
== HandleProcessId
)
1044 * We're locking an object that belongs to our process or it's a
1045 * global object if HandleProcessId is 0 here.
1048 if ( (Entry
->KernelData
!= NULL
) &&
1049 ((Entry
->Type
<< GDI_ENTRY_UPPER_SHIFT
) == HandleUpper
) )
1051 PTHREADINFO Thread
= (PTHREADINFO
)PsGetCurrentThreadWin32Thread();
1052 Object
= Entry
->KernelData
;
1054 if (Object
->cExclusiveLock
== 0)
1056 Object
->Tid
= Thread
;
1057 Object
->cExclusiveLock
= 1;
1058 GDIDBG_CAPTURELOCKER(GDI_HANDLE_GET_INDEX(hObj
))
1062 if (Object
->Tid
!= Thread
)
1064 /* Unlock the handle table entry. */
1065 (void)InterlockedExchangePointer((PVOID
*)&Entry
->ProcessId
, PrevProcId
);
1070 InterlockedIncrement((PLONG
)&Object
->cExclusiveLock
);
1076 * Debugging code. Report attempts to lock deleted handles and
1077 * locking type mismatches.
1079 LockErrorDebugOutput(hObj
, Entry
, "GDIOBJ_LockObj");
1082 /* Unlock the handle table entry. */
1083 (void)InterlockedExchangePointer((PVOID
*)&Entry
->ProcessId
, PrevProcId
);
1090 * The handle is currently locked, wait some time and try again.
1092 GDIDBG_TRACELOOP(hObj
, PrevProcId
, NULL
);
1099 KeLeaveCriticalRegion();
1106 * Return pointer to the object by handle (and allow sharing of the handle
1109 * \param hObj Object handle
1110 * \return Pointer to the object.
1112 * \note Process can only get pointer to the objects it created or global objects.
1114 * \todo Get rid of the ExpectedType parameter!
1116 PGDIOBJ INTERNAL_CALL
1117 GDIOBJ_ShareLockObj(HGDIOBJ hObj
, DWORD ExpectedType
)
1120 PGDI_TABLE_ENTRY Entry
;
1121 HANDLE ProcessId
, HandleProcessId
, LockedProcessId
, PrevProcId
;
1123 ULONG_PTR HandleType
, HandleUpper
;
1125 /* Check for dummy call */
1129 HandleIndex
= GDI_HANDLE_GET_INDEX(hObj
);
1130 HandleType
= GDI_HANDLE_GET_TYPE(hObj
);
1131 HandleUpper
= GDI_HANDLE_GET_UPPER(hObj
);
1133 /* Check that the handle index is valid. */
1134 if (HandleIndex
>= GDI_HANDLE_COUNT
)
1137 /* Check if we have the requested type */
1138 if ( (ExpectedType
!= GDI_OBJECT_TYPE_DONTCARE
&&
1139 HandleType
!= ExpectedType
) ||
1142 DPRINT1("Attempted to lock object 0x%x of wrong type (Handle: 0x%x, requested: 0x%x)\n",
1143 hObj
, HandleType
, ExpectedType
);
1147 Entry
= &GdiHandleTable
->Entries
[HandleIndex
];
1149 ProcessId
= (HANDLE
)((ULONG_PTR
)PsGetCurrentProcessId() & ~1);
1150 HandleProcessId
= (HANDLE
)((ULONG_PTR
)Entry
->ProcessId
& ~1);
1152 /* Check for invalid owner. */
1153 if (ProcessId
!= HandleProcessId
&& HandleProcessId
!= NULL
)
1159 * Prevent the thread from being terminated during the locking process.
1160 * It would result in undesired effects and inconsistency of the global
1164 KeEnterCriticalRegion();
1167 * Loop until we either successfully lock the handle entry & object or
1168 * fail some of the check.
1173 /* Lock the handle table entry. */
1174 LockedProcessId
= (HANDLE
)((ULONG_PTR
)HandleProcessId
| 0x1);
1175 PrevProcId
= InterlockedCompareExchangePointer((PVOID
*)&Entry
->ProcessId
,
1179 if (PrevProcId
== HandleProcessId
)
1182 * We're locking an object that belongs to our process or it's a
1183 * global object if HandleProcessId is 0 here.
1186 if ( (Entry
->KernelData
!= NULL
) &&
1187 (HandleUpper
== (Entry
->Type
<< GDI_ENTRY_UPPER_SHIFT
)) )
1189 Object
= (POBJ
)Entry
->KernelData
;
1191 GDIDBG_CAPTURESHARELOCKER(HandleIndex
);
1193 if (InterlockedIncrement((PLONG
)&Object
->ulShareCount
) == 1)
1195 memset(GDIHandleLocker
[HandleIndex
], 0x00, GDI_STACK_LEVELS
* sizeof(ULONG
));
1196 RtlCaptureStackBackTrace(1, GDI_STACK_LEVELS
, (PVOID
*)GDIHandleShareLocker
[HandleIndex
], NULL
);
1199 InterlockedIncrement((PLONG
)&Object
->ulShareCount
);
1205 * Debugging code. Report attempts to lock deleted handles and
1206 * locking type mismatches.
1208 LockErrorDebugOutput(hObj
, Entry
, "GDIOBJ_ShareLockObj");
1211 /* Unlock the handle table entry. */
1212 (void)InterlockedExchangePointer((PVOID
*)&Entry
->ProcessId
, PrevProcId
);
1219 * The handle is currently locked, wait some time and try again.
1227 KeLeaveCriticalRegion();
1233 GDIOBJ_OwnedByCurrentProcess(HGDIOBJ ObjectHandle
)
1235 PGDI_TABLE_ENTRY Entry
;
1239 DPRINT("GDIOBJ_OwnedByCurrentProcess: ObjectHandle: 0x%08x\n", ObjectHandle
);
1241 if (!GDI_HANDLE_IS_STOCKOBJ(ObjectHandle
))
1243 ProcessId
= PsGetCurrentProcessId();
1245 Entry
= GDI_HANDLE_GET_ENTRY(GdiHandleTable
, ObjectHandle
);
1246 Ret
= Entry
->KernelData
!= NULL
&&
1247 (Entry
->Type
& GDI_ENTRY_BASETYPE_MASK
) != 0 &&
1248 (HANDLE
)((ULONG_PTR
)Entry
->ProcessId
& ~0x1) == ProcessId
;
1257 GDIOBJ_ConvertToStockObj(HGDIOBJ
*phObj
)
1260 * FIXME !!!!! THIS FUNCTION NEEDS TO BE FIXED - IT IS NOT SAFE WHEN OTHER THREADS
1261 * MIGHT ATTEMPT TO LOCK THE OBJECT DURING THIS CALL!!!
1263 PGDI_TABLE_ENTRY Entry
;
1264 HANDLE ProcessId
, LockedProcessId
, PrevProcId
;
1268 GDIDBG_INITLOOPTRACE();
1273 DPRINT("GDIOBJ_ConvertToStockObj: hObj: 0x%08x\n", hObj
);
1275 Thread
= (PTHREADINFO
)PsGetCurrentThreadWin32Thread();
1277 if (!GDI_HANDLE_IS_STOCKOBJ(hObj
))
1279 ProcessId
= PsGetCurrentProcessId();
1280 LockedProcessId
= (HANDLE
)((ULONG_PTR
)ProcessId
| 0x1);
1282 Entry
= GDI_HANDLE_GET_ENTRY(GdiHandleTable
, hObj
);
1285 /* lock the object, we must not convert stock objects, so don't check!!! */
1286 PrevProcId
= InterlockedCompareExchangePointer((PVOID
*)&Entry
->ProcessId
, LockedProcessId
, ProcessId
);
1287 if (PrevProcId
== ProcessId
)
1289 LONG NewType
, PrevType
, OldType
;
1291 /* we're locking an object that belongs to our process. First calculate
1292 the new object type including the stock object flag and then try to
1294 /* On Windows the higher 16 bit of the type field don't contain the
1295 full type from the handle, but the base type.
1296 (type = BRSUH, PEN, EXTPEN, basetype = BRUSH) */
1297 OldType
= ((ULONG
)hObj
& GDI_HANDLE_BASETYPE_MASK
) | ((ULONG
)hObj
>> GDI_ENTRY_UPPER_SHIFT
);
1298 /* We are currently not using bits 24..31 (flags) of the type field, but for compatibility
1299 we copy them as we can't get them from the handle */
1300 OldType
|= Entry
->Type
& GDI_ENTRY_FLAGS_MASK
;
1302 /* As the object should be a stock object, set it's flag, but only in the lower 16 bits */
1303 NewType
= OldType
| GDI_ENTRY_STOCK_MASK
;
1305 /* Try to exchange the type field - but only if the old (previous type) matches! */
1306 PrevType
= InterlockedCompareExchange(&Entry
->Type
, NewType
, OldType
);
1307 if (PrevType
== OldType
&& Entry
->KernelData
!= NULL
)
1309 PTHREADINFO PrevThread
;
1312 /* We successfully set the stock object flag.
1313 KernelData should never be NULL here!!! */
1314 ASSERT(Entry
->KernelData
);
1316 Object
= Entry
->KernelData
;
1318 PrevThread
= Object
->Tid
;
1319 if (Object
->cExclusiveLock
== 0 || PrevThread
== Thread
)
1321 /* dereference the process' object counter */
1322 if (PrevProcId
!= GDI_GLOBAL_PROCESS
)
1324 PEPROCESS OldProcess
;
1325 PPROCESSINFO W32Process
;
1329 Status
= PsLookupProcessByProcessId((HANDLE
)((ULONG_PTR
)PrevProcId
& ~0x1), &OldProcess
);
1330 if (NT_SUCCESS(Status
))
1332 W32Process
= (PPROCESSINFO
)OldProcess
->Win32Process
;
1333 if (W32Process
!= NULL
)
1335 InterlockedDecrement(&W32Process
->GDIHandleCount
);
1337 ObDereferenceObject(OldProcess
);
1341 hObj
= (HGDIOBJ
)((ULONG
)(hObj
) | GDI_HANDLE_STOCK_MASK
);
1343 Object
->hHmgr
= hObj
;
1345 /* remove the process id lock and make it global */
1346 (void)InterlockedExchangePointer((PVOID
*)&Entry
->ProcessId
, GDI_GLOBAL_PROCESS
);
1348 /* we're done, successfully converted the object */
1353 GDIDBG_TRACELOOP(hObj
, PrevThread
, Thread
);
1355 /* WTF?! The object is already locked by a different thread!
1356 Release the lock, wait a bit and try again!
1357 FIXME - we should give up after some time unless we want to wait forever! */
1358 (void)InterlockedExchangePointer((PVOID
*)&Entry
->ProcessId
, PrevProcId
);
1366 DPRINT1("Attempted to convert object 0x%x that is deleted! Should never get here!!!\n", hObj
);
1367 DPRINT1("OldType = 0x%x, Entry->Type = 0x%x, NewType = 0x%x, Entry->KernelData = 0x%x\n", OldType
, Entry
->Type
, NewType
, Entry
->KernelData
);
1370 else if (PrevProcId
== LockedProcessId
)
1372 GDIDBG_TRACELOOP(hObj
, PrevProcId
, ProcessId
);
1374 /* the object is currently locked, wait some time and try again.
1375 FIXME - we shouldn't loop forever! Give up after some time! */
1382 DPRINT1("Attempted to convert invalid handle: 0x%x\n", hObj
);
1390 GDIOBJ_SetOwnership(HGDIOBJ ObjectHandle
, PEPROCESS NewOwner
)
1392 PGDI_TABLE_ENTRY Entry
;
1393 HANDLE ProcessId
, LockedProcessId
, PrevProcId
;
1397 GDIDBG_INITLOOPTRACE();
1399 DPRINT("GDIOBJ_SetOwnership: hObj: 0x%x, NewProcess: 0x%x\n", ObjectHandle
, (NewOwner
? PsGetProcessId(NewOwner
) : 0));
1401 Thread
= (PTHREADINFO
)PsGetCurrentThreadWin32Thread();
1403 if (!GDI_HANDLE_IS_STOCKOBJ(ObjectHandle
))
1405 ProcessId
= PsGetCurrentProcessId();
1406 LockedProcessId
= (HANDLE
)((ULONG_PTR
)ProcessId
| 0x1);
1408 Entry
= GDI_HANDLE_GET_ENTRY(GdiHandleTable
, ObjectHandle
);
1411 /* lock the object, we must not convert stock objects, so don't check!!! */
1412 PrevProcId
= InterlockedCompareExchangePointer((PVOID
*)&Entry
->ProcessId
, ProcessId
, LockedProcessId
);
1413 if (PrevProcId
== ProcessId
)
1415 PTHREADINFO PrevThread
;
1417 if ((Entry
->Type
& GDI_ENTRY_BASETYPE_MASK
) != 0)
1419 POBJ Object
= Entry
->KernelData
;
1421 PrevThread
= Object
->Tid
;
1422 if (Object
->cExclusiveLock
== 0 || PrevThread
== Thread
)
1424 PEPROCESS OldProcess
;
1425 PPROCESSINFO W32Process
;
1428 if (NewOwner
!= NULL
)
1430 ProcessId
= PsGetProcessId(NewOwner
);
1435 if((ULONG_PTR
)ProcessId
== ((ULONG_PTR
)PrevProcId
& ~0x1))
1437 DPRINT("Setting same process than previous one, nothing to do\n");
1441 /* dereference the process' object counter */
1443 if ((ULONG_PTR
)PrevProcId
& ~0x1)
1445 Status
= PsLookupProcessByProcessId((HANDLE
)((ULONG_PTR
)PrevProcId
& ~0x1), &OldProcess
);
1446 if (NT_SUCCESS(Status
))
1448 W32Process
= (PPROCESSINFO
)OldProcess
->Win32Process
;
1449 if (W32Process
!= NULL
)
1451 InterlockedDecrement(&W32Process
->GDIHandleCount
);
1453 ObDereferenceObject(OldProcess
);
1457 if (NewOwner
!= NULL
)
1459 /* Increase the new process' object counter */
1460 W32Process
= (PPROCESSINFO
)NewOwner
->Win32Process
;
1461 if (W32Process
!= NULL
)
1463 InterlockedIncrement(&W32Process
->GDIHandleCount
);
1468 /* remove the process id lock and change it to the new process id */
1469 (void)InterlockedExchangePointer((PVOID
*)&Entry
->ProcessId
, ProcessId
);
1476 GDIDBG_TRACELOOP(ObjectHandle
, PrevThread
, Thread
);
1478 /* WTF?! The object is already locked by a different thread!
1479 Release the lock, wait a bit and try again! DO reset the pid lock
1480 so we make sure we don't access invalid memory in case the object is
1481 being deleted in the meantime (because we don't have aquired a reference
1483 FIXME - we should give up after some time unless we want to wait forever! */
1484 (void)InterlockedExchangePointer((PVOID
*)&Entry
->ProcessId
, PrevProcId
);
1492 DPRINT1("Attempted to change ownership of an object 0x%x currently being destroyed!!!\n", ObjectHandle
);
1493 DPRINT1("Entry->Type = 0x%lx, Entry->KernelData = 0x%p\n", Entry
->Type
, Entry
->KernelData
);
1497 else if (PrevProcId
== LockedProcessId
)
1499 GDIDBG_TRACELOOP(ObjectHandle
, PrevProcId
, ProcessId
);
1501 /* the object is currently locked, wait some time and try again.
1502 FIXME - we shouldn't loop forever! Give up after some time! */
1507 else if (((ULONG_PTR
)PrevProcId
& ~0x1) == 0)
1509 /* allow changing ownership of global objects */
1511 LockedProcessId
= (HANDLE
)((ULONG_PTR
)ProcessId
| 0x1);
1514 else if ((HANDLE
)((ULONG_PTR
)PrevProcId
& ~0x1) != PsGetCurrentProcessId())
1516 DPRINT1("Attempted to change ownership of object 0x%x (pid: 0x%x) from pid 0x%x!!!\n", ObjectHandle
, (ULONG_PTR
)PrevProcId
& ~0x1, PsGetCurrentProcessId());
1521 DPRINT1("Attempted to change owner of invalid handle: 0x%x\n", ObjectHandle
);
1529 GDIOBJ_CopyOwnership(HGDIOBJ CopyFrom
, HGDIOBJ CopyTo
)
1531 PGDI_TABLE_ENTRY FromEntry
;
1533 HANDLE FromProcessId
, FromLockedProcessId
, FromPrevProcId
;
1536 GDIDBG_INITLOOPTRACE();
1538 DPRINT("GDIOBJ_CopyOwnership: from: 0x%x, to: 0x%x\n", CopyFrom
, CopyTo
);
1540 Thread
= (PTHREADINFO
)PsGetCurrentThreadWin32Thread();
1542 if (!GDI_HANDLE_IS_STOCKOBJ(CopyFrom
) && !GDI_HANDLE_IS_STOCKOBJ(CopyTo
))
1544 FromEntry
= GDI_HANDLE_GET_ENTRY(GdiHandleTable
, CopyFrom
);
1546 FromProcessId
= (HANDLE
)((ULONG_PTR
)FromEntry
->ProcessId
& ~0x1);
1547 FromLockedProcessId
= (HANDLE
)((ULONG_PTR
)FromProcessId
| 0x1);
1550 /* lock the object, we must not convert stock objects, so don't check!!! */
1551 FromPrevProcId
= InterlockedCompareExchangePointer((PVOID
*)&FromEntry
->ProcessId
, FromProcessId
, FromLockedProcessId
);
1552 if (FromPrevProcId
== FromProcessId
)
1554 PTHREADINFO PrevThread
;
1557 if ((FromEntry
->Type
& GDI_ENTRY_BASETYPE_MASK
) != 0)
1559 Object
= FromEntry
->KernelData
;
1561 /* save the pointer to the calling thread so we know it was this thread
1562 that locked the object */
1563 PrevThread
= Object
->Tid
;
1564 if (Object
->cExclusiveLock
== 0 || PrevThread
== Thread
)
1566 /* now let's change the ownership of the target object */
1568 if (((ULONG_PTR
)FromPrevProcId
& ~0x1) != 0)
1570 PEPROCESS ProcessTo
;
1572 if (NT_SUCCESS(PsLookupProcessByProcessId((HANDLE
)((ULONG_PTR
)FromPrevProcId
& ~0x1), &ProcessTo
)))
1574 GDIOBJ_SetOwnership(CopyTo
, ProcessTo
);
1575 ObDereferenceObject(ProcessTo
);
1580 /* mark the object as global */
1581 GDIOBJ_SetOwnership(CopyTo
, NULL
);
1584 (void)InterlockedExchangePointer((PVOID
*)&FromEntry
->ProcessId
, FromPrevProcId
);
1588 GDIDBG_TRACELOOP(CopyFrom
, PrevThread
, Thread
);
1590 /* WTF?! The object is already locked by a different thread!
1591 Release the lock, wait a bit and try again! DO reset the pid lock
1592 so we make sure we don't access invalid memory in case the object is
1593 being deleted in the meantime (because we don't have aquired a reference
1595 FIXME - we should give up after some time unless we want to wait forever! */
1596 (void)InterlockedExchangePointer((PVOID
*)&FromEntry
->ProcessId
, FromPrevProcId
);
1599 goto LockHandleFrom
;
1604 DPRINT1("Attempted to copy ownership from an object 0x%x currently being destroyed!!!\n", CopyFrom
);
1608 else if (FromPrevProcId
== FromLockedProcessId
)
1610 GDIDBG_TRACELOOP(CopyFrom
, FromPrevProcId
, FromProcessId
);
1612 /* the object is currently locked, wait some time and try again.
1613 FIXME - we shouldn't loop forever! Give up after some time! */
1616 goto LockHandleFrom
;
1618 else if ((HANDLE
)((ULONG_PTR
)FromPrevProcId
& ~0x1) != PsGetCurrentProcessId())
1620 /* FIXME - should we really allow copying ownership from objects that we don't even own? */
1621 DPRINT1("WARNING! Changing copying ownership of object 0x%x (pid: 0x%x) to pid 0x%x!!!\n", CopyFrom
, (ULONG_PTR
)FromPrevProcId
& ~0x1, PsGetCurrentProcessId());
1622 FromProcessId
= (HANDLE
)((ULONG_PTR
)FromPrevProcId
& ~0x1);
1623 FromLockedProcessId
= (HANDLE
)((ULONG_PTR
)FromProcessId
| 0x1);
1624 goto LockHandleFrom
;
1628 DPRINT1("Attempted to copy ownership from invalid handle: 0x%x\n", CopyFrom
);
1636 GDI_MapHandleTable(PSECTION_OBJECT SectionObject
, PEPROCESS Process
)
1638 PVOID MappedView
= NULL
;
1640 LARGE_INTEGER Offset
;
1641 ULONG ViewSize
= sizeof(GDI_HANDLE_TABLE
);
1643 Offset
.QuadPart
= 0;
1645 ASSERT(SectionObject
!= NULL
);
1646 ASSERT(Process
!= NULL
);
1648 Status
= MmMapViewOfSection(SectionObject
,
1659 if (!NT_SUCCESS(Status
))
1665 /* Locks 2 or 3 objects at a time */
1668 GDIOBJ_LockMultipleObjs(ULONG ulCount
,
1672 UINT auiIndices
[3] = {0,1,2};
1674 BOOL bUnsorted
= TRUE
;
1676 /* First is greatest */
1680 for(i
=1; i
<ulCount
; i
++)
1682 if((ULONG_PTR
)ahObj
[auiIndices
[i
-1]] < (ULONG_PTR
)ahObj
[auiIndices
[i
]])
1684 tmp
= auiIndices
[i
-1];
1685 auiIndices
[i
-1] = auiIndices
[i
];
1686 auiIndices
[i
] = tmp
;
1692 for(i
=0;i
<ulCount
;i
++)
1693 apObj
[auiIndices
[i
]] = GDIOBJ_LockObj(ahObj
[auiIndices
[i
]], GDI_OBJECT_TYPE_DONTCARE
);
1697 /** PUBLIC FUNCTIONS **********************************************************/
1701 IntGdiSetRegionOwner(HRGN hRgn
, DWORD OwnerMask
)
1704 PGDI_TABLE_ENTRY Entry
;
1707 These regions do not use attribute sections and when allocated, use gdiobj
1710 // FIXME! HAX!!! Remove this once we get everything right!
1711 Index
= GDI_HANDLE_GET_INDEX(hRgn
);
1712 Entry
= &GdiHandleTable
->Entries
[Index
];
1713 if (Entry
->UserData
) FreeObjectAttr(Entry
->UserData
);
1714 Entry
->UserData
= NULL
;
1716 if ((OwnerMask
== GDI_OBJ_HMGR_PUBLIC
) || OwnerMask
== GDI_OBJ_HMGR_NONE
)
1718 return GDIOBJ_SetOwnership(hRgn
, NULL
);
1720 if (OwnerMask
== GDI_OBJ_HMGR_POWNED
)
1722 return GDIOBJ_SetOwnership((HGDIOBJ
) hRgn
, PsGetCurrentProcess() );
1729 IntGdiSetBrushOwner(PBRUSH pbr
, DWORD OwnerMask
)
1732 PEPROCESS Owner
= NULL
;
1733 PGDI_TABLE_ENTRY pEntry
= NULL
;
1735 if (!pbr
) return FALSE
;
1737 hBR
= pbr
->BaseObject
.hHmgr
;
1739 if (!hBR
|| (GDI_HANDLE_GET_TYPE(hBR
) != GDI_OBJECT_TYPE_BRUSH
))
1743 INT Index
= GDI_HANDLE_GET_INDEX((HGDIOBJ
)hBR
);
1744 pEntry
= &GdiHandleTable
->Entries
[Index
];
1747 if (pbr
->flAttrs
& GDIBRUSH_IS_GLOBAL
)
1749 GDIOBJ_ShareUnlockObjByPtr((POBJ
)pbr
);
1753 if ((OwnerMask
== GDI_OBJ_HMGR_PUBLIC
) || OwnerMask
== GDI_OBJ_HMGR_NONE
)
1755 // Set this Brush to inaccessible mode and to an Owner of NONE.
1756 // if (OwnerMask == GDI_OBJ_HMGR_NONE) Owner = OwnerMask;
1758 if (!GDIOBJ_SetOwnership((HGDIOBJ
) hBR
, Owner
))
1761 // Deny user access to User Data.
1762 pEntry
->UserData
= NULL
; // This hBR is inaccessible!
1765 if (OwnerMask
== GDI_OBJ_HMGR_POWNED
)
1767 if (!GDIOBJ_SetOwnership((HGDIOBJ
) hBR
, PsGetCurrentProcess() ))
1770 // Allow user access to User Data.
1771 pEntry
->UserData
= pbr
->pBrushAttr
;
1778 IntGdiSetDCOwnerEx( HDC hDC
, DWORD OwnerMask
, BOOL NoSetBrush
)
1783 if (!hDC
|| (GDI_HANDLE_GET_TYPE(hDC
) != GDI_OBJECT_TYPE_DC
)) return FALSE
;
1785 if ((OwnerMask
== GDI_OBJ_HMGR_PUBLIC
) || OwnerMask
== GDI_OBJ_HMGR_NONE
)
1787 pDC
= DC_LockDc ( hDC
);
1788 MmCopyFromCaller(&pDC
->dcattr
, pDC
->pdcattr
, sizeof(DC_ATTR
));
1789 DC_vFreeDcAttr(pDC
);
1792 if (!DC_SetOwnership( hDC
, NULL
)) // This hDC is inaccessible!
1796 if (OwnerMask
== GDI_OBJ_HMGR_POWNED
)
1798 pDC
= DC_LockDc ( hDC
);
1799 ASSERT(pDC
->pdcattr
== &pDC
->dcattr
);
1802 if (!DC_SetOwnership( hDC
, PsGetCurrentProcess() )) return Ret
;
1804 DC_AllocateDcAttr( hDC
); // Allocate new dcattr
1806 DCU_SynchDcAttrtoUser( hDC
); // Copy data from dc to dcattr
1809 if ((OwnerMask
!= GDI_OBJ_HMGR_NONE
) && !NoSetBrush
)
1811 pDC
= DC_LockDc ( hDC
);
1812 if (IntGdiSetBrushOwner((PBRUSH
)pDC
->dclevel
.pbrFill
, OwnerMask
))
1813 IntGdiSetBrushOwner((PBRUSH
)pDC
->dclevel
.pbrLine
, OwnerMask
);
1821 GreGetObjectOwner(HGDIOBJ Handle
, GDIOBJTYPE ObjType
)
1823 INT Ret
= GDI_OBJ_HMGR_RESTRICTED
;
1825 if ( GDI_HANDLE_GET_INDEX(Handle
) < GDI_HANDLE_COUNT
)
1827 PGDI_TABLE_ENTRY pEntry
= &GdiHandleTable
->Entries
[GDI_HANDLE_GET_INDEX(Handle
)];
1829 if (pEntry
->ObjectType
== ObjType
)
1831 if (pEntry
->FullUnique
== (GDI_HANDLE_GET_UPPER(Handle
) >> GDI_ENTRY_UPPER_SHIFT
))
1832 Ret
= pEntry
->ProcessId
& ~1;
1841 NtGdiCreateClientObj(
1848 /* Mask out everything that would change the type in a wrong manner */
1849 ulType
&= (GDI_HANDLE_TYPE_MASK
& ~GDI_HANDLE_BASETYPE_MASK
);
1851 /* Allocate a new object */
1852 pObject
= GDIOBJ_AllocObjWithHandle(GDI_OBJECT_TYPE_CLIOBJ
| ulType
);
1858 /* get the handle */
1859 handle
= pObject
->hHmgr
;
1862 GDIOBJ_UnlockObjByPtr(pObject
);
1870 NtGdiDeleteClientObj(
1874 /* We first need to get the real type from the handle */
1875 ULONG type
= GDI_HANDLE_GET_TYPE(h
);
1877 /* Check if it's really a CLIENTOBJ */
1878 if ((type
& GDI_HANDLE_BASETYPE_MASK
) != GDILoObjType_LO_CLIENTOBJ_TYPE
)
1880 /* FIXME: SetLastError? */
1883 return GDIOBJ_FreeObjByHandle(h
, type
);
1888 IntGdiGetObject(IN HANDLE Handle
,
1896 pGdiObject
= GDIOBJ_LockObj(Handle
, GDI_OBJECT_TYPE_DONTCARE
);
1899 EngSetLastError(ERROR_INVALID_HANDLE
);
1903 dwObjectType
= GDIOBJ_GetObjectType(Handle
);
1904 switch (dwObjectType
)
1906 case GDI_OBJECT_TYPE_PEN
:
1907 case GDI_OBJECT_TYPE_EXTPEN
:
1908 Result
= PEN_GetObject((PBRUSH
) pGdiObject
, cbCount
, (PLOGPEN
) lpBuffer
); // IntGdiCreatePenIndirect
1911 case GDI_OBJECT_TYPE_BRUSH
:
1912 Result
= BRUSH_GetObject((PBRUSH
) pGdiObject
, cbCount
, (LPLOGBRUSH
)lpBuffer
);
1915 case GDI_OBJECT_TYPE_BITMAP
:
1916 Result
= BITMAP_GetObject((SURFACE
*) pGdiObject
, cbCount
, lpBuffer
);
1918 case GDI_OBJECT_TYPE_FONT
:
1919 Result
= FontGetObject((PTEXTOBJ
) pGdiObject
, cbCount
, lpBuffer
);
1921 // Fix the LOGFONT structure for the stock fonts
1922 if (FIRST_STOCK_HANDLE
<= Handle
&& Handle
<= LAST_STOCK_HANDLE
)
1924 FixStockFontSizeW(Handle
, cbCount
, lpBuffer
);
1929 case GDI_OBJECT_TYPE_PALETTE
:
1930 Result
= PALETTE_GetObject((PPALETTE
) pGdiObject
, cbCount
, lpBuffer
);
1934 DPRINT1("GDI object type 0x%08x not implemented\n", dwObjectType
);
1938 GDIOBJ_UnlockObjByPtr(pGdiObject
);
1948 NtGdiExtGetObjectW(IN HANDLE hGdiObj
,
1950 OUT LPVOID lpBuffer
)
1957 DIBSECTION dibsection
;
1961 EXTLOGFONTW extlogfontw
;
1962 ENUMLOGFONTEXDVW enumlogfontexdvw
;
1965 // Normalize to the largest supported object size
1966 cbCount
= min((UINT
)cbCount
, sizeof(Object
));
1968 // Now do the actual call
1969 iRetCount
= IntGdiGetObject(hGdiObj
, cbCount
, lpBuffer
? &Object
: NULL
);
1970 cbCopyCount
= min((UINT
)cbCount
, (UINT
)iRetCount
);
1972 // Make sure we have a buffer and a copy size
1973 if ((cbCopyCount
) && (lpBuffer
))
1975 // Enter SEH for buffer transfer
1978 // Probe the buffer and copy it
1979 ProbeForWrite(lpBuffer
, cbCopyCount
, sizeof(WORD
));
1980 RtlCopyMemory(lpBuffer
, &Object
, cbCopyCount
);
1982 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1984 // Clear the return value.
1985 // Do *NOT* set last error here!