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 ******************************************************************/
16 /* FIXME include right header for KeRosDumpStackFrames */
17 VOID NTAPI
KeRosDumpStackFrames(PULONG
, ULONG
);
22 BOOLEAN STDCALL
KiRosPrintAddress(PVOID Address
);
23 NTSYSAPI ULONG NTAPI
RtlWalkFrameChain(OUT PVOID
*Callers
, IN ULONG Count
, IN ULONG Flags
);
26 #define GDI_ENTRY_TO_INDEX(ht, e) \
27 (((ULONG_PTR)(e) - (ULONG_PTR)&((ht)->Entries[0])) / sizeof(GDI_TABLE_ENTRY))
28 #define GDI_HANDLE_GET_ENTRY(HandleTable, h) \
29 (&(HandleTable)->Entries[GDI_HANDLE_GET_INDEX((h))])
31 /* apparently the first 10 entries are never used in windows as they are empty */
32 #define RESERVE_ENTRIES_COUNT 10
34 #define DelayExecution() \
35 DPRINT("%s:%i: Delay\n", __FILE__, __LINE__); \
36 KeDelayExecutionThread(KernelMode, FALSE, &ShortDelay)
38 static BOOL INTERNAL_CALL
GDI_CleanupDummy(PVOID ObjectBody
);
40 /** GLOBALS *******************************************************************/
47 GDICLEANUPPROC CleanupProc
;
48 } OBJ_TYPE_INFO
, *POBJ_TYPE_INFO
;
51 OBJ_TYPE_INFO ObjTypeInfo
[] =
53 {0, 0, 0, NULL
}, /* 00 reserved entry */
54 {1, sizeof(DC
), TAG_DC
, DC_Cleanup
}, /* 01 DC */
55 {1, 0, 0, NULL
}, /* 02 UNUSED1 */
56 {1, 0, 0, NULL
}, /* 03 UNUSED2 */
57 {1, sizeof(ROSRGNDATA
), TAG_REGION
, REGION_Cleanup
}, /* 04 RGN */
58 {1, sizeof(BITMAPOBJ
), TAG_SURFACE
, BITMAP_Cleanup
}, /* 05 SURFACE */
59 {1, sizeof(CLIENTOBJ
), TAG_CLIENTOBJ
, GDI_CleanupDummy
}, /* 06 CLIENTOBJ: METADC,... */
60 {0, 0, TAG_PATH
, NULL
}, /* 07 PATH, unused */
61 {1, sizeof(PALGDI
), TAG_PALETTE
, PALETTE_Cleanup
}, /* 08 PAL */
62 {1, sizeof(COLORSPACE
), TAG_ICMLCS
, GDI_CleanupDummy
}, /* 09 ICMLCS, */
63 {1, sizeof(TEXTOBJ
), TAG_LFONT
, GDI_CleanupDummy
}, /* 0a LFONT */
64 {0, 0, TAG_RFONT
, NULL
}, /* 0b RFONT, unused */
65 {0, 0, TAG_PFE
, NULL
}, /* 0c PFE, unused */
66 {0, 0, TAG_PFT
, NULL
}, /* 0d PFT, unused */
67 {0, sizeof(GDICLRXFORM
), TAG_ICMCXF
, GDI_CleanupDummy
}, /* 0e ICMCXF, */
68 {0, 0, TAG_SPRITE
, NULL
}, /* 0f SPRITE, unused */
69 {1, sizeof(GDIBRUSHOBJ
), TAG_BRUSH
, BRUSH_Cleanup
}, /* 10 BRUSH, PEN, EXTPEN */
70 {0, 0, TAG_UMPD
, NULL
}, /* 11 UMPD, unused */
71 {0, 0, 0, NULL
}, /* 12 UNUSED4 */
72 {0, 0, TAG_SPACE
, NULL
}, /* 13 SPACE, unused */
73 {0, 0, 0, NULL
}, /* 14 UNUSED5 */
74 {0, 0, TAG_META
, NULL
}, /* 15 META, unused */
75 {0, 0, TAG_EFSTATE
, NULL
}, /* 16 EFSTATE, unused */
76 {0, 0, TAG_BMFD
, NULL
}, /* 17 BMFD, unused */
77 {0, 0, TAG_VTFD
, NULL
}, /* 18 VTFD, unused */
78 {0, 0, TAG_TTFD
, NULL
}, /* 19 TTFD, unused */
79 {0, 0, TAG_RC
, NULL
}, /* 1a RC, unused */
80 {0, 0, TAG_TEMP
, NULL
}, /* 1b TEMP, unused */
81 {0, 0, TAG_DRVOBJ
, NULL
}, /* 1c DRVOBJ, unused */
82 {0, 0, TAG_DCIOBJ
, NULL
}, /* 1d DCIOBJ, unused */
83 {0, 0, TAG_SPOOL
, NULL
}, /* 1e SPOOL, unused */
86 #define BASE_OBJTYPE_COUNT (sizeof(ObjTypeInfo) / sizeof(ObjTypeInfo[0]))
88 static LARGE_INTEGER ShortDelay
;
90 /** DEBUGGING *****************************************************************/
94 static int leak_reported
= 0;
95 #define GDI_STACK_LEVELS 12
96 static ULONG GDIHandleAllocator
[GDI_HANDLE_COUNT
][GDI_STACK_LEVELS
+1];
97 static ULONG GDIHandleLocker
[GDI_HANDLE_COUNT
][GDI_STACK_LEVELS
+1];
98 struct DbgOpenGDIHandle
104 static struct DbgOpenGDIHandle h
[H
];
106 void IntDumpHandleTable(PGDI_HANDLE_TABLE HandleTable
)
108 int i
, n
= 0, j
, k
, J
;
112 DPRINT1("gdi handle abusers already reported!\n");
117 DPRINT1("reporting gdi handle abusers:\n");
119 /* step through GDI handle table and find out who our culprit is... */
120 for (i
= RESERVE_ENTRIES_COUNT
; i
< GDI_HANDLE_COUNT
; i
++)
122 for (j
= 0; j
< n
; j
++)
126 for (k
= 0; k
< GDI_STACK_LEVELS
; k
++)
128 if (GDIHandleAllocator
[i
][k
]
129 != GDIHandleAllocator
[J
][k
])
152 /* bubble sort time! weeeeee!! */
153 for (i
= 0; i
< n
-1; i
++)
155 if (h
[i
].count
< h
[i
+1].count
)
157 struct DbgOpenGDIHandle t
;
161 while (j
> 0 && h
[j
-1].count
< t
.count
)
166 /* print the worst offenders... */
167 DbgPrint("Worst GDI Handle leak offenders (out of %i unique locations):\n", n
);
168 for (i
= 0; i
< n
&& h
[i
].count
> 1; i
++)
171 DbgPrint(" %i allocs: ", h
[i
].count
);
172 for (j
= 0; j
< GDI_STACK_LEVELS
; j
++)
174 ULONG Addr
= GDIHandleAllocator
[h
[i
].idx
][j
];
175 if (!KiRosPrintAddress((PVOID
)Addr
))
176 DbgPrint("<%X>", Addr
);
180 if (i
< n
&& h
[i
].count
== 1)
181 DbgPrint("(list terminated - the remaining entries have 1 allocation only)\n");
185 CaptureStackBackTace(PVOID
* pFrames
, ULONG nFramesToCapture
)
189 memset(pFrames
, 0x00, (nFramesToCapture
+ 1) * sizeof(PVOID
));
191 nFrameCount
= RtlCaptureStackBackTrace(1, nFramesToCapture
, pFrames
, NULL
);
193 if (nFrameCount
< nFramesToCapture
)
195 nFrameCount
+= RtlWalkFrameChain(pFrames
+ nFrameCount
, nFramesToCapture
- nFrameCount
, 1);
201 #define GDIDBG_TRACECALLER() \
202 DPRINT1("-> called from:\n"); \
203 KeRosDumpStackFrames(NULL, 20);
204 #define GDIDBG_TRACEALLOCATOR(index) \
205 DPRINT1("-> allocated from:\n"); \
206 KeRosDumpStackFrames(GDIHandleAllocator[index], GDI_STACK_LEVELS);
207 #define GDIDBG_TRACELOCKER(index) \
208 DPRINT1("-> locked from:\n"); \
209 KeRosDumpStackFrames(GDIHandleLocker[index], GDI_STACK_LEVELS);
210 #define GDIDBG_CAPTUREALLOCATOR(index) \
211 CaptureStackBackTace((PVOID*)GDIHandleAllocator[index], GDI_STACK_LEVELS);
212 #define GDIDBG_CAPTURELOCKER(index) \
213 CaptureStackBackTace((PVOID*)GDIHandleLocker[index], GDI_STACK_LEVELS);
214 #define GDIDBG_DUMPHANDLETABLE() \
215 IntDumpHandleTable(GdiHandleTable)
216 #define GDIDBG_INITLOOPTRACE() \
218 #define GDIDBG_TRACELOOP(Handle, PrevThread, Thread) \
219 if ((++Attempts % 20) == 0) \
221 DPRINT1("[%d] Handle 0x%p Locked by 0x%x (we're 0x%x)\n", Attempts, Handle, PrevThread, Thread); \
226 #define GDIDBG_TRACECALLER()
227 #define GDIDBG_TRACEALLOCATOR(index)
228 #define GDIDBG_TRACELOCKER(index)
229 #define GDIDBG_CAPTUREALLOCATOR(index)
230 #define GDIDBG_CAPTURELOCKER(index)
231 #define GDIDBG_DUMPHANDLETABLE()
232 #define GDIDBG_INITLOOPTRACE()
233 #define GDIDBG_TRACELOOP(Handle, PrevThread, Thread)
235 #endif /* GDI_DEBUG */
238 /** INTERNAL FUNCTIONS ********************************************************/
241 * Dummy GDI Cleanup Callback
243 static BOOL INTERNAL_CALL
244 GDI_CleanupDummy(PVOID ObjectBody
)
250 * Allocate GDI object table.
251 * \param Size - number of entries in the object table.
253 PGDI_HANDLE_TABLE INTERNAL_CALL
254 GDIOBJ_iAllocHandleTable(OUT PSECTION_OBJECT
*SectionObject
)
256 PGDI_HANDLE_TABLE HandleTable
= NULL
;
257 LARGE_INTEGER htSize
;
262 ASSERT(SectionObject
!= NULL
);
264 htSize
.QuadPart
= sizeof(GDI_HANDLE_TABLE
);
266 Status
= MmCreateSection((PVOID
*)SectionObject
,
274 if (!NT_SUCCESS(Status
))
277 /* FIXME - use MmMapViewInSessionSpace once available! */
278 Status
= MmMapViewInSystemSpace(*SectionObject
,
279 (PVOID
*)&HandleTable
,
281 if (!NT_SUCCESS(Status
))
283 ObDereferenceObject(*SectionObject
);
284 *SectionObject
= NULL
;
288 RtlZeroMemory(HandleTable
, sizeof(GDI_HANDLE_TABLE
));
290 HandleTable
->LookasideLists
= ExAllocatePoolWithTag(NonPagedPool
,
291 BASE_OBJTYPE_COUNT
* sizeof(PAGED_LOOKASIDE_LIST
),
293 if (HandleTable
->LookasideLists
== NULL
)
295 MmUnmapViewInSystemSpace(HandleTable
);
296 ObDereferenceObject(*SectionObject
);
297 *SectionObject
= NULL
;
301 for (ObjType
= 0; ObjType
< BASE_OBJTYPE_COUNT
; ObjType
++)
303 if (ObjTypeInfo
[ObjType
].bUseLookaside
)
305 ExInitializePagedLookasideList(HandleTable
->LookasideLists
+ ObjType
,
309 ObjTypeInfo
[ObjType
].ulBodySize
,
310 ObjTypeInfo
[ObjType
].Tag
,
315 ShortDelay
.QuadPart
= -5000LL; /* FIXME - 0.5 ms? */
317 HandleTable
->FirstFree
= 0;
318 HandleTable
->FirstUnused
= RESERVE_ENTRIES_COUNT
;
324 LockErrorDebugOutput(HGDIOBJ hObj
, PGDI_TABLE_ENTRY Entry
, LPSTR Function
)
326 if ((Entry
->Type
& GDI_ENTRY_BASETYPE_MASK
) == 0)
328 DPRINT1("%s: Attempted to lock object 0x%x that is deleted!\n", Function
, hObj
);
330 else if (GDI_HANDLE_GET_REUSECNT(hObj
) != GDI_ENTRY_GET_REUSECNT(Entry
->Type
))
332 DPRINT1("%s: Attempted to lock object 0x%x, wrong reuse counter (Handle: 0x%x, Entry: 0x%x)\n",
333 Function
, hObj
, GDI_HANDLE_GET_REUSECNT(hObj
), GDI_ENTRY_GET_REUSECNT(Entry
->Type
));
335 else if (GDI_HANDLE_GET_TYPE(hObj
) != ((Entry
->Type
<< GDI_ENTRY_UPPER_SHIFT
) & GDI_HANDLE_TYPE_MASK
))
337 DPRINT1("%s: Attempted to lock object 0x%x, type mismatch (Handle: 0x%x, Entry: 0x%x)\n",
338 Function
, hObj
, GDI_HANDLE_GET_TYPE(hObj
), (Entry
->Type
<< GDI_ENTRY_UPPER_SHIFT
) & GDI_HANDLE_TYPE_MASK
);
342 DPRINT1("%s: Attempted to lock object 0x%x, something went wrong, typeinfo = 0x%x\n",
343 Function
, hObj
, Entry
->Type
);
345 GDIDBG_TRACECALLER();
350 InterlockedPopFreeEntry()
352 ULONG idxFirstFree
, idxNextFree
, idxPrev
;
353 PGDI_TABLE_ENTRY pFreeEntry
;
355 DPRINT("Enter InterLockedPopFreeEntry\n");
359 idxFirstFree
= GdiHandleTable
->FirstFree
;
362 pFreeEntry
= GdiHandleTable
->Entries
+ idxFirstFree
;
363 ASSERT(((ULONG
)pFreeEntry
->KernelData
& ~GDI_HANDLE_INDEX_MASK
) == 0);
364 idxNextFree
= (ULONG
)pFreeEntry
->KernelData
;
365 idxPrev
= (ULONG
)_InterlockedCompareExchange((LONG
*)&GdiHandleTable
->FirstFree
, idxNextFree
, idxFirstFree
);
369 idxFirstFree
= GdiHandleTable
->FirstUnused
;
370 idxNextFree
= idxFirstFree
+ 1;
371 if (idxNextFree
>= GDI_HANDLE_COUNT
)
373 DPRINT1("No more gdi handles left!\n");
376 idxPrev
= (ULONG
)_InterlockedCompareExchange((LONG
*)&GdiHandleTable
->FirstUnused
, idxNextFree
, idxFirstFree
);
379 while (idxPrev
!= idxFirstFree
);
384 /* Pushes an entry of the handle table to the free list,
385 The entry must be unlocked and the base type field must be 0 */
388 InterlockedPushFreeEntry(ULONG idxToFree
)
390 ULONG idxFirstFree
, idxPrev
;
391 PGDI_TABLE_ENTRY pFreeEntry
;
393 DPRINT("Enter InterlockedPushFreeEntry\n");
395 pFreeEntry
= GdiHandleTable
->Entries
+ idxToFree
;
396 ASSERT((pFreeEntry
->Type
& GDI_ENTRY_BASETYPE_MASK
) == 0);
397 ASSERT(pFreeEntry
->ProcessId
== 0);
398 pFreeEntry
->UserData
= NULL
;
402 idxFirstFree
= GdiHandleTable
->FirstFree
;
403 pFreeEntry
->KernelData
= (PVOID
)idxFirstFree
;
405 idxPrev
= (ULONG
)_InterlockedCompareExchange((LONG
*)&GdiHandleTable
->FirstFree
, idxToFree
, idxFirstFree
);
407 while (idxPrev
!= idxFirstFree
);
413 GDIOBJ_ValidateHandle(HGDIOBJ hObj
, ULONG ObjectType
)
415 PGDI_TABLE_ENTRY Entry
= GDI_HANDLE_GET_ENTRY(GdiHandleTable
, hObj
);
416 if ((((ULONG_PTR
)hObj
& GDI_HANDLE_TYPE_MASK
) == ObjectType
) &&
417 (Entry
->Type
<< GDI_ENTRY_UPPER_SHIFT
) == GDI_HANDLE_GET_UPPER(hObj
))
419 HANDLE pid
= (HANDLE
)((ULONG_PTR
)Entry
->ProcessId
& ~0x1);
420 if (pid
== NULL
|| pid
== PsGetCurrentProcessId())
429 GDIOBJ_AllocObj(UCHAR BaseType
)
433 ASSERT((BaseType
& ~GDIObjTypeTotal
) == 0);
434 // BaseType &= GDI_HANDLE_BASETYPE_MASK;
436 if (ObjTypeInfo
[BaseType
].bUseLookaside
)
438 PPAGED_LOOKASIDE_LIST LookasideList
;
440 LookasideList
= GdiHandleTable
->LookasideLists
+ BaseType
;
441 pObject
= ExAllocateFromPagedLookasideList(LookasideList
);
445 pObject
= ExAllocatePoolWithTag(PagedPool
,
446 ObjTypeInfo
[BaseType
].ulBodySize
,
447 ObjTypeInfo
[BaseType
].Tag
);
452 RtlZeroMemory(pObject
, ObjTypeInfo
[BaseType
].ulBodySize
);
460 * Allocate memory for GDI object and return handle to it.
462 * \param ObjectType - type of object \ref GDI object types
464 * \return Pointer to the allocated object, which is locked.
467 GDIOBJ_AllocObjWithHandle(ULONG ObjectType
)
469 PW32PROCESS W32Process
;
470 POBJ newObject
= NULL
;
471 HANDLE CurrentProcessId
, LockedProcessId
;
474 GDIDBG_INITLOOPTRACE();
476 W32Process
= PsGetCurrentProcessWin32Process();
477 /* HACK HACK HACK: simplest-possible quota implementation - don't allow a process
478 to take too many GDI objects, itself. */
479 if (W32Process
&& W32Process
->GDIObjects
>= 0x2710)
482 ASSERT(ObjectType
!= GDI_OBJECT_TYPE_DONTCARE
);
484 TypeIndex
= GDI_OBJECT_GET_TYPE_INDEX(ObjectType
);
486 newObject
= GDIOBJ_AllocObj(TypeIndex
);
489 DPRINT1("Not enough memory to allocate gdi object!\n");
494 PGDI_TABLE_ENTRY Entry
;
497 CurrentProcessId
= PsGetCurrentProcessId();
498 LockedProcessId
= (HANDLE
)((ULONG_PTR
)CurrentProcessId
| 0x1);
500 // RtlZeroMemory(newObject, ObjTypeInfo[TypeIndex].ulBodySize);
502 /* On Windows the higher 16 bit of the type field don't contain the
503 full type from the handle, but the base type.
504 (type = BRSUH, PEN, EXTPEN, basetype = BRUSH) */
505 TypeInfo
= (ObjectType
& GDI_HANDLE_BASETYPE_MASK
) | (ObjectType
>> GDI_ENTRY_UPPER_SHIFT
);
507 Index
= InterlockedPopFreeEntry();
512 Entry
= &GdiHandleTable
->Entries
[Index
];
515 PrevProcId
= _InterlockedCompareExchangePointer((PVOID
*)&Entry
->ProcessId
, LockedProcessId
, 0);
516 if (PrevProcId
== NULL
)
518 PW32THREAD Thread
= PsGetCurrentThreadWin32Thread();
521 Entry
->KernelData
= newObject
;
523 /* copy the reuse-counter */
524 TypeInfo
|= Entry
->Type
& GDI_ENTRY_REUSE_MASK
;
526 /* we found a free entry, no need to exchange this field atomically
527 since we're holding the lock */
528 Entry
->Type
= TypeInfo
;
530 /* Create a handle */
531 Handle
= (HGDIOBJ
)((Index
& 0xFFFF) | (TypeInfo
<< GDI_ENTRY_UPPER_SHIFT
));
533 /* Initialize BaseObject fields */
534 newObject
->hHmgr
= Handle
;
535 newObject
->ulShareCount
= 0;
536 newObject
->cExclusiveLock
= 1;
537 newObject
->Tid
= Thread
;
539 /* unlock the entry */
540 (void)_InterlockedExchangePointer((PVOID
*)&Entry
->ProcessId
, CurrentProcessId
);
542 GDIDBG_CAPTUREALLOCATOR(Index
);
544 if (W32Process
!= NULL
)
546 _InterlockedIncrement(&W32Process
->GDIObjects
);
549 DPRINT("GDIOBJ_AllocObj: 0x%x ob: 0x%x\n", Handle
, newObject
);
554 GDIDBG_TRACELOOP(Index
, PrevProcId
, CurrentProcessId
);
555 /* damn, someone is trying to lock the object even though it doesn't
556 even exist anymore, wait a little and try again!
557 FIXME - we shouldn't loop forever! Give up after some time! */
564 GDIOBJ_FreeObj(newObject
, TypeIndex
);
566 DPRINT1("Failed to insert gdi object into the handle table, no handles left!\n");
567 GDIDBG_DUMPHANDLETABLE();
574 GDIOBJ_FreeObj(POBJ pObject
, UCHAR BaseType
)
576 /* Object must not have a handle! */
577 ASSERT(pObject
->hHmgr
== NULL
);
579 if (ObjTypeInfo
[BaseType
].bUseLookaside
)
581 PPAGED_LOOKASIDE_LIST LookasideList
;
583 LookasideList
= GdiHandleTable
->LookasideLists
+ BaseType
;
584 ExFreeToPagedLookasideList(LookasideList
, pObject
);
593 * Free memory allocated for the GDI object. For each object type this function calls the
594 * appropriate cleanup routine.
596 * \param hObj - handle of the object to be deleted.
598 * \return Returns TRUE if succesful.
599 * \return Returns FALSE if the cleanup routine returned FALSE or the object doesn't belong
600 * to the calling process.
603 GDIOBJ_FreeObjByHandle(HGDIOBJ hObj
, DWORD ExpectedType
)
605 PGDI_TABLE_ENTRY Entry
;
606 HANDLE ProcessId
, LockedProcessId
, PrevProcId
;
607 ULONG HandleType
, HandleUpper
, TypeIndex
;
610 GDIDBG_INITLOOPTRACE();
612 DPRINT("GDIOBJ_FreeObj: hObj: 0x%08x\n", hObj
);
614 if (GDI_HANDLE_IS_STOCKOBJ(hObj
))
616 DPRINT1("GDIOBJ_FreeObj() failed, can't delete stock object handle: 0x%x !!!\n", hObj
);
617 GDIDBG_TRACECALLER();
621 ProcessId
= PsGetCurrentProcessId();
622 LockedProcessId
= (HANDLE
)((ULONG_PTR
)ProcessId
| 0x1);
624 Silent
= (ExpectedType
& GDI_OBJECT_TYPE_SILENT
);
625 ExpectedType
&= ~GDI_OBJECT_TYPE_SILENT
;
627 HandleType
= GDI_HANDLE_GET_TYPE(hObj
);
628 HandleUpper
= GDI_HANDLE_GET_UPPER(hObj
);
630 /* Check if we have the requested type */
631 if ( (ExpectedType
!= GDI_OBJECT_TYPE_DONTCARE
&&
632 HandleType
!= ExpectedType
) ||
635 DPRINT1("Attempted to free object 0x%x of wrong type (Handle: 0x%x, expected: 0x%x)\n",
636 hObj
, HandleType
, ExpectedType
);
637 GDIDBG_TRACECALLER();
641 Entry
= GDI_HANDLE_GET_ENTRY(GdiHandleTable
, hObj
);
644 /* lock the object, we must not delete global objects, so don't exchange the locking
645 process ID to zero when attempting to lock a global object... */
646 PrevProcId
= _InterlockedCompareExchangePointer((PVOID
*)&Entry
->ProcessId
, LockedProcessId
, ProcessId
);
647 if (PrevProcId
== ProcessId
)
649 if ( (Entry
->KernelData
!= NULL
) &&
650 ((Entry
->Type
<< GDI_ENTRY_UPPER_SHIFT
) == HandleUpper
) &&
651 ((Entry
->Type
& GDI_ENTRY_BASETYPE_MASK
) == (HandleUpper
& GDI_ENTRY_BASETYPE_MASK
)) )
655 Object
= Entry
->KernelData
;
657 if (Object
->cExclusiveLock
== 0)
660 PW32PROCESS W32Process
= PsGetCurrentProcessWin32Process();
662 /* Clear the basetype field so when unlocking the handle it gets finally deleted and increment reuse counter */
663 Entry
->Type
= (Entry
->Type
+ GDI_ENTRY_REUSE_INC
) & ~GDI_ENTRY_BASETYPE_MASK
;
665 /* unlock the handle slot */
666 (void)_InterlockedExchangePointer((PVOID
*)&Entry
->ProcessId
, NULL
);
668 /* push this entry to the free list */
669 InterlockedPushFreeEntry(GDI_ENTRY_TO_INDEX(GdiHandleTable
, Entry
));
671 Object
->hHmgr
= NULL
;
673 if (W32Process
!= NULL
)
675 _InterlockedDecrement(&W32Process
->GDIObjects
);
678 /* call the cleanup routine. */
679 TypeIndex
= GDI_OBJECT_GET_TYPE_INDEX(HandleType
);
680 Ret
= ObjTypeInfo
[TypeIndex
].CleanupProc(Object
);
682 /* Now it's time to free the memory */
683 GDIOBJ_FreeObj(Object
, TypeIndex
);
690 * The object is currently locked, so freeing is forbidden!
692 DPRINT1("Object->cExclusiveLock = %d\n", Object
->cExclusiveLock
);
693 GDIDBG_TRACECALLER();
694 GDIDBG_TRACELOCKER(GDI_HANDLE_GET_INDEX(hObj
));
695 /* do not assert here for it will call again from dxg.sys it being call twice */
701 LockErrorDebugOutput(hObj
, Entry
, "GDIOBJ_FreeObj");
702 (void)_InterlockedExchangePointer((PVOID
*)&Entry
->ProcessId
, PrevProcId
);
705 else if (PrevProcId
== LockedProcessId
)
707 GDIDBG_TRACELOOP(hObj
, PrevProcId
, ProcessId
);
709 /* the object is currently locked, wait some time and try again.
710 FIXME - we shouldn't loop forever! Give up after some time! */
719 if (((ULONG_PTR
)PrevProcId
& ~0x1) == 0)
721 DPRINT1("Attempted to free global gdi handle 0x%x, caller needs to get ownership first!!!\n", hObj
);
722 DPRINT1("Type = 0x%lx, KernelData = 0x%p, ProcessId = 0x%p\n", Entry
->Type
, Entry
->KernelData
, Entry
->ProcessId
);
726 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);
728 GDIDBG_TRACECALLER();
729 GDIDBG_TRACEALLOCATOR(GDI_HANDLE_GET_INDEX(hObj
));
738 IsObjectDead(HGDIOBJ hObject
)
740 INT Index
= GDI_HANDLE_GET_INDEX(hObject
);
741 PGDI_TABLE_ENTRY Entry
= &GdiHandleTable
->Entries
[Index
];
742 // We check to see if the objects are knocking on deaths door.
743 if ((Entry
->Type
& ~GDI_ENTRY_REUSE_MASK
) != 0 && Entry
->KernelData
!= NULL
)
747 DPRINT1("Object 0x%x currently being destroyed!!!\n",hObject
);
748 return TRUE
; // return true and move on.
755 * \param hObject object handle
756 * \return if the function fails the returned value is FALSE.
760 NtGdiDeleteObject(HGDIOBJ hObject
)
762 DPRINT("NtGdiDeleteObject handle 0x%08x\n", hObject
);
763 if (!IsObjectDead(hObject
))
765 return NULL
!= hObject
766 ? GDIOBJ_FreeObjByHandle(hObject
, GDI_OBJECT_TYPE_DONTCARE
) : FALSE
;
770 DPRINT1("Attempt DeleteObject 0x%x currently being destroyed!!!\n",hObject
);
771 return TRUE
; // return true and move on.
776 * Internal function. Called when the process is destroyed to free the remaining GDI handles.
777 * \param Process - PID of the process that will be destroyed.
780 GDI_CleanupForProcess(struct _EPROCESS
*Process
)
782 PGDI_TABLE_ENTRY Entry
, End
;
783 PEPROCESS CurrentProcess
;
784 PW32PROCESS W32Process
;
786 ULONG Index
= RESERVE_ENTRIES_COUNT
;
788 DPRINT("Starting CleanupForProcess prochandle %x Pid %d\n", Process
, Process
->UniqueProcessId
);
789 CurrentProcess
= PsGetCurrentProcess();
790 if (CurrentProcess
!= Process
)
792 KeAttachProcess(&Process
->Pcb
);
794 W32Process
= (PW32PROCESS
)Process
->Win32Process
;
797 if (W32Process
->GDIObjects
> 0)
799 /* FIXME - Instead of building the handle here and delete it using GDIOBJ_FreeObj
800 we should delete it directly here! */
801 ProcId
= Process
->UniqueProcessId
;
803 End
= &GdiHandleTable
->Entries
[GDI_HANDLE_COUNT
];
804 for (Entry
= &GdiHandleTable
->Entries
[RESERVE_ENTRIES_COUNT
];
808 /* ignore the lock bit */
809 if ( (HANDLE
)((ULONG_PTR
)Entry
->ProcessId
& ~0x1) == ProcId
&&
810 (Entry
->Type
& ~GDI_ENTRY_REUSE_MASK
) != 0 )
812 HGDIOBJ ObjectHandle
;
814 /* Create the object handle for the entry, the lower(!) 16 bit of the
815 Type field includes the type of the object including the stock
816 object flag - but since stock objects don't have a process id we can
817 simply ignore this fact here. */
818 ObjectHandle
= (HGDIOBJ
)(Index
| (Entry
->Type
<< GDI_ENTRY_UPPER_SHIFT
));
820 if (GDIOBJ_FreeObjByHandle(ObjectHandle
, GDI_OBJECT_TYPE_DONTCARE
) &&
821 W32Process
->GDIObjects
== 0)
823 /* there are no more gdi handles for this process, bail */
830 if (CurrentProcess
!= Process
)
835 DPRINT("Completed cleanup for process %d\n", Process
->UniqueProcessId
);
841 * Return pointer to the object by handle.
843 * \param hObj Object handle
844 * \return Pointer to the object.
846 * \note Process can only get pointer to the objects it created or global objects.
848 * \todo Get rid of the ExpectedType parameter!
850 PGDIOBJ INTERNAL_CALL
851 GDIOBJ_LockObj(HGDIOBJ hObj
, DWORD ExpectedType
)
854 PGDI_TABLE_ENTRY Entry
;
855 HANDLE ProcessId
, HandleProcessId
, LockedProcessId
, PrevProcId
;
857 ULONG HandleType
, HandleUpper
;
859 HandleIndex
= GDI_HANDLE_GET_INDEX(hObj
);
860 HandleType
= GDI_HANDLE_GET_TYPE(hObj
);
861 HandleUpper
= GDI_HANDLE_GET_UPPER(hObj
);
863 /* Check that the handle index is valid. */
864 if (HandleIndex
>= GDI_HANDLE_COUNT
)
867 Entry
= &GdiHandleTable
->Entries
[HandleIndex
];
869 /* Check if we have the requested type */
870 if ( (ExpectedType
!= GDI_OBJECT_TYPE_DONTCARE
&&
871 HandleType
!= ExpectedType
) ||
874 DPRINT1("Attempted to lock object 0x%x of wrong type (Handle: 0x%x, requested: 0x%x)\n",
875 hObj
, HandleType
, ExpectedType
);
876 GDIDBG_TRACECALLER();
877 GDIDBG_TRACEALLOCATOR(GDI_HANDLE_GET_INDEX(hObj
));
881 ProcessId
= (HANDLE
)((ULONG_PTR
)PsGetCurrentProcessId() & ~1);
882 HandleProcessId
= (HANDLE
)((ULONG_PTR
)Entry
->ProcessId
& ~1);
884 /* Check for invalid owner. */
885 if (ProcessId
!= HandleProcessId
&& HandleProcessId
!= NULL
)
887 DPRINT1("Tried to lock object (0x%p) of wrong owner! ProcessId = %p, HandleProcessId = %p\n", hObj
, ProcessId
, HandleProcessId
);
888 GDIDBG_TRACECALLER();
889 GDIDBG_TRACEALLOCATOR(GDI_HANDLE_GET_INDEX(hObj
));
894 * Prevent the thread from being terminated during the locking process.
895 * It would result in undesired effects and inconsistency of the global
899 KeEnterCriticalRegion();
902 * Loop until we either successfully lock the handle entry & object or
903 * fail some of the check.
908 /* Lock the handle table entry. */
909 LockedProcessId
= (HANDLE
)((ULONG_PTR
)HandleProcessId
| 0x1);
910 PrevProcId
= _InterlockedCompareExchangePointer((PVOID
*)&Entry
->ProcessId
,
914 if (PrevProcId
== HandleProcessId
)
917 * We're locking an object that belongs to our process or it's a
918 * global object if HandleProcessId is 0 here.
921 if ( (Entry
->KernelData
!= NULL
) &&
922 ((Entry
->Type
<< GDI_ENTRY_UPPER_SHIFT
) == HandleUpper
) )
924 PW32THREAD Thread
= PsGetCurrentThreadWin32Thread();
925 Object
= Entry
->KernelData
;
927 if (Object
->cExclusiveLock
== 0)
929 Object
->Tid
= Thread
;
930 Object
->cExclusiveLock
= 1;
931 GDIDBG_CAPTURELOCKER(GDI_HANDLE_GET_INDEX(hObj
))
935 if (Object
->Tid
!= Thread
)
937 /* Unlock the handle table entry. */
938 (void)_InterlockedExchangePointer((PVOID
*)&Entry
->ProcessId
, PrevProcId
);
943 _InterlockedIncrement((PLONG
)&Object
->cExclusiveLock
);
949 * Debugging code. Report attempts to lock deleted handles and
950 * locking type mismatches.
952 LockErrorDebugOutput(hObj
, Entry
, "GDIOBJ_LockObj");
955 /* Unlock the handle table entry. */
956 (void)_InterlockedExchangePointer((PVOID
*)&Entry
->ProcessId
, PrevProcId
);
963 * The handle is currently locked, wait some time and try again.
971 KeLeaveCriticalRegion();
978 * Return pointer to the object by handle (and allow sharing of the handle
981 * \param hObj Object handle
982 * \return Pointer to the object.
984 * \note Process can only get pointer to the objects it created or global objects.
986 * \todo Get rid of the ExpectedType parameter!
988 PGDIOBJ INTERNAL_CALL
989 GDIOBJ_ShareLockObj(HGDIOBJ hObj
, DWORD ExpectedType
)
992 PGDI_TABLE_ENTRY Entry
;
993 HANDLE ProcessId
, HandleProcessId
, LockedProcessId
, PrevProcId
;
995 ULONG_PTR HandleType
, HandleUpper
;
997 HandleIndex
= GDI_HANDLE_GET_INDEX(hObj
);
998 HandleType
= GDI_HANDLE_GET_TYPE(hObj
);
999 HandleUpper
= GDI_HANDLE_GET_UPPER(hObj
);
1001 /* Check that the handle index is valid. */
1002 if (HandleIndex
>= GDI_HANDLE_COUNT
)
1005 /* Check if we have the requested type */
1006 if ( (ExpectedType
!= GDI_OBJECT_TYPE_DONTCARE
&&
1007 HandleType
!= ExpectedType
) ||
1010 DPRINT1("Attempted to lock object 0x%x of wrong type (Handle: 0x%x, requested: 0x%x)\n",
1011 hObj
, HandleType
, ExpectedType
);
1015 Entry
= &GdiHandleTable
->Entries
[HandleIndex
];
1017 ProcessId
= (HANDLE
)((ULONG_PTR
)PsGetCurrentProcessId() & ~1);
1018 HandleProcessId
= (HANDLE
)((ULONG_PTR
)Entry
->ProcessId
& ~1);
1020 /* Check for invalid owner. */
1021 if (ProcessId
!= HandleProcessId
&& HandleProcessId
!= NULL
)
1027 * Prevent the thread from being terminated during the locking process.
1028 * It would result in undesired effects and inconsistency of the global
1032 KeEnterCriticalRegion();
1035 * Loop until we either successfully lock the handle entry & object or
1036 * fail some of the check.
1041 /* Lock the handle table entry. */
1042 LockedProcessId
= (HANDLE
)((ULONG_PTR
)HandleProcessId
| 0x1);
1043 PrevProcId
= _InterlockedCompareExchangePointer((PVOID
*)&Entry
->ProcessId
,
1047 if (PrevProcId
== HandleProcessId
)
1050 * We're locking an object that belongs to our process or it's a
1051 * global object if HandleProcessId is 0 here.
1054 if ( (Entry
->KernelData
!= NULL
) &&
1055 (HandleUpper
== (Entry
->Type
<< GDI_ENTRY_UPPER_SHIFT
)) )
1057 Object
= (POBJ
)Entry
->KernelData
;
1060 if (_InterlockedIncrement((PLONG
)&Object
->ulShareCount
) == 1)
1062 memset(GDIHandleLocker
[HandleIndex
], 0x00, GDI_STACK_LEVELS
* sizeof(ULONG
));
1063 RtlCaptureStackBackTrace(1, GDI_STACK_LEVELS
, (PVOID
*)GDIHandleLocker
[HandleIndex
], NULL
);
1066 _InterlockedIncrement((PLONG
)&Object
->ulShareCount
);
1072 * Debugging code. Report attempts to lock deleted handles and
1073 * locking type mismatches.
1075 LockErrorDebugOutput(hObj
, Entry
, "GDIOBJ_ShareLockObj");
1078 /* Unlock the handle table entry. */
1079 (void)_InterlockedExchangePointer((PVOID
*)&Entry
->ProcessId
, PrevProcId
);
1086 * The handle is currently locked, wait some time and try again.
1094 KeLeaveCriticalRegion();
1101 * Release GDI object. Every object locked by GDIOBJ_LockObj() must be unlocked. You should unlock the object
1102 * as soon as you don't need to have access to it's data.
1104 * \param Object Object pointer (as returned by GDIOBJ_LockObj).
1107 GDIOBJ_UnlockObjByPtr(POBJ Object
)
1109 if (_InterlockedDecrement((PLONG
)&Object
->cExclusiveLock
) < 0)
1111 DPRINT1("Trying to unlock non-existant object\n");
1116 GDIOBJ_ShareUnlockObjByPtr(POBJ Object
)
1118 if (_InterlockedDecrement((PLONG
)&Object
->ulShareCount
) < 0)
1120 DPRINT1("Trying to unlock non-existant object\n");
1125 GDIOBJ_OwnedByCurrentProcess(HGDIOBJ ObjectHandle
)
1127 PGDI_TABLE_ENTRY Entry
;
1131 DPRINT("GDIOBJ_OwnedByCurrentProcess: ObjectHandle: 0x%08x\n", ObjectHandle
);
1133 if (!GDI_HANDLE_IS_STOCKOBJ(ObjectHandle
))
1135 ProcessId
= PsGetCurrentProcessId();
1137 Entry
= GDI_HANDLE_GET_ENTRY(GdiHandleTable
, ObjectHandle
);
1138 Ret
= Entry
->KernelData
!= NULL
&&
1139 (Entry
->Type
& ~GDI_ENTRY_REUSE_MASK
) != 0 &&
1140 (HANDLE
)((ULONG_PTR
)Entry
->ProcessId
& ~0x1) == ProcessId
;
1149 GDIOBJ_ConvertToStockObj(HGDIOBJ
*phObj
)
1152 * FIXME !!!!! THIS FUNCTION NEEDS TO BE FIXED - IT IS NOT SAFE WHEN OTHER THREADS
1153 * MIGHT ATTEMPT TO LOCK THE OBJECT DURING THIS CALL!!!
1155 PGDI_TABLE_ENTRY Entry
;
1156 HANDLE ProcessId
, LockedProcessId
, PrevProcId
;
1160 GDIDBG_INITLOOPTRACE();
1165 DPRINT("GDIOBJ_ConvertToStockObj: hObj: 0x%08x\n", hObj
);
1167 Thread
= PsGetCurrentThreadWin32Thread();
1169 if (!GDI_HANDLE_IS_STOCKOBJ(hObj
))
1171 ProcessId
= PsGetCurrentProcessId();
1172 LockedProcessId
= (HANDLE
)((ULONG_PTR
)ProcessId
| 0x1);
1174 Entry
= GDI_HANDLE_GET_ENTRY(GdiHandleTable
, hObj
);
1177 /* lock the object, we must not convert stock objects, so don't check!!! */
1178 PrevProcId
= _InterlockedCompareExchangePointer((PVOID
*)&Entry
->ProcessId
, LockedProcessId
, ProcessId
);
1179 if (PrevProcId
== ProcessId
)
1181 LONG NewType
, PrevType
, OldType
;
1183 /* we're locking an object that belongs to our process. First calculate
1184 the new object type including the stock object flag and then try to
1186 /* On Windows the higher 16 bit of the type field don't contain the
1187 full type from the handle, but the base type.
1188 (type = BRSUH, PEN, EXTPEN, basetype = BRUSH) */
1189 OldType
= ((ULONG
)hObj
& GDI_HANDLE_BASETYPE_MASK
) | ((ULONG
)hObj
>> GDI_ENTRY_UPPER_SHIFT
);
1190 /* We are currently not using bits 24..31 (flags) of the type field, but for compatibility
1191 we copy them as we can't get them from the handle */
1192 OldType
|= Entry
->Type
& GDI_ENTRY_FLAGS_MASK
;
1194 /* As the object should be a stock object, set it's flag, but only in the lower 16 bits */
1195 NewType
= OldType
| GDI_ENTRY_STOCK_MASK
;
1197 /* Try to exchange the type field - but only if the old (previous type) matches! */
1198 PrevType
= _InterlockedCompareExchange(&Entry
->Type
, NewType
, OldType
);
1199 if (PrevType
== OldType
&& Entry
->KernelData
!= NULL
)
1201 PW32THREAD PrevThread
;
1204 /* We successfully set the stock object flag.
1205 KernelData should never be NULL here!!! */
1206 ASSERT(Entry
->KernelData
);
1208 Object
= Entry
->KernelData
;
1210 PrevThread
= Object
->Tid
;
1211 if (Object
->cExclusiveLock
== 0 || PrevThread
== Thread
)
1213 /* dereference the process' object counter */
1214 if (PrevProcId
!= GDI_GLOBAL_PROCESS
)
1216 PEPROCESS OldProcess
;
1217 PW32PROCESS W32Process
;
1221 Status
= PsLookupProcessByProcessId((HANDLE
)((ULONG_PTR
)PrevProcId
& ~0x1), &OldProcess
);
1222 if (NT_SUCCESS(Status
))
1224 W32Process
= (PW32PROCESS
)OldProcess
->Win32Process
;
1225 if (W32Process
!= NULL
)
1227 _InterlockedDecrement(&W32Process
->GDIObjects
);
1229 ObDereferenceObject(OldProcess
);
1233 /* remove the process id lock and make it global */
1234 (void)_InterlockedExchangePointer((PVOID
*)&Entry
->ProcessId
, GDI_GLOBAL_PROCESS
);
1236 hObj
= (HGDIOBJ
)((ULONG
)(hObj
) | GDI_HANDLE_STOCK_MASK
);
1239 /* we're done, successfully converted the object */
1244 GDIDBG_TRACELOOP(hObj
, PrevThread
, Thread
);
1246 /* WTF?! The object is already locked by a different thread!
1247 Release the lock, wait a bit and try again!
1248 FIXME - we should give up after some time unless we want to wait forever! */
1249 (void)_InterlockedExchangePointer((PVOID
*)&Entry
->ProcessId
, PrevProcId
);
1257 DPRINT1("Attempted to convert object 0x%x that is deleted! Should never get here!!!\n", hObj
);
1258 DPRINT1("OldType = 0x%x, Entry->Type = 0x%x, NewType = 0x%x, Entry->KernelData = 0x%x\n", OldType
, Entry
->Type
, NewType
, Entry
->KernelData
);
1261 else if (PrevProcId
== LockedProcessId
)
1263 GDIDBG_TRACELOOP(hObj
, PrevProcId
, ProcessId
);
1265 /* the object is currently locked, wait some time and try again.
1266 FIXME - we shouldn't loop forever! Give up after some time! */
1273 DPRINT1("Attempted to convert invalid handle: 0x%x\n", hObj
);
1281 GDIOBJ_SetOwnership(HGDIOBJ ObjectHandle
, PEPROCESS NewOwner
)
1283 PGDI_TABLE_ENTRY Entry
;
1284 HANDLE ProcessId
, LockedProcessId
, PrevProcId
;
1287 GDIDBG_INITLOOPTRACE();
1289 DPRINT("GDIOBJ_SetOwnership: hObj: 0x%x, NewProcess: 0x%x\n", ObjectHandle
, (NewOwner
? PsGetProcessId(NewOwner
) : 0));
1291 Thread
= PsGetCurrentThreadWin32Thread();
1293 if (!GDI_HANDLE_IS_STOCKOBJ(ObjectHandle
))
1295 ProcessId
= PsGetCurrentProcessId();
1296 LockedProcessId
= (HANDLE
)((ULONG_PTR
)ProcessId
| 0x1);
1298 Entry
= GDI_HANDLE_GET_ENTRY(GdiHandleTable
, ObjectHandle
);
1301 /* lock the object, we must not convert stock objects, so don't check!!! */
1302 PrevProcId
= _InterlockedCompareExchangePointer((PVOID
*)&Entry
->ProcessId
, ProcessId
, LockedProcessId
);
1303 if (PrevProcId
== ProcessId
)
1305 PW32THREAD PrevThread
;
1307 if ((Entry
->Type
& ~GDI_ENTRY_REUSE_MASK
) != 0 && Entry
->KernelData
!= NULL
)
1309 POBJ Object
= Entry
->KernelData
;
1311 PrevThread
= Object
->Tid
;
1312 if (Object
->cExclusiveLock
== 0 || PrevThread
== Thread
)
1314 PEPROCESS OldProcess
;
1315 PW32PROCESS W32Process
;
1318 /* dereference the process' object counter */
1320 if ((ULONG_PTR
)PrevProcId
& ~0x1)
1322 Status
= PsLookupProcessByProcessId((HANDLE
)((ULONG_PTR
)PrevProcId
& ~0x1), &OldProcess
);
1323 if (NT_SUCCESS(Status
))
1325 W32Process
= (PW32PROCESS
)OldProcess
->Win32Process
;
1326 if (W32Process
!= NULL
)
1328 _InterlockedDecrement(&W32Process
->GDIObjects
);
1330 ObDereferenceObject(OldProcess
);
1334 if (NewOwner
!= NULL
)
1336 ProcessId
= PsGetProcessId(NewOwner
);
1338 /* Increase the new process' object counter */
1339 W32Process
= (PW32PROCESS
)NewOwner
->Win32Process
;
1340 if (W32Process
!= NULL
)
1342 _InterlockedIncrement(&W32Process
->GDIObjects
);
1348 /* remove the process id lock and change it to the new process id */
1349 (void)_InterlockedExchangePointer((PVOID
*)&Entry
->ProcessId
, ProcessId
);
1356 GDIDBG_TRACELOOP(ObjectHandle
, PrevThread
, Thread
);
1358 /* WTF?! The object is already locked by a different thread!
1359 Release the lock, wait a bit and try again! DO reset the pid lock
1360 so we make sure we don't access invalid memory in case the object is
1361 being deleted in the meantime (because we don't have aquired a reference
1363 FIXME - we should give up after some time unless we want to wait forever! */
1364 (void)_InterlockedExchangePointer((PVOID
*)&Entry
->ProcessId
, PrevProcId
);
1372 DPRINT1("Attempted to change ownership of an object 0x%x currently being destroyed!!!\n", ObjectHandle
);
1373 DPRINT1("Entry->Type = 0x%lx, Entry->KernelData = 0x%p\n", Entry
->Type
, Entry
->KernelData
);
1376 else if (PrevProcId
== LockedProcessId
)
1378 GDIDBG_TRACELOOP(ObjectHandle
, PrevProcId
, ProcessId
);
1380 /* the object is currently locked, wait some time and try again.
1381 FIXME - we shouldn't loop forever! Give up after some time! */
1386 else if (((ULONG_PTR
)PrevProcId
& ~0x1) == 0)
1388 /* allow changing ownership of global objects */
1390 LockedProcessId
= (HANDLE
)((ULONG_PTR
)ProcessId
| 0x1);
1393 else if ((HANDLE
)((ULONG_PTR
)PrevProcId
& ~0x1) != PsGetCurrentProcessId())
1395 DPRINT1("Attempted to change ownership of object 0x%x (pid: 0x%x) from pid 0x%x!!!\n", ObjectHandle
, (ULONG_PTR
)PrevProcId
& ~0x1, PsGetCurrentProcessId());
1399 DPRINT1("Attempted to change owner of invalid handle: 0x%x\n", ObjectHandle
);
1405 GDIOBJ_CopyOwnership(HGDIOBJ CopyFrom
, HGDIOBJ CopyTo
)
1407 PGDI_TABLE_ENTRY FromEntry
;
1409 HANDLE FromProcessId
, FromLockedProcessId
, FromPrevProcId
;
1411 GDIDBG_INITLOOPTRACE();
1413 DPRINT("GDIOBJ_CopyOwnership: from: 0x%x, to: 0x%x\n", CopyFrom
, CopyTo
);
1415 Thread
= PsGetCurrentThreadWin32Thread();
1417 if (!GDI_HANDLE_IS_STOCKOBJ(CopyFrom
) && !GDI_HANDLE_IS_STOCKOBJ(CopyTo
))
1419 FromEntry
= GDI_HANDLE_GET_ENTRY(GdiHandleTable
, CopyFrom
);
1421 FromProcessId
= (HANDLE
)((ULONG_PTR
)FromEntry
->ProcessId
& ~0x1);
1422 FromLockedProcessId
= (HANDLE
)((ULONG_PTR
)FromProcessId
| 0x1);
1425 /* lock the object, we must not convert stock objects, so don't check!!! */
1426 FromPrevProcId
= _InterlockedCompareExchangePointer((PVOID
*)&FromEntry
->ProcessId
, FromProcessId
, FromLockedProcessId
);
1427 if (FromPrevProcId
== FromProcessId
)
1429 PW32THREAD PrevThread
;
1432 if ((FromEntry
->Type
& ~GDI_ENTRY_REUSE_MASK
) != 0 && FromEntry
->KernelData
!= NULL
)
1434 Object
= FromEntry
->KernelData
;
1436 /* save the pointer to the calling thread so we know it was this thread
1437 that locked the object */
1438 PrevThread
= Object
->Tid
;
1439 if (Object
->cExclusiveLock
== 0 || PrevThread
== Thread
)
1441 /* now let's change the ownership of the target object */
1443 if (((ULONG_PTR
)FromPrevProcId
& ~0x1) != 0)
1445 PEPROCESS ProcessTo
;
1447 if (NT_SUCCESS(PsLookupProcessByProcessId((HANDLE
)((ULONG_PTR
)FromPrevProcId
& ~0x1), &ProcessTo
)))
1449 GDIOBJ_SetOwnership(CopyTo
, ProcessTo
);
1450 ObDereferenceObject(ProcessTo
);
1455 /* mark the object as global */
1456 GDIOBJ_SetOwnership(CopyTo
, NULL
);
1459 (void)_InterlockedExchangePointer((PVOID
*)&FromEntry
->ProcessId
, FromPrevProcId
);
1463 GDIDBG_TRACELOOP(CopyFrom
, PrevThread
, Thread
);
1465 /* WTF?! The object is already locked by a different thread!
1466 Release the lock, wait a bit and try again! DO reset the pid lock
1467 so we make sure we don't access invalid memory in case the object is
1468 being deleted in the meantime (because we don't have aquired a reference
1470 FIXME - we should give up after some time unless we want to wait forever! */
1471 (void)_InterlockedExchangePointer((PVOID
*)&FromEntry
->ProcessId
, FromPrevProcId
);
1474 goto LockHandleFrom
;
1479 DPRINT1("Attempted to copy ownership from an object 0x%x currently being destroyed!!!\n", CopyFrom
);
1482 else if (FromPrevProcId
== FromLockedProcessId
)
1484 GDIDBG_TRACELOOP(CopyFrom
, FromPrevProcId
, FromProcessId
);
1486 /* the object is currently locked, wait some time and try again.
1487 FIXME - we shouldn't loop forever! Give up after some time! */
1490 goto LockHandleFrom
;
1492 else if ((HANDLE
)((ULONG_PTR
)FromPrevProcId
& ~0x1) != PsGetCurrentProcessId())
1494 /* FIXME - should we really allow copying ownership from objects that we don't even own? */
1495 DPRINT1("WARNING! Changing copying ownership of object 0x%x (pid: 0x%x) to pid 0x%x!!!\n", CopyFrom
, (ULONG_PTR
)FromPrevProcId
& ~0x1, PsGetCurrentProcessId());
1496 FromProcessId
= (HANDLE
)((ULONG_PTR
)FromPrevProcId
& ~0x1);
1497 FromLockedProcessId
= (HANDLE
)((ULONG_PTR
)FromProcessId
| 0x1);
1498 goto LockHandleFrom
;
1502 DPRINT1("Attempted to copy ownership from invalid handle: 0x%x\n", CopyFrom
);
1508 GDI_MapHandleTable(PSECTION_OBJECT SectionObject
, PEPROCESS Process
)
1510 PVOID MappedView
= NULL
;
1512 LARGE_INTEGER Offset
;
1513 ULONG ViewSize
= sizeof(GDI_HANDLE_TABLE
);
1515 Offset
.QuadPart
= 0;
1517 ASSERT(SectionObject
!= NULL
);
1518 ASSERT(Process
!= NULL
);
1520 Status
= MmMapViewOfSection(SectionObject
,
1531 if (!NT_SUCCESS(Status
))
1537 /** PUBLIC FUNCTIONS **********************************************************/
1542 NtGdiCreateClientObj(
1546 // ATM we use DC object for KernelData. This is wrong.
1547 // The real type consists of BASEOBJECT and a pointer.
1548 // The UserData is set in user mode, so it is always NULL.
1549 // HANDLE should be HGDIOBJ
1554 /* Mask out everything that would change the type in a wrong manner */
1555 ulType
&= (GDI_HANDLE_TYPE_MASK
& ~GDI_HANDLE_BASETYPE_MASK
);
1557 /* Allocate a new object */
1558 pObject
= GDIOBJ_AllocObjWithHandle(GDI_OBJECT_TYPE_CLIOBJ
| ulType
);
1564 /* get the handle */
1565 handle
= pObject
->hHmgr
;
1568 GDIOBJ_UnlockObjByPtr(pObject
);
1576 NtGdiDeleteClientObj(
1580 /* We first need to get the real type from the handle */
1581 ULONG type
= GDI_HANDLE_GET_TYPE(h
);
1583 /* Check if it's really a CLIENTOBJ */
1584 if ((type
& GDI_HANDLE_BASETYPE_MASK
) != GDILoObjType_LO_CLIENTOBJ_TYPE
)
1586 /* FIXME: SetLastError? */
1589 return GDIOBJ_FreeObjByHandle(h
, type
);
1594 IntGdiGetObject(IN HANDLE Handle
,
1602 pGdiObject
= GDIOBJ_LockObj(Handle
, GDI_OBJECT_TYPE_DONTCARE
);
1605 SetLastWin32Error(ERROR_INVALID_HANDLE
);
1609 dwObjectType
= GDIOBJ_GetObjectType(Handle
);
1610 switch (dwObjectType
)
1612 case GDI_OBJECT_TYPE_PEN
:
1613 case GDI_OBJECT_TYPE_EXTPEN
:
1614 Result
= PEN_GetObject((PGDIBRUSHOBJ
) pGdiObject
, cbCount
, (PLOGPEN
) lpBuffer
); // IntGdiCreatePenIndirect
1617 case GDI_OBJECT_TYPE_BRUSH
:
1618 Result
= BRUSH_GetObject((PGDIBRUSHOBJ
) pGdiObject
, cbCount
, (LPLOGBRUSH
)lpBuffer
);
1621 case GDI_OBJECT_TYPE_BITMAP
:
1622 Result
= BITMAP_GetObject((BITMAPOBJ
*) pGdiObject
, cbCount
, lpBuffer
);
1624 case GDI_OBJECT_TYPE_FONT
:
1625 Result
= FontGetObject((PTEXTOBJ
) pGdiObject
, cbCount
, lpBuffer
);
1627 // Fix the LOGFONT structure for the stock fonts
1628 if (FIRST_STOCK_HANDLE
<= Handle
&& Handle
<= LAST_STOCK_HANDLE
)
1630 FixStockFontSizeW(Handle
, cbCount
, lpBuffer
);
1635 case GDI_OBJECT_TYPE_PALETTE
:
1636 Result
= PALETTE_GetObject((PPALGDI
) pGdiObject
, cbCount
, lpBuffer
);
1640 DPRINT1("GDI object type 0x%08x not implemented\n", dwObjectType
);
1644 GDIOBJ_UnlockObjByPtr(pGdiObject
);
1652 IntGdiSetDCOwnerEx( HGDIOBJ hObject
, DWORD OwnerMask
, BOOL NoSetBrush
)
1661 NtGdiExtGetObjectW(IN HANDLE hGdiObj
,
1663 OUT LPVOID lpBuffer
)
1670 DIBSECTION dibsection
;
1674 EXTLOGFONTW extlogfontw
;
1675 ENUMLOGFONTEXDVW enumlogfontexdvw
;
1678 // Normalize to the largest supported object size
1679 cbCount
= min((UINT
)cbCount
, sizeof(Object
));
1681 // Now do the actual call
1682 iRetCount
= IntGdiGetObject(hGdiObj
, cbCount
, lpBuffer
? &Object
: NULL
);
1683 cbCopyCount
= min((UINT
)cbCount
, (UINT
)iRetCount
);
1685 // Make sure we have a buffer and a copy size
1686 if ((cbCopyCount
) && (lpBuffer
))
1688 // Enter SEH for buffer transfer
1691 // Probe the buffer and copy it
1692 ProbeForWrite(lpBuffer
, cbCopyCount
, sizeof(WORD
));
1693 RtlCopyMemory(lpBuffer
, &Object
, cbCopyCount
);
1697 // Clear the return value.
1698 // Do *NOT* set last error here!