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)
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
;
84 PGDI_HANDLE_TABLE GdiHandleTable
= NULL
;
85 PSECTION_OBJECT GdiTableSection
= NULL
;
87 /** INTERNAL FUNCTIONS ********************************************************/
97 AllocTypeDataDump(INT TypeInfo
)
99 switch( TypeInfo
& GDI_HANDLE_TYPE_MASK
)
101 case GDILoObjType_LO_BRUSH_TYPE
:
104 case GDILoObjType_LO_DC_TYPE
:
107 case GDILoObjType_LO_BITMAP_TYPE
:
110 case GDILoObjType_LO_FONT_TYPE
:
113 case GDILoObjType_LO_REGION_TYPE
:
120 DeAllocTypeDataDump(INT TypeInfo
)
122 switch( TypeInfo
& GDI_HANDLE_TYPE_MASK
)
124 case GDILoObjType_LO_BRUSH_TYPE
:
127 case GDILoObjType_LO_DC_TYPE
:
130 case GDILoObjType_LO_BITMAP_TYPE
:
133 case GDILoObjType_LO_FONT_TYPE
:
136 case GDILoObjType_LO_REGION_TYPE
:
143 * Dummy GDI Cleanup Callback
147 GDI_CleanupDummy(PVOID ObjectBody
)
153 * Allocate GDI object table.
154 * \param Size - number of entries in the object table.
159 GDIOBJ_iAllocHandleTable(OUT PSECTION_OBJECT
*SectionObject
)
161 PGDI_HANDLE_TABLE HandleTable
= NULL
;
162 LARGE_INTEGER htSize
;
167 ASSERT(SectionObject
!= NULL
);
169 htSize
.QuadPart
= sizeof(GDI_HANDLE_TABLE
);
171 Status
= MmCreateSection((PVOID
*)SectionObject
,
179 if (!NT_SUCCESS(Status
))
182 /* FIXME - use MmMapViewInSessionSpace once available! */
183 Status
= MmMapViewInSystemSpace(*SectionObject
,
184 (PVOID
*)&HandleTable
,
186 if (!NT_SUCCESS(Status
))
188 ObDereferenceObject(*SectionObject
);
189 *SectionObject
= NULL
;
193 RtlZeroMemory(HandleTable
, sizeof(GDI_HANDLE_TABLE
));
195 HandleTable
->LookasideLists
= ExAllocatePoolWithTag(NonPagedPool
,
196 BASE_OBJTYPE_COUNT
* sizeof(PAGED_LOOKASIDE_LIST
),
198 if (HandleTable
->LookasideLists
== NULL
)
200 MmUnmapViewInSystemSpace(HandleTable
);
201 ObDereferenceObject(*SectionObject
);
202 *SectionObject
= NULL
;
206 for (ObjType
= 0; ObjType
< BASE_OBJTYPE_COUNT
; ObjType
++)
208 if (ObjTypeInfo
[ObjType
].bUseLookaside
)
210 ExInitializePagedLookasideList(HandleTable
->LookasideLists
+ ObjType
,
214 ObjTypeInfo
[ObjType
].ulBodySize
,
215 ObjTypeInfo
[ObjType
].Tag
,
220 ShortDelay
.QuadPart
= -5000LL; /* FIXME - 0.5 ms? */
222 HandleTable
->FirstFree
= 0;
223 HandleTable
->FirstUnused
= RESERVE_ENTRIES_COUNT
;
233 /* Create the GDI handle table */
234 GdiHandleTable
= GDIOBJ_iAllocHandleTable(&GdiTableSection
);
235 if (GdiHandleTable
== NULL
)
237 DPRINT1("Failed to initialize the GDI handle table.\n");
238 return STATUS_UNSUCCESSFUL
;
241 return STATUS_SUCCESS
;
246 LockErrorDebugOutput(HGDIOBJ hObj
, PGDI_TABLE_ENTRY Entry
, LPSTR Function
)
248 if ((Entry
->Type
& GDI_ENTRY_BASETYPE_MASK
) == 0)
250 DPRINT1("%s: Attempted to lock object 0x%x that is deleted!\n", Function
, hObj
);
251 GDIDBG_TRACEDELETER(hObj
);
253 else if (GDI_HANDLE_GET_REUSECNT(hObj
) != GDI_ENTRY_GET_REUSECNT(Entry
->Type
))
255 DPRINT1("%s: Attempted to lock object 0x%x, wrong reuse counter (Handle: 0x%x, Entry: 0x%x)\n",
256 Function
, hObj
, GDI_HANDLE_GET_REUSECNT(hObj
), GDI_ENTRY_GET_REUSECNT(Entry
->Type
));
258 else if (GDI_HANDLE_GET_TYPE(hObj
) != ((Entry
->Type
<< GDI_ENTRY_UPPER_SHIFT
) & GDI_HANDLE_TYPE_MASK
))
260 DPRINT1("%s: Attempted to lock object 0x%x, type mismatch (Handle: 0x%x, Entry: 0x%x)\n",
261 Function
, hObj
, GDI_HANDLE_GET_TYPE(hObj
), (Entry
->Type
<< GDI_ENTRY_UPPER_SHIFT
) & GDI_HANDLE_TYPE_MASK
);
265 DPRINT1("%s: Attempted to lock object 0x%x, something went wrong, typeinfo = 0x%x\n",
266 Function
, hObj
, Entry
->Type
);
268 GDIDBG_TRACECALLER();
273 InterlockedPopFreeEntry(VOID
)
275 ULONG iFirst
, iNext
, iPrev
;
276 PGDI_TABLE_ENTRY pEntry
;
278 DPRINT("Enter InterLockedPopFreeEntry\n");
282 /* Get the index and sequence number of the first free entry */
283 iFirst
= GdiHandleTable
->FirstFree
;
285 /* Check if we have a free entry */
286 if (!(iFirst
& GDI_HANDLE_INDEX_MASK
))
288 /* Increment FirstUnused and get the new index */
289 iFirst
= InterlockedIncrement((LONG
*)&GdiHandleTable
->FirstUnused
) - 1;
291 /* Check if we have unused entries left */
292 if (iFirst
>= GDI_HANDLE_COUNT
)
294 DPRINT1("No more gdi handles left!\n");
298 /* Return the old index */
302 /* Get a pointer to the first free entry */
303 pEntry
= GdiHandleTable
->Entries
+ (iFirst
& GDI_HANDLE_INDEX_MASK
);
305 /* Create a new value with an increased sequence number */
306 iNext
= (USHORT
)(ULONG_PTR
)pEntry
->KernelData
;
307 iNext
|= (iFirst
& ~GDI_HANDLE_INDEX_MASK
) + 0x10000;
309 /* Try to exchange the FirstFree value */
310 iPrev
= InterlockedCompareExchange((LONG
*)&GdiHandleTable
->FirstFree
,
314 while (iPrev
!= iFirst
);
316 /* Sanity check: is entry really free? */
317 ASSERT(((ULONG_PTR
)pEntry
->KernelData
& ~GDI_HANDLE_INDEX_MASK
) == 0);
319 return iFirst
& GDI_HANDLE_INDEX_MASK
;
322 /* Pushes an entry of the handle table to the free list,
323 The entry must be unlocked and the base type field must be 0 */
326 InterlockedPushFreeEntry(ULONG idxToFree
)
328 ULONG iToFree
, iFirst
, iPrev
;
329 PGDI_TABLE_ENTRY pFreeEntry
;
331 DPRINT("Enter InterlockedPushFreeEntry\n");
333 pFreeEntry
= GdiHandleTable
->Entries
+ idxToFree
;
334 ASSERT((pFreeEntry
->Type
& GDI_ENTRY_BASETYPE_MASK
) == 0);
335 ASSERT(pFreeEntry
->ProcessId
== 0);
336 pFreeEntry
->UserData
= NULL
; // FIXME
337 ASSERT(pFreeEntry
->UserData
== NULL
);
341 /* Get the current first free index and sequence number */
342 iFirst
= GdiHandleTable
->FirstFree
;
344 /* Set the KernelData member to the index of the first free entry */
345 pFreeEntry
->KernelData
= UlongToPtr(iFirst
& GDI_HANDLE_INDEX_MASK
);
347 /* Combine new index and increased sequence number in iToFree */
348 iToFree
= idxToFree
| ((iFirst
& ~GDI_HANDLE_INDEX_MASK
) + 0x10000);
350 /* Try to atomically update the first free entry */
351 iPrev
= InterlockedCompareExchange((LONG
*)&GdiHandleTable
->FirstFree
,
355 while (iPrev
!= iFirst
);
361 GDIOBJ_ValidateHandle(HGDIOBJ hObj
, ULONG ObjectType
)
363 PGDI_TABLE_ENTRY Entry
= GDI_HANDLE_GET_ENTRY(GdiHandleTable
, hObj
);
364 if ((((ULONG_PTR
)hObj
& GDI_HANDLE_TYPE_MASK
) == ObjectType
) &&
365 (Entry
->Type
<< GDI_ENTRY_UPPER_SHIFT
) == GDI_HANDLE_GET_UPPER(hObj
))
367 HANDLE pid
= (HANDLE
)((ULONG_PTR
)Entry
->ProcessId
& ~0x1);
368 if (pid
== NULL
|| pid
== PsGetCurrentProcessId())
377 GDIOBJ_AllocObj(UCHAR BaseType
)
381 ASSERT((BaseType
& ~GDIObjTypeTotal
) == 0);
382 // BaseType &= GDI_HANDLE_BASETYPE_MASK;
384 if (ObjTypeInfo
[BaseType
].bUseLookaside
)
386 PPAGED_LOOKASIDE_LIST LookasideList
;
388 LookasideList
= GdiHandleTable
->LookasideLists
+ BaseType
;
389 pObject
= ExAllocateFromPagedLookasideList(LookasideList
);
393 pObject
= ExAllocatePoolWithTag(PagedPool
,
394 ObjTypeInfo
[BaseType
].ulBodySize
,
395 ObjTypeInfo
[BaseType
].Tag
);
400 RtlZeroMemory(pObject
, ObjTypeInfo
[BaseType
].ulBodySize
);
408 * Allocate memory for GDI object and return handle to it.
410 * \param ObjectType - type of object \ref GDI object types
412 * \return Pointer to the allocated object, which is locked.
415 GDIOBJ_AllocObjWithHandle(ULONG ObjectType
)
417 PPROCESSINFO W32Process
;
418 POBJ newObject
= NULL
;
419 HANDLE CurrentProcessId
, LockedProcessId
;
422 PGDI_TABLE_ENTRY Entry
;
425 GDIDBG_INITLOOPTRACE();
427 W32Process
= PsGetCurrentProcessWin32Process();
428 /* HACK HACK HACK: simplest-possible quota implementation - don't allow a process
429 to take too many GDI objects, itself. */
430 if (W32Process
&& W32Process
->GDIHandleCount
>= 0x2710)
432 DPRINT1("Too many objects for process!!!\n");
433 DPRINT1("DC %d BRUSH %d BITMAP %d FONT %d RGN %d\n",tDC
,tBRUSH
,tBITMAP
,tFONT
,tRGN
);
434 GDIDBG_DUMPHANDLETABLE();
438 ASSERT(ObjectType
!= GDI_OBJECT_TYPE_DONTCARE
);
440 TypeIndex
= GDI_OBJECT_GET_TYPE_INDEX(ObjectType
);
442 newObject
= GDIOBJ_AllocObj(TypeIndex
);
445 DPRINT1("Not enough memory to allocate gdi object!\n");
449 CurrentProcessId
= PsGetCurrentProcessId();
450 LockedProcessId
= (HANDLE
)((ULONG_PTR
)CurrentProcessId
| 0x1);
452 // RtlZeroMemory(newObject, ObjTypeInfo[TypeIndex].ulBodySize);
454 /* On Windows the higher 16 bit of the type field don't contain the
455 full type from the handle, but the base type.
456 (type = BRSUH, PEN, EXTPEN, basetype = BRUSH) */
457 TypeInfo
= (ObjectType
& GDI_HANDLE_BASETYPE_MASK
) | (ObjectType
>> GDI_ENTRY_UPPER_SHIFT
);
459 Index
= InterlockedPopFreeEntry();
464 Entry
= &GdiHandleTable
->Entries
[Index
];
467 PrevProcId
= InterlockedCompareExchangePointer((PVOID
*)&Entry
->ProcessId
, LockedProcessId
, 0);
468 if (PrevProcId
== NULL
)
470 PTHREADINFO Thread
= (PTHREADINFO
)PsGetCurrentThreadWin32Thread();
473 Entry
->KernelData
= newObject
;
475 /* copy the reuse-counter */
476 TypeInfo
|= Entry
->Type
& GDI_ENTRY_REUSE_MASK
;
478 /* we found a free entry, no need to exchange this field atomically
479 since we're holding the lock */
480 Entry
->Type
= TypeInfo
;
482 /* Create a handle */
483 Handle
= (HGDIOBJ
)((Index
& 0xFFFF) | (TypeInfo
<< GDI_ENTRY_UPPER_SHIFT
));
485 /* Initialize BaseObject fields */
486 newObject
->hHmgr
= Handle
;
487 newObject
->ulShareCount
= 0;
488 newObject
->cExclusiveLock
= 1;
489 newObject
->Tid
= Thread
;
491 AllocTypeDataDump(TypeInfo
);
493 /* unlock the entry */
494 (void)InterlockedExchangePointer((PVOID
*)&Entry
->ProcessId
, CurrentProcessId
);
496 GDIDBG_CAPTUREALLOCATOR(Index
);
498 if (W32Process
!= NULL
)
500 InterlockedIncrement(&W32Process
->GDIHandleCount
);
503 DPRINT("GDIOBJ_AllocObj: 0x%x ob: 0x%x\n", Handle
, newObject
);
508 GDIDBG_TRACELOOP(Index
, PrevProcId
, CurrentProcessId
);
509 /* damn, someone is trying to lock the object even though it doesn't
510 even exist anymore, wait a little and try again!
511 FIXME - we shouldn't loop forever! Give up after some time! */
518 GDIOBJ_FreeObj(newObject
, TypeIndex
);
520 DPRINT1("Failed to insert gdi object into the handle table, no handles left!\n");
521 GDIDBG_DUMPHANDLETABLE();
528 GDIOBJ_FreeObj(POBJ pObject
, UCHAR BaseType
)
530 /* Object must not have a handle! */
531 ASSERT(pObject
->hHmgr
== NULL
);
533 if (ObjTypeInfo
[BaseType
].bUseLookaside
)
535 PPAGED_LOOKASIDE_LIST LookasideList
;
537 LookasideList
= GdiHandleTable
->LookasideLists
+ BaseType
;
538 ExFreeToPagedLookasideList(LookasideList
, pObject
);
547 * Free memory allocated for the GDI object. For each object type this function calls the
548 * appropriate cleanup routine.
550 * \param hObj - handle of the object to be deleted.
552 * \return Returns TRUE if succesful.
553 * \return Returns FALSE if the cleanup routine returned FALSE or the object doesn't belong
554 * to the calling process.
556 * \bug This function should return VOID and kill the object no matter what...
559 GDIOBJ_FreeObjByHandle(HGDIOBJ hObj
, DWORD ExpectedType
)
561 PGDI_TABLE_ENTRY Entry
;
562 HANDLE ProcessId
, LockedProcessId
, PrevProcId
;
563 ULONG HandleType
, HandleUpper
, TypeIndex
;
566 GDIDBG_INITLOOPTRACE();
568 DPRINT("GDIOBJ_FreeObj: hObj: 0x%08x\n", hObj
);
570 if (GDI_HANDLE_IS_STOCKOBJ(hObj
))
572 DPRINT1("GDIOBJ_FreeObj() failed, can't delete stock object handle: 0x%x !!!\n", hObj
);
573 GDIDBG_TRACECALLER();
577 ProcessId
= PsGetCurrentProcessId();
578 LockedProcessId
= (HANDLE
)((ULONG_PTR
)ProcessId
| 0x1);
580 Silent
= (ExpectedType
& GDI_OBJECT_TYPE_SILENT
);
581 ExpectedType
&= ~GDI_OBJECT_TYPE_SILENT
;
583 HandleType
= GDI_HANDLE_GET_TYPE(hObj
);
584 HandleUpper
= GDI_HANDLE_GET_UPPER(hObj
);
586 /* Check if we have the requested type */
587 if ( (ExpectedType
!= GDI_OBJECT_TYPE_DONTCARE
&&
588 HandleType
!= ExpectedType
) ||
591 DPRINT1("Attempted to free object 0x%x of wrong type (Handle: 0x%x, expected: 0x%x)\n",
592 hObj
, HandleType
, ExpectedType
);
593 GDIDBG_TRACECALLER();
597 Entry
= GDI_HANDLE_GET_ENTRY(GdiHandleTable
, hObj
);
600 /* lock the object, we must not delete global objects, so don't exchange the locking
601 process ID to zero when attempting to lock a global object... */
602 PrevProcId
= InterlockedCompareExchangePointer((PVOID
*)&Entry
->ProcessId
, LockedProcessId
, ProcessId
);
603 if (PrevProcId
== ProcessId
)
605 if ( (Entry
->KernelData
!= NULL
) &&
606 ((Entry
->Type
<< GDI_ENTRY_UPPER_SHIFT
) == HandleUpper
) &&
607 ((Entry
->Type
& GDI_ENTRY_BASETYPE_MASK
) == (HandleUpper
& GDI_ENTRY_BASETYPE_MASK
)) )
611 Object
= Entry
->KernelData
;
613 if ((Object
->cExclusiveLock
== 0 ||
614 Object
->Tid
== (PTHREADINFO
)PsGetCurrentThreadWin32Thread()) &&
615 Object
->ulShareCount
== 0)
618 PPROCESSINFO W32Process
= PsGetCurrentProcessWin32Process();
620 /* Clear the basetype field so when unlocking the handle it gets finally deleted and increment reuse counter */
621 Entry
->Type
= (Entry
->Type
+ GDI_ENTRY_REUSE_INC
) & ~GDI_ENTRY_BASETYPE_MASK
;
623 /* unlock the handle slot */
624 (void)InterlockedExchangePointer((PVOID
*)&Entry
->ProcessId
, NULL
);
626 /* push this entry to the free list */
627 InterlockedPushFreeEntry(GDI_ENTRY_TO_INDEX(GdiHandleTable
, Entry
));
629 Object
->hHmgr
= NULL
;
631 if (W32Process
!= NULL
)
633 InterlockedDecrement(&W32Process
->GDIHandleCount
);
636 /* call the cleanup routine. */
637 TypeIndex
= GDI_OBJECT_GET_TYPE_INDEX(HandleType
);
638 Ret
= ObjTypeInfo
[TypeIndex
].CleanupProc(Object
);
640 DeAllocTypeDataDump(HandleType
);
642 /* Now it's time to free the memory */
643 GDIOBJ_FreeObj(Object
, TypeIndex
);
645 GDIDBG_CAPTUREDELETER(hObj
);
648 else if (Object
->ulShareCount
!= 0)
651 PEPROCESS OldProcess
;
652 Object
->BaseFlags
|= BASEFLAG_READY_TO_DIE
;
653 DPRINT("Object %p, ulShareCount = %d\n", Object
->hHmgr
, Object
->ulShareCount
);
654 /* Set NULL owner. Do the work here to avoid race conditions */
655 Status
= PsLookupProcessByProcessId((HANDLE
)((ULONG_PTR
)PrevProcId
& ~0x1), &OldProcess
);
656 if (NT_SUCCESS(Status
))
658 PPROCESSINFO W32Process
= (PPROCESSINFO
)OldProcess
->Win32Process
;
659 if (W32Process
!= NULL
)
661 InterlockedDecrement(&W32Process
->GDIHandleCount
);
663 ObDereferenceObject(OldProcess
);
665 (void)InterlockedExchangePointer((PVOID
*)&Entry
->ProcessId
, NULL
);
666 /* Don't wait on shared locks */
672 * The object is currently locked by another thread, so freeing is forbidden!
674 DPRINT1("Object->cExclusiveLock = %d\n", Object
->cExclusiveLock
);
675 GDIDBG_TRACECALLER();
676 GDIDBG_TRACELOCKER(hObj
);
677 (void)InterlockedExchangePointer((PVOID
*)&Entry
->ProcessId
, PrevProcId
);
678 /* do not assert here for it will call again from dxg.sys it being call twice */
686 LockErrorDebugOutput(hObj
, Entry
, "GDIOBJ_FreeObj");
687 (void)InterlockedExchangePointer((PVOID
*)&Entry
->ProcessId
, PrevProcId
);
690 else if (PrevProcId
== LockedProcessId
)
692 GDIDBG_TRACELOOP(hObj
, PrevProcId
, ProcessId
);
694 /* the object is currently locked, wait some time and try again.
695 FIXME - we shouldn't loop forever! Give up after some time! */
704 if ((Entry
->Type
& GDI_ENTRY_BASETYPE_MASK
) == 0)
706 DPRINT1("Attempted to free gdi handle 0x%x that is already deleted!\n", hObj
);
708 else if (((ULONG_PTR
)PrevProcId
& ~0x1) == 0)
710 DPRINT1("Attempted to free global gdi handle 0x%x, caller needs to get ownership first!!!\n", hObj
);
714 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);
716 DPRINT1("Type = 0x%lx, KernelData = 0x%p, ProcessId = 0x%p\n", Entry
->Type
, Entry
->KernelData
, Entry
->ProcessId
);
717 GDIDBG_TRACECALLER();
718 GDIDBG_TRACEALLOCATOR(hObj
);
727 IsObjectDead(HGDIOBJ hObject
)
729 INT Index
= GDI_HANDLE_GET_INDEX(hObject
);
730 PGDI_TABLE_ENTRY Entry
= &GdiHandleTable
->Entries
[Index
];
731 // We check to see if the objects are knocking on deaths door.
732 if ((Entry
->Type
& GDI_ENTRY_BASETYPE_MASK
) != 0)
736 DPRINT1("Object 0x%x currently being destroyed!!!\n",hObject
);
737 return TRUE
; // return true and move on.
744 bPEBCacheHandle(HGDIOBJ Handle
, int oType
, PVOID pAttr
)
746 PGDIHANDLECACHE GdiHandleCache
;
749 int Offset
= 0, Number
;
752 GdiHandleCache
= (PGDIHANDLECACHE
)NtCurrentTeb()->ProcessEnvironmentBlock
->GdiHandleBuffer
;
761 Offset
= CACHE_BRUSH_ENTRIES
;
764 case hctRegionHandle
:
765 Offset
= CACHE_BRUSH_ENTRIES
+CACHE_PEN_ENTRIES
;
772 Lock
= InterlockedCompareExchangePointer( (PVOID
*)&GdiHandleCache
->ulLock
,
775 if (Lock
) return FALSE
;
779 Number
= GdiHandleCache
->ulNumHandles
[oType
];
781 hPtr
= GdiHandleCache
->Handle
+ Offset
;
783 if ( pAttr
&& oType
== hctRegionHandle
)
785 if ( Number
< CACHE_REGION_ENTRIES
)
787 ((PRGN_ATTR
)pAttr
)->AttrFlags
|= ATTR_CACHED
;
788 hPtr
[Number
] = Handle
;
789 GdiHandleCache
->ulNumHandles
[oType
]++;
790 DPRINT("Put Handle Count %d PEB 0x%x\n", GdiHandleCache
->ulNumHandles
[oType
], NtCurrentTeb()->ProcessEnvironmentBlock
);
795 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
801 (void)InterlockedExchangePointer((PVOID
*)&GdiHandleCache
->ulLock
, Lock
);
807 * \param hObject object handle
808 * \return if the function fails the returned value is FALSE.
812 GreDeleteObject(HGDIOBJ hObject
)
815 PGDI_TABLE_ENTRY Entry
;
819 DPRINT("NtGdiDeleteObject handle 0x%08x\n", hObject
);
820 if (!IsObjectDead(hObject
))
822 dwObjectType
= GDIOBJ_GetObjectType(hObject
);
824 Index
= GDI_HANDLE_GET_INDEX(hObject
);
825 Entry
= &GdiHandleTable
->Entries
[Index
];
826 pAttr
= Entry
->UserData
;
828 switch (dwObjectType
)
830 case GDI_OBJECT_TYPE_BRUSH
:
833 case GDI_OBJECT_TYPE_REGION
:
834 /* If pAttr NULL, the probability is high for System Region. */
836 bPEBCacheHandle(hObject
, hctRegionHandle
, pAttr
))
838 /* User space handle only! */
843 FreeObjectAttr(pAttr
);
844 Entry
->UserData
= NULL
;
848 case GDI_OBJECT_TYPE_DC
:
849 // DC_FreeDcAttr(hObject);
853 return NULL
!= hObject
854 ? GDIOBJ_FreeObjByHandle(hObject
, dwObjectType
) : FALSE
;
858 DPRINT1("Attempt DeleteObject 0x%x currently being destroyed!!!\n",hObject
);
859 return TRUE
; // return true and move on.
865 IntDeleteHandlesForProcess(struct _EPROCESS
*Process
, ULONG ObjectType
)
867 PGDI_TABLE_ENTRY Entry
, End
;
868 ULONG Index
= RESERVE_ENTRIES_COUNT
;
870 PPROCESSINFO W32Process
;
872 W32Process
= (PPROCESSINFO
)Process
->Win32Process
;
875 if (W32Process
->GDIHandleCount
> 0)
877 ProcId
= Process
->UniqueProcessId
;
879 /* FIXME - Instead of building the handle here and delete it using GDIOBJ_FreeObj
880 we should delete it directly here! */
882 End
= &GdiHandleTable
->Entries
[GDI_HANDLE_COUNT
];
883 for (Entry
= &GdiHandleTable
->Entries
[RESERVE_ENTRIES_COUNT
];
887 /* ignore the lock bit */
888 if ( (HANDLE
)((ULONG_PTR
)Entry
->ProcessId
& ~0x1) == ProcId
)
890 if ( (Entry
->Type
& GDI_ENTRY_BASETYPE_MASK
) == ObjectType
||
891 ObjectType
== GDI_OBJECT_TYPE_DONTCARE
)
893 HGDIOBJ ObjectHandle
;
895 /* Create the object handle for the entry, the lower(!) 16 bit of the
896 Type field includes the type of the object including the stock
897 object flag - but since stock objects don't have a process id we can
898 simply ignore this fact here. */
899 ObjectHandle
= (HGDIOBJ
)(Index
| (Entry
->Type
<< GDI_ENTRY_UPPER_SHIFT
));
901 if (!GDIOBJ_FreeObjByHandle(ObjectHandle
, GDI_OBJECT_TYPE_DONTCARE
))
903 DPRINT1("Failed to delete object %p!\n", ObjectHandle
);
906 if (W32Process
->GDIHandleCount
== 0)
908 /* there are no more gdi handles for this process, bail */
919 * Internal function. Called when the process is destroyed to free the remaining GDI handles.
920 * \param Process - PID of the process that will be destroyed.
923 GDI_CleanupForProcess(struct _EPROCESS
*Process
)
925 PEPROCESS CurrentProcess
;
926 PPROCESSINFO W32Process
;
928 DPRINT("Starting CleanupForProcess prochandle %x Pid %d\n", Process
, Process
->UniqueProcessId
);
929 CurrentProcess
= PsGetCurrentProcess();
930 if (CurrentProcess
!= Process
)
932 KeAttachProcess(&Process
->Pcb
);
935 W32Process
= (PPROCESSINFO
)CurrentProcess
->Win32Process
;
937 /* Delete objects. Begin with types that are not referenced by other types */
938 IntDeleteHandlesForProcess(Process
, GDILoObjType_LO_DC_TYPE
);
939 IntDeleteHandlesForProcess(Process
, GDILoObjType_LO_BRUSH_TYPE
);
940 IntDeleteHandlesForProcess(Process
, GDILoObjType_LO_BITMAP_TYPE
);
942 /* Finally finish with what's left */
943 IntDeleteHandlesForProcess(Process
, GDI_OBJECT_TYPE_DONTCARE
);
945 if (CurrentProcess
!= Process
)
951 GdiDbgHTIntegrityCheck();
954 DPRINT("Completed cleanup for process %d\n", Process
->UniqueProcessId
);
955 if (W32Process
->GDIHandleCount
> 0)
957 DPRINT1("Leaking %d handles!\n", W32Process
->GDIHandleCount
);
964 * Return pointer to the object by handle.
966 * \param hObj Object handle
967 * \return Pointer to the object.
969 * \note Process can only get pointer to the objects it created or global objects.
971 * \todo Get rid of the ExpectedType parameter!
973 PGDIOBJ INTERNAL_CALL
974 GDIOBJ_LockObj(HGDIOBJ hObj
, DWORD ExpectedType
)
977 PGDI_TABLE_ENTRY Entry
;
978 HANDLE ProcessId
, HandleProcessId
, LockedProcessId
, PrevProcId
;
980 ULONG HandleType
, HandleUpper
;
982 /* Check for dummy call */
986 GDIDBG_INITLOOPTRACE();
988 HandleIndex
= GDI_HANDLE_GET_INDEX(hObj
);
989 HandleType
= GDI_HANDLE_GET_TYPE(hObj
);
990 HandleUpper
= GDI_HANDLE_GET_UPPER(hObj
);
992 /* Check that the handle index is valid. */
993 if (HandleIndex
>= GDI_HANDLE_COUNT
)
996 Entry
= &GdiHandleTable
->Entries
[HandleIndex
];
998 /* Check if we have the requested type */
999 if ( (ExpectedType
!= GDI_OBJECT_TYPE_DONTCARE
&&
1000 HandleType
!= ExpectedType
) ||
1003 DPRINT("Attempted to lock object 0x%x of wrong type (Handle: 0x%x, requested: 0x%x)\n",
1004 hObj
, HandleType
, ExpectedType
);
1005 GDIDBG_TRACECALLER();
1006 GDIDBG_TRACEALLOCATOR(hObj
);
1007 GDIDBG_TRACEDELETER(hObj
);
1011 ProcessId
= (HANDLE
)((ULONG_PTR
)PsGetCurrentProcessId() & ~1);
1012 HandleProcessId
= (HANDLE
)((ULONG_PTR
)Entry
->ProcessId
& ~1);
1014 /* Check for invalid owner. */
1015 if (ProcessId
!= HandleProcessId
&& HandleProcessId
!= NULL
)
1017 DPRINT1("Tried to lock object (0x%p) of wrong owner! ProcessId = %p, HandleProcessId = %p\n", hObj
, ProcessId
, HandleProcessId
);
1018 GDIDBG_TRACECALLER();
1019 GDIDBG_TRACEALLOCATOR(hObj
);
1024 * Prevent the thread from being terminated during the locking process.
1025 * It would result in undesired effects and inconsistency of the global
1029 KeEnterCriticalRegion();
1032 * Loop until we either successfully lock the handle entry & object or
1033 * fail some of the check.
1038 /* Lock the handle table entry. */
1039 LockedProcessId
= (HANDLE
)((ULONG_PTR
)HandleProcessId
| 0x1);
1040 PrevProcId
= InterlockedCompareExchangePointer((PVOID
*)&Entry
->ProcessId
,
1044 if (PrevProcId
== HandleProcessId
)
1047 * We're locking an object that belongs to our process or it's a
1048 * global object if HandleProcessId is 0 here.
1051 if ( (Entry
->KernelData
!= NULL
) &&
1052 ((Entry
->Type
<< GDI_ENTRY_UPPER_SHIFT
) == HandleUpper
) )
1054 PTHREADINFO Thread
= (PTHREADINFO
)PsGetCurrentThreadWin32Thread();
1055 Object
= Entry
->KernelData
;
1057 if (Object
->cExclusiveLock
== 0)
1059 Object
->Tid
= Thread
;
1060 Object
->cExclusiveLock
= 1;
1061 GDIDBG_CAPTURELOCKER(GDI_HANDLE_GET_INDEX(hObj
))
1065 if (Object
->Tid
!= Thread
)
1067 /* Unlock the handle table entry. */
1068 (void)InterlockedExchangePointer((PVOID
*)&Entry
->ProcessId
, PrevProcId
);
1073 InterlockedIncrement((PLONG
)&Object
->cExclusiveLock
);
1079 * Debugging code. Report attempts to lock deleted handles and
1080 * locking type mismatches.
1082 LockErrorDebugOutput(hObj
, Entry
, "GDIOBJ_LockObj");
1085 /* Unlock the handle table entry. */
1086 (void)InterlockedExchangePointer((PVOID
*)&Entry
->ProcessId
, PrevProcId
);
1093 * The handle is currently locked, wait some time and try again.
1095 GDIDBG_TRACELOOP(hObj
, PrevProcId
, NULL
);
1102 KeLeaveCriticalRegion();
1109 * Return pointer to the object by handle (and allow sharing of the handle
1112 * \param hObj Object handle
1113 * \return Pointer to the object.
1115 * \note Process can only get pointer to the objects it created or global objects.
1117 * \todo Get rid of the ExpectedType parameter!
1119 PGDIOBJ INTERNAL_CALL
1120 GDIOBJ_ShareLockObj(HGDIOBJ hObj
, DWORD ExpectedType
)
1123 PGDI_TABLE_ENTRY Entry
;
1124 HANDLE ProcessId
, HandleProcessId
, LockedProcessId
, PrevProcId
;
1126 ULONG_PTR HandleType
, HandleUpper
;
1128 /* Check for dummy call */
1132 HandleIndex
= GDI_HANDLE_GET_INDEX(hObj
);
1133 HandleType
= GDI_HANDLE_GET_TYPE(hObj
);
1134 HandleUpper
= GDI_HANDLE_GET_UPPER(hObj
);
1136 /* Check that the handle index is valid. */
1137 if (HandleIndex
>= GDI_HANDLE_COUNT
)
1140 /* Check if we have the requested type */
1141 if ( (ExpectedType
!= GDI_OBJECT_TYPE_DONTCARE
&&
1142 HandleType
!= ExpectedType
) ||
1145 DPRINT1("Attempted to lock object 0x%x of wrong type (Handle: 0x%x, requested: 0x%x)\n",
1146 hObj
, HandleType
, ExpectedType
);
1150 Entry
= &GdiHandleTable
->Entries
[HandleIndex
];
1152 ProcessId
= (HANDLE
)((ULONG_PTR
)PsGetCurrentProcessId() & ~1);
1153 HandleProcessId
= (HANDLE
)((ULONG_PTR
)Entry
->ProcessId
& ~1);
1155 /* Check for invalid owner. */
1156 if (ProcessId
!= HandleProcessId
&& HandleProcessId
!= NULL
)
1162 * Prevent the thread from being terminated during the locking process.
1163 * It would result in undesired effects and inconsistency of the global
1167 KeEnterCriticalRegion();
1170 * Loop until we either successfully lock the handle entry & object or
1171 * fail some of the check.
1176 /* Lock the handle table entry. */
1177 LockedProcessId
= (HANDLE
)((ULONG_PTR
)HandleProcessId
| 0x1);
1178 PrevProcId
= InterlockedCompareExchangePointer((PVOID
*)&Entry
->ProcessId
,
1182 if (PrevProcId
== HandleProcessId
)
1185 * We're locking an object that belongs to our process or it's a
1186 * global object if HandleProcessId is 0 here.
1189 if ( (Entry
->KernelData
!= NULL
) &&
1190 (HandleUpper
== (Entry
->Type
<< GDI_ENTRY_UPPER_SHIFT
)) )
1192 Object
= (POBJ
)Entry
->KernelData
;
1194 GDIDBG_CAPTURESHARELOCKER(HandleIndex
);
1196 if (InterlockedIncrement((PLONG
)&Object
->ulShareCount
) == 1)
1198 memset(GDIHandleLocker
[HandleIndex
], 0x00, GDI_STACK_LEVELS
* sizeof(ULONG
));
1199 RtlCaptureStackBackTrace(1, GDI_STACK_LEVELS
, (PVOID
*)GDIHandleShareLocker
[HandleIndex
], NULL
);
1202 InterlockedIncrement((PLONG
)&Object
->ulShareCount
);
1208 * Debugging code. Report attempts to lock deleted handles and
1209 * locking type mismatches.
1211 LockErrorDebugOutput(hObj
, Entry
, "GDIOBJ_ShareLockObj");
1214 /* Unlock the handle table entry. */
1215 (void)InterlockedExchangePointer((PVOID
*)&Entry
->ProcessId
, PrevProcId
);
1222 * The handle is currently locked, wait some time and try again.
1230 KeLeaveCriticalRegion();
1236 GDIOBJ_OwnedByCurrentProcess(HGDIOBJ ObjectHandle
)
1238 PGDI_TABLE_ENTRY Entry
;
1242 DPRINT("GDIOBJ_OwnedByCurrentProcess: ObjectHandle: 0x%08x\n", ObjectHandle
);
1244 if (!GDI_HANDLE_IS_STOCKOBJ(ObjectHandle
))
1246 ProcessId
= PsGetCurrentProcessId();
1248 Entry
= GDI_HANDLE_GET_ENTRY(GdiHandleTable
, ObjectHandle
);
1249 Ret
= Entry
->KernelData
!= NULL
&&
1250 (Entry
->Type
& GDI_ENTRY_BASETYPE_MASK
) != 0 &&
1251 (HANDLE
)((ULONG_PTR
)Entry
->ProcessId
& ~0x1) == ProcessId
;
1260 GDIOBJ_ConvertToStockObj(HGDIOBJ
*phObj
)
1263 * FIXME !!!!! THIS FUNCTION NEEDS TO BE FIXED - IT IS NOT SAFE WHEN OTHER THREADS
1264 * MIGHT ATTEMPT TO LOCK THE OBJECT DURING THIS CALL!!!
1266 PGDI_TABLE_ENTRY Entry
;
1267 HANDLE ProcessId
, LockedProcessId
, PrevProcId
;
1271 GDIDBG_INITLOOPTRACE();
1276 DPRINT("GDIOBJ_ConvertToStockObj: hObj: 0x%08x\n", hObj
);
1278 Thread
= (PTHREADINFO
)PsGetCurrentThreadWin32Thread();
1280 if (!GDI_HANDLE_IS_STOCKOBJ(hObj
))
1282 ProcessId
= PsGetCurrentProcessId();
1283 LockedProcessId
= (HANDLE
)((ULONG_PTR
)ProcessId
| 0x1);
1285 Entry
= GDI_HANDLE_GET_ENTRY(GdiHandleTable
, hObj
);
1288 /* lock the object, we must not convert stock objects, so don't check!!! */
1289 PrevProcId
= InterlockedCompareExchangePointer((PVOID
*)&Entry
->ProcessId
, LockedProcessId
, ProcessId
);
1290 if (PrevProcId
== ProcessId
)
1292 LONG NewType
, PrevType
, OldType
;
1294 /* we're locking an object that belongs to our process. First calculate
1295 the new object type including the stock object flag and then try to
1297 /* On Windows the higher 16 bit of the type field don't contain the
1298 full type from the handle, but the base type.
1299 (type = BRSUH, PEN, EXTPEN, basetype = BRUSH) */
1300 OldType
= ((ULONG
)hObj
& GDI_HANDLE_BASETYPE_MASK
) | ((ULONG
)hObj
>> GDI_ENTRY_UPPER_SHIFT
);
1301 /* We are currently not using bits 24..31 (flags) of the type field, but for compatibility
1302 we copy them as we can't get them from the handle */
1303 OldType
|= Entry
->Type
& GDI_ENTRY_FLAGS_MASK
;
1305 /* As the object should be a stock object, set it's flag, but only in the lower 16 bits */
1306 NewType
= OldType
| GDI_ENTRY_STOCK_MASK
;
1308 /* Try to exchange the type field - but only if the old (previous type) matches! */
1309 PrevType
= InterlockedCompareExchange(&Entry
->Type
, NewType
, OldType
);
1310 if (PrevType
== OldType
&& Entry
->KernelData
!= NULL
)
1312 PTHREADINFO PrevThread
;
1315 /* We successfully set the stock object flag.
1316 KernelData should never be NULL here!!! */
1317 ASSERT(Entry
->KernelData
);
1319 Object
= Entry
->KernelData
;
1321 PrevThread
= Object
->Tid
;
1322 if (Object
->cExclusiveLock
== 0 || PrevThread
== Thread
)
1324 /* dereference the process' object counter */
1325 if (PrevProcId
!= GDI_GLOBAL_PROCESS
)
1327 PEPROCESS OldProcess
;
1328 PPROCESSINFO W32Process
;
1332 Status
= PsLookupProcessByProcessId((HANDLE
)((ULONG_PTR
)PrevProcId
& ~0x1), &OldProcess
);
1333 if (NT_SUCCESS(Status
))
1335 W32Process
= (PPROCESSINFO
)OldProcess
->Win32Process
;
1336 if (W32Process
!= NULL
)
1338 InterlockedDecrement(&W32Process
->GDIHandleCount
);
1340 ObDereferenceObject(OldProcess
);
1344 hObj
= (HGDIOBJ
)((ULONG
)(hObj
) | GDI_HANDLE_STOCK_MASK
);
1346 Object
->hHmgr
= hObj
;
1348 /* remove the process id lock and make it global */
1349 (void)InterlockedExchangePointer((PVOID
*)&Entry
->ProcessId
, GDI_GLOBAL_PROCESS
);
1351 /* we're done, successfully converted the object */
1356 GDIDBG_TRACELOOP(hObj
, PrevThread
, Thread
);
1358 /* WTF?! The object is already locked by a different thread!
1359 Release the lock, wait a bit and try again!
1360 FIXME - we should give up after some time unless we want to wait forever! */
1361 (void)InterlockedExchangePointer((PVOID
*)&Entry
->ProcessId
, PrevProcId
);
1369 DPRINT1("Attempted to convert object 0x%x that is deleted! Should never get here!!!\n", hObj
);
1370 DPRINT1("OldType = 0x%x, Entry->Type = 0x%x, NewType = 0x%x, Entry->KernelData = 0x%x\n", OldType
, Entry
->Type
, NewType
, Entry
->KernelData
);
1373 else if (PrevProcId
== LockedProcessId
)
1375 GDIDBG_TRACELOOP(hObj
, PrevProcId
, ProcessId
);
1377 /* the object is currently locked, wait some time and try again.
1378 FIXME - we shouldn't loop forever! Give up after some time! */
1385 DPRINT1("Attempted to convert invalid handle: 0x%x\n", hObj
);
1393 GDIOBJ_SetOwnership(HGDIOBJ ObjectHandle
, PEPROCESS NewOwner
)
1395 PGDI_TABLE_ENTRY Entry
;
1396 HANDLE ProcessId
, LockedProcessId
, PrevProcId
;
1400 GDIDBG_INITLOOPTRACE();
1402 DPRINT("GDIOBJ_SetOwnership: hObj: 0x%x, NewProcess: 0x%x\n", ObjectHandle
, (NewOwner
? PsGetProcessId(NewOwner
) : 0));
1404 Thread
= (PTHREADINFO
)PsGetCurrentThreadWin32Thread();
1406 if (!GDI_HANDLE_IS_STOCKOBJ(ObjectHandle
))
1408 ProcessId
= PsGetCurrentProcessId();
1409 LockedProcessId
= (HANDLE
)((ULONG_PTR
)ProcessId
| 0x1);
1411 Entry
= GDI_HANDLE_GET_ENTRY(GdiHandleTable
, ObjectHandle
);
1414 /* lock the object, we must not convert stock objects, so don't check!!! */
1415 PrevProcId
= InterlockedCompareExchangePointer((PVOID
*)&Entry
->ProcessId
, ProcessId
, LockedProcessId
);
1416 if (PrevProcId
== ProcessId
)
1418 PTHREADINFO PrevThread
;
1420 if ((Entry
->Type
& GDI_ENTRY_BASETYPE_MASK
) != 0)
1422 POBJ Object
= Entry
->KernelData
;
1424 PrevThread
= Object
->Tid
;
1425 if (Object
->cExclusiveLock
== 0 || PrevThread
== Thread
)
1427 PEPROCESS OldProcess
;
1428 PPROCESSINFO W32Process
;
1431 if (NewOwner
!= NULL
)
1433 ProcessId
= PsGetProcessId(NewOwner
);
1438 if((ULONG_PTR
)ProcessId
== ((ULONG_PTR
)PrevProcId
& ~0x1))
1440 DPRINT("Setting same process than previous one, nothing to do\n");
1444 /* dereference the process' object counter */
1446 if ((ULONG_PTR
)PrevProcId
& ~0x1)
1448 Status
= PsLookupProcessByProcessId((HANDLE
)((ULONG_PTR
)PrevProcId
& ~0x1), &OldProcess
);
1449 if (NT_SUCCESS(Status
))
1451 W32Process
= (PPROCESSINFO
)OldProcess
->Win32Process
;
1452 if (W32Process
!= NULL
)
1454 InterlockedDecrement(&W32Process
->GDIHandleCount
);
1456 ObDereferenceObject(OldProcess
);
1460 if (NewOwner
!= NULL
)
1462 /* Increase the new process' object counter */
1463 W32Process
= (PPROCESSINFO
)NewOwner
->Win32Process
;
1464 if (W32Process
!= NULL
)
1466 InterlockedIncrement(&W32Process
->GDIHandleCount
);
1471 /* remove the process id lock and change it to the new process id */
1472 (void)InterlockedExchangePointer((PVOID
*)&Entry
->ProcessId
, ProcessId
);
1479 GDIDBG_TRACELOOP(ObjectHandle
, PrevThread
, Thread
);
1481 /* WTF?! The object is already locked by a different thread!
1482 Release the lock, wait a bit and try again! DO reset the pid lock
1483 so we make sure we don't access invalid memory in case the object is
1484 being deleted in the meantime (because we don't have aquired a reference
1486 FIXME - we should give up after some time unless we want to wait forever! */
1487 (void)InterlockedExchangePointer((PVOID
*)&Entry
->ProcessId
, PrevProcId
);
1495 DPRINT1("Attempted to change ownership of an object 0x%x currently being destroyed!!!\n", ObjectHandle
);
1496 DPRINT1("Entry->Type = 0x%lx, Entry->KernelData = 0x%p\n", Entry
->Type
, Entry
->KernelData
);
1500 else if (PrevProcId
== LockedProcessId
)
1502 GDIDBG_TRACELOOP(ObjectHandle
, PrevProcId
, ProcessId
);
1504 /* the object is currently locked, wait some time and try again.
1505 FIXME - we shouldn't loop forever! Give up after some time! */
1510 else if (((ULONG_PTR
)PrevProcId
& ~0x1) == 0)
1512 /* allow changing ownership of global objects */
1514 LockedProcessId
= (HANDLE
)((ULONG_PTR
)ProcessId
| 0x1);
1517 else if ((HANDLE
)((ULONG_PTR
)PrevProcId
& ~0x1) != PsGetCurrentProcessId())
1519 DPRINT1("Attempted to change ownership of object 0x%x (pid: 0x%x) from pid 0x%x!!!\n", ObjectHandle
, (ULONG_PTR
)PrevProcId
& ~0x1, PsGetCurrentProcessId());
1524 DPRINT1("Attempted to change owner of invalid handle: 0x%x\n", ObjectHandle
);
1532 GDIOBJ_CopyOwnership(HGDIOBJ CopyFrom
, HGDIOBJ CopyTo
)
1534 PGDI_TABLE_ENTRY FromEntry
;
1536 HANDLE FromProcessId
, FromLockedProcessId
, FromPrevProcId
;
1539 GDIDBG_INITLOOPTRACE();
1541 DPRINT("GDIOBJ_CopyOwnership: from: 0x%x, to: 0x%x\n", CopyFrom
, CopyTo
);
1543 Thread
= (PTHREADINFO
)PsGetCurrentThreadWin32Thread();
1545 if (!GDI_HANDLE_IS_STOCKOBJ(CopyFrom
) && !GDI_HANDLE_IS_STOCKOBJ(CopyTo
))
1547 FromEntry
= GDI_HANDLE_GET_ENTRY(GdiHandleTable
, CopyFrom
);
1549 FromProcessId
= (HANDLE
)((ULONG_PTR
)FromEntry
->ProcessId
& ~0x1);
1550 FromLockedProcessId
= (HANDLE
)((ULONG_PTR
)FromProcessId
| 0x1);
1553 /* lock the object, we must not convert stock objects, so don't check!!! */
1554 FromPrevProcId
= InterlockedCompareExchangePointer((PVOID
*)&FromEntry
->ProcessId
, FromProcessId
, FromLockedProcessId
);
1555 if (FromPrevProcId
== FromProcessId
)
1557 PTHREADINFO PrevThread
;
1560 if ((FromEntry
->Type
& GDI_ENTRY_BASETYPE_MASK
) != 0)
1562 Object
= FromEntry
->KernelData
;
1564 /* save the pointer to the calling thread so we know it was this thread
1565 that locked the object */
1566 PrevThread
= Object
->Tid
;
1567 if (Object
->cExclusiveLock
== 0 || PrevThread
== Thread
)
1569 /* now let's change the ownership of the target object */
1571 if (((ULONG_PTR
)FromPrevProcId
& ~0x1) != 0)
1573 PEPROCESS ProcessTo
;
1575 if (NT_SUCCESS(PsLookupProcessByProcessId((HANDLE
)((ULONG_PTR
)FromPrevProcId
& ~0x1), &ProcessTo
)))
1577 GDIOBJ_SetOwnership(CopyTo
, ProcessTo
);
1578 ObDereferenceObject(ProcessTo
);
1583 /* mark the object as global */
1584 GDIOBJ_SetOwnership(CopyTo
, NULL
);
1587 (void)InterlockedExchangePointer((PVOID
*)&FromEntry
->ProcessId
, FromPrevProcId
);
1591 GDIDBG_TRACELOOP(CopyFrom
, PrevThread
, Thread
);
1593 /* WTF?! The object is already locked by a different thread!
1594 Release the lock, wait a bit and try again! DO reset the pid lock
1595 so we make sure we don't access invalid memory in case the object is
1596 being deleted in the meantime (because we don't have aquired a reference
1598 FIXME - we should give up after some time unless we want to wait forever! */
1599 (void)InterlockedExchangePointer((PVOID
*)&FromEntry
->ProcessId
, FromPrevProcId
);
1602 goto LockHandleFrom
;
1607 DPRINT1("Attempted to copy ownership from an object 0x%x currently being destroyed!!!\n", CopyFrom
);
1611 else if (FromPrevProcId
== FromLockedProcessId
)
1613 GDIDBG_TRACELOOP(CopyFrom
, FromPrevProcId
, FromProcessId
);
1615 /* the object is currently locked, wait some time and try again.
1616 FIXME - we shouldn't loop forever! Give up after some time! */
1619 goto LockHandleFrom
;
1621 else if ((HANDLE
)((ULONG_PTR
)FromPrevProcId
& ~0x1) != PsGetCurrentProcessId())
1623 /* FIXME - should we really allow copying ownership from objects that we don't even own? */
1624 DPRINT1("WARNING! Changing copying ownership of object 0x%x (pid: 0x%x) to pid 0x%x!!!\n", CopyFrom
, (ULONG_PTR
)FromPrevProcId
& ~0x1, PsGetCurrentProcessId());
1625 FromProcessId
= (HANDLE
)((ULONG_PTR
)FromPrevProcId
& ~0x1);
1626 FromLockedProcessId
= (HANDLE
)((ULONG_PTR
)FromProcessId
| 0x1);
1627 goto LockHandleFrom
;
1631 DPRINT1("Attempted to copy ownership from invalid handle: 0x%x\n", CopyFrom
);
1639 GDI_MapHandleTable(PSECTION_OBJECT SectionObject
, PEPROCESS Process
)
1641 PVOID MappedView
= NULL
;
1643 LARGE_INTEGER Offset
;
1644 ULONG ViewSize
= sizeof(GDI_HANDLE_TABLE
);
1646 Offset
.QuadPart
= 0;
1648 ASSERT(SectionObject
!= NULL
);
1649 ASSERT(Process
!= NULL
);
1651 Status
= MmMapViewOfSection(SectionObject
,
1662 if (!NT_SUCCESS(Status
))
1668 /* Locks 2 or 3 objects at a time */
1671 GDIOBJ_LockMultipleObjs(ULONG ulCount
,
1675 UINT auiIndices
[3] = {0,1,2};
1677 BOOL bUnsorted
= TRUE
;
1679 /* First is greatest */
1683 for(i
=1; i
<ulCount
; i
++)
1685 if((ULONG_PTR
)ahObj
[auiIndices
[i
-1]] < (ULONG_PTR
)ahObj
[auiIndices
[i
]])
1687 tmp
= auiIndices
[i
-1];
1688 auiIndices
[i
-1] = auiIndices
[i
];
1689 auiIndices
[i
] = tmp
;
1695 for(i
=0;i
<ulCount
;i
++)
1696 apObj
[auiIndices
[i
]] = GDIOBJ_LockObj(ahObj
[auiIndices
[i
]], GDI_OBJECT_TYPE_DONTCARE
);
1700 /** PUBLIC FUNCTIONS **********************************************************/
1704 IntGdiSetRegionOwner(HRGN hRgn
, DWORD OwnerMask
)
1707 PGDI_TABLE_ENTRY Entry
;
1710 These regions do not use attribute sections and when allocated, use gdiobj
1713 // FIXME! HAX!!! Remove this once we get everything right!
1714 Index
= GDI_HANDLE_GET_INDEX(hRgn
);
1715 Entry
= &GdiHandleTable
->Entries
[Index
];
1716 if (Entry
->UserData
) FreeObjectAttr(Entry
->UserData
);
1717 Entry
->UserData
= NULL
;
1719 if ((OwnerMask
== GDI_OBJ_HMGR_PUBLIC
) || OwnerMask
== GDI_OBJ_HMGR_NONE
)
1721 return GDIOBJ_SetOwnership(hRgn
, NULL
);
1723 if (OwnerMask
== GDI_OBJ_HMGR_POWNED
)
1725 return GDIOBJ_SetOwnership((HGDIOBJ
) hRgn
, PsGetCurrentProcess() );
1732 IntGdiSetBrushOwner(PBRUSH pbr
, DWORD OwnerMask
)
1735 PEPROCESS Owner
= NULL
;
1736 PGDI_TABLE_ENTRY pEntry
= NULL
;
1738 if (!pbr
) return FALSE
;
1740 hBR
= pbr
->BaseObject
.hHmgr
;
1742 if (!hBR
|| (GDI_HANDLE_GET_TYPE(hBR
) != GDI_OBJECT_TYPE_BRUSH
))
1746 INT Index
= GDI_HANDLE_GET_INDEX((HGDIOBJ
)hBR
);
1747 pEntry
= &GdiHandleTable
->Entries
[Index
];
1750 if (pbr
->flAttrs
& GDIBRUSH_IS_GLOBAL
)
1752 GDIOBJ_ShareUnlockObjByPtr((POBJ
)pbr
);
1756 if ((OwnerMask
== GDI_OBJ_HMGR_PUBLIC
) || OwnerMask
== GDI_OBJ_HMGR_NONE
)
1758 // Set this Brush to inaccessible mode and to an Owner of NONE.
1759 // if (OwnerMask == GDI_OBJ_HMGR_NONE) Owner = OwnerMask;
1761 if (!GDIOBJ_SetOwnership((HGDIOBJ
) hBR
, Owner
))
1764 // Deny user access to User Data.
1765 pEntry
->UserData
= NULL
; // This hBR is inaccessible!
1768 if (OwnerMask
== GDI_OBJ_HMGR_POWNED
)
1770 if (!GDIOBJ_SetOwnership((HGDIOBJ
) hBR
, PsGetCurrentProcess() ))
1773 // Allow user access to User Data.
1774 pEntry
->UserData
= pbr
->pBrushAttr
;
1781 IntGdiSetDCOwnerEx( HDC hDC
, DWORD OwnerMask
, BOOL NoSetBrush
)
1786 if (!hDC
|| (GDI_HANDLE_GET_TYPE(hDC
) != GDI_OBJECT_TYPE_DC
)) return FALSE
;
1788 if ((OwnerMask
== GDI_OBJ_HMGR_PUBLIC
) || OwnerMask
== GDI_OBJ_HMGR_NONE
)
1790 pDC
= DC_LockDc ( hDC
);
1791 MmCopyFromCaller(&pDC
->dcattr
, pDC
->pdcattr
, sizeof(DC_ATTR
));
1792 DC_vFreeDcAttr(pDC
);
1795 if (!DC_SetOwnership( hDC
, NULL
)) // This hDC is inaccessible!
1799 if (OwnerMask
== GDI_OBJ_HMGR_POWNED
)
1801 pDC
= DC_LockDc ( hDC
);
1802 ASSERT(pDC
->pdcattr
== &pDC
->dcattr
);
1805 if (!DC_SetOwnership( hDC
, PsGetCurrentProcess() )) return Ret
;
1807 DC_AllocateDcAttr( hDC
); // Allocate new dcattr
1809 DCU_SynchDcAttrtoUser( hDC
); // Copy data from dc to dcattr
1812 if ((OwnerMask
!= GDI_OBJ_HMGR_NONE
) && !NoSetBrush
)
1814 pDC
= DC_LockDc ( hDC
);
1815 if (IntGdiSetBrushOwner((PBRUSH
)pDC
->dclevel
.pbrFill
, OwnerMask
))
1816 IntGdiSetBrushOwner((PBRUSH
)pDC
->dclevel
.pbrLine
, OwnerMask
);
1824 GreGetObjectOwner(HGDIOBJ Handle
, GDIOBJTYPE ObjType
)
1826 INT Ret
= GDI_OBJ_HMGR_RESTRICTED
;
1828 if ( GDI_HANDLE_GET_INDEX(Handle
) < GDI_HANDLE_COUNT
)
1830 PGDI_TABLE_ENTRY pEntry
= &GdiHandleTable
->Entries
[GDI_HANDLE_GET_INDEX(Handle
)];
1832 if (pEntry
->ObjectType
== ObjType
)
1834 if (pEntry
->FullUnique
== (GDI_HANDLE_GET_UPPER(Handle
) >> GDI_ENTRY_UPPER_SHIFT
))
1835 Ret
= pEntry
->ProcessId
& ~1;
1844 NtGdiCreateClientObj(
1851 /* Mask out everything that would change the type in a wrong manner */
1852 ulType
&= (GDI_HANDLE_TYPE_MASK
& ~GDI_HANDLE_BASETYPE_MASK
);
1854 /* Allocate a new object */
1855 pObject
= GDIOBJ_AllocObjWithHandle(GDI_OBJECT_TYPE_CLIOBJ
| ulType
);
1861 /* get the handle */
1862 handle
= pObject
->hHmgr
;
1865 GDIOBJ_UnlockObjByPtr(pObject
);
1873 NtGdiDeleteClientObj(
1877 /* We first need to get the real type from the handle */
1878 ULONG type
= GDI_HANDLE_GET_TYPE(h
);
1880 /* Check if it's really a CLIENTOBJ */
1881 if ((type
& GDI_HANDLE_BASETYPE_MASK
) != GDILoObjType_LO_CLIENTOBJ_TYPE
)
1883 /* FIXME: SetLastError? */
1886 return GDIOBJ_FreeObjByHandle(h
, type
);
1891 IntGdiGetObject(IN HANDLE Handle
,
1899 pGdiObject
= GDIOBJ_LockObj(Handle
, GDI_OBJECT_TYPE_DONTCARE
);
1902 EngSetLastError(ERROR_INVALID_HANDLE
);
1906 dwObjectType
= GDIOBJ_GetObjectType(Handle
);
1907 switch (dwObjectType
)
1909 case GDI_OBJECT_TYPE_PEN
:
1910 case GDI_OBJECT_TYPE_EXTPEN
:
1911 Result
= PEN_GetObject((PBRUSH
) pGdiObject
, cbCount
, (PLOGPEN
) lpBuffer
); // IntGdiCreatePenIndirect
1914 case GDI_OBJECT_TYPE_BRUSH
:
1915 Result
= BRUSH_GetObject((PBRUSH
) pGdiObject
, cbCount
, (LPLOGBRUSH
)lpBuffer
);
1918 case GDI_OBJECT_TYPE_BITMAP
:
1919 Result
= BITMAP_GetObject((SURFACE
*) pGdiObject
, cbCount
, lpBuffer
);
1921 case GDI_OBJECT_TYPE_FONT
:
1922 Result
= FontGetObject((PTEXTOBJ
) pGdiObject
, cbCount
, lpBuffer
);
1924 // Fix the LOGFONT structure for the stock fonts
1925 if (FIRST_STOCK_HANDLE
<= Handle
&& Handle
<= LAST_STOCK_HANDLE
)
1927 FixStockFontSizeW(Handle
, cbCount
, lpBuffer
);
1932 case GDI_OBJECT_TYPE_PALETTE
:
1933 Result
= PALETTE_GetObject((PPALETTE
) pGdiObject
, cbCount
, lpBuffer
);
1937 DPRINT1("GDI object type 0x%08x not implemented\n", dwObjectType
);
1941 GDIOBJ_UnlockObjByPtr(pGdiObject
);
1951 NtGdiExtGetObjectW(IN HANDLE hGdiObj
,
1953 OUT LPVOID lpBuffer
)
1960 DIBSECTION dibsection
;
1964 EXTLOGFONTW extlogfontw
;
1965 ENUMLOGFONTEXDVW enumlogfontexdvw
;
1968 // Normalize to the largest supported object size
1969 cbCount
= min((UINT
)cbCount
, sizeof(Object
));
1971 // Now do the actual call
1972 iRetCount
= IntGdiGetObject(hGdiObj
, cbCount
, lpBuffer
? &Object
: NULL
);
1973 cbCopyCount
= min((UINT
)cbCount
, (UINT
)iRetCount
);
1975 // Make sure we have a buffer and a copy size
1976 if ((cbCopyCount
) && (lpBuffer
))
1978 // Enter SEH for buffer transfer
1981 // Probe the buffer and copy it
1982 ProbeForWrite(lpBuffer
, cbCopyCount
, sizeof(WORD
));
1983 RtlCopyMemory(lpBuffer
, &Object
, cbCopyCount
);
1985 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1987 // Clear the return value.
1988 // Do *NOT* set last error here!