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 FreeObjectAttr(pAttr
);
757 KeLeaveCriticalRegion();
761 case GDI_OBJECT_TYPE_DC
:
762 DC_FreeDcAttr(hObject
);
766 return NULL
!= hObject
767 ? GDIOBJ_FreeObjByHandle(hObject
, dwObjectType
) : FALSE
;
771 DPRINT1("Attempt DeleteObject 0x%x currently being destroyed!!!\n",hObject
);
772 return TRUE
; // return true and move on.
778 IntDeleteHandlesForProcess(struct _EPROCESS
*Process
, ULONG ObjectType
)
780 PGDI_TABLE_ENTRY Entry
, End
;
781 ULONG Index
= RESERVE_ENTRIES_COUNT
;
783 PPROCESSINFO W32Process
;
785 W32Process
= (PPROCESSINFO
)Process
->Win32Process
;
788 if (W32Process
->GDIHandleCount
> 0)
790 ProcId
= Process
->UniqueProcessId
;
792 /* FIXME - Instead of building the handle here and delete it using GDIOBJ_FreeObj
793 we should delete it directly here! */
795 End
= &GdiHandleTable
->Entries
[GDI_HANDLE_COUNT
];
796 for (Entry
= &GdiHandleTable
->Entries
[RESERVE_ENTRIES_COUNT
];
800 /* ignore the lock bit */
801 if ( (HANDLE
)((ULONG_PTR
)Entry
->ProcessId
& ~0x1) == ProcId
)
803 if ( (Entry
->Type
& GDI_ENTRY_BASETYPE_MASK
) == ObjectType
||
804 ObjectType
== GDI_OBJECT_TYPE_DONTCARE
)
806 HGDIOBJ ObjectHandle
;
808 /* Create the object handle for the entry, the lower(!) 16 bit of the
809 Type field includes the type of the object including the stock
810 object flag - but since stock objects don't have a process id we can
811 simply ignore this fact here. */
812 ObjectHandle
= (HGDIOBJ
)(Index
| (Entry
->Type
<< GDI_ENTRY_UPPER_SHIFT
));
814 if (!GDIOBJ_FreeObjByHandle(ObjectHandle
, GDI_OBJECT_TYPE_DONTCARE
))
816 DPRINT1("Failed to delete object %p!\n", ObjectHandle
);
819 if (W32Process
->GDIHandleCount
== 0)
821 /* there are no more gdi handles for this process, bail */
832 * Internal function. Called when the process is destroyed to free the remaining GDI handles.
833 * \param Process - PID of the process that will be destroyed.
836 GDI_CleanupForProcess(struct _EPROCESS
*Process
)
838 PEPROCESS CurrentProcess
;
839 PPROCESSINFO W32Process
;
841 DPRINT("Starting CleanupForProcess prochandle %x Pid %d\n", Process
, Process
->UniqueProcessId
);
842 CurrentProcess
= PsGetCurrentProcess();
843 if (CurrentProcess
!= Process
)
845 KeAttachProcess(&Process
->Pcb
);
848 W32Process
= (PPROCESSINFO
)CurrentProcess
->Win32Process
;
850 /* Delete objects. Begin with types that are not referenced by other types */
851 IntDeleteHandlesForProcess(Process
, GDILoObjType_LO_DC_TYPE
);
852 IntDeleteHandlesForProcess(Process
, GDILoObjType_LO_BRUSH_TYPE
);
853 IntDeleteHandlesForProcess(Process
, GDILoObjType_LO_BITMAP_TYPE
);
855 /* Finally finish with what's left */
856 IntDeleteHandlesForProcess(Process
, GDI_OBJECT_TYPE_DONTCARE
);
858 if (CurrentProcess
!= Process
)
864 GdiDbgHTIntegrityCheck();
867 DPRINT("Completed cleanup for process %d\n", Process
->UniqueProcessId
);
868 if (W32Process
->GDIHandleCount
> 0)
870 DPRINT1("Leaking %d handles!\n", W32Process
->GDIHandleCount
);
877 * Return pointer to the object by handle.
879 * \param hObj Object handle
880 * \return Pointer to the object.
882 * \note Process can only get pointer to the objects it created or global objects.
884 * \todo Get rid of the ExpectedType parameter!
886 PGDIOBJ INTERNAL_CALL
887 GDIOBJ_LockObj(HGDIOBJ hObj
, DWORD ExpectedType
)
890 PGDI_TABLE_ENTRY Entry
;
891 HANDLE ProcessId
, HandleProcessId
, LockedProcessId
, PrevProcId
;
893 ULONG HandleType
, HandleUpper
;
895 HandleIndex
= GDI_HANDLE_GET_INDEX(hObj
);
896 HandleType
= GDI_HANDLE_GET_TYPE(hObj
);
897 HandleUpper
= GDI_HANDLE_GET_UPPER(hObj
);
899 /* Check that the handle index is valid. */
900 if (HandleIndex
>= GDI_HANDLE_COUNT
)
903 Entry
= &GdiHandleTable
->Entries
[HandleIndex
];
905 /* Check if we have the requested type */
906 if ( (ExpectedType
!= GDI_OBJECT_TYPE_DONTCARE
&&
907 HandleType
!= ExpectedType
) ||
910 DPRINT("Attempted to lock object 0x%x of wrong type (Handle: 0x%x, requested: 0x%x)\n",
911 hObj
, HandleType
, ExpectedType
);
912 GDIDBG_TRACECALLER();
913 GDIDBG_TRACEALLOCATOR(hObj
);
914 GDIDBG_TRACEDELETER(hObj
);
918 ProcessId
= (HANDLE
)((ULONG_PTR
)PsGetCurrentProcessId() & ~1);
919 HandleProcessId
= (HANDLE
)((ULONG_PTR
)Entry
->ProcessId
& ~1);
921 /* Check for invalid owner. */
922 if (ProcessId
!= HandleProcessId
&& HandleProcessId
!= NULL
)
924 DPRINT1("Tried to lock object (0x%p) of wrong owner! ProcessId = %p, HandleProcessId = %p\n", hObj
, ProcessId
, HandleProcessId
);
925 GDIDBG_TRACECALLER();
926 GDIDBG_TRACEALLOCATOR(GDI_HANDLE_GET_INDEX(hObj
));
931 * Prevent the thread from being terminated during the locking process.
932 * It would result in undesired effects and inconsistency of the global
936 KeEnterCriticalRegion();
939 * Loop until we either successfully lock the handle entry & object or
940 * fail some of the check.
945 /* Lock the handle table entry. */
946 LockedProcessId
= (HANDLE
)((ULONG_PTR
)HandleProcessId
| 0x1);
947 PrevProcId
= InterlockedCompareExchangePointer((PVOID
*)&Entry
->ProcessId
,
951 if (PrevProcId
== HandleProcessId
)
954 * We're locking an object that belongs to our process or it's a
955 * global object if HandleProcessId is 0 here.
958 if ( (Entry
->KernelData
!= NULL
) &&
959 ((Entry
->Type
<< GDI_ENTRY_UPPER_SHIFT
) == HandleUpper
) )
961 PTHREADINFO Thread
= (PTHREADINFO
)PsGetCurrentThreadWin32Thread();
962 Object
= Entry
->KernelData
;
964 if (Object
->cExclusiveLock
== 0)
966 Object
->Tid
= Thread
;
967 Object
->cExclusiveLock
= 1;
968 GDIDBG_CAPTURELOCKER(GDI_HANDLE_GET_INDEX(hObj
))
972 if (Object
->Tid
!= Thread
)
974 /* Unlock the handle table entry. */
975 (void)InterlockedExchangePointer((PVOID
*)&Entry
->ProcessId
, PrevProcId
);
980 InterlockedIncrement((PLONG
)&Object
->cExclusiveLock
);
986 * Debugging code. Report attempts to lock deleted handles and
987 * locking type mismatches.
989 LockErrorDebugOutput(hObj
, Entry
, "GDIOBJ_LockObj");
992 /* Unlock the handle table entry. */
993 (void)InterlockedExchangePointer((PVOID
*)&Entry
->ProcessId
, PrevProcId
);
1000 * The handle is currently locked, wait some time and try again.
1008 KeLeaveCriticalRegion();
1015 * Return pointer to the object by handle (and allow sharing of the handle
1018 * \param hObj Object handle
1019 * \return Pointer to the object.
1021 * \note Process can only get pointer to the objects it created or global objects.
1023 * \todo Get rid of the ExpectedType parameter!
1025 PGDIOBJ INTERNAL_CALL
1026 GDIOBJ_ShareLockObj(HGDIOBJ hObj
, DWORD ExpectedType
)
1029 PGDI_TABLE_ENTRY Entry
;
1030 HANDLE ProcessId
, HandleProcessId
, LockedProcessId
, PrevProcId
;
1032 ULONG_PTR HandleType
, HandleUpper
;
1034 HandleIndex
= GDI_HANDLE_GET_INDEX(hObj
);
1035 HandleType
= GDI_HANDLE_GET_TYPE(hObj
);
1036 HandleUpper
= GDI_HANDLE_GET_UPPER(hObj
);
1038 /* Check that the handle index is valid. */
1039 if (HandleIndex
>= GDI_HANDLE_COUNT
)
1042 /* Check if we have the requested type */
1043 if ( (ExpectedType
!= GDI_OBJECT_TYPE_DONTCARE
&&
1044 HandleType
!= ExpectedType
) ||
1047 DPRINT1("Attempted to lock object 0x%x of wrong type (Handle: 0x%x, requested: 0x%x)\n",
1048 hObj
, HandleType
, ExpectedType
);
1052 Entry
= &GdiHandleTable
->Entries
[HandleIndex
];
1054 ProcessId
= (HANDLE
)((ULONG_PTR
)PsGetCurrentProcessId() & ~1);
1055 HandleProcessId
= (HANDLE
)((ULONG_PTR
)Entry
->ProcessId
& ~1);
1057 /* Check for invalid owner. */
1058 if (ProcessId
!= HandleProcessId
&& HandleProcessId
!= NULL
)
1064 * Prevent the thread from being terminated during the locking process.
1065 * It would result in undesired effects and inconsistency of the global
1069 KeEnterCriticalRegion();
1072 * Loop until we either successfully lock the handle entry & object or
1073 * fail some of the check.
1078 /* Lock the handle table entry. */
1079 LockedProcessId
= (HANDLE
)((ULONG_PTR
)HandleProcessId
| 0x1);
1080 PrevProcId
= InterlockedCompareExchangePointer((PVOID
*)&Entry
->ProcessId
,
1084 if (PrevProcId
== HandleProcessId
)
1087 * We're locking an object that belongs to our process or it's a
1088 * global object if HandleProcessId is 0 here.
1091 if ( (Entry
->KernelData
!= NULL
) &&
1092 (HandleUpper
== (Entry
->Type
<< GDI_ENTRY_UPPER_SHIFT
)) )
1094 Object
= (POBJ
)Entry
->KernelData
;
1096 GDIDBG_CAPTURESHARELOCKER(HandleIndex
);
1098 if (InterlockedIncrement((PLONG
)&Object
->ulShareCount
) == 1)
1100 memset(GDIHandleLocker
[HandleIndex
], 0x00, GDI_STACK_LEVELS
* sizeof(ULONG
));
1101 RtlCaptureStackBackTrace(1, GDI_STACK_LEVELS
, (PVOID
*)GDIHandleShareLocker
[HandleIndex
], NULL
);
1104 InterlockedIncrement((PLONG
)&Object
->ulShareCount
);
1110 * Debugging code. Report attempts to lock deleted handles and
1111 * locking type mismatches.
1113 LockErrorDebugOutput(hObj
, Entry
, "GDIOBJ_ShareLockObj");
1116 /* Unlock the handle table entry. */
1117 (void)InterlockedExchangePointer((PVOID
*)&Entry
->ProcessId
, PrevProcId
);
1124 * The handle is currently locked, wait some time and try again.
1132 KeLeaveCriticalRegion();
1138 GDIOBJ_OwnedByCurrentProcess(HGDIOBJ ObjectHandle
)
1140 PGDI_TABLE_ENTRY Entry
;
1144 DPRINT("GDIOBJ_OwnedByCurrentProcess: ObjectHandle: 0x%08x\n", ObjectHandle
);
1146 if (!GDI_HANDLE_IS_STOCKOBJ(ObjectHandle
))
1148 ProcessId
= PsGetCurrentProcessId();
1150 Entry
= GDI_HANDLE_GET_ENTRY(GdiHandleTable
, ObjectHandle
);
1151 Ret
= Entry
->KernelData
!= NULL
&&
1152 (Entry
->Type
& GDI_ENTRY_BASETYPE_MASK
) != 0 &&
1153 (HANDLE
)((ULONG_PTR
)Entry
->ProcessId
& ~0x1) == ProcessId
;
1162 GDIOBJ_ConvertToStockObj(HGDIOBJ
*phObj
)
1165 * FIXME !!!!! THIS FUNCTION NEEDS TO BE FIXED - IT IS NOT SAFE WHEN OTHER THREADS
1166 * MIGHT ATTEMPT TO LOCK THE OBJECT DURING THIS CALL!!!
1168 PGDI_TABLE_ENTRY Entry
;
1169 HANDLE ProcessId
, LockedProcessId
, PrevProcId
;
1173 GDIDBG_INITLOOPTRACE();
1178 DPRINT("GDIOBJ_ConvertToStockObj: hObj: 0x%08x\n", hObj
);
1180 Thread
= (PTHREADINFO
)PsGetCurrentThreadWin32Thread();
1182 if (!GDI_HANDLE_IS_STOCKOBJ(hObj
))
1184 ProcessId
= PsGetCurrentProcessId();
1185 LockedProcessId
= (HANDLE
)((ULONG_PTR
)ProcessId
| 0x1);
1187 Entry
= GDI_HANDLE_GET_ENTRY(GdiHandleTable
, hObj
);
1190 /* lock the object, we must not convert stock objects, so don't check!!! */
1191 PrevProcId
= InterlockedCompareExchangePointer((PVOID
*)&Entry
->ProcessId
, LockedProcessId
, ProcessId
);
1192 if (PrevProcId
== ProcessId
)
1194 LONG NewType
, PrevType
, OldType
;
1196 /* we're locking an object that belongs to our process. First calculate
1197 the new object type including the stock object flag and then try to
1199 /* On Windows the higher 16 bit of the type field don't contain the
1200 full type from the handle, but the base type.
1201 (type = BRSUH, PEN, EXTPEN, basetype = BRUSH) */
1202 OldType
= ((ULONG
)hObj
& GDI_HANDLE_BASETYPE_MASK
) | ((ULONG
)hObj
>> GDI_ENTRY_UPPER_SHIFT
);
1203 /* We are currently not using bits 24..31 (flags) of the type field, but for compatibility
1204 we copy them as we can't get them from the handle */
1205 OldType
|= Entry
->Type
& GDI_ENTRY_FLAGS_MASK
;
1207 /* As the object should be a stock object, set it's flag, but only in the lower 16 bits */
1208 NewType
= OldType
| GDI_ENTRY_STOCK_MASK
;
1210 /* Try to exchange the type field - but only if the old (previous type) matches! */
1211 PrevType
= InterlockedCompareExchange(&Entry
->Type
, NewType
, OldType
);
1212 if (PrevType
== OldType
&& Entry
->KernelData
!= NULL
)
1214 PTHREADINFO PrevThread
;
1217 /* We successfully set the stock object flag.
1218 KernelData should never be NULL here!!! */
1219 ASSERT(Entry
->KernelData
);
1221 Object
= Entry
->KernelData
;
1223 PrevThread
= Object
->Tid
;
1224 if (Object
->cExclusiveLock
== 0 || PrevThread
== Thread
)
1226 /* dereference the process' object counter */
1227 if (PrevProcId
!= GDI_GLOBAL_PROCESS
)
1229 PEPROCESS OldProcess
;
1230 PPROCESSINFO W32Process
;
1234 Status
= PsLookupProcessByProcessId((HANDLE
)((ULONG_PTR
)PrevProcId
& ~0x1), &OldProcess
);
1235 if (NT_SUCCESS(Status
))
1237 W32Process
= (PPROCESSINFO
)OldProcess
->Win32Process
;
1238 if (W32Process
!= NULL
)
1240 InterlockedDecrement(&W32Process
->GDIHandleCount
);
1242 ObDereferenceObject(OldProcess
);
1246 hObj
= (HGDIOBJ
)((ULONG
)(hObj
) | GDI_HANDLE_STOCK_MASK
);
1248 Object
->hHmgr
= hObj
;
1250 /* remove the process id lock and make it global */
1251 (void)InterlockedExchangePointer((PVOID
*)&Entry
->ProcessId
, GDI_GLOBAL_PROCESS
);
1253 /* we're done, successfully converted the object */
1258 GDIDBG_TRACELOOP(hObj
, PrevThread
, Thread
);
1260 /* WTF?! The object is already locked by a different thread!
1261 Release the lock, wait a bit and try again!
1262 FIXME - we should give up after some time unless we want to wait forever! */
1263 (void)InterlockedExchangePointer((PVOID
*)&Entry
->ProcessId
, PrevProcId
);
1271 DPRINT1("Attempted to convert object 0x%x that is deleted! Should never get here!!!\n", hObj
);
1272 DPRINT1("OldType = 0x%x, Entry->Type = 0x%x, NewType = 0x%x, Entry->KernelData = 0x%x\n", OldType
, Entry
->Type
, NewType
, Entry
->KernelData
);
1275 else if (PrevProcId
== LockedProcessId
)
1277 GDIDBG_TRACELOOP(hObj
, PrevProcId
, ProcessId
);
1279 /* the object is currently locked, wait some time and try again.
1280 FIXME - we shouldn't loop forever! Give up after some time! */
1287 DPRINT1("Attempted to convert invalid handle: 0x%x\n", hObj
);
1295 GDIOBJ_SetOwnership(HGDIOBJ ObjectHandle
, PEPROCESS NewOwner
)
1297 PGDI_TABLE_ENTRY Entry
;
1298 HANDLE ProcessId
, LockedProcessId
, PrevProcId
;
1302 GDIDBG_INITLOOPTRACE();
1304 DPRINT("GDIOBJ_SetOwnership: hObj: 0x%x, NewProcess: 0x%x\n", ObjectHandle
, (NewOwner
? PsGetProcessId(NewOwner
) : 0));
1306 Thread
= (PTHREADINFO
)PsGetCurrentThreadWin32Thread();
1308 if (!GDI_HANDLE_IS_STOCKOBJ(ObjectHandle
))
1310 ProcessId
= PsGetCurrentProcessId();
1311 LockedProcessId
= (HANDLE
)((ULONG_PTR
)ProcessId
| 0x1);
1313 Entry
= GDI_HANDLE_GET_ENTRY(GdiHandleTable
, ObjectHandle
);
1316 /* lock the object, we must not convert stock objects, so don't check!!! */
1317 PrevProcId
= InterlockedCompareExchangePointer((PVOID
*)&Entry
->ProcessId
, ProcessId
, LockedProcessId
);
1318 if (PrevProcId
== ProcessId
)
1320 PTHREADINFO PrevThread
;
1322 if ((Entry
->Type
& GDI_ENTRY_BASETYPE_MASK
) != 0)
1324 POBJ Object
= Entry
->KernelData
;
1326 PrevThread
= Object
->Tid
;
1327 if (Object
->cExclusiveLock
== 0 || PrevThread
== Thread
)
1329 PEPROCESS OldProcess
;
1330 PPROCESSINFO W32Process
;
1333 /* dereference the process' object counter */
1335 if ((ULONG_PTR
)PrevProcId
& ~0x1)
1337 Status
= PsLookupProcessByProcessId((HANDLE
)((ULONG_PTR
)PrevProcId
& ~0x1), &OldProcess
);
1338 if (NT_SUCCESS(Status
))
1340 W32Process
= (PPROCESSINFO
)OldProcess
->Win32Process
;
1341 if (W32Process
!= NULL
)
1343 InterlockedDecrement(&W32Process
->GDIHandleCount
);
1345 ObDereferenceObject(OldProcess
);
1349 if (NewOwner
!= NULL
)
1351 ProcessId
= PsGetProcessId(NewOwner
);
1353 /* Increase the new process' object counter */
1354 W32Process
= (PPROCESSINFO
)NewOwner
->Win32Process
;
1355 if (W32Process
!= NULL
)
1357 InterlockedIncrement(&W32Process
->GDIHandleCount
);
1363 /* remove the process id lock and change it to the new process id */
1364 (void)InterlockedExchangePointer((PVOID
*)&Entry
->ProcessId
, ProcessId
);
1371 GDIDBG_TRACELOOP(ObjectHandle
, PrevThread
, Thread
);
1373 /* WTF?! The object is already locked by a different thread!
1374 Release the lock, wait a bit and try again! DO reset the pid lock
1375 so we make sure we don't access invalid memory in case the object is
1376 being deleted in the meantime (because we don't have aquired a reference
1378 FIXME - we should give up after some time unless we want to wait forever! */
1379 (void)InterlockedExchangePointer((PVOID
*)&Entry
->ProcessId
, PrevProcId
);
1387 DPRINT1("Attempted to change ownership of an object 0x%x currently being destroyed!!!\n", ObjectHandle
);
1388 DPRINT1("Entry->Type = 0x%lx, Entry->KernelData = 0x%p\n", Entry
->Type
, Entry
->KernelData
);
1392 else if (PrevProcId
== LockedProcessId
)
1394 GDIDBG_TRACELOOP(ObjectHandle
, PrevProcId
, ProcessId
);
1396 /* the object is currently locked, wait some time and try again.
1397 FIXME - we shouldn't loop forever! Give up after some time! */
1402 else if (((ULONG_PTR
)PrevProcId
& ~0x1) == 0)
1404 /* allow changing ownership of global objects */
1406 LockedProcessId
= (HANDLE
)((ULONG_PTR
)ProcessId
| 0x1);
1409 else if ((HANDLE
)((ULONG_PTR
)PrevProcId
& ~0x1) != PsGetCurrentProcessId())
1411 DPRINT1("Attempted to change ownership of object 0x%x (pid: 0x%x) from pid 0x%x!!!\n", ObjectHandle
, (ULONG_PTR
)PrevProcId
& ~0x1, PsGetCurrentProcessId());
1416 DPRINT1("Attempted to change owner of invalid handle: 0x%x\n", ObjectHandle
);
1424 GDIOBJ_CopyOwnership(HGDIOBJ CopyFrom
, HGDIOBJ CopyTo
)
1426 PGDI_TABLE_ENTRY FromEntry
;
1428 HANDLE FromProcessId
, FromLockedProcessId
, FromPrevProcId
;
1431 GDIDBG_INITLOOPTRACE();
1433 DPRINT("GDIOBJ_CopyOwnership: from: 0x%x, to: 0x%x\n", CopyFrom
, CopyTo
);
1435 Thread
= (PTHREADINFO
)PsGetCurrentThreadWin32Thread();
1437 if (!GDI_HANDLE_IS_STOCKOBJ(CopyFrom
) && !GDI_HANDLE_IS_STOCKOBJ(CopyTo
))
1439 FromEntry
= GDI_HANDLE_GET_ENTRY(GdiHandleTable
, CopyFrom
);
1441 FromProcessId
= (HANDLE
)((ULONG_PTR
)FromEntry
->ProcessId
& ~0x1);
1442 FromLockedProcessId
= (HANDLE
)((ULONG_PTR
)FromProcessId
| 0x1);
1445 /* lock the object, we must not convert stock objects, so don't check!!! */
1446 FromPrevProcId
= InterlockedCompareExchangePointer((PVOID
*)&FromEntry
->ProcessId
, FromProcessId
, FromLockedProcessId
);
1447 if (FromPrevProcId
== FromProcessId
)
1449 PTHREADINFO PrevThread
;
1452 if ((FromEntry
->Type
& GDI_ENTRY_BASETYPE_MASK
) != 0)
1454 Object
= FromEntry
->KernelData
;
1456 /* save the pointer to the calling thread so we know it was this thread
1457 that locked the object */
1458 PrevThread
= Object
->Tid
;
1459 if (Object
->cExclusiveLock
== 0 || PrevThread
== Thread
)
1461 /* now let's change the ownership of the target object */
1463 if (((ULONG_PTR
)FromPrevProcId
& ~0x1) != 0)
1465 PEPROCESS ProcessTo
;
1467 if (NT_SUCCESS(PsLookupProcessByProcessId((HANDLE
)((ULONG_PTR
)FromPrevProcId
& ~0x1), &ProcessTo
)))
1469 GDIOBJ_SetOwnership(CopyTo
, ProcessTo
);
1470 ObDereferenceObject(ProcessTo
);
1475 /* mark the object as global */
1476 GDIOBJ_SetOwnership(CopyTo
, NULL
);
1479 (void)InterlockedExchangePointer((PVOID
*)&FromEntry
->ProcessId
, FromPrevProcId
);
1483 GDIDBG_TRACELOOP(CopyFrom
, PrevThread
, Thread
);
1485 /* WTF?! The object is already locked by a different thread!
1486 Release the lock, wait a bit and try again! DO reset the pid lock
1487 so we make sure we don't access invalid memory in case the object is
1488 being deleted in the meantime (because we don't have aquired a reference
1490 FIXME - we should give up after some time unless we want to wait forever! */
1491 (void)InterlockedExchangePointer((PVOID
*)&FromEntry
->ProcessId
, FromPrevProcId
);
1494 goto LockHandleFrom
;
1499 DPRINT1("Attempted to copy ownership from an object 0x%x currently being destroyed!!!\n", CopyFrom
);
1503 else if (FromPrevProcId
== FromLockedProcessId
)
1505 GDIDBG_TRACELOOP(CopyFrom
, FromPrevProcId
, FromProcessId
);
1507 /* the object is currently locked, wait some time and try again.
1508 FIXME - we shouldn't loop forever! Give up after some time! */
1511 goto LockHandleFrom
;
1513 else if ((HANDLE
)((ULONG_PTR
)FromPrevProcId
& ~0x1) != PsGetCurrentProcessId())
1515 /* FIXME - should we really allow copying ownership from objects that we don't even own? */
1516 DPRINT1("WARNING! Changing copying ownership of object 0x%x (pid: 0x%x) to pid 0x%x!!!\n", CopyFrom
, (ULONG_PTR
)FromPrevProcId
& ~0x1, PsGetCurrentProcessId());
1517 FromProcessId
= (HANDLE
)((ULONG_PTR
)FromPrevProcId
& ~0x1);
1518 FromLockedProcessId
= (HANDLE
)((ULONG_PTR
)FromProcessId
| 0x1);
1519 goto LockHandleFrom
;
1523 DPRINT1("Attempted to copy ownership from invalid handle: 0x%x\n", CopyFrom
);
1531 GDI_MapHandleTable(PSECTION_OBJECT SectionObject
, PEPROCESS Process
)
1533 PVOID MappedView
= NULL
;
1535 LARGE_INTEGER Offset
;
1536 ULONG ViewSize
= sizeof(GDI_HANDLE_TABLE
);
1538 Offset
.QuadPart
= 0;
1540 ASSERT(SectionObject
!= NULL
);
1541 ASSERT(Process
!= NULL
);
1543 Status
= MmMapViewOfSection(SectionObject
,
1554 if (!NT_SUCCESS(Status
))
1560 /** PUBLIC FUNCTIONS **********************************************************/
1564 IntGdiSetBrushOwner(PBRUSH pbr
, DWORD OwnerMask
)
1567 PEPROCESS Owner
= NULL
;
1568 PGDI_TABLE_ENTRY pEntry
= NULL
;
1570 if (!pbr
) return FALSE
;
1572 hBR
= pbr
->BaseObject
.hHmgr
;
1574 if (!hBR
|| (GDI_HANDLE_GET_TYPE(hBR
) != GDI_OBJECT_TYPE_BRUSH
))
1578 INT Index
= GDI_HANDLE_GET_INDEX((HGDIOBJ
)hBR
);
1579 pEntry
= &GdiHandleTable
->Entries
[Index
];
1582 if (pbr
->flAttrs
& GDIBRUSH_IS_GLOBAL
)
1584 GDIOBJ_ShareUnlockObjByPtr((POBJ
)pbr
);
1588 if ((OwnerMask
== GDI_OBJ_HMGR_PUBLIC
) || OwnerMask
== GDI_OBJ_HMGR_NONE
)
1590 // Set this Brush to inaccessible mode and to an Owner of NONE.
1591 // if (OwnerMask == GDI_OBJ_HMGR_NONE) Owner = OwnerMask;
1593 if (!GDIOBJ_SetOwnership((HGDIOBJ
) hBR
, Owner
))
1596 // Deny user access to User Data.
1597 pEntry
->UserData
= NULL
; // This hBR is inaccessible!
1600 if (OwnerMask
== GDI_OBJ_HMGR_POWNED
)
1602 if (!GDIOBJ_SetOwnership((HGDIOBJ
) hBR
, PsGetCurrentProcess() ))
1605 // Allow user access to User Data.
1606 pEntry
->UserData
= pbr
->pBrushAttr
;
1614 IntGdiSetDCOwnerEx( HDC hDC
, DWORD OwnerMask
, BOOL NoSetBrush
)
1619 if (!hDC
|| (GDI_HANDLE_GET_TYPE(hDC
) != GDI_OBJECT_TYPE_DC
)) return FALSE
;
1621 if ((OwnerMask
== GDI_OBJ_HMGR_PUBLIC
) || OwnerMask
== GDI_OBJ_HMGR_NONE
)
1623 pDC
= DC_LockDc ( hDC
);
1624 MmCopyFromCaller(&pDC
->dcattr
, pDC
->pdcattr
, sizeof(DC_ATTR
));
1627 DC_FreeDcAttr( hDC
); // Free the dcattr!
1629 if (!DC_SetOwnership( hDC
, NULL
)) // This hDC is inaccessible!
1633 if (OwnerMask
== GDI_OBJ_HMGR_POWNED
)
1635 pDC
= DC_LockDc ( hDC
);
1636 ASSERT(pDC
->pdcattr
== &pDC
->dcattr
);
1639 if (!DC_SetOwnership( hDC
, PsGetCurrentProcess() )) return Ret
;
1641 DC_AllocateDcAttr( hDC
); // Allocate new dcattr
1643 DCU_SynchDcAttrtoUser( hDC
); // Copy data from dc to dcattr
1646 if ((OwnerMask
!= GDI_OBJ_HMGR_NONE
) && !NoSetBrush
)
1648 pDC
= DC_LockDc ( hDC
);
1649 if (IntGdiSetBrushOwner((PBRUSH
)pDC
->dclevel
.pbrFill
, OwnerMask
))
1650 IntGdiSetBrushOwner((PBRUSH
)pDC
->dclevel
.pbrLine
, OwnerMask
);
1658 GreGetObjectOwner(HGDIOBJ Handle
, GDIOBJTYPE ObjType
)
1660 INT Ret
= GDI_OBJ_HMGR_RESTRICTED
;
1662 if ( GDI_HANDLE_GET_INDEX(Handle
) < GDI_HANDLE_COUNT
)
1664 PGDI_TABLE_ENTRY pEntry
= &GdiHandleTable
->Entries
[GDI_HANDLE_GET_INDEX(Handle
)];
1666 if (pEntry
->ObjectType
== ObjType
)
1668 if (pEntry
->FullUnique
== (GDI_HANDLE_GET_UPPER(Handle
) >> GDI_ENTRY_UPPER_SHIFT
))
1669 Ret
= pEntry
->ProcessId
& ~1;
1679 NtGdiCreateClientObj(
1686 /* Mask out everything that would change the type in a wrong manner */
1687 ulType
&= (GDI_HANDLE_TYPE_MASK
& ~GDI_HANDLE_BASETYPE_MASK
);
1689 /* Allocate a new object */
1690 pObject
= GDIOBJ_AllocObjWithHandle(GDI_OBJECT_TYPE_CLIOBJ
| ulType
);
1696 /* get the handle */
1697 handle
= pObject
->hHmgr
;
1700 GDIOBJ_UnlockObjByPtr(pObject
);
1708 NtGdiDeleteClientObj(
1712 /* We first need to get the real type from the handle */
1713 ULONG type
= GDI_HANDLE_GET_TYPE(h
);
1715 /* Check if it's really a CLIENTOBJ */
1716 if ((type
& GDI_HANDLE_BASETYPE_MASK
) != GDILoObjType_LO_CLIENTOBJ_TYPE
)
1718 /* FIXME: SetLastError? */
1721 return GDIOBJ_FreeObjByHandle(h
, type
);
1726 IntGdiGetObject(IN HANDLE Handle
,
1734 pGdiObject
= GDIOBJ_LockObj(Handle
, GDI_OBJECT_TYPE_DONTCARE
);
1737 SetLastWin32Error(ERROR_INVALID_HANDLE
);
1741 dwObjectType
= GDIOBJ_GetObjectType(Handle
);
1742 switch (dwObjectType
)
1744 case GDI_OBJECT_TYPE_PEN
:
1745 case GDI_OBJECT_TYPE_EXTPEN
:
1746 Result
= PEN_GetObject((PBRUSH
) pGdiObject
, cbCount
, (PLOGPEN
) lpBuffer
); // IntGdiCreatePenIndirect
1749 case GDI_OBJECT_TYPE_BRUSH
:
1750 Result
= BRUSH_GetObject((PBRUSH
) pGdiObject
, cbCount
, (LPLOGBRUSH
)lpBuffer
);
1753 case GDI_OBJECT_TYPE_BITMAP
:
1754 Result
= BITMAP_GetObject((SURFACE
*) pGdiObject
, cbCount
, lpBuffer
);
1756 case GDI_OBJECT_TYPE_FONT
:
1757 Result
= FontGetObject((PTEXTOBJ
) pGdiObject
, cbCount
, lpBuffer
);
1759 // Fix the LOGFONT structure for the stock fonts
1760 if (FIRST_STOCK_HANDLE
<= Handle
&& Handle
<= LAST_STOCK_HANDLE
)
1762 FixStockFontSizeW(Handle
, cbCount
, lpBuffer
);
1767 case GDI_OBJECT_TYPE_PALETTE
:
1768 Result
= PALETTE_GetObject((PPALETTE
) pGdiObject
, cbCount
, lpBuffer
);
1772 DPRINT1("GDI object type 0x%08x not implemented\n", dwObjectType
);
1776 GDIOBJ_UnlockObjByPtr(pGdiObject
);
1786 NtGdiExtGetObjectW(IN HANDLE hGdiObj
,
1788 OUT LPVOID lpBuffer
)
1795 DIBSECTION dibsection
;
1799 EXTLOGFONTW extlogfontw
;
1800 ENUMLOGFONTEXDVW enumlogfontexdvw
;
1803 // Normalize to the largest supported object size
1804 cbCount
= min((UINT
)cbCount
, sizeof(Object
));
1806 // Now do the actual call
1807 iRetCount
= IntGdiGetObject(hGdiObj
, cbCount
, lpBuffer
? &Object
: NULL
);
1808 cbCopyCount
= min((UINT
)cbCount
, (UINT
)iRetCount
);
1810 // Make sure we have a buffer and a copy size
1811 if ((cbCopyCount
) && (lpBuffer
))
1813 // Enter SEH for buffer transfer
1816 // Probe the buffer and copy it
1817 ProbeForWrite(lpBuffer
, cbCopyCount
, sizeof(WORD
));
1818 RtlCopyMemory(lpBuffer
, &Object
, cbCopyCount
);
1820 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1822 // Clear the return value.
1823 // Do *NOT* set last error here!