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 ******************************************************************/
17 #define GDI_ENTRY_TO_INDEX(ht, e) \
18 (((ULONG_PTR)(e) - (ULONG_PTR)&((ht)->Entries[0])) / sizeof(GDI_TABLE_ENTRY))
19 #define GDI_HANDLE_GET_ENTRY(HandleTable, h) \
20 (&(HandleTable)->Entries[GDI_HANDLE_GET_INDEX((h))])
22 /* apparently the first 10 entries are never used in windows as they are empty */
23 #define RESERVE_ENTRIES_COUNT 10
25 #define BASE_OBJTYPE_COUNT 32
27 #define DelayExecution() \
28 DPRINT("%s:%i: Delay\n", __FILE__, __LINE__); \
29 KeDelayExecutionThread(KernelMode, FALSE, &ShortDelay)
33 /* static */ /* FIXME: -fno-unit-at-a-time breaks this */
34 BOOL INTERNAL_CALL
GDI_CleanupDummy(PVOID ObjectBody
);
36 /** GLOBALS *******************************************************************/
43 GDICLEANUPPROC CleanupProc
;
44 } OBJ_TYPE_INFO
, *POBJ_TYPE_INFO
;
47 OBJ_TYPE_INFO ObjTypeInfo
[BASE_OBJTYPE_COUNT
] =
49 {0, 0, 0, NULL
}, /* 00 reserved entry */
50 {1, sizeof(DC
), TAG_DC
, DC_Cleanup
}, /* 01 DC */
51 {1, 0, 0, NULL
}, /* 02 UNUSED1 */
52 {1, 0, 0, NULL
}, /* 03 UNUSED2 */
53 {1, sizeof(ROSRGNDATA
), TAG_REGION
, REGION_Cleanup
}, /* 04 RGN */
54 {1, sizeof(SURFACE
), TAG_SURFACE
, SURFACE_Cleanup
}, /* 05 SURFACE */
55 {1, sizeof(CLIENTOBJ
), TAG_CLIENTOBJ
, GDI_CleanupDummy
}, /* 06 CLIENTOBJ: METADC,... */
56 {1, sizeof(PATH
), TAG_PATH
, GDI_CleanupDummy
}, /* 07 PATH */
57 {1, sizeof(PALETTE
), TAG_PALETTE
, PALETTE_Cleanup
}, /* 08 PAL */
58 {1, sizeof(COLORSPACE
), TAG_ICMLCS
, GDI_CleanupDummy
}, /* 09 ICMLCS, */
59 {1, sizeof(TEXTOBJ
), TAG_LFONT
, GDI_CleanupDummy
}, /* 0a LFONT */
60 {0, 0, TAG_RFONT
, NULL
}, /* 0b RFONT, unused */
61 {0, 0, TAG_PFE
, NULL
}, /* 0c PFE, unused */
62 {0, 0, TAG_PFT
, NULL
}, /* 0d PFT, unused */
63 {0, sizeof(GDICLRXFORM
), TAG_ICMCXF
, GDI_CleanupDummy
}, /* 0e ICMCXF, */
64 {0, 0, TAG_SPRITE
, NULL
}, /* 0f SPRITE, unused */
65 {1, sizeof(BRUSH
), TAG_BRUSH
, BRUSH_Cleanup
}, /* 10 BRUSH, PEN, EXTPEN */
66 {0, 0, TAG_UMPD
, NULL
}, /* 11 UMPD, unused */
67 {0, 0, 0, NULL
}, /* 12 UNUSED4 */
68 {0, 0, TAG_SPACE
, NULL
}, /* 13 SPACE, unused */
69 {0, 0, 0, NULL
}, /* 14 UNUSED5 */
70 {0, 0, TAG_META
, NULL
}, /* 15 META, unused */
71 {0, 0, TAG_EFSTATE
, NULL
}, /* 16 EFSTATE, unused */
72 {0, 0, TAG_BMFD
, NULL
}, /* 17 BMFD, unused */
73 {0, 0, TAG_VTFD
, NULL
}, /* 18 VTFD, unused */
74 {0, 0, TAG_TTFD
, NULL
}, /* 19 TTFD, unused */
75 {0, 0, TAG_RC
, NULL
}, /* 1a RC, unused */
76 {0, 0, TAG_TEMP
, NULL
}, /* 1b TEMP, unused */
77 {0, sizeof(EDRIVEROBJ
), TAG_DRVOBJ
, DRIVEROBJ_Cleanup
},/* 1c DRVOBJ */
78 {0, 0, TAG_DCIOBJ
, NULL
}, /* 1d DCIOBJ, unused */
79 {0, 0, TAG_SPOOL
, NULL
}, /* 1e SPOOL, unused */
80 {0, 0, 0, NULL
}, /* 1f reserved entry */
83 static LARGE_INTEGER ShortDelay
;
85 /** INTERNAL FUNCTIONS ********************************************************/
95 AllocTypeDataDump(INT TypeInfo
)
97 switch( TypeInfo
& GDI_HANDLE_TYPE_MASK
)
99 case GDILoObjType_LO_BRUSH_TYPE
:
102 case GDILoObjType_LO_DC_TYPE
:
105 case GDILoObjType_LO_BITMAP_TYPE
:
108 case GDILoObjType_LO_FONT_TYPE
:
111 case GDILoObjType_LO_REGION_TYPE
:
118 DeAllocTypeDataDump(INT TypeInfo
)
120 switch( TypeInfo
& GDI_HANDLE_TYPE_MASK
)
122 case GDILoObjType_LO_BRUSH_TYPE
:
125 case GDILoObjType_LO_DC_TYPE
:
128 case GDILoObjType_LO_BITMAP_TYPE
:
131 case GDILoObjType_LO_FONT_TYPE
:
134 case GDILoObjType_LO_REGION_TYPE
:
141 * Dummy GDI Cleanup Callback
143 /* static */ /* FIXME: -fno-unit-at-a-time breaks this */
145 GDI_CleanupDummy(PVOID ObjectBody
)
151 * Allocate GDI object table.
152 * \param Size - number of entries in the object table.
154 PGDI_HANDLE_TABLE INTERNAL_CALL
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
;
225 LockErrorDebugOutput(HGDIOBJ hObj
, PGDI_TABLE_ENTRY Entry
, LPSTR Function
)
227 if ((Entry
->Type
& GDI_ENTRY_BASETYPE_MASK
) == 0)
229 DPRINT1("%s: Attempted to lock object 0x%x that is deleted!\n", Function
, hObj
);
230 GDIDBG_TRACEDELETER(hObj
);
232 else if (GDI_HANDLE_GET_REUSECNT(hObj
) != GDI_ENTRY_GET_REUSECNT(Entry
->Type
))
234 DPRINT1("%s: Attempted to lock object 0x%x, wrong reuse counter (Handle: 0x%x, Entry: 0x%x)\n",
235 Function
, hObj
, GDI_HANDLE_GET_REUSECNT(hObj
), GDI_ENTRY_GET_REUSECNT(Entry
->Type
));
237 else if (GDI_HANDLE_GET_TYPE(hObj
) != ((Entry
->Type
<< GDI_ENTRY_UPPER_SHIFT
) & GDI_HANDLE_TYPE_MASK
))
239 DPRINT1("%s: Attempted to lock object 0x%x, type mismatch (Handle: 0x%x, Entry: 0x%x)\n",
240 Function
, hObj
, GDI_HANDLE_GET_TYPE(hObj
), (Entry
->Type
<< GDI_ENTRY_UPPER_SHIFT
) & GDI_HANDLE_TYPE_MASK
);
244 DPRINT1("%s: Attempted to lock object 0x%x, something went wrong, typeinfo = 0x%x\n",
245 Function
, hObj
, Entry
->Type
);
247 GDIDBG_TRACECALLER();
252 InterlockedPopFreeEntry(VOID
)
254 ULONG idxFirst
, idxNext
, idxPrev
;
255 PGDI_TABLE_ENTRY pEntry
;
258 DPRINT("Enter InterLockedPopFreeEntry\n");
262 idxFirst
= GdiHandleTable
->FirstFree
;
266 /* Increment FirstUnused and get the new index */
267 idxFirst
= InterlockedIncrement((LONG
*)&GdiHandleTable
->FirstUnused
) - 1;
269 /* Check if we have entries left */
270 if (idxFirst
>= GDI_HANDLE_COUNT
)
272 DPRINT1("No more gdi handles left!\n");
276 /* Return the old index */
280 /* Get a pointer to the first free entry */
281 pEntry
= GdiHandleTable
->Entries
+ idxFirst
;
283 /* Try to lock the entry */
284 PrevProcId
= InterlockedCompareExchange((LONG
*)&pEntry
->ProcessId
, 1, 0);
287 /* The entry was locked or not free, wait and start over */
292 /* Sanity check: is entry really free? */
293 ASSERT(((ULONG_PTR
)pEntry
->KernelData
& ~GDI_HANDLE_INDEX_MASK
) == 0);
295 /* Try to exchange the FirstFree value */
296 idxNext
= (ULONG_PTR
)pEntry
->KernelData
;
297 idxPrev
= InterlockedCompareExchange((LONG
*)&GdiHandleTable
->FirstFree
,
301 /* Unlock the free entry */
302 (void)InterlockedExchange((LONG
*)&pEntry
->ProcessId
, 0);
304 /* If we succeeded, break out of the loop */
305 if (idxPrev
== idxFirst
)
314 /* Pushes an entry of the handle table to the free list,
315 The entry must be unlocked and the base type field must be 0 */
318 InterlockedPushFreeEntry(ULONG idxToFree
)
320 ULONG idxFirstFree
, idxPrev
;
321 PGDI_TABLE_ENTRY pFreeEntry
;
323 DPRINT("Enter InterlockedPushFreeEntry\n");
325 pFreeEntry
= GdiHandleTable
->Entries
+ idxToFree
;
326 ASSERT((pFreeEntry
->Type
& GDI_ENTRY_BASETYPE_MASK
) == 0);
327 ASSERT(pFreeEntry
->ProcessId
== 0);
328 pFreeEntry
->UserData
= NULL
;
332 idxFirstFree
= GdiHandleTable
->FirstFree
;
333 pFreeEntry
->KernelData
= (PVOID
)(ULONG_PTR
)idxFirstFree
;
335 idxPrev
= InterlockedCompareExchange((LONG
*)&GdiHandleTable
->FirstFree
,
339 while (idxPrev
!= idxFirstFree
);
345 GDIOBJ_ValidateHandle(HGDIOBJ hObj
, ULONG ObjectType
)
347 PGDI_TABLE_ENTRY Entry
= GDI_HANDLE_GET_ENTRY(GdiHandleTable
, hObj
);
348 if ((((ULONG_PTR
)hObj
& GDI_HANDLE_TYPE_MASK
) == ObjectType
) &&
349 (Entry
->Type
<< GDI_ENTRY_UPPER_SHIFT
) == GDI_HANDLE_GET_UPPER(hObj
))
351 HANDLE pid
= (HANDLE
)((ULONG_PTR
)Entry
->ProcessId
& ~0x1);
352 if (pid
== NULL
|| pid
== PsGetCurrentProcessId())
361 GDIOBJ_AllocObj(UCHAR BaseType
)
365 ASSERT((BaseType
& ~GDIObjTypeTotal
) == 0);
366 // BaseType &= GDI_HANDLE_BASETYPE_MASK;
368 if (ObjTypeInfo
[BaseType
].bUseLookaside
)
370 PPAGED_LOOKASIDE_LIST LookasideList
;
372 LookasideList
= GdiHandleTable
->LookasideLists
+ BaseType
;
373 pObject
= ExAllocateFromPagedLookasideList(LookasideList
);
377 pObject
= ExAllocatePoolWithTag(PagedPool
,
378 ObjTypeInfo
[BaseType
].ulBodySize
,
379 ObjTypeInfo
[BaseType
].Tag
);
384 RtlZeroMemory(pObject
, ObjTypeInfo
[BaseType
].ulBodySize
);
392 * Allocate memory for GDI object and return handle to it.
394 * \param ObjectType - type of object \ref GDI object types
396 * \return Pointer to the allocated object, which is locked.
399 GDIOBJ_AllocObjWithHandle(ULONG ObjectType
)
401 PPROCESSINFO W32Process
;
402 POBJ newObject
= NULL
;
403 HANDLE CurrentProcessId
, LockedProcessId
;
406 PGDI_TABLE_ENTRY Entry
;
409 GDIDBG_INITLOOPTRACE();
411 W32Process
= PsGetCurrentProcessWin32Process();
412 /* HACK HACK HACK: simplest-possible quota implementation - don't allow a process
413 to take too many GDI objects, itself. */
414 if (W32Process
&& W32Process
->GDIHandleCount
>= 0x2710)
416 DPRINT1("Too many objects for process!!!\n");
417 DPRINT1("DC %d BRUSH %d BITMAP %d FONT %d RGN %d\n",tDC
,tBRUSH
,tBITMAP
,tFONT
,tRGN
);
418 GDIDBG_DUMPHANDLETABLE();
422 ASSERT(ObjectType
!= GDI_OBJECT_TYPE_DONTCARE
);
424 TypeIndex
= GDI_OBJECT_GET_TYPE_INDEX(ObjectType
);
426 newObject
= GDIOBJ_AllocObj(TypeIndex
);
429 DPRINT1("Not enough memory to allocate gdi object!\n");
433 CurrentProcessId
= PsGetCurrentProcessId();
434 LockedProcessId
= (HANDLE
)((ULONG_PTR
)CurrentProcessId
| 0x1);
436 // RtlZeroMemory(newObject, ObjTypeInfo[TypeIndex].ulBodySize);
438 /* On Windows the higher 16 bit of the type field don't contain the
439 full type from the handle, but the base type.
440 (type = BRSUH, PEN, EXTPEN, basetype = BRUSH) */
441 TypeInfo
= (ObjectType
& GDI_HANDLE_BASETYPE_MASK
) | (ObjectType
>> GDI_ENTRY_UPPER_SHIFT
);
443 Index
= InterlockedPopFreeEntry();
448 Entry
= &GdiHandleTable
->Entries
[Index
];
451 PrevProcId
= InterlockedCompareExchangePointer((PVOID
*)&Entry
->ProcessId
, LockedProcessId
, 0);
452 if (PrevProcId
== NULL
)
454 PTHREADINFO Thread
= (PTHREADINFO
)PsGetCurrentThreadWin32Thread();
457 Entry
->KernelData
= newObject
;
459 /* copy the reuse-counter */
460 TypeInfo
|= Entry
->Type
& GDI_ENTRY_REUSE_MASK
;
462 /* we found a free entry, no need to exchange this field atomically
463 since we're holding the lock */
464 Entry
->Type
= TypeInfo
;
466 /* Create a handle */
467 Handle
= (HGDIOBJ
)((Index
& 0xFFFF) | (TypeInfo
<< GDI_ENTRY_UPPER_SHIFT
));
469 /* Initialize BaseObject fields */
470 newObject
->hHmgr
= Handle
;
471 newObject
->ulShareCount
= 0;
472 newObject
->cExclusiveLock
= 1;
473 newObject
->Tid
= Thread
;
475 AllocTypeDataDump(TypeInfo
);
477 /* unlock the entry */
478 (void)InterlockedExchangePointer((PVOID
*)&Entry
->ProcessId
, CurrentProcessId
);
480 GDIDBG_CAPTUREALLOCATOR(Index
);
482 if (W32Process
!= NULL
)
484 InterlockedIncrement(&W32Process
->GDIHandleCount
);
487 DPRINT("GDIOBJ_AllocObj: 0x%x ob: 0x%x\n", Handle
, newObject
);
492 GDIDBG_TRACELOOP(Index
, PrevProcId
, CurrentProcessId
);
493 /* damn, someone is trying to lock the object even though it doesn't
494 even exist anymore, wait a little and try again!
495 FIXME - we shouldn't loop forever! Give up after some time! */
502 GDIOBJ_FreeObj(newObject
, TypeIndex
);
504 DPRINT1("Failed to insert gdi object into the handle table, no handles left!\n");
505 GDIDBG_DUMPHANDLETABLE();
512 GDIOBJ_FreeObj(POBJ pObject
, UCHAR BaseType
)
514 /* Object must not have a handle! */
515 ASSERT(pObject
->hHmgr
== NULL
);
517 if (ObjTypeInfo
[BaseType
].bUseLookaside
)
519 PPAGED_LOOKASIDE_LIST LookasideList
;
521 LookasideList
= GdiHandleTable
->LookasideLists
+ BaseType
;
522 ExFreeToPagedLookasideList(LookasideList
, pObject
);
531 * Free memory allocated for the GDI object. For each object type this function calls the
532 * appropriate cleanup routine.
534 * \param hObj - handle of the object to be deleted.
536 * \return Returns TRUE if succesful.
537 * \return Returns FALSE if the cleanup routine returned FALSE or the object doesn't belong
538 * to the calling process.
540 * \bug This function should return VOID and kill the object no matter what...
543 GDIOBJ_FreeObjByHandle(HGDIOBJ hObj
, DWORD ExpectedType
)
545 PGDI_TABLE_ENTRY Entry
;
546 HANDLE ProcessId
, LockedProcessId
, PrevProcId
;
547 ULONG HandleType
, HandleUpper
, TypeIndex
;
550 GDIDBG_INITLOOPTRACE();
552 DPRINT("GDIOBJ_FreeObj: hObj: 0x%08x\n", hObj
);
554 if (GDI_HANDLE_IS_STOCKOBJ(hObj
))
556 DPRINT1("GDIOBJ_FreeObj() failed, can't delete stock object handle: 0x%x !!!\n", hObj
);
557 GDIDBG_TRACECALLER();
561 ProcessId
= PsGetCurrentProcessId();
562 LockedProcessId
= (HANDLE
)((ULONG_PTR
)ProcessId
| 0x1);
564 Silent
= (ExpectedType
& GDI_OBJECT_TYPE_SILENT
);
565 ExpectedType
&= ~GDI_OBJECT_TYPE_SILENT
;
567 HandleType
= GDI_HANDLE_GET_TYPE(hObj
);
568 HandleUpper
= GDI_HANDLE_GET_UPPER(hObj
);
570 /* Check if we have the requested type */
571 if ( (ExpectedType
!= GDI_OBJECT_TYPE_DONTCARE
&&
572 HandleType
!= ExpectedType
) ||
575 DPRINT1("Attempted to free object 0x%x of wrong type (Handle: 0x%x, expected: 0x%x)\n",
576 hObj
, HandleType
, ExpectedType
);
577 GDIDBG_TRACECALLER();
581 Entry
= GDI_HANDLE_GET_ENTRY(GdiHandleTable
, hObj
);
584 /* lock the object, we must not delete global objects, so don't exchange the locking
585 process ID to zero when attempting to lock a global object... */
586 PrevProcId
= InterlockedCompareExchangePointer((PVOID
*)&Entry
->ProcessId
, LockedProcessId
, ProcessId
);
587 if (PrevProcId
== ProcessId
)
589 if ( (Entry
->KernelData
!= NULL
) &&
590 ((Entry
->Type
<< GDI_ENTRY_UPPER_SHIFT
) == HandleUpper
) &&
591 ((Entry
->Type
& GDI_ENTRY_BASETYPE_MASK
) == (HandleUpper
& GDI_ENTRY_BASETYPE_MASK
)) )
595 Object
= Entry
->KernelData
;
597 if ((Object
->cExclusiveLock
== 0 ||
598 Object
->Tid
== (PTHREADINFO
)PsGetCurrentThreadWin32Thread()) &&
599 Object
->ulShareCount
== 0)
602 PPROCESSINFO W32Process
= PsGetCurrentProcessWin32Process();
604 /* Clear the basetype field so when unlocking the handle it gets finally deleted and increment reuse counter */
605 Entry
->Type
= (Entry
->Type
+ GDI_ENTRY_REUSE_INC
) & ~GDI_ENTRY_BASETYPE_MASK
;
607 /* unlock the handle slot */
608 (void)InterlockedExchangePointer((PVOID
*)&Entry
->ProcessId
, NULL
);
610 /* push this entry to the free list */
611 InterlockedPushFreeEntry(GDI_ENTRY_TO_INDEX(GdiHandleTable
, Entry
));
613 Object
->hHmgr
= NULL
;
615 if (W32Process
!= NULL
)
617 InterlockedDecrement(&W32Process
->GDIHandleCount
);
620 /* call the cleanup routine. */
621 TypeIndex
= GDI_OBJECT_GET_TYPE_INDEX(HandleType
);
622 Ret
= ObjTypeInfo
[TypeIndex
].CleanupProc(Object
);
624 DeAllocTypeDataDump(HandleType
);
626 /* Now it's time to free the memory */
627 GDIOBJ_FreeObj(Object
, TypeIndex
);
629 GDIDBG_CAPTUREDELETER(hObj
);
632 else if (Object
->ulShareCount
!= 0)
634 Object
->BaseFlags
|= BASEFLAG_READY_TO_DIE
;
635 DPRINT("Object %p, ulShareCount = %d\n", Object
->hHmgr
, Object
->ulShareCount
);
636 //GDIDBG_TRACECALLER();
637 //GDIDBG_TRACESHARELOCKER(GDI_HANDLE_GET_INDEX(hObj));
638 (void)InterlockedExchangePointer((PVOID
*)&Entry
->ProcessId
, PrevProcId
);
639 /* Don't wait on shared locks */
645 * The object is currently locked by another thread, so freeing is forbidden!
647 DPRINT1("Object->cExclusiveLock = %d\n", Object
->cExclusiveLock
);
648 GDIDBG_TRACECALLER();
649 GDIDBG_TRACELOCKER(GDI_HANDLE_GET_INDEX(hObj
));
650 (void)InterlockedExchangePointer((PVOID
*)&Entry
->ProcessId
, PrevProcId
);
651 /* do not assert here for it will call again from dxg.sys it being call twice */
659 LockErrorDebugOutput(hObj
, Entry
, "GDIOBJ_FreeObj");
660 (void)InterlockedExchangePointer((PVOID
*)&Entry
->ProcessId
, PrevProcId
);
663 else if (PrevProcId
== LockedProcessId
)
665 GDIDBG_TRACELOOP(hObj
, PrevProcId
, ProcessId
);
667 /* the object is currently locked, wait some time and try again.
668 FIXME - we shouldn't loop forever! Give up after some time! */
677 if ((Entry
->Type
& GDI_ENTRY_BASETYPE_MASK
) == 0)
679 DPRINT1("Attempted to free gdi handle 0x%x that is already deleted!\n", hObj
);
681 else if (((ULONG_PTR
)PrevProcId
& ~0x1) == 0)
683 DPRINT1("Attempted to free global gdi handle 0x%x, caller needs to get ownership first!!!\n", hObj
);
687 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);
689 DPRINT1("Type = 0x%lx, KernelData = 0x%p, ProcessId = 0x%p\n", Entry
->Type
, Entry
->KernelData
, Entry
->ProcessId
);
690 GDIDBG_TRACECALLER();
691 GDIDBG_TRACEALLOCATOR(GDI_HANDLE_GET_INDEX(hObj
));
700 IsObjectDead(HGDIOBJ hObject
)
702 INT Index
= GDI_HANDLE_GET_INDEX(hObject
);
703 PGDI_TABLE_ENTRY Entry
= &GdiHandleTable
->Entries
[Index
];
704 // We check to see if the objects are knocking on deaths door.
705 if ((Entry
->Type
& GDI_ENTRY_BASETYPE_MASK
) != 0)
709 DPRINT1("Object 0x%x currently being destroyed!!!\n",hObject
);
710 return TRUE
; // return true and move on.
717 bPEBCacheHandle(HGDIOBJ Handle
, int oType
, PVOID pAttr
)
719 PGDIHANDLECACHE GdiHandleCache
;
722 int Offset
= 0, Number
;
725 GdiHandleCache
= (PGDIHANDLECACHE
)NtCurrentTeb()->ProcessEnvironmentBlock
->GdiHandleBuffer
;
734 Offset
= CACHE_BRUSH_ENTRIES
;
737 case hctRegionHandle
:
738 Offset
= CACHE_BRUSH_ENTRIES
+CACHE_PEN_ENTRIES
;
745 Lock
= InterlockedCompareExchangePointer( (PVOID
*)&GdiHandleCache
->ulLock
,
748 if (Lock
) return FALSE
;
752 Number
= GdiHandleCache
->ulNumHandles
[oType
];
754 hPtr
= GdiHandleCache
->Handle
+ Offset
;
756 if ( pAttr
&& oType
== hctRegionHandle
)
758 if ( Number
< CACHE_REGION_ENTRIES
)
760 ((PRGN_ATTR
)pAttr
)->AttrFlags
|= ATTR_CACHED
;
761 hPtr
[Number
] = Handle
;
762 GdiHandleCache
->ulNumHandles
[oType
]++;
763 DPRINT1("Put Handle Count %d PEB 0x%x\n", GdiHandleCache
->ulNumHandles
[oType
], NtCurrentTeb()->ProcessEnvironmentBlock
);
768 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
774 (void)InterlockedExchangePointer((PVOID
*)&GdiHandleCache
->ulLock
, Lock
);
780 * \param hObject object handle
781 * \return if the function fails the returned value is FALSE.
785 GreDeleteObject(HGDIOBJ hObject
)
788 PGDI_TABLE_ENTRY Entry
;
792 DPRINT("NtGdiDeleteObject handle 0x%08x\n", hObject
);
793 if (!IsObjectDead(hObject
))
795 dwObjectType
= GDIOBJ_GetObjectType(hObject
);
797 Index
= GDI_HANDLE_GET_INDEX(hObject
);
798 Entry
= &GdiHandleTable
->Entries
[Index
];
799 pAttr
= Entry
->UserData
;
801 switch (dwObjectType
)
803 case GDI_OBJECT_TYPE_BRUSH
:
806 case GDI_OBJECT_TYPE_REGION
:
807 /* If pAttr NULL, the probability is high for System Region. */
809 bPEBCacheHandle(hObject
, hctRegionHandle
, pAttr
))
811 /* User space handle only! */
816 FreeObjectAttr(pAttr
);
817 Entry
->UserData
= NULL
;
821 case GDI_OBJECT_TYPE_DC
:
822 DC_FreeDcAttr(hObject
);
826 return NULL
!= hObject
827 ? GDIOBJ_FreeObjByHandle(hObject
, dwObjectType
) : FALSE
;
831 DPRINT1("Attempt DeleteObject 0x%x currently being destroyed!!!\n",hObject
);
832 return TRUE
; // return true and move on.
838 IntDeleteHandlesForProcess(struct _EPROCESS
*Process
, ULONG ObjectType
)
840 PGDI_TABLE_ENTRY Entry
, End
;
841 ULONG Index
= RESERVE_ENTRIES_COUNT
;
843 PPROCESSINFO W32Process
;
845 W32Process
= (PPROCESSINFO
)Process
->Win32Process
;
848 if (W32Process
->GDIHandleCount
> 0)
850 ProcId
= Process
->UniqueProcessId
;
852 /* FIXME - Instead of building the handle here and delete it using GDIOBJ_FreeObj
853 we should delete it directly here! */
855 End
= &GdiHandleTable
->Entries
[GDI_HANDLE_COUNT
];
856 for (Entry
= &GdiHandleTable
->Entries
[RESERVE_ENTRIES_COUNT
];
860 /* ignore the lock bit */
861 if ( (HANDLE
)((ULONG_PTR
)Entry
->ProcessId
& ~0x1) == ProcId
)
863 if ( (Entry
->Type
& GDI_ENTRY_BASETYPE_MASK
) == ObjectType
||
864 ObjectType
== GDI_OBJECT_TYPE_DONTCARE
)
866 HGDIOBJ ObjectHandle
;
868 /* Create the object handle for the entry, the lower(!) 16 bit of the
869 Type field includes the type of the object including the stock
870 object flag - but since stock objects don't have a process id we can
871 simply ignore this fact here. */
872 ObjectHandle
= (HGDIOBJ
)(Index
| (Entry
->Type
<< GDI_ENTRY_UPPER_SHIFT
));
874 if (!GDIOBJ_FreeObjByHandle(ObjectHandle
, GDI_OBJECT_TYPE_DONTCARE
))
876 DPRINT1("Failed to delete object %p!\n", ObjectHandle
);
879 if (W32Process
->GDIHandleCount
== 0)
881 /* there are no more gdi handles for this process, bail */
892 * Internal function. Called when the process is destroyed to free the remaining GDI handles.
893 * \param Process - PID of the process that will be destroyed.
896 GDI_CleanupForProcess(struct _EPROCESS
*Process
)
898 PEPROCESS CurrentProcess
;
899 PPROCESSINFO W32Process
;
901 DPRINT("Starting CleanupForProcess prochandle %x Pid %d\n", Process
, Process
->UniqueProcessId
);
902 CurrentProcess
= PsGetCurrentProcess();
903 if (CurrentProcess
!= Process
)
905 KeAttachProcess(&Process
->Pcb
);
908 W32Process
= (PPROCESSINFO
)CurrentProcess
->Win32Process
;
910 /* Delete objects. Begin with types that are not referenced by other types */
911 IntDeleteHandlesForProcess(Process
, GDILoObjType_LO_DC_TYPE
);
912 IntDeleteHandlesForProcess(Process
, GDILoObjType_LO_BRUSH_TYPE
);
913 IntDeleteHandlesForProcess(Process
, GDILoObjType_LO_BITMAP_TYPE
);
915 /* Finally finish with what's left */
916 IntDeleteHandlesForProcess(Process
, GDI_OBJECT_TYPE_DONTCARE
);
918 if (CurrentProcess
!= Process
)
924 GdiDbgHTIntegrityCheck();
927 DPRINT("Completed cleanup for process %d\n", Process
->UniqueProcessId
);
928 if (W32Process
->GDIHandleCount
> 0)
930 DPRINT1("Leaking %d handles!\n", W32Process
->GDIHandleCount
);
937 * Return pointer to the object by handle.
939 * \param hObj Object handle
940 * \return Pointer to the object.
942 * \note Process can only get pointer to the objects it created or global objects.
944 * \todo Get rid of the ExpectedType parameter!
946 PGDIOBJ INTERNAL_CALL
947 GDIOBJ_LockObj(HGDIOBJ hObj
, DWORD ExpectedType
)
950 PGDI_TABLE_ENTRY Entry
;
951 HANDLE ProcessId
, HandleProcessId
, LockedProcessId
, PrevProcId
;
953 ULONG HandleType
, HandleUpper
;
955 HandleIndex
= GDI_HANDLE_GET_INDEX(hObj
);
956 HandleType
= GDI_HANDLE_GET_TYPE(hObj
);
957 HandleUpper
= GDI_HANDLE_GET_UPPER(hObj
);
959 /* Check that the handle index is valid. */
960 if (HandleIndex
>= GDI_HANDLE_COUNT
)
963 Entry
= &GdiHandleTable
->Entries
[HandleIndex
];
965 /* Check if we have the requested type */
966 if ( (ExpectedType
!= GDI_OBJECT_TYPE_DONTCARE
&&
967 HandleType
!= ExpectedType
) ||
970 DPRINT("Attempted to lock object 0x%x of wrong type (Handle: 0x%x, requested: 0x%x)\n",
971 hObj
, HandleType
, ExpectedType
);
972 GDIDBG_TRACECALLER();
973 GDIDBG_TRACEALLOCATOR(hObj
);
974 GDIDBG_TRACEDELETER(hObj
);
978 ProcessId
= (HANDLE
)((ULONG_PTR
)PsGetCurrentProcessId() & ~1);
979 HandleProcessId
= (HANDLE
)((ULONG_PTR
)Entry
->ProcessId
& ~1);
981 /* Check for invalid owner. */
982 if (ProcessId
!= HandleProcessId
&& HandleProcessId
!= NULL
)
984 DPRINT1("Tried to lock object (0x%p) of wrong owner! ProcessId = %p, HandleProcessId = %p\n", hObj
, ProcessId
, HandleProcessId
);
985 GDIDBG_TRACECALLER();
986 GDIDBG_TRACEALLOCATOR(GDI_HANDLE_GET_INDEX(hObj
));
991 * Prevent the thread from being terminated during the locking process.
992 * It would result in undesired effects and inconsistency of the global
996 KeEnterCriticalRegion();
999 * Loop until we either successfully lock the handle entry & object or
1000 * fail some of the check.
1005 /* Lock the handle table entry. */
1006 LockedProcessId
= (HANDLE
)((ULONG_PTR
)HandleProcessId
| 0x1);
1007 PrevProcId
= InterlockedCompareExchangePointer((PVOID
*)&Entry
->ProcessId
,
1011 if (PrevProcId
== HandleProcessId
)
1014 * We're locking an object that belongs to our process or it's a
1015 * global object if HandleProcessId is 0 here.
1018 if ( (Entry
->KernelData
!= NULL
) &&
1019 ((Entry
->Type
<< GDI_ENTRY_UPPER_SHIFT
) == HandleUpper
) )
1021 PTHREADINFO Thread
= (PTHREADINFO
)PsGetCurrentThreadWin32Thread();
1022 Object
= Entry
->KernelData
;
1024 if (Object
->cExclusiveLock
== 0)
1026 Object
->Tid
= Thread
;
1027 Object
->cExclusiveLock
= 1;
1028 GDIDBG_CAPTURELOCKER(GDI_HANDLE_GET_INDEX(hObj
))
1032 if (Object
->Tid
!= Thread
)
1034 /* Unlock the handle table entry. */
1035 (void)InterlockedExchangePointer((PVOID
*)&Entry
->ProcessId
, PrevProcId
);
1040 InterlockedIncrement((PLONG
)&Object
->cExclusiveLock
);
1046 * Debugging code. Report attempts to lock deleted handles and
1047 * locking type mismatches.
1049 LockErrorDebugOutput(hObj
, Entry
, "GDIOBJ_LockObj");
1052 /* Unlock the handle table entry. */
1053 (void)InterlockedExchangePointer((PVOID
*)&Entry
->ProcessId
, PrevProcId
);
1060 * The handle is currently locked, wait some time and try again.
1068 KeLeaveCriticalRegion();
1075 * Return pointer to the object by handle (and allow sharing of the handle
1078 * \param hObj Object handle
1079 * \return Pointer to the object.
1081 * \note Process can only get pointer to the objects it created or global objects.
1083 * \todo Get rid of the ExpectedType parameter!
1085 PGDIOBJ INTERNAL_CALL
1086 GDIOBJ_ShareLockObj(HGDIOBJ hObj
, DWORD ExpectedType
)
1089 PGDI_TABLE_ENTRY Entry
;
1090 HANDLE ProcessId
, HandleProcessId
, LockedProcessId
, PrevProcId
;
1092 ULONG_PTR HandleType
, HandleUpper
;
1094 HandleIndex
= GDI_HANDLE_GET_INDEX(hObj
);
1095 HandleType
= GDI_HANDLE_GET_TYPE(hObj
);
1096 HandleUpper
= GDI_HANDLE_GET_UPPER(hObj
);
1098 /* Check that the handle index is valid. */
1099 if (HandleIndex
>= GDI_HANDLE_COUNT
)
1102 /* Check if we have the requested type */
1103 if ( (ExpectedType
!= GDI_OBJECT_TYPE_DONTCARE
&&
1104 HandleType
!= ExpectedType
) ||
1107 DPRINT1("Attempted to lock object 0x%x of wrong type (Handle: 0x%x, requested: 0x%x)\n",
1108 hObj
, HandleType
, ExpectedType
);
1112 Entry
= &GdiHandleTable
->Entries
[HandleIndex
];
1114 ProcessId
= (HANDLE
)((ULONG_PTR
)PsGetCurrentProcessId() & ~1);
1115 HandleProcessId
= (HANDLE
)((ULONG_PTR
)Entry
->ProcessId
& ~1);
1117 /* Check for invalid owner. */
1118 if (ProcessId
!= HandleProcessId
&& HandleProcessId
!= NULL
)
1124 * Prevent the thread from being terminated during the locking process.
1125 * It would result in undesired effects and inconsistency of the global
1129 KeEnterCriticalRegion();
1132 * Loop until we either successfully lock the handle entry & object or
1133 * fail some of the check.
1138 /* Lock the handle table entry. */
1139 LockedProcessId
= (HANDLE
)((ULONG_PTR
)HandleProcessId
| 0x1);
1140 PrevProcId
= InterlockedCompareExchangePointer((PVOID
*)&Entry
->ProcessId
,
1144 if (PrevProcId
== HandleProcessId
)
1147 * We're locking an object that belongs to our process or it's a
1148 * global object if HandleProcessId is 0 here.
1151 if ( (Entry
->KernelData
!= NULL
) &&
1152 (HandleUpper
== (Entry
->Type
<< GDI_ENTRY_UPPER_SHIFT
)) )
1154 Object
= (POBJ
)Entry
->KernelData
;
1156 GDIDBG_CAPTURESHARELOCKER(HandleIndex
);
1158 if (InterlockedIncrement((PLONG
)&Object
->ulShareCount
) == 1)
1160 memset(GDIHandleLocker
[HandleIndex
], 0x00, GDI_STACK_LEVELS
* sizeof(ULONG
));
1161 RtlCaptureStackBackTrace(1, GDI_STACK_LEVELS
, (PVOID
*)GDIHandleShareLocker
[HandleIndex
], NULL
);
1164 InterlockedIncrement((PLONG
)&Object
->ulShareCount
);
1170 * Debugging code. Report attempts to lock deleted handles and
1171 * locking type mismatches.
1173 LockErrorDebugOutput(hObj
, Entry
, "GDIOBJ_ShareLockObj");
1176 /* Unlock the handle table entry. */
1177 (void)InterlockedExchangePointer((PVOID
*)&Entry
->ProcessId
, PrevProcId
);
1184 * The handle is currently locked, wait some time and try again.
1192 KeLeaveCriticalRegion();
1198 GDIOBJ_OwnedByCurrentProcess(HGDIOBJ ObjectHandle
)
1200 PGDI_TABLE_ENTRY Entry
;
1204 DPRINT("GDIOBJ_OwnedByCurrentProcess: ObjectHandle: 0x%08x\n", ObjectHandle
);
1206 if (!GDI_HANDLE_IS_STOCKOBJ(ObjectHandle
))
1208 ProcessId
= PsGetCurrentProcessId();
1210 Entry
= GDI_HANDLE_GET_ENTRY(GdiHandleTable
, ObjectHandle
);
1211 Ret
= Entry
->KernelData
!= NULL
&&
1212 (Entry
->Type
& GDI_ENTRY_BASETYPE_MASK
) != 0 &&
1213 (HANDLE
)((ULONG_PTR
)Entry
->ProcessId
& ~0x1) == ProcessId
;
1222 GDIOBJ_ConvertToStockObj(HGDIOBJ
*phObj
)
1225 * FIXME !!!!! THIS FUNCTION NEEDS TO BE FIXED - IT IS NOT SAFE WHEN OTHER THREADS
1226 * MIGHT ATTEMPT TO LOCK THE OBJECT DURING THIS CALL!!!
1228 PGDI_TABLE_ENTRY Entry
;
1229 HANDLE ProcessId
, LockedProcessId
, PrevProcId
;
1233 GDIDBG_INITLOOPTRACE();
1238 DPRINT("GDIOBJ_ConvertToStockObj: hObj: 0x%08x\n", hObj
);
1240 Thread
= (PTHREADINFO
)PsGetCurrentThreadWin32Thread();
1242 if (!GDI_HANDLE_IS_STOCKOBJ(hObj
))
1244 ProcessId
= PsGetCurrentProcessId();
1245 LockedProcessId
= (HANDLE
)((ULONG_PTR
)ProcessId
| 0x1);
1247 Entry
= GDI_HANDLE_GET_ENTRY(GdiHandleTable
, hObj
);
1250 /* lock the object, we must not convert stock objects, so don't check!!! */
1251 PrevProcId
= InterlockedCompareExchangePointer((PVOID
*)&Entry
->ProcessId
, LockedProcessId
, ProcessId
);
1252 if (PrevProcId
== ProcessId
)
1254 LONG NewType
, PrevType
, OldType
;
1256 /* we're locking an object that belongs to our process. First calculate
1257 the new object type including the stock object flag and then try to
1259 /* On Windows the higher 16 bit of the type field don't contain the
1260 full type from the handle, but the base type.
1261 (type = BRSUH, PEN, EXTPEN, basetype = BRUSH) */
1262 OldType
= ((ULONG
)hObj
& GDI_HANDLE_BASETYPE_MASK
) | ((ULONG
)hObj
>> GDI_ENTRY_UPPER_SHIFT
);
1263 /* We are currently not using bits 24..31 (flags) of the type field, but for compatibility
1264 we copy them as we can't get them from the handle */
1265 OldType
|= Entry
->Type
& GDI_ENTRY_FLAGS_MASK
;
1267 /* As the object should be a stock object, set it's flag, but only in the lower 16 bits */
1268 NewType
= OldType
| GDI_ENTRY_STOCK_MASK
;
1270 /* Try to exchange the type field - but only if the old (previous type) matches! */
1271 PrevType
= InterlockedCompareExchange(&Entry
->Type
, NewType
, OldType
);
1272 if (PrevType
== OldType
&& Entry
->KernelData
!= NULL
)
1274 PTHREADINFO PrevThread
;
1277 /* We successfully set the stock object flag.
1278 KernelData should never be NULL here!!! */
1279 ASSERT(Entry
->KernelData
);
1281 Object
= Entry
->KernelData
;
1283 PrevThread
= Object
->Tid
;
1284 if (Object
->cExclusiveLock
== 0 || PrevThread
== Thread
)
1286 /* dereference the process' object counter */
1287 if (PrevProcId
!= GDI_GLOBAL_PROCESS
)
1289 PEPROCESS OldProcess
;
1290 PPROCESSINFO W32Process
;
1294 Status
= PsLookupProcessByProcessId((HANDLE
)((ULONG_PTR
)PrevProcId
& ~0x1), &OldProcess
);
1295 if (NT_SUCCESS(Status
))
1297 W32Process
= (PPROCESSINFO
)OldProcess
->Win32Process
;
1298 if (W32Process
!= NULL
)
1300 InterlockedDecrement(&W32Process
->GDIHandleCount
);
1302 ObDereferenceObject(OldProcess
);
1306 hObj
= (HGDIOBJ
)((ULONG
)(hObj
) | GDI_HANDLE_STOCK_MASK
);
1308 Object
->hHmgr
= hObj
;
1310 /* remove the process id lock and make it global */
1311 (void)InterlockedExchangePointer((PVOID
*)&Entry
->ProcessId
, GDI_GLOBAL_PROCESS
);
1313 /* we're done, successfully converted the object */
1318 GDIDBG_TRACELOOP(hObj
, PrevThread
, Thread
);
1320 /* WTF?! The object is already locked by a different thread!
1321 Release the lock, wait a bit and try again!
1322 FIXME - we should give up after some time unless we want to wait forever! */
1323 (void)InterlockedExchangePointer((PVOID
*)&Entry
->ProcessId
, PrevProcId
);
1331 DPRINT1("Attempted to convert object 0x%x that is deleted! Should never get here!!!\n", hObj
);
1332 DPRINT1("OldType = 0x%x, Entry->Type = 0x%x, NewType = 0x%x, Entry->KernelData = 0x%x\n", OldType
, Entry
->Type
, NewType
, Entry
->KernelData
);
1335 else if (PrevProcId
== LockedProcessId
)
1337 GDIDBG_TRACELOOP(hObj
, PrevProcId
, ProcessId
);
1339 /* the object is currently locked, wait some time and try again.
1340 FIXME - we shouldn't loop forever! Give up after some time! */
1347 DPRINT1("Attempted to convert invalid handle: 0x%x\n", hObj
);
1355 GDIOBJ_SetOwnership(HGDIOBJ ObjectHandle
, PEPROCESS NewOwner
)
1357 PGDI_TABLE_ENTRY Entry
;
1358 HANDLE ProcessId
, LockedProcessId
, PrevProcId
;
1362 GDIDBG_INITLOOPTRACE();
1364 DPRINT("GDIOBJ_SetOwnership: hObj: 0x%x, NewProcess: 0x%x\n", ObjectHandle
, (NewOwner
? PsGetProcessId(NewOwner
) : 0));
1366 Thread
= (PTHREADINFO
)PsGetCurrentThreadWin32Thread();
1368 if (!GDI_HANDLE_IS_STOCKOBJ(ObjectHandle
))
1370 ProcessId
= PsGetCurrentProcessId();
1371 LockedProcessId
= (HANDLE
)((ULONG_PTR
)ProcessId
| 0x1);
1373 Entry
= GDI_HANDLE_GET_ENTRY(GdiHandleTable
, ObjectHandle
);
1376 /* lock the object, we must not convert stock objects, so don't check!!! */
1377 PrevProcId
= InterlockedCompareExchangePointer((PVOID
*)&Entry
->ProcessId
, ProcessId
, LockedProcessId
);
1378 if (PrevProcId
== ProcessId
)
1380 PTHREADINFO PrevThread
;
1382 if ((Entry
->Type
& GDI_ENTRY_BASETYPE_MASK
) != 0)
1384 POBJ Object
= Entry
->KernelData
;
1386 PrevThread
= Object
->Tid
;
1387 if (Object
->cExclusiveLock
== 0 || PrevThread
== Thread
)
1389 PEPROCESS OldProcess
;
1390 PPROCESSINFO W32Process
;
1393 /* dereference the process' object counter */
1395 if ((ULONG_PTR
)PrevProcId
& ~0x1)
1397 Status
= PsLookupProcessByProcessId((HANDLE
)((ULONG_PTR
)PrevProcId
& ~0x1), &OldProcess
);
1398 if (NT_SUCCESS(Status
))
1400 W32Process
= (PPROCESSINFO
)OldProcess
->Win32Process
;
1401 if (W32Process
!= NULL
)
1403 InterlockedDecrement(&W32Process
->GDIHandleCount
);
1405 ObDereferenceObject(OldProcess
);
1409 if (NewOwner
!= NULL
)
1411 ProcessId
= PsGetProcessId(NewOwner
);
1413 /* Increase the new process' object counter */
1414 W32Process
= (PPROCESSINFO
)NewOwner
->Win32Process
;
1415 if (W32Process
!= NULL
)
1417 InterlockedIncrement(&W32Process
->GDIHandleCount
);
1423 /* remove the process id lock and change it to the new process id */
1424 (void)InterlockedExchangePointer((PVOID
*)&Entry
->ProcessId
, ProcessId
);
1431 GDIDBG_TRACELOOP(ObjectHandle
, PrevThread
, Thread
);
1433 /* WTF?! The object is already locked by a different thread!
1434 Release the lock, wait a bit and try again! DO reset the pid lock
1435 so we make sure we don't access invalid memory in case the object is
1436 being deleted in the meantime (because we don't have aquired a reference
1438 FIXME - we should give up after some time unless we want to wait forever! */
1439 (void)InterlockedExchangePointer((PVOID
*)&Entry
->ProcessId
, PrevProcId
);
1447 DPRINT1("Attempted to change ownership of an object 0x%x currently being destroyed!!!\n", ObjectHandle
);
1448 DPRINT1("Entry->Type = 0x%lx, Entry->KernelData = 0x%p\n", Entry
->Type
, Entry
->KernelData
);
1452 else if (PrevProcId
== LockedProcessId
)
1454 GDIDBG_TRACELOOP(ObjectHandle
, PrevProcId
, ProcessId
);
1456 /* the object is currently locked, wait some time and try again.
1457 FIXME - we shouldn't loop forever! Give up after some time! */
1462 else if (((ULONG_PTR
)PrevProcId
& ~0x1) == 0)
1464 /* allow changing ownership of global objects */
1466 LockedProcessId
= (HANDLE
)((ULONG_PTR
)ProcessId
| 0x1);
1469 else if ((HANDLE
)((ULONG_PTR
)PrevProcId
& ~0x1) != PsGetCurrentProcessId())
1471 DPRINT1("Attempted to change ownership of object 0x%x (pid: 0x%x) from pid 0x%x!!!\n", ObjectHandle
, (ULONG_PTR
)PrevProcId
& ~0x1, PsGetCurrentProcessId());
1476 DPRINT1("Attempted to change owner of invalid handle: 0x%x\n", ObjectHandle
);
1484 GDIOBJ_CopyOwnership(HGDIOBJ CopyFrom
, HGDIOBJ CopyTo
)
1486 PGDI_TABLE_ENTRY FromEntry
;
1488 HANDLE FromProcessId
, FromLockedProcessId
, FromPrevProcId
;
1491 GDIDBG_INITLOOPTRACE();
1493 DPRINT("GDIOBJ_CopyOwnership: from: 0x%x, to: 0x%x\n", CopyFrom
, CopyTo
);
1495 Thread
= (PTHREADINFO
)PsGetCurrentThreadWin32Thread();
1497 if (!GDI_HANDLE_IS_STOCKOBJ(CopyFrom
) && !GDI_HANDLE_IS_STOCKOBJ(CopyTo
))
1499 FromEntry
= GDI_HANDLE_GET_ENTRY(GdiHandleTable
, CopyFrom
);
1501 FromProcessId
= (HANDLE
)((ULONG_PTR
)FromEntry
->ProcessId
& ~0x1);
1502 FromLockedProcessId
= (HANDLE
)((ULONG_PTR
)FromProcessId
| 0x1);
1505 /* lock the object, we must not convert stock objects, so don't check!!! */
1506 FromPrevProcId
= InterlockedCompareExchangePointer((PVOID
*)&FromEntry
->ProcessId
, FromProcessId
, FromLockedProcessId
);
1507 if (FromPrevProcId
== FromProcessId
)
1509 PTHREADINFO PrevThread
;
1512 if ((FromEntry
->Type
& GDI_ENTRY_BASETYPE_MASK
) != 0)
1514 Object
= FromEntry
->KernelData
;
1516 /* save the pointer to the calling thread so we know it was this thread
1517 that locked the object */
1518 PrevThread
= Object
->Tid
;
1519 if (Object
->cExclusiveLock
== 0 || PrevThread
== Thread
)
1521 /* now let's change the ownership of the target object */
1523 if (((ULONG_PTR
)FromPrevProcId
& ~0x1) != 0)
1525 PEPROCESS ProcessTo
;
1527 if (NT_SUCCESS(PsLookupProcessByProcessId((HANDLE
)((ULONG_PTR
)FromPrevProcId
& ~0x1), &ProcessTo
)))
1529 GDIOBJ_SetOwnership(CopyTo
, ProcessTo
);
1530 ObDereferenceObject(ProcessTo
);
1535 /* mark the object as global */
1536 GDIOBJ_SetOwnership(CopyTo
, NULL
);
1539 (void)InterlockedExchangePointer((PVOID
*)&FromEntry
->ProcessId
, FromPrevProcId
);
1543 GDIDBG_TRACELOOP(CopyFrom
, PrevThread
, Thread
);
1545 /* WTF?! The object is already locked by a different thread!
1546 Release the lock, wait a bit and try again! DO reset the pid lock
1547 so we make sure we don't access invalid memory in case the object is
1548 being deleted in the meantime (because we don't have aquired a reference
1550 FIXME - we should give up after some time unless we want to wait forever! */
1551 (void)InterlockedExchangePointer((PVOID
*)&FromEntry
->ProcessId
, FromPrevProcId
);
1554 goto LockHandleFrom
;
1559 DPRINT1("Attempted to copy ownership from an object 0x%x currently being destroyed!!!\n", CopyFrom
);
1563 else if (FromPrevProcId
== FromLockedProcessId
)
1565 GDIDBG_TRACELOOP(CopyFrom
, FromPrevProcId
, FromProcessId
);
1567 /* the object is currently locked, wait some time and try again.
1568 FIXME - we shouldn't loop forever! Give up after some time! */
1571 goto LockHandleFrom
;
1573 else if ((HANDLE
)((ULONG_PTR
)FromPrevProcId
& ~0x1) != PsGetCurrentProcessId())
1575 /* FIXME - should we really allow copying ownership from objects that we don't even own? */
1576 DPRINT1("WARNING! Changing copying ownership of object 0x%x (pid: 0x%x) to pid 0x%x!!!\n", CopyFrom
, (ULONG_PTR
)FromPrevProcId
& ~0x1, PsGetCurrentProcessId());
1577 FromProcessId
= (HANDLE
)((ULONG_PTR
)FromPrevProcId
& ~0x1);
1578 FromLockedProcessId
= (HANDLE
)((ULONG_PTR
)FromProcessId
| 0x1);
1579 goto LockHandleFrom
;
1583 DPRINT1("Attempted to copy ownership from invalid handle: 0x%x\n", CopyFrom
);
1591 GDI_MapHandleTable(PSECTION_OBJECT SectionObject
, PEPROCESS Process
)
1593 PVOID MappedView
= NULL
;
1595 LARGE_INTEGER Offset
;
1596 ULONG ViewSize
= sizeof(GDI_HANDLE_TABLE
);
1598 Offset
.QuadPart
= 0;
1600 ASSERT(SectionObject
!= NULL
);
1601 ASSERT(Process
!= NULL
);
1603 Status
= MmMapViewOfSection(SectionObject
,
1614 if (!NT_SUCCESS(Status
))
1620 /** PUBLIC FUNCTIONS **********************************************************/
1624 IntGdiSetRegionOwner(HRGN hRgn
, DWORD OwnerMask
)
1627 PGDI_TABLE_ENTRY Entry
;
1630 These regions do not use attribute sections and when allocated, use gdiobj
1633 // FIXME! HAX!!! Remove this once we get everything right!
1634 Index
= GDI_HANDLE_GET_INDEX(hRgn
);
1635 Entry
= &GdiHandleTable
->Entries
[Index
];
1636 if (Entry
->UserData
) FreeObjectAttr(Entry
->UserData
);
1637 Entry
->UserData
= NULL
;
1639 if ((OwnerMask
== GDI_OBJ_HMGR_PUBLIC
) || OwnerMask
== GDI_OBJ_HMGR_NONE
)
1641 return GDIOBJ_SetOwnership(hRgn
, NULL
);
1643 if (OwnerMask
== GDI_OBJ_HMGR_POWNED
)
1645 return GDIOBJ_SetOwnership((HGDIOBJ
) hRgn
, PsGetCurrentProcess() );
1652 IntGdiSetBrushOwner(PBRUSH pbr
, DWORD OwnerMask
)
1655 PEPROCESS Owner
= NULL
;
1656 PGDI_TABLE_ENTRY pEntry
= NULL
;
1658 if (!pbr
) return FALSE
;
1660 hBR
= pbr
->BaseObject
.hHmgr
;
1662 if (!hBR
|| (GDI_HANDLE_GET_TYPE(hBR
) != GDI_OBJECT_TYPE_BRUSH
))
1666 INT Index
= GDI_HANDLE_GET_INDEX((HGDIOBJ
)hBR
);
1667 pEntry
= &GdiHandleTable
->Entries
[Index
];
1670 if (pbr
->flAttrs
& GDIBRUSH_IS_GLOBAL
)
1672 GDIOBJ_ShareUnlockObjByPtr((POBJ
)pbr
);
1676 if ((OwnerMask
== GDI_OBJ_HMGR_PUBLIC
) || OwnerMask
== GDI_OBJ_HMGR_NONE
)
1678 // Set this Brush to inaccessible mode and to an Owner of NONE.
1679 // if (OwnerMask == GDI_OBJ_HMGR_NONE) Owner = OwnerMask;
1681 if (!GDIOBJ_SetOwnership((HGDIOBJ
) hBR
, Owner
))
1684 // Deny user access to User Data.
1685 pEntry
->UserData
= NULL
; // This hBR is inaccessible!
1688 if (OwnerMask
== GDI_OBJ_HMGR_POWNED
)
1690 if (!GDIOBJ_SetOwnership((HGDIOBJ
) hBR
, PsGetCurrentProcess() ))
1693 // Allow user access to User Data.
1694 pEntry
->UserData
= pbr
->pBrushAttr
;
1701 IntGdiSetDCOwnerEx( HDC hDC
, DWORD OwnerMask
, BOOL NoSetBrush
)
1706 if (!hDC
|| (GDI_HANDLE_GET_TYPE(hDC
) != GDI_OBJECT_TYPE_DC
)) return FALSE
;
1708 if ((OwnerMask
== GDI_OBJ_HMGR_PUBLIC
) || OwnerMask
== GDI_OBJ_HMGR_NONE
)
1710 pDC
= DC_LockDc ( hDC
);
1711 MmCopyFromCaller(&pDC
->dcattr
, pDC
->pdcattr
, sizeof(DC_ATTR
));
1714 DC_FreeDcAttr( hDC
); // Free the dcattr!
1716 if (!DC_SetOwnership( hDC
, NULL
)) // This hDC is inaccessible!
1720 if (OwnerMask
== GDI_OBJ_HMGR_POWNED
)
1722 pDC
= DC_LockDc ( hDC
);
1723 ASSERT(pDC
->pdcattr
== &pDC
->dcattr
);
1726 if (!DC_SetOwnership( hDC
, PsGetCurrentProcess() )) return Ret
;
1728 DC_AllocateDcAttr( hDC
); // Allocate new dcattr
1730 DCU_SynchDcAttrtoUser( hDC
); // Copy data from dc to dcattr
1733 if ((OwnerMask
!= GDI_OBJ_HMGR_NONE
) && !NoSetBrush
)
1735 pDC
= DC_LockDc ( hDC
);
1736 if (IntGdiSetBrushOwner((PBRUSH
)pDC
->dclevel
.pbrFill
, OwnerMask
))
1737 IntGdiSetBrushOwner((PBRUSH
)pDC
->dclevel
.pbrLine
, OwnerMask
);
1745 GreGetObjectOwner(HGDIOBJ Handle
, GDIOBJTYPE ObjType
)
1747 INT Ret
= GDI_OBJ_HMGR_RESTRICTED
;
1749 if ( GDI_HANDLE_GET_INDEX(Handle
) < GDI_HANDLE_COUNT
)
1751 PGDI_TABLE_ENTRY pEntry
= &GdiHandleTable
->Entries
[GDI_HANDLE_GET_INDEX(Handle
)];
1753 if (pEntry
->ObjectType
== ObjType
)
1755 if (pEntry
->FullUnique
== (GDI_HANDLE_GET_UPPER(Handle
) >> GDI_ENTRY_UPPER_SHIFT
))
1756 Ret
= pEntry
->ProcessId
& ~1;
1765 NtGdiCreateClientObj(
1772 /* Mask out everything that would change the type in a wrong manner */
1773 ulType
&= (GDI_HANDLE_TYPE_MASK
& ~GDI_HANDLE_BASETYPE_MASK
);
1775 /* Allocate a new object */
1776 pObject
= GDIOBJ_AllocObjWithHandle(GDI_OBJECT_TYPE_CLIOBJ
| ulType
);
1782 /* get the handle */
1783 handle
= pObject
->hHmgr
;
1786 GDIOBJ_UnlockObjByPtr(pObject
);
1794 NtGdiDeleteClientObj(
1798 /* We first need to get the real type from the handle */
1799 ULONG type
= GDI_HANDLE_GET_TYPE(h
);
1801 /* Check if it's really a CLIENTOBJ */
1802 if ((type
& GDI_HANDLE_BASETYPE_MASK
) != GDILoObjType_LO_CLIENTOBJ_TYPE
)
1804 /* FIXME: SetLastError? */
1807 return GDIOBJ_FreeObjByHandle(h
, type
);
1812 IntGdiGetObject(IN HANDLE Handle
,
1820 pGdiObject
= GDIOBJ_LockObj(Handle
, GDI_OBJECT_TYPE_DONTCARE
);
1823 SetLastWin32Error(ERROR_INVALID_HANDLE
);
1827 dwObjectType
= GDIOBJ_GetObjectType(Handle
);
1828 switch (dwObjectType
)
1830 case GDI_OBJECT_TYPE_PEN
:
1831 case GDI_OBJECT_TYPE_EXTPEN
:
1832 Result
= PEN_GetObject((PBRUSH
) pGdiObject
, cbCount
, (PLOGPEN
) lpBuffer
); // IntGdiCreatePenIndirect
1835 case GDI_OBJECT_TYPE_BRUSH
:
1836 Result
= BRUSH_GetObject((PBRUSH
) pGdiObject
, cbCount
, (LPLOGBRUSH
)lpBuffer
);
1839 case GDI_OBJECT_TYPE_BITMAP
:
1840 Result
= BITMAP_GetObject((SURFACE
*) pGdiObject
, cbCount
, lpBuffer
);
1842 case GDI_OBJECT_TYPE_FONT
:
1843 Result
= FontGetObject((PTEXTOBJ
) pGdiObject
, cbCount
, lpBuffer
);
1845 // Fix the LOGFONT structure for the stock fonts
1846 if (FIRST_STOCK_HANDLE
<= Handle
&& Handle
<= LAST_STOCK_HANDLE
)
1848 FixStockFontSizeW(Handle
, cbCount
, lpBuffer
);
1853 case GDI_OBJECT_TYPE_PALETTE
:
1854 Result
= PALETTE_GetObject((PPALETTE
) pGdiObject
, cbCount
, lpBuffer
);
1858 DPRINT1("GDI object type 0x%08x not implemented\n", dwObjectType
);
1862 GDIOBJ_UnlockObjByPtr(pGdiObject
);
1872 NtGdiExtGetObjectW(IN HANDLE hGdiObj
,
1874 OUT LPVOID lpBuffer
)
1881 DIBSECTION dibsection
;
1885 EXTLOGFONTW extlogfontw
;
1886 ENUMLOGFONTEXDVW enumlogfontexdvw
;
1889 // Normalize to the largest supported object size
1890 cbCount
= min((UINT
)cbCount
, sizeof(Object
));
1892 // Now do the actual call
1893 iRetCount
= IntGdiGetObject(hGdiObj
, cbCount
, lpBuffer
? &Object
: NULL
);
1894 cbCopyCount
= min((UINT
)cbCount
, (UINT
)iRetCount
);
1896 // Make sure we have a buffer and a copy size
1897 if ((cbCopyCount
) && (lpBuffer
))
1899 // Enter SEH for buffer transfer
1902 // Probe the buffer and copy it
1903 ProbeForWrite(lpBuffer
, cbCopyCount
, sizeof(WORD
));
1904 RtlCopyMemory(lpBuffer
, &Object
, cbCopyCount
);
1906 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1908 // Clear the return value.
1909 // Do *NOT* set last error here!