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 ( pAttr
&& 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 pAttr NULL, the probability is high for System Region. */
751 bPEBCacheHandle(hObject
, hctRegionHandle
, pAttr
))
753 /* User space handle only! */
758 FreeObjectAttr(pAttr
);
759 Entry
->UserData
= NULL
;
763 case GDI_OBJECT_TYPE_DC
:
764 DC_FreeDcAttr(hObject
);
768 return NULL
!= hObject
769 ? GDIOBJ_FreeObjByHandle(hObject
, dwObjectType
) : FALSE
;
773 DPRINT1("Attempt DeleteObject 0x%x currently being destroyed!!!\n",hObject
);
774 return TRUE
; // return true and move on.
780 IntDeleteHandlesForProcess(struct _EPROCESS
*Process
, ULONG ObjectType
)
782 PGDI_TABLE_ENTRY Entry
, End
;
783 ULONG Index
= RESERVE_ENTRIES_COUNT
;
785 PPROCESSINFO W32Process
;
787 W32Process
= (PPROCESSINFO
)Process
->Win32Process
;
790 if (W32Process
->GDIHandleCount
> 0)
792 ProcId
= Process
->UniqueProcessId
;
794 /* FIXME - Instead of building the handle here and delete it using GDIOBJ_FreeObj
795 we should delete it directly here! */
797 End
= &GdiHandleTable
->Entries
[GDI_HANDLE_COUNT
];
798 for (Entry
= &GdiHandleTable
->Entries
[RESERVE_ENTRIES_COUNT
];
802 /* ignore the lock bit */
803 if ( (HANDLE
)((ULONG_PTR
)Entry
->ProcessId
& ~0x1) == ProcId
)
805 if ( (Entry
->Type
& GDI_ENTRY_BASETYPE_MASK
) == ObjectType
||
806 ObjectType
== GDI_OBJECT_TYPE_DONTCARE
)
808 HGDIOBJ ObjectHandle
;
810 /* Create the object handle for the entry, the lower(!) 16 bit of the
811 Type field includes the type of the object including the stock
812 object flag - but since stock objects don't have a process id we can
813 simply ignore this fact here. */
814 ObjectHandle
= (HGDIOBJ
)(Index
| (Entry
->Type
<< GDI_ENTRY_UPPER_SHIFT
));
816 if (!GDIOBJ_FreeObjByHandle(ObjectHandle
, GDI_OBJECT_TYPE_DONTCARE
))
818 DPRINT1("Failed to delete object %p!\n", ObjectHandle
);
821 if (W32Process
->GDIHandleCount
== 0)
823 /* there are no more gdi handles for this process, bail */
834 * Internal function. Called when the process is destroyed to free the remaining GDI handles.
835 * \param Process - PID of the process that will be destroyed.
838 GDI_CleanupForProcess(struct _EPROCESS
*Process
)
840 PEPROCESS CurrentProcess
;
841 PPROCESSINFO W32Process
;
843 DPRINT("Starting CleanupForProcess prochandle %x Pid %d\n", Process
, Process
->UniqueProcessId
);
844 CurrentProcess
= PsGetCurrentProcess();
845 if (CurrentProcess
!= Process
)
847 KeAttachProcess(&Process
->Pcb
);
850 W32Process
= (PPROCESSINFO
)CurrentProcess
->Win32Process
;
852 /* Delete objects. Begin with types that are not referenced by other types */
853 IntDeleteHandlesForProcess(Process
, GDILoObjType_LO_DC_TYPE
);
854 IntDeleteHandlesForProcess(Process
, GDILoObjType_LO_BRUSH_TYPE
);
855 IntDeleteHandlesForProcess(Process
, GDILoObjType_LO_BITMAP_TYPE
);
857 /* Finally finish with what's left */
858 IntDeleteHandlesForProcess(Process
, GDI_OBJECT_TYPE_DONTCARE
);
860 if (CurrentProcess
!= Process
)
866 GdiDbgHTIntegrityCheck();
869 DPRINT("Completed cleanup for process %d\n", Process
->UniqueProcessId
);
870 if (W32Process
->GDIHandleCount
> 0)
872 DPRINT1("Leaking %d handles!\n", W32Process
->GDIHandleCount
);
879 * Return pointer to the object by handle.
881 * \param hObj Object handle
882 * \return Pointer to the object.
884 * \note Process can only get pointer to the objects it created or global objects.
886 * \todo Get rid of the ExpectedType parameter!
888 PGDIOBJ INTERNAL_CALL
889 GDIOBJ_LockObj(HGDIOBJ hObj
, DWORD ExpectedType
)
892 PGDI_TABLE_ENTRY Entry
;
893 HANDLE ProcessId
, HandleProcessId
, LockedProcessId
, PrevProcId
;
895 ULONG HandleType
, HandleUpper
;
897 HandleIndex
= GDI_HANDLE_GET_INDEX(hObj
);
898 HandleType
= GDI_HANDLE_GET_TYPE(hObj
);
899 HandleUpper
= GDI_HANDLE_GET_UPPER(hObj
);
901 /* Check that the handle index is valid. */
902 if (HandleIndex
>= GDI_HANDLE_COUNT
)
905 Entry
= &GdiHandleTable
->Entries
[HandleIndex
];
907 /* Check if we have the requested type */
908 if ( (ExpectedType
!= GDI_OBJECT_TYPE_DONTCARE
&&
909 HandleType
!= ExpectedType
) ||
912 DPRINT("Attempted to lock object 0x%x of wrong type (Handle: 0x%x, requested: 0x%x)\n",
913 hObj
, HandleType
, ExpectedType
);
914 GDIDBG_TRACECALLER();
915 GDIDBG_TRACEALLOCATOR(hObj
);
916 GDIDBG_TRACEDELETER(hObj
);
920 ProcessId
= (HANDLE
)((ULONG_PTR
)PsGetCurrentProcessId() & ~1);
921 HandleProcessId
= (HANDLE
)((ULONG_PTR
)Entry
->ProcessId
& ~1);
923 /* Check for invalid owner. */
924 if (ProcessId
!= HandleProcessId
&& HandleProcessId
!= NULL
)
926 DPRINT1("Tried to lock object (0x%p) of wrong owner! ProcessId = %p, HandleProcessId = %p\n", hObj
, ProcessId
, HandleProcessId
);
927 GDIDBG_TRACECALLER();
928 GDIDBG_TRACEALLOCATOR(GDI_HANDLE_GET_INDEX(hObj
));
933 * Prevent the thread from being terminated during the locking process.
934 * It would result in undesired effects and inconsistency of the global
938 KeEnterCriticalRegion();
941 * Loop until we either successfully lock the handle entry & object or
942 * fail some of the check.
947 /* Lock the handle table entry. */
948 LockedProcessId
= (HANDLE
)((ULONG_PTR
)HandleProcessId
| 0x1);
949 PrevProcId
= InterlockedCompareExchangePointer((PVOID
*)&Entry
->ProcessId
,
953 if (PrevProcId
== HandleProcessId
)
956 * We're locking an object that belongs to our process or it's a
957 * global object if HandleProcessId is 0 here.
960 if ( (Entry
->KernelData
!= NULL
) &&
961 ((Entry
->Type
<< GDI_ENTRY_UPPER_SHIFT
) == HandleUpper
) )
963 PTHREADINFO Thread
= (PTHREADINFO
)PsGetCurrentThreadWin32Thread();
964 Object
= Entry
->KernelData
;
966 if (Object
->cExclusiveLock
== 0)
968 Object
->Tid
= Thread
;
969 Object
->cExclusiveLock
= 1;
970 GDIDBG_CAPTURELOCKER(GDI_HANDLE_GET_INDEX(hObj
))
974 if (Object
->Tid
!= Thread
)
976 /* Unlock the handle table entry. */
977 (void)InterlockedExchangePointer((PVOID
*)&Entry
->ProcessId
, PrevProcId
);
982 InterlockedIncrement((PLONG
)&Object
->cExclusiveLock
);
988 * Debugging code. Report attempts to lock deleted handles and
989 * locking type mismatches.
991 LockErrorDebugOutput(hObj
, Entry
, "GDIOBJ_LockObj");
994 /* Unlock the handle table entry. */
995 (void)InterlockedExchangePointer((PVOID
*)&Entry
->ProcessId
, PrevProcId
);
1002 * The handle is currently locked, wait some time and try again.
1010 KeLeaveCriticalRegion();
1017 * Return pointer to the object by handle (and allow sharing of the handle
1020 * \param hObj Object handle
1021 * \return Pointer to the object.
1023 * \note Process can only get pointer to the objects it created or global objects.
1025 * \todo Get rid of the ExpectedType parameter!
1027 PGDIOBJ INTERNAL_CALL
1028 GDIOBJ_ShareLockObj(HGDIOBJ hObj
, DWORD ExpectedType
)
1031 PGDI_TABLE_ENTRY Entry
;
1032 HANDLE ProcessId
, HandleProcessId
, LockedProcessId
, PrevProcId
;
1034 ULONG_PTR HandleType
, HandleUpper
;
1036 HandleIndex
= GDI_HANDLE_GET_INDEX(hObj
);
1037 HandleType
= GDI_HANDLE_GET_TYPE(hObj
);
1038 HandleUpper
= GDI_HANDLE_GET_UPPER(hObj
);
1040 /* Check that the handle index is valid. */
1041 if (HandleIndex
>= GDI_HANDLE_COUNT
)
1044 /* Check if we have the requested type */
1045 if ( (ExpectedType
!= GDI_OBJECT_TYPE_DONTCARE
&&
1046 HandleType
!= ExpectedType
) ||
1049 DPRINT1("Attempted to lock object 0x%x of wrong type (Handle: 0x%x, requested: 0x%x)\n",
1050 hObj
, HandleType
, ExpectedType
);
1054 Entry
= &GdiHandleTable
->Entries
[HandleIndex
];
1056 ProcessId
= (HANDLE
)((ULONG_PTR
)PsGetCurrentProcessId() & ~1);
1057 HandleProcessId
= (HANDLE
)((ULONG_PTR
)Entry
->ProcessId
& ~1);
1059 /* Check for invalid owner. */
1060 if (ProcessId
!= HandleProcessId
&& HandleProcessId
!= NULL
)
1066 * Prevent the thread from being terminated during the locking process.
1067 * It would result in undesired effects and inconsistency of the global
1071 KeEnterCriticalRegion();
1074 * Loop until we either successfully lock the handle entry & object or
1075 * fail some of the check.
1080 /* Lock the handle table entry. */
1081 LockedProcessId
= (HANDLE
)((ULONG_PTR
)HandleProcessId
| 0x1);
1082 PrevProcId
= InterlockedCompareExchangePointer((PVOID
*)&Entry
->ProcessId
,
1086 if (PrevProcId
== HandleProcessId
)
1089 * We're locking an object that belongs to our process or it's a
1090 * global object if HandleProcessId is 0 here.
1093 if ( (Entry
->KernelData
!= NULL
) &&
1094 (HandleUpper
== (Entry
->Type
<< GDI_ENTRY_UPPER_SHIFT
)) )
1096 Object
= (POBJ
)Entry
->KernelData
;
1098 GDIDBG_CAPTURESHARELOCKER(HandleIndex
);
1100 if (InterlockedIncrement((PLONG
)&Object
->ulShareCount
) == 1)
1102 memset(GDIHandleLocker
[HandleIndex
], 0x00, GDI_STACK_LEVELS
* sizeof(ULONG
));
1103 RtlCaptureStackBackTrace(1, GDI_STACK_LEVELS
, (PVOID
*)GDIHandleShareLocker
[HandleIndex
], NULL
);
1106 InterlockedIncrement((PLONG
)&Object
->ulShareCount
);
1112 * Debugging code. Report attempts to lock deleted handles and
1113 * locking type mismatches.
1115 LockErrorDebugOutput(hObj
, Entry
, "GDIOBJ_ShareLockObj");
1118 /* Unlock the handle table entry. */
1119 (void)InterlockedExchangePointer((PVOID
*)&Entry
->ProcessId
, PrevProcId
);
1126 * The handle is currently locked, wait some time and try again.
1134 KeLeaveCriticalRegion();
1140 GDIOBJ_OwnedByCurrentProcess(HGDIOBJ ObjectHandle
)
1142 PGDI_TABLE_ENTRY Entry
;
1146 DPRINT("GDIOBJ_OwnedByCurrentProcess: ObjectHandle: 0x%08x\n", ObjectHandle
);
1148 if (!GDI_HANDLE_IS_STOCKOBJ(ObjectHandle
))
1150 ProcessId
= PsGetCurrentProcessId();
1152 Entry
= GDI_HANDLE_GET_ENTRY(GdiHandleTable
, ObjectHandle
);
1153 Ret
= Entry
->KernelData
!= NULL
&&
1154 (Entry
->Type
& GDI_ENTRY_BASETYPE_MASK
) != 0 &&
1155 (HANDLE
)((ULONG_PTR
)Entry
->ProcessId
& ~0x1) == ProcessId
;
1164 GDIOBJ_ConvertToStockObj(HGDIOBJ
*phObj
)
1167 * FIXME !!!!! THIS FUNCTION NEEDS TO BE FIXED - IT IS NOT SAFE WHEN OTHER THREADS
1168 * MIGHT ATTEMPT TO LOCK THE OBJECT DURING THIS CALL!!!
1170 PGDI_TABLE_ENTRY Entry
;
1171 HANDLE ProcessId
, LockedProcessId
, PrevProcId
;
1175 GDIDBG_INITLOOPTRACE();
1180 DPRINT("GDIOBJ_ConvertToStockObj: hObj: 0x%08x\n", hObj
);
1182 Thread
= (PTHREADINFO
)PsGetCurrentThreadWin32Thread();
1184 if (!GDI_HANDLE_IS_STOCKOBJ(hObj
))
1186 ProcessId
= PsGetCurrentProcessId();
1187 LockedProcessId
= (HANDLE
)((ULONG_PTR
)ProcessId
| 0x1);
1189 Entry
= GDI_HANDLE_GET_ENTRY(GdiHandleTable
, hObj
);
1192 /* lock the object, we must not convert stock objects, so don't check!!! */
1193 PrevProcId
= InterlockedCompareExchangePointer((PVOID
*)&Entry
->ProcessId
, LockedProcessId
, ProcessId
);
1194 if (PrevProcId
== ProcessId
)
1196 LONG NewType
, PrevType
, OldType
;
1198 /* we're locking an object that belongs to our process. First calculate
1199 the new object type including the stock object flag and then try to
1201 /* On Windows the higher 16 bit of the type field don't contain the
1202 full type from the handle, but the base type.
1203 (type = BRSUH, PEN, EXTPEN, basetype = BRUSH) */
1204 OldType
= ((ULONG
)hObj
& GDI_HANDLE_BASETYPE_MASK
) | ((ULONG
)hObj
>> GDI_ENTRY_UPPER_SHIFT
);
1205 /* We are currently not using bits 24..31 (flags) of the type field, but for compatibility
1206 we copy them as we can't get them from the handle */
1207 OldType
|= Entry
->Type
& GDI_ENTRY_FLAGS_MASK
;
1209 /* As the object should be a stock object, set it's flag, but only in the lower 16 bits */
1210 NewType
= OldType
| GDI_ENTRY_STOCK_MASK
;
1212 /* Try to exchange the type field - but only if the old (previous type) matches! */
1213 PrevType
= InterlockedCompareExchange(&Entry
->Type
, NewType
, OldType
);
1214 if (PrevType
== OldType
&& Entry
->KernelData
!= NULL
)
1216 PTHREADINFO PrevThread
;
1219 /* We successfully set the stock object flag.
1220 KernelData should never be NULL here!!! */
1221 ASSERT(Entry
->KernelData
);
1223 Object
= Entry
->KernelData
;
1225 PrevThread
= Object
->Tid
;
1226 if (Object
->cExclusiveLock
== 0 || PrevThread
== Thread
)
1228 /* dereference the process' object counter */
1229 if (PrevProcId
!= GDI_GLOBAL_PROCESS
)
1231 PEPROCESS OldProcess
;
1232 PPROCESSINFO W32Process
;
1236 Status
= PsLookupProcessByProcessId((HANDLE
)((ULONG_PTR
)PrevProcId
& ~0x1), &OldProcess
);
1237 if (NT_SUCCESS(Status
))
1239 W32Process
= (PPROCESSINFO
)OldProcess
->Win32Process
;
1240 if (W32Process
!= NULL
)
1242 InterlockedDecrement(&W32Process
->GDIHandleCount
);
1244 ObDereferenceObject(OldProcess
);
1248 hObj
= (HGDIOBJ
)((ULONG
)(hObj
) | GDI_HANDLE_STOCK_MASK
);
1250 Object
->hHmgr
= hObj
;
1252 /* remove the process id lock and make it global */
1253 (void)InterlockedExchangePointer((PVOID
*)&Entry
->ProcessId
, GDI_GLOBAL_PROCESS
);
1255 /* we're done, successfully converted the object */
1260 GDIDBG_TRACELOOP(hObj
, PrevThread
, Thread
);
1262 /* WTF?! The object is already locked by a different thread!
1263 Release the lock, wait a bit and try again!
1264 FIXME - we should give up after some time unless we want to wait forever! */
1265 (void)InterlockedExchangePointer((PVOID
*)&Entry
->ProcessId
, PrevProcId
);
1273 DPRINT1("Attempted to convert object 0x%x that is deleted! Should never get here!!!\n", hObj
);
1274 DPRINT1("OldType = 0x%x, Entry->Type = 0x%x, NewType = 0x%x, Entry->KernelData = 0x%x\n", OldType
, Entry
->Type
, NewType
, Entry
->KernelData
);
1277 else if (PrevProcId
== LockedProcessId
)
1279 GDIDBG_TRACELOOP(hObj
, PrevProcId
, ProcessId
);
1281 /* the object is currently locked, wait some time and try again.
1282 FIXME - we shouldn't loop forever! Give up after some time! */
1289 DPRINT1("Attempted to convert invalid handle: 0x%x\n", hObj
);
1297 GDIOBJ_SetOwnership(HGDIOBJ ObjectHandle
, PEPROCESS NewOwner
)
1299 PGDI_TABLE_ENTRY Entry
;
1300 HANDLE ProcessId
, LockedProcessId
, PrevProcId
;
1304 GDIDBG_INITLOOPTRACE();
1306 DPRINT("GDIOBJ_SetOwnership: hObj: 0x%x, NewProcess: 0x%x\n", ObjectHandle
, (NewOwner
? PsGetProcessId(NewOwner
) : 0));
1308 Thread
= (PTHREADINFO
)PsGetCurrentThreadWin32Thread();
1310 if (!GDI_HANDLE_IS_STOCKOBJ(ObjectHandle
))
1312 ProcessId
= PsGetCurrentProcessId();
1313 LockedProcessId
= (HANDLE
)((ULONG_PTR
)ProcessId
| 0x1);
1315 Entry
= GDI_HANDLE_GET_ENTRY(GdiHandleTable
, ObjectHandle
);
1318 /* lock the object, we must not convert stock objects, so don't check!!! */
1319 PrevProcId
= InterlockedCompareExchangePointer((PVOID
*)&Entry
->ProcessId
, ProcessId
, LockedProcessId
);
1320 if (PrevProcId
== ProcessId
)
1322 PTHREADINFO PrevThread
;
1324 if ((Entry
->Type
& GDI_ENTRY_BASETYPE_MASK
) != 0)
1326 POBJ Object
= Entry
->KernelData
;
1328 PrevThread
= Object
->Tid
;
1329 if (Object
->cExclusiveLock
== 0 || PrevThread
== Thread
)
1331 PEPROCESS OldProcess
;
1332 PPROCESSINFO W32Process
;
1335 /* dereference the process' object counter */
1337 if ((ULONG_PTR
)PrevProcId
& ~0x1)
1339 Status
= PsLookupProcessByProcessId((HANDLE
)((ULONG_PTR
)PrevProcId
& ~0x1), &OldProcess
);
1340 if (NT_SUCCESS(Status
))
1342 W32Process
= (PPROCESSINFO
)OldProcess
->Win32Process
;
1343 if (W32Process
!= NULL
)
1345 InterlockedDecrement(&W32Process
->GDIHandleCount
);
1347 ObDereferenceObject(OldProcess
);
1351 if (NewOwner
!= NULL
)
1353 ProcessId
= PsGetProcessId(NewOwner
);
1355 /* Increase the new process' object counter */
1356 W32Process
= (PPROCESSINFO
)NewOwner
->Win32Process
;
1357 if (W32Process
!= NULL
)
1359 InterlockedIncrement(&W32Process
->GDIHandleCount
);
1365 /* remove the process id lock and change it to the new process id */
1366 (void)InterlockedExchangePointer((PVOID
*)&Entry
->ProcessId
, ProcessId
);
1373 GDIDBG_TRACELOOP(ObjectHandle
, PrevThread
, Thread
);
1375 /* WTF?! The object is already locked by a different thread!
1376 Release the lock, wait a bit and try again! DO reset the pid lock
1377 so we make sure we don't access invalid memory in case the object is
1378 being deleted in the meantime (because we don't have aquired a reference
1380 FIXME - we should give up after some time unless we want to wait forever! */
1381 (void)InterlockedExchangePointer((PVOID
*)&Entry
->ProcessId
, PrevProcId
);
1389 DPRINT1("Attempted to change ownership of an object 0x%x currently being destroyed!!!\n", ObjectHandle
);
1390 DPRINT1("Entry->Type = 0x%lx, Entry->KernelData = 0x%p\n", Entry
->Type
, Entry
->KernelData
);
1394 else if (PrevProcId
== LockedProcessId
)
1396 GDIDBG_TRACELOOP(ObjectHandle
, PrevProcId
, ProcessId
);
1398 /* the object is currently locked, wait some time and try again.
1399 FIXME - we shouldn't loop forever! Give up after some time! */
1404 else if (((ULONG_PTR
)PrevProcId
& ~0x1) == 0)
1406 /* allow changing ownership of global objects */
1408 LockedProcessId
= (HANDLE
)((ULONG_PTR
)ProcessId
| 0x1);
1411 else if ((HANDLE
)((ULONG_PTR
)PrevProcId
& ~0x1) != PsGetCurrentProcessId())
1413 DPRINT1("Attempted to change ownership of object 0x%x (pid: 0x%x) from pid 0x%x!!!\n", ObjectHandle
, (ULONG_PTR
)PrevProcId
& ~0x1, PsGetCurrentProcessId());
1418 DPRINT1("Attempted to change owner of invalid handle: 0x%x\n", ObjectHandle
);
1426 GDIOBJ_CopyOwnership(HGDIOBJ CopyFrom
, HGDIOBJ CopyTo
)
1428 PGDI_TABLE_ENTRY FromEntry
;
1430 HANDLE FromProcessId
, FromLockedProcessId
, FromPrevProcId
;
1433 GDIDBG_INITLOOPTRACE();
1435 DPRINT("GDIOBJ_CopyOwnership: from: 0x%x, to: 0x%x\n", CopyFrom
, CopyTo
);
1437 Thread
= (PTHREADINFO
)PsGetCurrentThreadWin32Thread();
1439 if (!GDI_HANDLE_IS_STOCKOBJ(CopyFrom
) && !GDI_HANDLE_IS_STOCKOBJ(CopyTo
))
1441 FromEntry
= GDI_HANDLE_GET_ENTRY(GdiHandleTable
, CopyFrom
);
1443 FromProcessId
= (HANDLE
)((ULONG_PTR
)FromEntry
->ProcessId
& ~0x1);
1444 FromLockedProcessId
= (HANDLE
)((ULONG_PTR
)FromProcessId
| 0x1);
1447 /* lock the object, we must not convert stock objects, so don't check!!! */
1448 FromPrevProcId
= InterlockedCompareExchangePointer((PVOID
*)&FromEntry
->ProcessId
, FromProcessId
, FromLockedProcessId
);
1449 if (FromPrevProcId
== FromProcessId
)
1451 PTHREADINFO PrevThread
;
1454 if ((FromEntry
->Type
& GDI_ENTRY_BASETYPE_MASK
) != 0)
1456 Object
= FromEntry
->KernelData
;
1458 /* save the pointer to the calling thread so we know it was this thread
1459 that locked the object */
1460 PrevThread
= Object
->Tid
;
1461 if (Object
->cExclusiveLock
== 0 || PrevThread
== Thread
)
1463 /* now let's change the ownership of the target object */
1465 if (((ULONG_PTR
)FromPrevProcId
& ~0x1) != 0)
1467 PEPROCESS ProcessTo
;
1469 if (NT_SUCCESS(PsLookupProcessByProcessId((HANDLE
)((ULONG_PTR
)FromPrevProcId
& ~0x1), &ProcessTo
)))
1471 GDIOBJ_SetOwnership(CopyTo
, ProcessTo
);
1472 ObDereferenceObject(ProcessTo
);
1477 /* mark the object as global */
1478 GDIOBJ_SetOwnership(CopyTo
, NULL
);
1481 (void)InterlockedExchangePointer((PVOID
*)&FromEntry
->ProcessId
, FromPrevProcId
);
1485 GDIDBG_TRACELOOP(CopyFrom
, PrevThread
, Thread
);
1487 /* WTF?! The object is already locked by a different thread!
1488 Release the lock, wait a bit and try again! DO reset the pid lock
1489 so we make sure we don't access invalid memory in case the object is
1490 being deleted in the meantime (because we don't have aquired a reference
1492 FIXME - we should give up after some time unless we want to wait forever! */
1493 (void)InterlockedExchangePointer((PVOID
*)&FromEntry
->ProcessId
, FromPrevProcId
);
1496 goto LockHandleFrom
;
1501 DPRINT1("Attempted to copy ownership from an object 0x%x currently being destroyed!!!\n", CopyFrom
);
1505 else if (FromPrevProcId
== FromLockedProcessId
)
1507 GDIDBG_TRACELOOP(CopyFrom
, FromPrevProcId
, FromProcessId
);
1509 /* the object is currently locked, wait some time and try again.
1510 FIXME - we shouldn't loop forever! Give up after some time! */
1513 goto LockHandleFrom
;
1515 else if ((HANDLE
)((ULONG_PTR
)FromPrevProcId
& ~0x1) != PsGetCurrentProcessId())
1517 /* FIXME - should we really allow copying ownership from objects that we don't even own? */
1518 DPRINT1("WARNING! Changing copying ownership of object 0x%x (pid: 0x%x) to pid 0x%x!!!\n", CopyFrom
, (ULONG_PTR
)FromPrevProcId
& ~0x1, PsGetCurrentProcessId());
1519 FromProcessId
= (HANDLE
)((ULONG_PTR
)FromPrevProcId
& ~0x1);
1520 FromLockedProcessId
= (HANDLE
)((ULONG_PTR
)FromProcessId
| 0x1);
1521 goto LockHandleFrom
;
1525 DPRINT1("Attempted to copy ownership from invalid handle: 0x%x\n", CopyFrom
);
1533 GDI_MapHandleTable(PSECTION_OBJECT SectionObject
, PEPROCESS Process
)
1535 PVOID MappedView
= NULL
;
1537 LARGE_INTEGER Offset
;
1538 ULONG ViewSize
= sizeof(GDI_HANDLE_TABLE
);
1540 Offset
.QuadPart
= 0;
1542 ASSERT(SectionObject
!= NULL
);
1543 ASSERT(Process
!= NULL
);
1545 Status
= MmMapViewOfSection(SectionObject
,
1556 if (!NT_SUCCESS(Status
))
1562 /** PUBLIC FUNCTIONS **********************************************************/
1566 IntGdiSetRegionOwner(HRGN hRgn
, DWORD OwnerMask
)
1569 PGDI_TABLE_ENTRY Entry
;
1572 These regions do not use attribute sections and when allocated, use gdiobj
1575 // FIXME! HAX!!! Remove this once we get everything right!
1576 Index
= GDI_HANDLE_GET_INDEX(hRgn
);
1577 Entry
= &GdiHandleTable
->Entries
[Index
];
1578 if (Entry
->UserData
) FreeObjectAttr(Entry
->UserData
);
1579 Entry
->UserData
= NULL
;
1581 if ((OwnerMask
== GDI_OBJ_HMGR_PUBLIC
) || OwnerMask
== GDI_OBJ_HMGR_NONE
)
1583 return GDIOBJ_SetOwnership(hRgn
, NULL
);
1585 if (OwnerMask
== GDI_OBJ_HMGR_POWNED
)
1587 return GDIOBJ_SetOwnership((HGDIOBJ
) hRgn
, PsGetCurrentProcess() );
1594 IntGdiSetBrushOwner(PBRUSH pbr
, DWORD OwnerMask
)
1597 PEPROCESS Owner
= NULL
;
1598 PGDI_TABLE_ENTRY pEntry
= NULL
;
1600 if (!pbr
) return FALSE
;
1602 hBR
= pbr
->BaseObject
.hHmgr
;
1604 if (!hBR
|| (GDI_HANDLE_GET_TYPE(hBR
) != GDI_OBJECT_TYPE_BRUSH
))
1608 INT Index
= GDI_HANDLE_GET_INDEX((HGDIOBJ
)hBR
);
1609 pEntry
= &GdiHandleTable
->Entries
[Index
];
1612 if (pbr
->flAttrs
& GDIBRUSH_IS_GLOBAL
)
1614 GDIOBJ_ShareUnlockObjByPtr((POBJ
)pbr
);
1618 if ((OwnerMask
== GDI_OBJ_HMGR_PUBLIC
) || OwnerMask
== GDI_OBJ_HMGR_NONE
)
1620 // Set this Brush to inaccessible mode and to an Owner of NONE.
1621 // if (OwnerMask == GDI_OBJ_HMGR_NONE) Owner = OwnerMask;
1623 if (!GDIOBJ_SetOwnership((HGDIOBJ
) hBR
, Owner
))
1626 // Deny user access to User Data.
1627 pEntry
->UserData
= NULL
; // This hBR is inaccessible!
1630 if (OwnerMask
== GDI_OBJ_HMGR_POWNED
)
1632 if (!GDIOBJ_SetOwnership((HGDIOBJ
) hBR
, PsGetCurrentProcess() ))
1635 // Allow user access to User Data.
1636 pEntry
->UserData
= pbr
->pBrushAttr
;
1643 IntGdiSetDCOwnerEx( HDC hDC
, DWORD OwnerMask
, BOOL NoSetBrush
)
1648 if (!hDC
|| (GDI_HANDLE_GET_TYPE(hDC
) != GDI_OBJECT_TYPE_DC
)) return FALSE
;
1650 if ((OwnerMask
== GDI_OBJ_HMGR_PUBLIC
) || OwnerMask
== GDI_OBJ_HMGR_NONE
)
1652 pDC
= DC_LockDc ( hDC
);
1653 MmCopyFromCaller(&pDC
->dcattr
, pDC
->pdcattr
, sizeof(DC_ATTR
));
1656 DC_FreeDcAttr( hDC
); // Free the dcattr!
1658 if (!DC_SetOwnership( hDC
, NULL
)) // This hDC is inaccessible!
1662 if (OwnerMask
== GDI_OBJ_HMGR_POWNED
)
1664 pDC
= DC_LockDc ( hDC
);
1665 ASSERT(pDC
->pdcattr
== &pDC
->dcattr
);
1668 if (!DC_SetOwnership( hDC
, PsGetCurrentProcess() )) return Ret
;
1670 DC_AllocateDcAttr( hDC
); // Allocate new dcattr
1672 DCU_SynchDcAttrtoUser( hDC
); // Copy data from dc to dcattr
1675 if ((OwnerMask
!= GDI_OBJ_HMGR_NONE
) && !NoSetBrush
)
1677 pDC
= DC_LockDc ( hDC
);
1678 if (IntGdiSetBrushOwner((PBRUSH
)pDC
->dclevel
.pbrFill
, OwnerMask
))
1679 IntGdiSetBrushOwner((PBRUSH
)pDC
->dclevel
.pbrLine
, OwnerMask
);
1687 GreGetObjectOwner(HGDIOBJ Handle
, GDIOBJTYPE ObjType
)
1689 INT Ret
= GDI_OBJ_HMGR_RESTRICTED
;
1691 if ( GDI_HANDLE_GET_INDEX(Handle
) < GDI_HANDLE_COUNT
)
1693 PGDI_TABLE_ENTRY pEntry
= &GdiHandleTable
->Entries
[GDI_HANDLE_GET_INDEX(Handle
)];
1695 if (pEntry
->ObjectType
== ObjType
)
1697 if (pEntry
->FullUnique
== (GDI_HANDLE_GET_UPPER(Handle
) >> GDI_ENTRY_UPPER_SHIFT
))
1698 Ret
= pEntry
->ProcessId
& ~1;
1707 NtGdiCreateClientObj(
1714 /* Mask out everything that would change the type in a wrong manner */
1715 ulType
&= (GDI_HANDLE_TYPE_MASK
& ~GDI_HANDLE_BASETYPE_MASK
);
1717 /* Allocate a new object */
1718 pObject
= GDIOBJ_AllocObjWithHandle(GDI_OBJECT_TYPE_CLIOBJ
| ulType
);
1724 /* get the handle */
1725 handle
= pObject
->hHmgr
;
1728 GDIOBJ_UnlockObjByPtr(pObject
);
1736 NtGdiDeleteClientObj(
1740 /* We first need to get the real type from the handle */
1741 ULONG type
= GDI_HANDLE_GET_TYPE(h
);
1743 /* Check if it's really a CLIENTOBJ */
1744 if ((type
& GDI_HANDLE_BASETYPE_MASK
) != GDILoObjType_LO_CLIENTOBJ_TYPE
)
1746 /* FIXME: SetLastError? */
1749 return GDIOBJ_FreeObjByHandle(h
, type
);
1754 IntGdiGetObject(IN HANDLE Handle
,
1762 pGdiObject
= GDIOBJ_LockObj(Handle
, GDI_OBJECT_TYPE_DONTCARE
);
1765 SetLastWin32Error(ERROR_INVALID_HANDLE
);
1769 dwObjectType
= GDIOBJ_GetObjectType(Handle
);
1770 switch (dwObjectType
)
1772 case GDI_OBJECT_TYPE_PEN
:
1773 case GDI_OBJECT_TYPE_EXTPEN
:
1774 Result
= PEN_GetObject((PBRUSH
) pGdiObject
, cbCount
, (PLOGPEN
) lpBuffer
); // IntGdiCreatePenIndirect
1777 case GDI_OBJECT_TYPE_BRUSH
:
1778 Result
= BRUSH_GetObject((PBRUSH
) pGdiObject
, cbCount
, (LPLOGBRUSH
)lpBuffer
);
1781 case GDI_OBJECT_TYPE_BITMAP
:
1782 Result
= BITMAP_GetObject((SURFACE
*) pGdiObject
, cbCount
, lpBuffer
);
1784 case GDI_OBJECT_TYPE_FONT
:
1785 Result
= FontGetObject((PTEXTOBJ
) pGdiObject
, cbCount
, lpBuffer
);
1787 // Fix the LOGFONT structure for the stock fonts
1788 if (FIRST_STOCK_HANDLE
<= Handle
&& Handle
<= LAST_STOCK_HANDLE
)
1790 FixStockFontSizeW(Handle
, cbCount
, lpBuffer
);
1795 case GDI_OBJECT_TYPE_PALETTE
:
1796 Result
= PALETTE_GetObject((PPALETTE
) pGdiObject
, cbCount
, lpBuffer
);
1800 DPRINT1("GDI object type 0x%08x not implemented\n", dwObjectType
);
1804 GDIOBJ_UnlockObjByPtr(pGdiObject
);
1814 NtGdiExtGetObjectW(IN HANDLE hGdiObj
,
1816 OUT LPVOID lpBuffer
)
1823 DIBSECTION dibsection
;
1827 EXTLOGFONTW extlogfontw
;
1828 ENUMLOGFONTEXDVW enumlogfontexdvw
;
1831 // Normalize to the largest supported object size
1832 cbCount
= min((UINT
)cbCount
, sizeof(Object
));
1834 // Now do the actual call
1835 iRetCount
= IntGdiGetObject(hGdiObj
, cbCount
, lpBuffer
? &Object
: NULL
);
1836 cbCopyCount
= min((UINT
)cbCount
, (UINT
)iRetCount
);
1838 // Make sure we have a buffer and a copy size
1839 if ((cbCopyCount
) && (lpBuffer
))
1841 // Enter SEH for buffer transfer
1844 // Probe the buffer and copy it
1845 ProbeForWrite(lpBuffer
, cbCopyCount
, sizeof(WORD
));
1846 RtlCopyMemory(lpBuffer
, &Object
, cbCopyCount
);
1848 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1850 // Clear the return value.
1851 // Do *NOT* set last error here!