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 ********************************************************/
88 * Dummy GDI Cleanup Callback
90 /* static */ /* FIXME: -fno-unit-at-a-time breaks this */
92 GDI_CleanupDummy(PVOID ObjectBody
)
98 * Allocate GDI object table.
99 * \param Size - number of entries in the object table.
101 PGDI_HANDLE_TABLE INTERNAL_CALL
102 GDIOBJ_iAllocHandleTable(OUT PSECTION_OBJECT
*SectionObject
)
104 PGDI_HANDLE_TABLE HandleTable
= NULL
;
105 LARGE_INTEGER htSize
;
110 ASSERT(SectionObject
!= NULL
);
112 htSize
.QuadPart
= sizeof(GDI_HANDLE_TABLE
);
114 Status
= MmCreateSection((PVOID
*)SectionObject
,
122 if (!NT_SUCCESS(Status
))
125 /* FIXME - use MmMapViewInSessionSpace once available! */
126 Status
= MmMapViewInSystemSpace(*SectionObject
,
127 (PVOID
*)&HandleTable
,
129 if (!NT_SUCCESS(Status
))
131 ObDereferenceObject(*SectionObject
);
132 *SectionObject
= NULL
;
136 RtlZeroMemory(HandleTable
, sizeof(GDI_HANDLE_TABLE
));
138 HandleTable
->LookasideLists
= ExAllocatePoolWithTag(NonPagedPool
,
139 BASE_OBJTYPE_COUNT
* sizeof(PAGED_LOOKASIDE_LIST
),
141 if (HandleTable
->LookasideLists
== NULL
)
143 MmUnmapViewInSystemSpace(HandleTable
);
144 ObDereferenceObject(*SectionObject
);
145 *SectionObject
= NULL
;
149 for (ObjType
= 0; ObjType
< BASE_OBJTYPE_COUNT
; ObjType
++)
151 if (ObjTypeInfo
[ObjType
].bUseLookaside
)
153 ExInitializePagedLookasideList(HandleTable
->LookasideLists
+ ObjType
,
157 ObjTypeInfo
[ObjType
].ulBodySize
,
158 ObjTypeInfo
[ObjType
].Tag
,
163 ShortDelay
.QuadPart
= -5000LL; /* FIXME - 0.5 ms? */
165 HandleTable
->FirstFree
= 0;
166 HandleTable
->FirstUnused
= RESERVE_ENTRIES_COUNT
;
172 LockErrorDebugOutput(HGDIOBJ hObj
, PGDI_TABLE_ENTRY Entry
, LPSTR Function
)
174 if ((Entry
->Type
& GDI_ENTRY_BASETYPE_MASK
) == 0)
176 DPRINT1("%s: Attempted to lock object 0x%x that is deleted!\n", Function
, hObj
);
177 GDIDBG_TRACEDELETER(hObj
);
179 else if (GDI_HANDLE_GET_REUSECNT(hObj
) != GDI_ENTRY_GET_REUSECNT(Entry
->Type
))
181 DPRINT1("%s: Attempted to lock object 0x%x, wrong reuse counter (Handle: 0x%x, Entry: 0x%x)\n",
182 Function
, hObj
, GDI_HANDLE_GET_REUSECNT(hObj
), GDI_ENTRY_GET_REUSECNT(Entry
->Type
));
184 else if (GDI_HANDLE_GET_TYPE(hObj
) != ((Entry
->Type
<< GDI_ENTRY_UPPER_SHIFT
) & GDI_HANDLE_TYPE_MASK
))
186 DPRINT1("%s: Attempted to lock object 0x%x, type mismatch (Handle: 0x%x, Entry: 0x%x)\n",
187 Function
, hObj
, GDI_HANDLE_GET_TYPE(hObj
), (Entry
->Type
<< GDI_ENTRY_UPPER_SHIFT
) & GDI_HANDLE_TYPE_MASK
);
191 DPRINT1("%s: Attempted to lock object 0x%x, something went wrong, typeinfo = 0x%x\n",
192 Function
, hObj
, Entry
->Type
);
194 GDIDBG_TRACECALLER();
199 InterlockedPopFreeEntry(VOID
)
201 ULONG idxFirst
, idxNext
, idxPrev
;
202 PGDI_TABLE_ENTRY pEntry
;
205 DPRINT("Enter InterLockedPopFreeEntry\n");
209 idxFirst
= GdiHandleTable
->FirstFree
;
213 /* Increment FirstUnused and get the new index */
214 idxFirst
= InterlockedIncrement((LONG
*)&GdiHandleTable
->FirstUnused
) - 1;
216 /* Check if we have entries left */
217 if (idxFirst
>= GDI_HANDLE_COUNT
)
219 DPRINT1("No more gdi handles left!\n");
223 /* Return the old index */
227 /* Get a pointer to the first free entry */
228 pEntry
= GdiHandleTable
->Entries
+ idxFirst
;
230 /* Try to lock the entry */
231 PrevProcId
= InterlockedCompareExchange((LONG
*)&pEntry
->ProcessId
, 1, 0);
234 /* The entry was locked or not free, wait and start over */
239 /* Sanity check: is entry really free? */
240 ASSERT(((ULONG_PTR
)pEntry
->KernelData
& ~GDI_HANDLE_INDEX_MASK
) == 0);
242 /* Try to exchange the FirstFree value */
243 idxNext
= (ULONG_PTR
)pEntry
->KernelData
;
244 idxPrev
= InterlockedCompareExchange((LONG
*)&GdiHandleTable
->FirstFree
,
248 /* Unlock the free entry */
249 (void)InterlockedExchange((LONG
*)&pEntry
->ProcessId
, 0);
251 /* If we succeeded, break out of the loop */
252 if (idxPrev
== idxFirst
)
261 /* Pushes an entry of the handle table to the free list,
262 The entry must be unlocked and the base type field must be 0 */
265 InterlockedPushFreeEntry(ULONG idxToFree
)
267 ULONG idxFirstFree
, idxPrev
;
268 PGDI_TABLE_ENTRY pFreeEntry
;
270 DPRINT("Enter InterlockedPushFreeEntry\n");
272 pFreeEntry
= GdiHandleTable
->Entries
+ idxToFree
;
273 ASSERT((pFreeEntry
->Type
& GDI_ENTRY_BASETYPE_MASK
) == 0);
274 ASSERT(pFreeEntry
->ProcessId
== 0);
275 pFreeEntry
->UserData
= NULL
;
279 idxFirstFree
= GdiHandleTable
->FirstFree
;
280 pFreeEntry
->KernelData
= (PVOID
)(ULONG_PTR
)idxFirstFree
;
282 idxPrev
= InterlockedCompareExchange((LONG
*)&GdiHandleTable
->FirstFree
,
286 while (idxPrev
!= idxFirstFree
);
292 GDIOBJ_ValidateHandle(HGDIOBJ hObj
, ULONG ObjectType
)
294 PGDI_TABLE_ENTRY Entry
= GDI_HANDLE_GET_ENTRY(GdiHandleTable
, hObj
);
295 if ((((ULONG_PTR
)hObj
& GDI_HANDLE_TYPE_MASK
) == ObjectType
) &&
296 (Entry
->Type
<< GDI_ENTRY_UPPER_SHIFT
) == GDI_HANDLE_GET_UPPER(hObj
))
298 HANDLE pid
= (HANDLE
)((ULONG_PTR
)Entry
->ProcessId
& ~0x1);
299 if (pid
== NULL
|| pid
== PsGetCurrentProcessId())
308 GDIOBJ_AllocObj(UCHAR BaseType
)
312 ASSERT((BaseType
& ~GDIObjTypeTotal
) == 0);
313 // BaseType &= GDI_HANDLE_BASETYPE_MASK;
315 if (ObjTypeInfo
[BaseType
].bUseLookaside
)
317 PPAGED_LOOKASIDE_LIST LookasideList
;
319 LookasideList
= GdiHandleTable
->LookasideLists
+ BaseType
;
320 pObject
= ExAllocateFromPagedLookasideList(LookasideList
);
324 pObject
= ExAllocatePoolWithTag(PagedPool
,
325 ObjTypeInfo
[BaseType
].ulBodySize
,
326 ObjTypeInfo
[BaseType
].Tag
);
331 RtlZeroMemory(pObject
, ObjTypeInfo
[BaseType
].ulBodySize
);
339 * Allocate memory for GDI object and return handle to it.
341 * \param ObjectType - type of object \ref GDI object types
343 * \return Pointer to the allocated object, which is locked.
346 GDIOBJ_AllocObjWithHandle(ULONG ObjectType
)
348 PPROCESSINFO W32Process
;
349 POBJ newObject
= NULL
;
350 HANDLE CurrentProcessId
, LockedProcessId
;
353 PGDI_TABLE_ENTRY Entry
;
356 GDIDBG_INITLOOPTRACE();
358 W32Process
= PsGetCurrentProcessWin32Process();
359 /* HACK HACK HACK: simplest-possible quota implementation - don't allow a process
360 to take too many GDI objects, itself. */
361 if (W32Process
&& W32Process
->GDIHandleCount
>= 0x2710)
363 DPRINT1("Too many objects for process!!!\n");
364 GDIDBG_DUMPHANDLETABLE();
368 ASSERT(ObjectType
!= GDI_OBJECT_TYPE_DONTCARE
);
370 TypeIndex
= GDI_OBJECT_GET_TYPE_INDEX(ObjectType
);
372 newObject
= GDIOBJ_AllocObj(TypeIndex
);
375 DPRINT1("Not enough memory to allocate gdi object!\n");
379 CurrentProcessId
= PsGetCurrentProcessId();
380 LockedProcessId
= (HANDLE
)((ULONG_PTR
)CurrentProcessId
| 0x1);
382 // RtlZeroMemory(newObject, ObjTypeInfo[TypeIndex].ulBodySize);
384 /* On Windows the higher 16 bit of the type field don't contain the
385 full type from the handle, but the base type.
386 (type = BRSUH, PEN, EXTPEN, basetype = BRUSH) */
387 TypeInfo
= (ObjectType
& GDI_HANDLE_BASETYPE_MASK
) | (ObjectType
>> GDI_ENTRY_UPPER_SHIFT
);
389 Index
= InterlockedPopFreeEntry();
394 Entry
= &GdiHandleTable
->Entries
[Index
];
397 PrevProcId
= InterlockedCompareExchangePointer((PVOID
*)&Entry
->ProcessId
, LockedProcessId
, 0);
398 if (PrevProcId
== NULL
)
400 PTHREADINFO Thread
= (PTHREADINFO
)PsGetCurrentThreadWin32Thread();
403 Entry
->KernelData
= newObject
;
405 /* copy the reuse-counter */
406 TypeInfo
|= Entry
->Type
& GDI_ENTRY_REUSE_MASK
;
408 /* we found a free entry, no need to exchange this field atomically
409 since we're holding the lock */
410 Entry
->Type
= TypeInfo
;
412 /* Create a handle */
413 Handle
= (HGDIOBJ
)((Index
& 0xFFFF) | (TypeInfo
<< GDI_ENTRY_UPPER_SHIFT
));
415 /* Initialize BaseObject fields */
416 newObject
->hHmgr
= Handle
;
417 newObject
->ulShareCount
= 0;
418 newObject
->cExclusiveLock
= 1;
419 newObject
->Tid
= Thread
;
421 /* unlock the entry */
422 (void)InterlockedExchangePointer((PVOID
*)&Entry
->ProcessId
, CurrentProcessId
);
424 GDIDBG_CAPTUREALLOCATOR(Index
);
426 if (W32Process
!= NULL
)
428 InterlockedIncrement(&W32Process
->GDIHandleCount
);
431 DPRINT("GDIOBJ_AllocObj: 0x%x ob: 0x%x\n", Handle
, newObject
);
436 GDIDBG_TRACELOOP(Index
, PrevProcId
, CurrentProcessId
);
437 /* damn, someone is trying to lock the object even though it doesn't
438 even exist anymore, wait a little and try again!
439 FIXME - we shouldn't loop forever! Give up after some time! */
446 GDIOBJ_FreeObj(newObject
, TypeIndex
);
448 DPRINT1("Failed to insert gdi object into the handle table, no handles left!\n");
449 GDIDBG_DUMPHANDLETABLE();
456 GDIOBJ_FreeObj(POBJ pObject
, UCHAR BaseType
)
458 /* Object must not have a handle! */
459 ASSERT(pObject
->hHmgr
== NULL
);
461 if (ObjTypeInfo
[BaseType
].bUseLookaside
)
463 PPAGED_LOOKASIDE_LIST LookasideList
;
465 LookasideList
= GdiHandleTable
->LookasideLists
+ BaseType
;
466 ExFreeToPagedLookasideList(LookasideList
, pObject
);
475 * Free memory allocated for the GDI object. For each object type this function calls the
476 * appropriate cleanup routine.
478 * \param hObj - handle of the object to be deleted.
480 * \return Returns TRUE if succesful.
481 * \return Returns FALSE if the cleanup routine returned FALSE or the object doesn't belong
482 * to the calling process.
484 * \bug This function should return VOID and kill the object no matter what...
487 GDIOBJ_FreeObjByHandle(HGDIOBJ hObj
, DWORD ExpectedType
)
489 PGDI_TABLE_ENTRY Entry
;
490 HANDLE ProcessId
, LockedProcessId
, PrevProcId
;
491 ULONG HandleType
, HandleUpper
, TypeIndex
;
494 GDIDBG_INITLOOPTRACE();
496 DPRINT("GDIOBJ_FreeObj: hObj: 0x%08x\n", hObj
);
498 if (GDI_HANDLE_IS_STOCKOBJ(hObj
))
500 DPRINT1("GDIOBJ_FreeObj() failed, can't delete stock object handle: 0x%x !!!\n", hObj
);
501 GDIDBG_TRACECALLER();
505 ProcessId
= PsGetCurrentProcessId();
506 LockedProcessId
= (HANDLE
)((ULONG_PTR
)ProcessId
| 0x1);
508 Silent
= (ExpectedType
& GDI_OBJECT_TYPE_SILENT
);
509 ExpectedType
&= ~GDI_OBJECT_TYPE_SILENT
;
511 HandleType
= GDI_HANDLE_GET_TYPE(hObj
);
512 HandleUpper
= GDI_HANDLE_GET_UPPER(hObj
);
514 /* Check if we have the requested type */
515 if ( (ExpectedType
!= GDI_OBJECT_TYPE_DONTCARE
&&
516 HandleType
!= ExpectedType
) ||
519 DPRINT1("Attempted to free object 0x%x of wrong type (Handle: 0x%x, expected: 0x%x)\n",
520 hObj
, HandleType
, ExpectedType
);
521 GDIDBG_TRACECALLER();
525 Entry
= GDI_HANDLE_GET_ENTRY(GdiHandleTable
, hObj
);
528 /* lock the object, we must not delete global objects, so don't exchange the locking
529 process ID to zero when attempting to lock a global object... */
530 PrevProcId
= InterlockedCompareExchangePointer((PVOID
*)&Entry
->ProcessId
, LockedProcessId
, ProcessId
);
531 if (PrevProcId
== ProcessId
)
533 if ( (Entry
->KernelData
!= NULL
) &&
534 ((Entry
->Type
<< GDI_ENTRY_UPPER_SHIFT
) == HandleUpper
) &&
535 ((Entry
->Type
& GDI_ENTRY_BASETYPE_MASK
) == (HandleUpper
& GDI_ENTRY_BASETYPE_MASK
)) )
539 Object
= Entry
->KernelData
;
541 if ((Object
->cExclusiveLock
== 0 ||
542 Object
->Tid
== (PTHREADINFO
)PsGetCurrentThreadWin32Thread()) &&
543 Object
->ulShareCount
== 0)
546 PPROCESSINFO W32Process
= PsGetCurrentProcessWin32Process();
548 /* Clear the basetype field so when unlocking the handle it gets finally deleted and increment reuse counter */
549 Entry
->Type
= (Entry
->Type
+ GDI_ENTRY_REUSE_INC
) & ~GDI_ENTRY_BASETYPE_MASK
;
551 /* unlock the handle slot */
552 (void)InterlockedExchangePointer((PVOID
*)&Entry
->ProcessId
, NULL
);
554 /* push this entry to the free list */
555 InterlockedPushFreeEntry(GDI_ENTRY_TO_INDEX(GdiHandleTable
, Entry
));
557 Object
->hHmgr
= NULL
;
559 if (W32Process
!= NULL
)
561 InterlockedDecrement(&W32Process
->GDIHandleCount
);
564 /* call the cleanup routine. */
565 TypeIndex
= GDI_OBJECT_GET_TYPE_INDEX(HandleType
);
566 Ret
= ObjTypeInfo
[TypeIndex
].CleanupProc(Object
);
568 /* Now it's time to free the memory */
569 GDIOBJ_FreeObj(Object
, TypeIndex
);
571 GDIDBG_CAPTUREDELETER(hObj
);
574 else if (Object
->ulShareCount
!= 0)
576 Object
->BaseFlags
|= BASEFLAG_READY_TO_DIE
;
577 DPRINT("Object %p, ulShareCount = %d\n", Object
->hHmgr
, Object
->ulShareCount
);
578 //GDIDBG_TRACECALLER();
579 //GDIDBG_TRACESHARELOCKER(GDI_HANDLE_GET_INDEX(hObj));
580 (void)InterlockedExchangePointer((PVOID
*)&Entry
->ProcessId
, PrevProcId
);
581 /* Don't wait on shared locks */
587 * The object is currently locked by another thread, so freeing is forbidden!
589 DPRINT1("Object->cExclusiveLock = %d\n", Object
->cExclusiveLock
);
590 GDIDBG_TRACECALLER();
591 GDIDBG_TRACELOCKER(GDI_HANDLE_GET_INDEX(hObj
));
592 (void)InterlockedExchangePointer((PVOID
*)&Entry
->ProcessId
, PrevProcId
);
593 /* do not assert here for it will call again from dxg.sys it being call twice */
601 LockErrorDebugOutput(hObj
, Entry
, "GDIOBJ_FreeObj");
602 (void)InterlockedExchangePointer((PVOID
*)&Entry
->ProcessId
, PrevProcId
);
605 else if (PrevProcId
== LockedProcessId
)
607 GDIDBG_TRACELOOP(hObj
, PrevProcId
, ProcessId
);
609 /* the object is currently locked, wait some time and try again.
610 FIXME - we shouldn't loop forever! Give up after some time! */
619 if ((Entry
->Type
& GDI_ENTRY_BASETYPE_MASK
) == 0)
621 DPRINT1("Attempted to free gdi handle 0x%x that is already deleted!\n", hObj
);
623 else if (((ULONG_PTR
)PrevProcId
& ~0x1) == 0)
625 DPRINT1("Attempted to free global gdi handle 0x%x, caller needs to get ownership first!!!\n", hObj
);
629 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);
631 DPRINT1("Type = 0x%lx, KernelData = 0x%p, ProcessId = 0x%p\n", Entry
->Type
, Entry
->KernelData
, Entry
->ProcessId
);
632 GDIDBG_TRACECALLER();
633 GDIDBG_TRACEALLOCATOR(GDI_HANDLE_GET_INDEX(hObj
));
642 IsObjectDead(HGDIOBJ hObject
)
644 INT Index
= GDI_HANDLE_GET_INDEX(hObject
);
645 PGDI_TABLE_ENTRY Entry
= &GdiHandleTable
->Entries
[Index
];
646 // We check to see if the objects are knocking on deaths door.
647 if ((Entry
->Type
& GDI_ENTRY_BASETYPE_MASK
) != 0)
651 DPRINT1("Object 0x%x currently being destroyed!!!\n",hObject
);
652 return TRUE
; // return true and move on.
659 bPEBCacheHandle(HGDIOBJ Handle
, int oType
, PVOID pAttr
)
661 PGDIHANDLECACHE GdiHandleCache
;
664 int Offset
= 0, Number
;
667 GdiHandleCache
= (PGDIHANDLECACHE
)NtCurrentTeb()->ProcessEnvironmentBlock
->GdiHandleBuffer
;
676 Offset
= CACHE_BRUSH_ENTRIES
;
679 case hctRegionHandle
:
680 Offset
= CACHE_BRUSH_ENTRIES
+CACHE_PEN_ENTRIES
;
687 Lock
= InterlockedCompareExchangePointer( (PVOID
*)&GdiHandleCache
->ulLock
,
690 if (Lock
) return FALSE
;
694 Number
= GdiHandleCache
->ulNumHandles
[oType
];
696 hPtr
= GdiHandleCache
->Handle
+ Offset
;
698 if ( oType
== hctRegionHandle
)
700 if ( Number
< CACHE_REGION_ENTRIES
)
702 ((PRGN_ATTR
)pAttr
)->AttrFlags
|= ATTR_CACHED
;
703 hPtr
[Number
] = Handle
;
704 GdiHandleCache
->ulNumHandles
[oType
]++;
705 DPRINT("Put Handle Count %d\n", GdiHandleCache
->ulNumHandles
[oType
]);
710 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
716 (void)InterlockedExchangePointer((PVOID
*)&GdiHandleCache
->ulLock
, Lock
);
722 * \param hObject object handle
723 * \return if the function fails the returned value is FALSE.
727 GreDeleteObject(HGDIOBJ hObject
)
730 PGDI_TABLE_ENTRY Entry
;
734 DPRINT("NtGdiDeleteObject handle 0x%08x\n", hObject
);
735 if (!IsObjectDead(hObject
))
737 dwObjectType
= GDIOBJ_GetObjectType(hObject
);
739 Index
= GDI_HANDLE_GET_INDEX(hObject
);
740 Entry
= &GdiHandleTable
->Entries
[Index
];
741 pAttr
= Entry
->UserData
;
743 switch (dwObjectType
)
745 case GDI_OBJECT_TYPE_BRUSH
:
748 case GDI_OBJECT_TYPE_REGION
:
749 if (bPEBCacheHandle(hObject
, hctRegionHandle
, pAttr
))
755 KeEnterCriticalRegion();
756 if (pAttr
) FreeObjectAttr(pAttr
);
757 Entry
->UserData
= NULL
;
758 KeLeaveCriticalRegion();
762 case GDI_OBJECT_TYPE_DC
:
763 DC_FreeDcAttr(hObject
);
767 return NULL
!= hObject
768 ? GDIOBJ_FreeObjByHandle(hObject
, dwObjectType
) : FALSE
;
772 DPRINT1("Attempt DeleteObject 0x%x currently being destroyed!!!\n",hObject
);
773 return TRUE
; // return true and move on.
779 IntDeleteHandlesForProcess(struct _EPROCESS
*Process
, ULONG ObjectType
)
781 PGDI_TABLE_ENTRY Entry
, End
;
782 ULONG Index
= RESERVE_ENTRIES_COUNT
;
784 PPROCESSINFO W32Process
;
786 W32Process
= (PPROCESSINFO
)Process
->Win32Process
;
789 if (W32Process
->GDIHandleCount
> 0)
791 ProcId
= Process
->UniqueProcessId
;
793 /* FIXME - Instead of building the handle here and delete it using GDIOBJ_FreeObj
794 we should delete it directly here! */
796 End
= &GdiHandleTable
->Entries
[GDI_HANDLE_COUNT
];
797 for (Entry
= &GdiHandleTable
->Entries
[RESERVE_ENTRIES_COUNT
];
801 /* ignore the lock bit */
802 if ( (HANDLE
)((ULONG_PTR
)Entry
->ProcessId
& ~0x1) == ProcId
)
804 if ( (Entry
->Type
& GDI_ENTRY_BASETYPE_MASK
) == ObjectType
||
805 ObjectType
== GDI_OBJECT_TYPE_DONTCARE
)
807 HGDIOBJ ObjectHandle
;
809 /* Create the object handle for the entry, the lower(!) 16 bit of the
810 Type field includes the type of the object including the stock
811 object flag - but since stock objects don't have a process id we can
812 simply ignore this fact here. */
813 ObjectHandle
= (HGDIOBJ
)(Index
| (Entry
->Type
<< GDI_ENTRY_UPPER_SHIFT
));
815 if (!GDIOBJ_FreeObjByHandle(ObjectHandle
, GDI_OBJECT_TYPE_DONTCARE
))
817 DPRINT1("Failed to delete object %p!\n", ObjectHandle
);
820 if (W32Process
->GDIHandleCount
== 0)
822 /* there are no more gdi handles for this process, bail */
833 * Internal function. Called when the process is destroyed to free the remaining GDI handles.
834 * \param Process - PID of the process that will be destroyed.
837 GDI_CleanupForProcess(struct _EPROCESS
*Process
)
839 PEPROCESS CurrentProcess
;
840 PPROCESSINFO W32Process
;
842 DPRINT("Starting CleanupForProcess prochandle %x Pid %d\n", Process
, Process
->UniqueProcessId
);
843 CurrentProcess
= PsGetCurrentProcess();
844 if (CurrentProcess
!= Process
)
846 KeAttachProcess(&Process
->Pcb
);
849 W32Process
= (PPROCESSINFO
)CurrentProcess
->Win32Process
;
851 /* Delete objects. Begin with types that are not referenced by other types */
852 IntDeleteHandlesForProcess(Process
, GDILoObjType_LO_DC_TYPE
);
853 IntDeleteHandlesForProcess(Process
, GDILoObjType_LO_BRUSH_TYPE
);
854 IntDeleteHandlesForProcess(Process
, GDILoObjType_LO_BITMAP_TYPE
);
856 /* Finally finish with what's left */
857 IntDeleteHandlesForProcess(Process
, GDI_OBJECT_TYPE_DONTCARE
);
859 if (CurrentProcess
!= Process
)
865 GdiDbgHTIntegrityCheck();
868 DPRINT("Completed cleanup for process %d\n", Process
->UniqueProcessId
);
869 if (W32Process
->GDIHandleCount
> 0)
871 DPRINT1("Leaking %d handles!\n", W32Process
->GDIHandleCount
);
878 * Return pointer to the object by handle.
880 * \param hObj Object handle
881 * \return Pointer to the object.
883 * \note Process can only get pointer to the objects it created or global objects.
885 * \todo Get rid of the ExpectedType parameter!
887 PGDIOBJ INTERNAL_CALL
888 GDIOBJ_LockObj(HGDIOBJ hObj
, DWORD ExpectedType
)
891 PGDI_TABLE_ENTRY Entry
;
892 HANDLE ProcessId
, HandleProcessId
, LockedProcessId
, PrevProcId
;
894 ULONG HandleType
, HandleUpper
;
896 HandleIndex
= GDI_HANDLE_GET_INDEX(hObj
);
897 HandleType
= GDI_HANDLE_GET_TYPE(hObj
);
898 HandleUpper
= GDI_HANDLE_GET_UPPER(hObj
);
900 /* Check that the handle index is valid. */
901 if (HandleIndex
>= GDI_HANDLE_COUNT
)
904 Entry
= &GdiHandleTable
->Entries
[HandleIndex
];
906 /* Check if we have the requested type */
907 if ( (ExpectedType
!= GDI_OBJECT_TYPE_DONTCARE
&&
908 HandleType
!= ExpectedType
) ||
911 DPRINT("Attempted to lock object 0x%x of wrong type (Handle: 0x%x, requested: 0x%x)\n",
912 hObj
, HandleType
, ExpectedType
);
913 GDIDBG_TRACECALLER();
914 GDIDBG_TRACEALLOCATOR(hObj
);
915 GDIDBG_TRACEDELETER(hObj
);
919 ProcessId
= (HANDLE
)((ULONG_PTR
)PsGetCurrentProcessId() & ~1);
920 HandleProcessId
= (HANDLE
)((ULONG_PTR
)Entry
->ProcessId
& ~1);
922 /* Check for invalid owner. */
923 if (ProcessId
!= HandleProcessId
&& HandleProcessId
!= NULL
)
925 DPRINT1("Tried to lock object (0x%p) of wrong owner! ProcessId = %p, HandleProcessId = %p\n", hObj
, ProcessId
, HandleProcessId
);
926 GDIDBG_TRACECALLER();
927 GDIDBG_TRACEALLOCATOR(GDI_HANDLE_GET_INDEX(hObj
));
932 * Prevent the thread from being terminated during the locking process.
933 * It would result in undesired effects and inconsistency of the global
937 KeEnterCriticalRegion();
940 * Loop until we either successfully lock the handle entry & object or
941 * fail some of the check.
946 /* Lock the handle table entry. */
947 LockedProcessId
= (HANDLE
)((ULONG_PTR
)HandleProcessId
| 0x1);
948 PrevProcId
= InterlockedCompareExchangePointer((PVOID
*)&Entry
->ProcessId
,
952 if (PrevProcId
== HandleProcessId
)
955 * We're locking an object that belongs to our process or it's a
956 * global object if HandleProcessId is 0 here.
959 if ( (Entry
->KernelData
!= NULL
) &&
960 ((Entry
->Type
<< GDI_ENTRY_UPPER_SHIFT
) == HandleUpper
) )
962 PTHREADINFO Thread
= (PTHREADINFO
)PsGetCurrentThreadWin32Thread();
963 Object
= Entry
->KernelData
;
965 if (Object
->cExclusiveLock
== 0)
967 Object
->Tid
= Thread
;
968 Object
->cExclusiveLock
= 1;
969 GDIDBG_CAPTURELOCKER(GDI_HANDLE_GET_INDEX(hObj
))
973 if (Object
->Tid
!= Thread
)
975 /* Unlock the handle table entry. */
976 (void)InterlockedExchangePointer((PVOID
*)&Entry
->ProcessId
, PrevProcId
);
981 InterlockedIncrement((PLONG
)&Object
->cExclusiveLock
);
987 * Debugging code. Report attempts to lock deleted handles and
988 * locking type mismatches.
990 LockErrorDebugOutput(hObj
, Entry
, "GDIOBJ_LockObj");
993 /* Unlock the handle table entry. */
994 (void)InterlockedExchangePointer((PVOID
*)&Entry
->ProcessId
, PrevProcId
);
1001 * The handle is currently locked, wait some time and try again.
1009 KeLeaveCriticalRegion();
1016 * Return pointer to the object by handle (and allow sharing of the handle
1019 * \param hObj Object handle
1020 * \return Pointer to the object.
1022 * \note Process can only get pointer to the objects it created or global objects.
1024 * \todo Get rid of the ExpectedType parameter!
1026 PGDIOBJ INTERNAL_CALL
1027 GDIOBJ_ShareLockObj(HGDIOBJ hObj
, DWORD ExpectedType
)
1030 PGDI_TABLE_ENTRY Entry
;
1031 HANDLE ProcessId
, HandleProcessId
, LockedProcessId
, PrevProcId
;
1033 ULONG_PTR HandleType
, HandleUpper
;
1035 HandleIndex
= GDI_HANDLE_GET_INDEX(hObj
);
1036 HandleType
= GDI_HANDLE_GET_TYPE(hObj
);
1037 HandleUpper
= GDI_HANDLE_GET_UPPER(hObj
);
1039 /* Check that the handle index is valid. */
1040 if (HandleIndex
>= GDI_HANDLE_COUNT
)
1043 /* Check if we have the requested type */
1044 if ( (ExpectedType
!= GDI_OBJECT_TYPE_DONTCARE
&&
1045 HandleType
!= ExpectedType
) ||
1048 DPRINT1("Attempted to lock object 0x%x of wrong type (Handle: 0x%x, requested: 0x%x)\n",
1049 hObj
, HandleType
, ExpectedType
);
1053 Entry
= &GdiHandleTable
->Entries
[HandleIndex
];
1055 ProcessId
= (HANDLE
)((ULONG_PTR
)PsGetCurrentProcessId() & ~1);
1056 HandleProcessId
= (HANDLE
)((ULONG_PTR
)Entry
->ProcessId
& ~1);
1058 /* Check for invalid owner. */
1059 if (ProcessId
!= HandleProcessId
&& HandleProcessId
!= NULL
)
1065 * Prevent the thread from being terminated during the locking process.
1066 * It would result in undesired effects and inconsistency of the global
1070 KeEnterCriticalRegion();
1073 * Loop until we either successfully lock the handle entry & object or
1074 * fail some of the check.
1079 /* Lock the handle table entry. */
1080 LockedProcessId
= (HANDLE
)((ULONG_PTR
)HandleProcessId
| 0x1);
1081 PrevProcId
= InterlockedCompareExchangePointer((PVOID
*)&Entry
->ProcessId
,
1085 if (PrevProcId
== HandleProcessId
)
1088 * We're locking an object that belongs to our process or it's a
1089 * global object if HandleProcessId is 0 here.
1092 if ( (Entry
->KernelData
!= NULL
) &&
1093 (HandleUpper
== (Entry
->Type
<< GDI_ENTRY_UPPER_SHIFT
)) )
1095 Object
= (POBJ
)Entry
->KernelData
;
1097 GDIDBG_CAPTURESHARELOCKER(HandleIndex
);
1099 if (InterlockedIncrement((PLONG
)&Object
->ulShareCount
) == 1)
1101 memset(GDIHandleLocker
[HandleIndex
], 0x00, GDI_STACK_LEVELS
* sizeof(ULONG
));
1102 RtlCaptureStackBackTrace(1, GDI_STACK_LEVELS
, (PVOID
*)GDIHandleShareLocker
[HandleIndex
], NULL
);
1105 InterlockedIncrement((PLONG
)&Object
->ulShareCount
);
1111 * Debugging code. Report attempts to lock deleted handles and
1112 * locking type mismatches.
1114 LockErrorDebugOutput(hObj
, Entry
, "GDIOBJ_ShareLockObj");
1117 /* Unlock the handle table entry. */
1118 (void)InterlockedExchangePointer((PVOID
*)&Entry
->ProcessId
, PrevProcId
);
1125 * The handle is currently locked, wait some time and try again.
1133 KeLeaveCriticalRegion();
1139 GDIOBJ_OwnedByCurrentProcess(HGDIOBJ ObjectHandle
)
1141 PGDI_TABLE_ENTRY Entry
;
1145 DPRINT("GDIOBJ_OwnedByCurrentProcess: ObjectHandle: 0x%08x\n", ObjectHandle
);
1147 if (!GDI_HANDLE_IS_STOCKOBJ(ObjectHandle
))
1149 ProcessId
= PsGetCurrentProcessId();
1151 Entry
= GDI_HANDLE_GET_ENTRY(GdiHandleTable
, ObjectHandle
);
1152 Ret
= Entry
->KernelData
!= NULL
&&
1153 (Entry
->Type
& GDI_ENTRY_BASETYPE_MASK
) != 0 &&
1154 (HANDLE
)((ULONG_PTR
)Entry
->ProcessId
& ~0x1) == ProcessId
;
1163 GDIOBJ_ConvertToStockObj(HGDIOBJ
*phObj
)
1166 * FIXME !!!!! THIS FUNCTION NEEDS TO BE FIXED - IT IS NOT SAFE WHEN OTHER THREADS
1167 * MIGHT ATTEMPT TO LOCK THE OBJECT DURING THIS CALL!!!
1169 PGDI_TABLE_ENTRY Entry
;
1170 HANDLE ProcessId
, LockedProcessId
, PrevProcId
;
1174 GDIDBG_INITLOOPTRACE();
1179 DPRINT("GDIOBJ_ConvertToStockObj: hObj: 0x%08x\n", hObj
);
1181 Thread
= (PTHREADINFO
)PsGetCurrentThreadWin32Thread();
1183 if (!GDI_HANDLE_IS_STOCKOBJ(hObj
))
1185 ProcessId
= PsGetCurrentProcessId();
1186 LockedProcessId
= (HANDLE
)((ULONG_PTR
)ProcessId
| 0x1);
1188 Entry
= GDI_HANDLE_GET_ENTRY(GdiHandleTable
, hObj
);
1191 /* lock the object, we must not convert stock objects, so don't check!!! */
1192 PrevProcId
= InterlockedCompareExchangePointer((PVOID
*)&Entry
->ProcessId
, LockedProcessId
, ProcessId
);
1193 if (PrevProcId
== ProcessId
)
1195 LONG NewType
, PrevType
, OldType
;
1197 /* we're locking an object that belongs to our process. First calculate
1198 the new object type including the stock object flag and then try to
1200 /* On Windows the higher 16 bit of the type field don't contain the
1201 full type from the handle, but the base type.
1202 (type = BRSUH, PEN, EXTPEN, basetype = BRUSH) */
1203 OldType
= ((ULONG
)hObj
& GDI_HANDLE_BASETYPE_MASK
) | ((ULONG
)hObj
>> GDI_ENTRY_UPPER_SHIFT
);
1204 /* We are currently not using bits 24..31 (flags) of the type field, but for compatibility
1205 we copy them as we can't get them from the handle */
1206 OldType
|= Entry
->Type
& GDI_ENTRY_FLAGS_MASK
;
1208 /* As the object should be a stock object, set it's flag, but only in the lower 16 bits */
1209 NewType
= OldType
| GDI_ENTRY_STOCK_MASK
;
1211 /* Try to exchange the type field - but only if the old (previous type) matches! */
1212 PrevType
= InterlockedCompareExchange(&Entry
->Type
, NewType
, OldType
);
1213 if (PrevType
== OldType
&& Entry
->KernelData
!= NULL
)
1215 PTHREADINFO PrevThread
;
1218 /* We successfully set the stock object flag.
1219 KernelData should never be NULL here!!! */
1220 ASSERT(Entry
->KernelData
);
1222 Object
= Entry
->KernelData
;
1224 PrevThread
= Object
->Tid
;
1225 if (Object
->cExclusiveLock
== 0 || PrevThread
== Thread
)
1227 /* dereference the process' object counter */
1228 if (PrevProcId
!= GDI_GLOBAL_PROCESS
)
1230 PEPROCESS OldProcess
;
1231 PPROCESSINFO W32Process
;
1235 Status
= PsLookupProcessByProcessId((HANDLE
)((ULONG_PTR
)PrevProcId
& ~0x1), &OldProcess
);
1236 if (NT_SUCCESS(Status
))
1238 W32Process
= (PPROCESSINFO
)OldProcess
->Win32Process
;
1239 if (W32Process
!= NULL
)
1241 InterlockedDecrement(&W32Process
->GDIHandleCount
);
1243 ObDereferenceObject(OldProcess
);
1247 hObj
= (HGDIOBJ
)((ULONG
)(hObj
) | GDI_HANDLE_STOCK_MASK
);
1249 Object
->hHmgr
= hObj
;
1251 /* remove the process id lock and make it global */
1252 (void)InterlockedExchangePointer((PVOID
*)&Entry
->ProcessId
, GDI_GLOBAL_PROCESS
);
1254 /* we're done, successfully converted the object */
1259 GDIDBG_TRACELOOP(hObj
, PrevThread
, Thread
);
1261 /* WTF?! The object is already locked by a different thread!
1262 Release the lock, wait a bit and try again!
1263 FIXME - we should give up after some time unless we want to wait forever! */
1264 (void)InterlockedExchangePointer((PVOID
*)&Entry
->ProcessId
, PrevProcId
);
1272 DPRINT1("Attempted to convert object 0x%x that is deleted! Should never get here!!!\n", hObj
);
1273 DPRINT1("OldType = 0x%x, Entry->Type = 0x%x, NewType = 0x%x, Entry->KernelData = 0x%x\n", OldType
, Entry
->Type
, NewType
, Entry
->KernelData
);
1276 else if (PrevProcId
== LockedProcessId
)
1278 GDIDBG_TRACELOOP(hObj
, PrevProcId
, ProcessId
);
1280 /* the object is currently locked, wait some time and try again.
1281 FIXME - we shouldn't loop forever! Give up after some time! */
1288 DPRINT1("Attempted to convert invalid handle: 0x%x\n", hObj
);
1296 GDIOBJ_SetOwnership(HGDIOBJ ObjectHandle
, PEPROCESS NewOwner
)
1298 PGDI_TABLE_ENTRY Entry
;
1299 HANDLE ProcessId
, LockedProcessId
, PrevProcId
;
1303 GDIDBG_INITLOOPTRACE();
1305 DPRINT("GDIOBJ_SetOwnership: hObj: 0x%x, NewProcess: 0x%x\n", ObjectHandle
, (NewOwner
? PsGetProcessId(NewOwner
) : 0));
1307 Thread
= (PTHREADINFO
)PsGetCurrentThreadWin32Thread();
1309 if (!GDI_HANDLE_IS_STOCKOBJ(ObjectHandle
))
1311 ProcessId
= PsGetCurrentProcessId();
1312 LockedProcessId
= (HANDLE
)((ULONG_PTR
)ProcessId
| 0x1);
1314 Entry
= GDI_HANDLE_GET_ENTRY(GdiHandleTable
, ObjectHandle
);
1317 /* lock the object, we must not convert stock objects, so don't check!!! */
1318 PrevProcId
= InterlockedCompareExchangePointer((PVOID
*)&Entry
->ProcessId
, ProcessId
, LockedProcessId
);
1319 if (PrevProcId
== ProcessId
)
1321 PTHREADINFO PrevThread
;
1323 if ((Entry
->Type
& GDI_ENTRY_BASETYPE_MASK
) != 0)
1325 POBJ Object
= Entry
->KernelData
;
1327 PrevThread
= Object
->Tid
;
1328 if (Object
->cExclusiveLock
== 0 || PrevThread
== Thread
)
1330 PEPROCESS OldProcess
;
1331 PPROCESSINFO W32Process
;
1334 /* dereference the process' object counter */
1336 if ((ULONG_PTR
)PrevProcId
& ~0x1)
1338 Status
= PsLookupProcessByProcessId((HANDLE
)((ULONG_PTR
)PrevProcId
& ~0x1), &OldProcess
);
1339 if (NT_SUCCESS(Status
))
1341 W32Process
= (PPROCESSINFO
)OldProcess
->Win32Process
;
1342 if (W32Process
!= NULL
)
1344 InterlockedDecrement(&W32Process
->GDIHandleCount
);
1346 ObDereferenceObject(OldProcess
);
1350 if (NewOwner
!= NULL
)
1352 ProcessId
= PsGetProcessId(NewOwner
);
1354 /* Increase the new process' object counter */
1355 W32Process
= (PPROCESSINFO
)NewOwner
->Win32Process
;
1356 if (W32Process
!= NULL
)
1358 InterlockedIncrement(&W32Process
->GDIHandleCount
);
1364 /* remove the process id lock and change it to the new process id */
1365 (void)InterlockedExchangePointer((PVOID
*)&Entry
->ProcessId
, ProcessId
);
1372 GDIDBG_TRACELOOP(ObjectHandle
, PrevThread
, Thread
);
1374 /* WTF?! The object is already locked by a different thread!
1375 Release the lock, wait a bit and try again! DO reset the pid lock
1376 so we make sure we don't access invalid memory in case the object is
1377 being deleted in the meantime (because we don't have aquired a reference
1379 FIXME - we should give up after some time unless we want to wait forever! */
1380 (void)InterlockedExchangePointer((PVOID
*)&Entry
->ProcessId
, PrevProcId
);
1388 DPRINT1("Attempted to change ownership of an object 0x%x currently being destroyed!!!\n", ObjectHandle
);
1389 DPRINT1("Entry->Type = 0x%lx, Entry->KernelData = 0x%p\n", Entry
->Type
, Entry
->KernelData
);
1393 else if (PrevProcId
== LockedProcessId
)
1395 GDIDBG_TRACELOOP(ObjectHandle
, PrevProcId
, ProcessId
);
1397 /* the object is currently locked, wait some time and try again.
1398 FIXME - we shouldn't loop forever! Give up after some time! */
1403 else if (((ULONG_PTR
)PrevProcId
& ~0x1) == 0)
1405 /* allow changing ownership of global objects */
1407 LockedProcessId
= (HANDLE
)((ULONG_PTR
)ProcessId
| 0x1);
1410 else if ((HANDLE
)((ULONG_PTR
)PrevProcId
& ~0x1) != PsGetCurrentProcessId())
1412 DPRINT1("Attempted to change ownership of object 0x%x (pid: 0x%x) from pid 0x%x!!!\n", ObjectHandle
, (ULONG_PTR
)PrevProcId
& ~0x1, PsGetCurrentProcessId());
1417 DPRINT1("Attempted to change owner of invalid handle: 0x%x\n", ObjectHandle
);
1425 GDIOBJ_CopyOwnership(HGDIOBJ CopyFrom
, HGDIOBJ CopyTo
)
1427 PGDI_TABLE_ENTRY FromEntry
;
1429 HANDLE FromProcessId
, FromLockedProcessId
, FromPrevProcId
;
1432 GDIDBG_INITLOOPTRACE();
1434 DPRINT("GDIOBJ_CopyOwnership: from: 0x%x, to: 0x%x\n", CopyFrom
, CopyTo
);
1436 Thread
= (PTHREADINFO
)PsGetCurrentThreadWin32Thread();
1438 if (!GDI_HANDLE_IS_STOCKOBJ(CopyFrom
) && !GDI_HANDLE_IS_STOCKOBJ(CopyTo
))
1440 FromEntry
= GDI_HANDLE_GET_ENTRY(GdiHandleTable
, CopyFrom
);
1442 FromProcessId
= (HANDLE
)((ULONG_PTR
)FromEntry
->ProcessId
& ~0x1);
1443 FromLockedProcessId
= (HANDLE
)((ULONG_PTR
)FromProcessId
| 0x1);
1446 /* lock the object, we must not convert stock objects, so don't check!!! */
1447 FromPrevProcId
= InterlockedCompareExchangePointer((PVOID
*)&FromEntry
->ProcessId
, FromProcessId
, FromLockedProcessId
);
1448 if (FromPrevProcId
== FromProcessId
)
1450 PTHREADINFO PrevThread
;
1453 if ((FromEntry
->Type
& GDI_ENTRY_BASETYPE_MASK
) != 0)
1455 Object
= FromEntry
->KernelData
;
1457 /* save the pointer to the calling thread so we know it was this thread
1458 that locked the object */
1459 PrevThread
= Object
->Tid
;
1460 if (Object
->cExclusiveLock
== 0 || PrevThread
== Thread
)
1462 /* now let's change the ownership of the target object */
1464 if (((ULONG_PTR
)FromPrevProcId
& ~0x1) != 0)
1466 PEPROCESS ProcessTo
;
1468 if (NT_SUCCESS(PsLookupProcessByProcessId((HANDLE
)((ULONG_PTR
)FromPrevProcId
& ~0x1), &ProcessTo
)))
1470 GDIOBJ_SetOwnership(CopyTo
, ProcessTo
);
1471 ObDereferenceObject(ProcessTo
);
1476 /* mark the object as global */
1477 GDIOBJ_SetOwnership(CopyTo
, NULL
);
1480 (void)InterlockedExchangePointer((PVOID
*)&FromEntry
->ProcessId
, FromPrevProcId
);
1484 GDIDBG_TRACELOOP(CopyFrom
, PrevThread
, Thread
);
1486 /* WTF?! The object is already locked by a different thread!
1487 Release the lock, wait a bit and try again! DO reset the pid lock
1488 so we make sure we don't access invalid memory in case the object is
1489 being deleted in the meantime (because we don't have aquired a reference
1491 FIXME - we should give up after some time unless we want to wait forever! */
1492 (void)InterlockedExchangePointer((PVOID
*)&FromEntry
->ProcessId
, FromPrevProcId
);
1495 goto LockHandleFrom
;
1500 DPRINT1("Attempted to copy ownership from an object 0x%x currently being destroyed!!!\n", CopyFrom
);
1504 else if (FromPrevProcId
== FromLockedProcessId
)
1506 GDIDBG_TRACELOOP(CopyFrom
, FromPrevProcId
, FromProcessId
);
1508 /* the object is currently locked, wait some time and try again.
1509 FIXME - we shouldn't loop forever! Give up after some time! */
1512 goto LockHandleFrom
;
1514 else if ((HANDLE
)((ULONG_PTR
)FromPrevProcId
& ~0x1) != PsGetCurrentProcessId())
1516 /* FIXME - should we really allow copying ownership from objects that we don't even own? */
1517 DPRINT1("WARNING! Changing copying ownership of object 0x%x (pid: 0x%x) to pid 0x%x!!!\n", CopyFrom
, (ULONG_PTR
)FromPrevProcId
& ~0x1, PsGetCurrentProcessId());
1518 FromProcessId
= (HANDLE
)((ULONG_PTR
)FromPrevProcId
& ~0x1);
1519 FromLockedProcessId
= (HANDLE
)((ULONG_PTR
)FromProcessId
| 0x1);
1520 goto LockHandleFrom
;
1524 DPRINT1("Attempted to copy ownership from invalid handle: 0x%x\n", CopyFrom
);
1532 GDI_MapHandleTable(PSECTION_OBJECT SectionObject
, PEPROCESS Process
)
1534 PVOID MappedView
= NULL
;
1536 LARGE_INTEGER Offset
;
1537 ULONG ViewSize
= sizeof(GDI_HANDLE_TABLE
);
1539 Offset
.QuadPart
= 0;
1541 ASSERT(SectionObject
!= NULL
);
1542 ASSERT(Process
!= NULL
);
1544 Status
= MmMapViewOfSection(SectionObject
,
1555 if (!NT_SUCCESS(Status
))
1561 /** PUBLIC FUNCTIONS **********************************************************/
1565 IntGdiSetRegeionOwner(HRGN hRgn
, DWORD OwnerMask
)
1568 PGDI_TABLE_ENTRY Entry
;
1571 These regions do not use attribute sections and when allocated, use gdiobj
1574 // FIXME! HAX!!! Remove this once we get everything right!
1575 KeEnterCriticalRegion();
1576 Index
= GDI_HANDLE_GET_INDEX(hRgn
);
1577 Entry
= &GdiHandleTable
->Entries
[Index
];
1578 if (Entry
->UserData
) FreeObjectAttr(Entry
->UserData
);
1579 Entry
->UserData
= NULL
;
1580 KeLeaveCriticalRegion();
1582 if ((OwnerMask
== GDI_OBJ_HMGR_PUBLIC
) || OwnerMask
== GDI_OBJ_HMGR_NONE
)
1584 return GDIOBJ_SetOwnership(hRgn
, NULL
);
1586 if (OwnerMask
== GDI_OBJ_HMGR_POWNED
)
1588 return GDIOBJ_SetOwnership((HGDIOBJ
) hRgn
, PsGetCurrentProcess() );
1595 IntGdiSetBrushOwner(PBRUSH pbr
, DWORD OwnerMask
)
1598 PEPROCESS Owner
= NULL
;
1599 PGDI_TABLE_ENTRY pEntry
= NULL
;
1601 if (!pbr
) return FALSE
;
1603 hBR
= pbr
->BaseObject
.hHmgr
;
1605 if (!hBR
|| (GDI_HANDLE_GET_TYPE(hBR
) != GDI_OBJECT_TYPE_BRUSH
))
1609 INT Index
= GDI_HANDLE_GET_INDEX((HGDIOBJ
)hBR
);
1610 pEntry
= &GdiHandleTable
->Entries
[Index
];
1613 if (pbr
->flAttrs
& GDIBRUSH_IS_GLOBAL
)
1615 GDIOBJ_ShareUnlockObjByPtr((POBJ
)pbr
);
1619 if ((OwnerMask
== GDI_OBJ_HMGR_PUBLIC
) || OwnerMask
== GDI_OBJ_HMGR_NONE
)
1621 // Set this Brush to inaccessible mode and to an Owner of NONE.
1622 // if (OwnerMask == GDI_OBJ_HMGR_NONE) Owner = OwnerMask;
1624 if (!GDIOBJ_SetOwnership((HGDIOBJ
) hBR
, Owner
))
1627 // Deny user access to User Data.
1628 pEntry
->UserData
= NULL
; // This hBR is inaccessible!
1631 if (OwnerMask
== GDI_OBJ_HMGR_POWNED
)
1633 if (!GDIOBJ_SetOwnership((HGDIOBJ
) hBR
, PsGetCurrentProcess() ))
1636 // Allow user access to User Data.
1637 pEntry
->UserData
= pbr
->pBrushAttr
;
1644 IntGdiSetDCOwnerEx( HDC hDC
, DWORD OwnerMask
, BOOL NoSetBrush
)
1649 if (!hDC
|| (GDI_HANDLE_GET_TYPE(hDC
) != GDI_OBJECT_TYPE_DC
)) return FALSE
;
1651 if ((OwnerMask
== GDI_OBJ_HMGR_PUBLIC
) || OwnerMask
== GDI_OBJ_HMGR_NONE
)
1653 pDC
= DC_LockDc ( hDC
);
1654 MmCopyFromCaller(&pDC
->dcattr
, pDC
->pdcattr
, sizeof(DC_ATTR
));
1657 DC_FreeDcAttr( hDC
); // Free the dcattr!
1659 if (!DC_SetOwnership( hDC
, NULL
)) // This hDC is inaccessible!
1663 if (OwnerMask
== GDI_OBJ_HMGR_POWNED
)
1665 pDC
= DC_LockDc ( hDC
);
1666 ASSERT(pDC
->pdcattr
== &pDC
->dcattr
);
1669 if (!DC_SetOwnership( hDC
, PsGetCurrentProcess() )) return Ret
;
1671 DC_AllocateDcAttr( hDC
); // Allocate new dcattr
1673 DCU_SynchDcAttrtoUser( hDC
); // Copy data from dc to dcattr
1676 if ((OwnerMask
!= GDI_OBJ_HMGR_NONE
) && !NoSetBrush
)
1678 pDC
= DC_LockDc ( hDC
);
1679 if (IntGdiSetBrushOwner((PBRUSH
)pDC
->dclevel
.pbrFill
, OwnerMask
))
1680 IntGdiSetBrushOwner((PBRUSH
)pDC
->dclevel
.pbrLine
, OwnerMask
);
1688 GreGetObjectOwner(HGDIOBJ Handle
, GDIOBJTYPE ObjType
)
1690 INT Ret
= GDI_OBJ_HMGR_RESTRICTED
;
1692 if ( GDI_HANDLE_GET_INDEX(Handle
) < GDI_HANDLE_COUNT
)
1694 PGDI_TABLE_ENTRY pEntry
= &GdiHandleTable
->Entries
[GDI_HANDLE_GET_INDEX(Handle
)];
1696 if (pEntry
->ObjectType
== ObjType
)
1698 if (pEntry
->FullUnique
== (GDI_HANDLE_GET_UPPER(Handle
) >> GDI_ENTRY_UPPER_SHIFT
))
1699 Ret
= pEntry
->ProcessId
& ~1;
1708 NtGdiCreateClientObj(
1715 /* Mask out everything that would change the type in a wrong manner */
1716 ulType
&= (GDI_HANDLE_TYPE_MASK
& ~GDI_HANDLE_BASETYPE_MASK
);
1718 /* Allocate a new object */
1719 pObject
= GDIOBJ_AllocObjWithHandle(GDI_OBJECT_TYPE_CLIOBJ
| ulType
);
1725 /* get the handle */
1726 handle
= pObject
->hHmgr
;
1729 GDIOBJ_UnlockObjByPtr(pObject
);
1737 NtGdiDeleteClientObj(
1741 /* We first need to get the real type from the handle */
1742 ULONG type
= GDI_HANDLE_GET_TYPE(h
);
1744 /* Check if it's really a CLIENTOBJ */
1745 if ((type
& GDI_HANDLE_BASETYPE_MASK
) != GDILoObjType_LO_CLIENTOBJ_TYPE
)
1747 /* FIXME: SetLastError? */
1750 return GDIOBJ_FreeObjByHandle(h
, type
);
1755 IntGdiGetObject(IN HANDLE Handle
,
1763 pGdiObject
= GDIOBJ_LockObj(Handle
, GDI_OBJECT_TYPE_DONTCARE
);
1766 SetLastWin32Error(ERROR_INVALID_HANDLE
);
1770 dwObjectType
= GDIOBJ_GetObjectType(Handle
);
1771 switch (dwObjectType
)
1773 case GDI_OBJECT_TYPE_PEN
:
1774 case GDI_OBJECT_TYPE_EXTPEN
:
1775 Result
= PEN_GetObject((PBRUSH
) pGdiObject
, cbCount
, (PLOGPEN
) lpBuffer
); // IntGdiCreatePenIndirect
1778 case GDI_OBJECT_TYPE_BRUSH
:
1779 Result
= BRUSH_GetObject((PBRUSH
) pGdiObject
, cbCount
, (LPLOGBRUSH
)lpBuffer
);
1782 case GDI_OBJECT_TYPE_BITMAP
:
1783 Result
= BITMAP_GetObject((SURFACE
*) pGdiObject
, cbCount
, lpBuffer
);
1785 case GDI_OBJECT_TYPE_FONT
:
1786 Result
= FontGetObject((PTEXTOBJ
) pGdiObject
, cbCount
, lpBuffer
);
1788 // Fix the LOGFONT structure for the stock fonts
1789 if (FIRST_STOCK_HANDLE
<= Handle
&& Handle
<= LAST_STOCK_HANDLE
)
1791 FixStockFontSizeW(Handle
, cbCount
, lpBuffer
);
1796 case GDI_OBJECT_TYPE_PALETTE
:
1797 Result
= PALETTE_GetObject((PPALETTE
) pGdiObject
, cbCount
, lpBuffer
);
1801 DPRINT1("GDI object type 0x%08x not implemented\n", dwObjectType
);
1805 GDIOBJ_UnlockObjByPtr(pGdiObject
);
1815 NtGdiExtGetObjectW(IN HANDLE hGdiObj
,
1817 OUT LPVOID lpBuffer
)
1824 DIBSECTION dibsection
;
1828 EXTLOGFONTW extlogfontw
;
1829 ENUMLOGFONTEXDVW enumlogfontexdvw
;
1832 // Normalize to the largest supported object size
1833 cbCount
= min((UINT
)cbCount
, sizeof(Object
));
1835 // Now do the actual call
1836 iRetCount
= IntGdiGetObject(hGdiObj
, cbCount
, lpBuffer
? &Object
: NULL
);
1837 cbCopyCount
= min((UINT
)cbCount
, (UINT
)iRetCount
);
1839 // Make sure we have a buffer and a copy size
1840 if ((cbCopyCount
) && (lpBuffer
))
1842 // Enter SEH for buffer transfer
1845 // Probe the buffer and copy it
1846 ProbeForWrite(lpBuffer
, cbCopyCount
, sizeof(WORD
));
1847 RtlCopyMemory(lpBuffer
, &Object
, cbCopyCount
);
1849 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1851 // Clear the return value.
1852 // Do *NOT* set last error here!