2 * ReactOS W32 Subsystem
3 * Copyright (C) 1998 - 2004 ReactOS Team
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 * GDIOBJ.C - GDI object manipulation routines
30 /* FIXME include right header for KeRosDumpStackFrames */
31 VOID NTAPI
KeRosDumpStackFrames(PULONG
, ULONG
);
34 BOOLEAN STDCALL
KiRosPrintAddress(PVOID Address
);
35 NTSYSAPI ULONG NTAPI
RtlWalkFrameChain(OUT PVOID
*Callers
, IN ULONG Count
, IN ULONG Flags
);
38 #define GDI_ENTRY_TO_INDEX(ht, e) \
39 (((ULONG_PTR)(e) - (ULONG_PTR)&((ht)->Entries[0])) / sizeof(GDI_TABLE_ENTRY))
40 #define GDI_HANDLE_GET_ENTRY(HandleTable, h) \
41 (&(HandleTable)->Entries[GDI_HANDLE_GET_INDEX((h))])
43 #define GDIBdyToHdr(body) \
44 ((PGDIOBJHDR)(body) - 1)
45 #define GDIHdrToBdy(hdr) \
46 (PGDIOBJ)((PGDIOBJHDR)(hdr) + 1)
48 /* apparently the first 10 entries are never used in windows as they are empty */
49 #define RESERVE_ENTRIES_COUNT 10
52 * Dummy GDI Cleanup Callback
54 static BOOL INTERNAL_CALL
55 GDI_CleanupDummy(PVOID ObjectBody
)
65 GDICLEANUPPROC CleanupProc
;
66 } OBJ_TYPE_INFO
, *POBJ_TYPE_INFO
;
69 OBJ_TYPE_INFO ObjTypeInfo
[] =
71 {0, 0, 0, NULL
}, /* 00 reserved entry */
72 {1, sizeof(DC
), TAG_DC
, DC_Cleanup
}, /* 01 DC */
73 {1, 0, 0, NULL
}, /* 02 UNUSED1 */
74 {1, 0, 0, NULL
}, /* 03 UNUSED2 */
75 {1, sizeof(ROSRGNDATA
), TAG_REGION
, REGION_Cleanup
}, /* 04 RGN */
76 {1, sizeof(BITMAPOBJ
), TAG_SURFACE
, BITMAP_Cleanup
}, /* 05 SURFACE */
77 {0, sizeof(DC
), TAG_CLIENTOBJ
, GDI_CleanupDummy
}, /* 06 CLIENTOBJ: METADC,... FIXME: don't use DC struct */
78 {0, 0, TAG_PATH
, NULL
}, /* 07 PATH, unused */
79 {1, sizeof(PALGDI
), TAG_PALETTE
, PALETTE_Cleanup
}, /* 08 PAL */
80 {0, 0, TAG_ICMLCS
, NULL
}, /* 09 ICMLCS, unused */
81 {1, sizeof(TEXTOBJ
), TAG_LFONT
, GDI_CleanupDummy
}, /* 0a LFONT */
82 {0, 0, TAG_RFONT
, NULL
}, /* 0b RFONT, unused */
83 {0, 0, TAG_PFE
, NULL
}, /* 0c PFE, unused */
84 {0, 0, TAG_PFT
, NULL
}, /* 0d PFT, unused */
85 {0, 0, TAG_ICMCXF
, NULL
}, /* 0e ICMCXF, unused */
86 {0, 0, TAG_SPRITE
, NULL
}, /* 0f SPRITE, unused */
87 {1, sizeof(GDIBRUSHOBJ
), TAG_BRUSH
, BRUSH_Cleanup
}, /* 10 BRUSH, PEN, EXTPEN */
88 {0, 0, TAG_UMPD
, NULL
}, /* 11 UMPD, unused */
89 {0, 0, 0, NULL
}, /* 12 UNUSED4 */
90 {0, 0, TAG_SPACE
, NULL
}, /* 13 SPACE, unused */
91 {0, 0, 0, NULL
}, /* 14 UNUSED5 */
92 {0, 0, TAG_META
, NULL
}, /* 15 META, unused */
93 {0, 0, TAG_EFSTATE
, NULL
}, /* 16 EFSTATE, unused */
94 {0, 0, TAG_BMFD
, NULL
}, /* 17 BMFD, unused */
95 {0, 0, TAG_TTFD
, NULL
}, /* 18 VTFD, unused */
96 {0, 0, TAG_TTFD
, NULL
}, /* 19 TTFD, unused */
97 {0, 0, TAG_RC
, NULL
}, /* 1a RC, unused */
98 {0, 0, TAG_TEMP
, NULL
}, /* 1b TEMP, unused */
99 {0, 0, TAG_DRVOBJ
, NULL
}, /* 1c DRVOBJ, unused */
100 {0, 0, TAG_DCIOBJ
, NULL
}, /* 1d DCIOBJ, unused */
101 {0, 0, TAG_SPOOL
, NULL
}, /* 1e SPOOL, unused */
104 #define BASE_OBJTYPE_COUNT (sizeof(ObjTypeInfo) / sizeof(ObjTypeInfo[0]))
106 static LARGE_INTEGER ShortDelay
;
108 #define DelayExecution() \
109 DPRINT("%s:%i: Delay\n", __FILE__, __LINE__); \
110 KeDelayExecutionThread(KernelMode, FALSE, &ShortDelay)
113 * Allocate GDI object table.
114 * \param Size - number of entries in the object table.
116 PGDI_HANDLE_TABLE INTERNAL_CALL
117 GDIOBJ_iAllocHandleTable(OUT PSECTION_OBJECT
*SectionObject
)
119 PGDI_HANDLE_TABLE HandleTable
= NULL
;
120 LARGE_INTEGER htSize
;
125 ASSERT(SectionObject
!= NULL
);
127 htSize
.QuadPart
= sizeof(GDI_HANDLE_TABLE
);
129 Status
= MmCreateSection((PVOID
*)SectionObject
,
137 if (!NT_SUCCESS(Status
))
140 /* FIXME - use MmMapViewInSessionSpace once available! */
141 Status
= MmMapViewInSystemSpace(*SectionObject
,
142 (PVOID
*)&HandleTable
,
144 if (!NT_SUCCESS(Status
))
146 ObDereferenceObject(*SectionObject
);
147 *SectionObject
= NULL
;
151 RtlZeroMemory(HandleTable
, sizeof(GDI_HANDLE_TABLE
));
153 HandleTable
->LookasideLists
= ExAllocatePoolWithTag(NonPagedPool
,
154 BASE_OBJTYPE_COUNT
* sizeof(PAGED_LOOKASIDE_LIST
),
156 if (HandleTable
->LookasideLists
== NULL
)
158 MmUnmapViewInSystemSpace(HandleTable
);
159 ObDereferenceObject(*SectionObject
);
160 *SectionObject
= NULL
;
164 for (ObjType
= 0; ObjType
< BASE_OBJTYPE_COUNT
; ObjType
++)
166 if (ObjTypeInfo
[ObjType
].bUseLookaside
)
168 ExInitializePagedLookasideList(HandleTable
->LookasideLists
+ ObjType
,
172 ObjTypeInfo
[ObjType
].ulBodySize
,
173 ObjTypeInfo
[ObjType
].Tag
,
178 ShortDelay
.QuadPart
= -5000LL; /* FIXME - 0.5 ms? */
180 HandleTable
->FirstFree
= 0;
181 HandleTable
->FirstUnused
= RESERVE_ENTRIES_COUNT
;
186 static __inline PPAGED_LOOKASIDE_LIST
187 FindLookasideList(ULONG TypeIndex
)
189 return GdiHandleTable
->LookasideLists
+ TypeIndex
;
193 RunCleanupCallback(PGDIOBJ pObj
, ULONG TypeIndex
)
195 return ((GDICLEANUPPROC
)ObjTypeInfo
[TypeIndex
].CleanupProc
)(pObj
);
198 static __inline ULONG
199 GetObjectSize(ULONG TypeIndex
)
201 return ObjTypeInfo
[TypeIndex
].ulBodySize
;
206 static int leak_reported
= 0;
207 #define GDI_STACK_LEVELS 12
208 static ULONG GDIHandleAllocator
[GDI_HANDLE_COUNT
][GDI_STACK_LEVELS
+1];
209 static ULONG GDIHandleLocker
[GDI_HANDLE_COUNT
][GDI_STACK_LEVELS
+1];
210 struct DbgOpenGDIHandle
216 static struct DbgOpenGDIHandle h
[H
];
218 void IntDumpHandleTable(PGDI_HANDLE_TABLE HandleTable
)
220 int i
, n
= 0, j
, k
, J
;
224 DPRINT1("gdi handle abusers already reported!\n");
229 DPRINT1("reporting gdi handle abusers:\n");
231 /* step through GDI handle table and find out who our culprit is... */
232 for (i
= RESERVE_ENTRIES_COUNT
; i
< GDI_HANDLE_COUNT
; i
++)
234 for (j
= 0; j
< n
; j
++)
238 for (k
= 0; k
< GDI_STACK_LEVELS
; k
++)
240 if (GDIHandleAllocator
[i
][k
]
241 != GDIHandleAllocator
[J
][k
])
264 /* bubble sort time! weeeeee!! */
265 for (i
= 0; i
< n
-1; i
++)
267 if (h
[i
].count
< h
[i
+1].count
)
269 struct DbgOpenGDIHandle t
;
273 while (j
> 0 && h
[j
-1].count
< t
.count
)
278 /* print the worst offenders... */
279 DbgPrint("Worst GDI Handle leak offenders (out of %i unique locations):\n", n
);
280 for (i
= 0; i
< n
&& h
[i
].count
> 1; i
++)
283 DbgPrint(" %i allocs: ", h
[i
].count
);
284 for (j
= 0; j
< GDI_STACK_LEVELS
; j
++)
286 ULONG Addr
= GDIHandleAllocator
[h
[i
].idx
][j
];
287 if (!KiRosPrintAddress((PVOID
)Addr
))
288 DbgPrint("<%X>", Addr
);
292 if (i
< n
&& h
[i
].count
== 1)
293 DbgPrint("(list terminated - the remaining entries have 1 allocation only)\n");
297 CaptureStackBackTace(PVOID
* pFrames
, ULONG nFramesToCapture
)
301 memset(pFrames
, 0x00, (nFramesToCapture
+ 1) * sizeof(PVOID
));
303 nFrameCount
= RtlCaptureStackBackTrace(1, nFramesToCapture
, pFrames
, NULL
);
305 if (nFrameCount
< nFramesToCapture
)
307 nFrameCount
+= RtlWalkFrameChain(pFrames
+ nFrameCount
, nFramesToCapture
- nFrameCount
, 1);
313 #define GDIDBG_TRACECALLER() \
314 DPRINT1("-> called from:\n"); \
315 KeRosDumpStackFrames(NULL, 20);
316 #define GDIDBG_TRACEALLOCATOR(index) \
317 DPRINT1("-> allocated from:\n"); \
318 KeRosDumpStackFrames(GDIHandleAllocator[index], GDI_STACK_LEVELS);
319 #define GDIDBG_TRACELOCKER(index) \
320 DPRINT1("-> locked from:\n"); \
321 KeRosDumpStackFrames(GDIHandleLocker[index], GDI_STACK_LEVELS);
322 #define GDIDBG_CAPTUREALLOCATOR(index) \
323 CaptureStackBackTace((PVOID*)GDIHandleAllocator[index], GDI_STACK_LEVELS);
324 #define GDIDBG_CAPTURELOCKER(index) \
325 CaptureStackBackTace((PVOID*)GDIHandleLocker[index], GDI_STACK_LEVELS);
327 #define GDIDBG_DUMPHANDLETABLE() \
328 IntDumpHandleTable(GdiHandleTable)
332 #define GDIDBG_TRACECALLER()
333 #define GDIDBG_TRACEALLOCATOR(index)
334 #define GDIDBG_TRACELOCKER(index)
335 #define GDIDBG_CAPTUREALLOCATOR(index)
336 #define GDIDBG_CAPTURELOCKER(index)
337 #define GDIDBG_DUMPHANDLETABLE()
339 #endif /* GDI_DEBUG */
343 LockErrorDebugOutput(HGDIOBJ hObj
, PGDI_TABLE_ENTRY Entry
, LPSTR Function
)
345 if ((Entry
->Type
& GDI_ENTRY_BASETYPE_MASK
) == 0)
347 DPRINT1("%s: Attempted to lock object 0x%x that is deleted!\n", Function
, hObj
);
349 else if (GDI_HANDLE_GET_REUSECNT(hObj
) != GDI_ENTRY_GET_REUSECNT(Entry
->Type
))
351 DPRINT1("%s: Attempted to lock object 0x%x, wrong reuse counter (Handle: 0x%x, Entry: 0x%x)\n",
352 Function
, hObj
, GDI_HANDLE_GET_REUSECNT(hObj
), GDI_ENTRY_GET_REUSECNT(Entry
->Type
));
354 else if (GDI_HANDLE_GET_TYPE(hObj
) != ((Entry
->Type
<< GDI_ENTRY_UPPER_SHIFT
) & GDI_HANDLE_TYPE_MASK
))
356 DPRINT1("%s: Attempted to lock object 0x%x, type mismatch (Handle: 0x%x, Entry: 0x%x)\n",
357 Function
, hObj
, GDI_HANDLE_GET_TYPE(hObj
), (Entry
->Type
<< GDI_ENTRY_UPPER_SHIFT
) & GDI_HANDLE_TYPE_MASK
);
361 DPRINT1("%s: Attempted to lock object 0x%x, something went wrong, typeinfo = 0x%x\n",
362 Function
, hObj
, Entry
->Type
);
364 GDIDBG_TRACECALLER();
369 InterlockedPopFreeEntry()
371 ULONG idxFirstFree
, idxNextFree
, idxPrev
;
372 PGDI_TABLE_ENTRY pFreeEntry
;
374 DPRINT("Enter InterLockedPopFreeEntry\n");
378 idxFirstFree
= GdiHandleTable
->FirstFree
;
381 pFreeEntry
= GdiHandleTable
->Entries
+ idxFirstFree
;
382 ASSERT(((ULONG
)pFreeEntry
->KernelData
& ~GDI_HANDLE_INDEX_MASK
) == 0);
383 idxNextFree
= (ULONG
)pFreeEntry
->KernelData
;
384 idxPrev
= (ULONG
)_InterlockedCompareExchange((LONG
*)&GdiHandleTable
->FirstFree
, idxNextFree
, idxFirstFree
);
388 idxFirstFree
= GdiHandleTable
->FirstUnused
;
389 idxNextFree
= idxFirstFree
+ 1;
390 if (idxNextFree
>= GDI_HANDLE_COUNT
)
392 DPRINT1("No more gdi handles left!\n");
395 idxPrev
= (ULONG
)_InterlockedCompareExchange((LONG
*)&GdiHandleTable
->FirstUnused
, idxNextFree
, idxFirstFree
);
398 while (idxPrev
!= idxFirstFree
);
403 /* Pushes an entry of the handle table to the free list,
404 The entry must be unlocked and the base type field must be 0 */
407 InterlockedPushFreeEntry(ULONG idxToFree
)
409 ULONG idxFirstFree
, idxPrev
;
410 PGDI_TABLE_ENTRY pFreeEntry
;
412 DPRINT("Enter InterlockedPushFreeEntry\n");
414 pFreeEntry
= GdiHandleTable
->Entries
+ idxToFree
;
415 ASSERT((pFreeEntry
->Type
& GDI_ENTRY_BASETYPE_MASK
) == 0);
416 ASSERT(pFreeEntry
->ProcessId
== 0);
417 pFreeEntry
->UserData
= NULL
;
421 idxFirstFree
= GdiHandleTable
->FirstFree
;
422 pFreeEntry
->KernelData
= (PVOID
)idxFirstFree
;
424 idxPrev
= (ULONG
)_InterlockedCompareExchange((LONG
*)&GdiHandleTable
->FirstFree
, idxToFree
, idxFirstFree
);
426 while (idxPrev
!= idxFirstFree
);
432 GDIOBJ_ValidateHandle(HGDIOBJ hObj
, ULONG ObjectType
)
434 PGDI_TABLE_ENTRY Entry
= GDI_HANDLE_GET_ENTRY(GdiHandleTable
, hObj
);
435 if ((((ULONG_PTR
)hObj
& GDI_HANDLE_TYPE_MASK
) == ObjectType
) &&
436 (Entry
->Type
<< GDI_ENTRY_UPPER_SHIFT
) == GDI_HANDLE_GET_UPPER(hObj
))
438 HANDLE pid
= (HANDLE
)((ULONG_PTR
)Entry
->ProcessId
& ~0x1);
439 if (pid
== NULL
|| pid
== PsGetCurrentProcessId())
448 GDIOBJ_AllocObj(UCHAR BaseType
)
452 ASSERT((BaseType
& ~GDIObjTypeTotal
) == 0);
453 // BaseType &= GDI_HANDLE_BASETYPE_MASK;
455 if (ObjTypeInfo
[BaseType
].bUseLookaside
)
457 PPAGED_LOOKASIDE_LIST LookasideList
;
459 LookasideList
= GdiHandleTable
->LookasideLists
+ BaseType
;
460 pObject
= ExAllocateFromPagedLookasideList(LookasideList
);
464 pObject
= ExAllocatePoolWithTag(PagedPool
,
465 ObjTypeInfo
[BaseType
].ulBodySize
,
466 ObjTypeInfo
[BaseType
].Tag
);
471 RtlZeroMemory(pObject
, ObjTypeInfo
[BaseType
].ulBodySize
);
479 * Allocate memory for GDI object and return handle to it.
481 * \param ObjectType - type of object \ref GDI object types
483 * \return Pointer to the allocated object, which is locked.
486 GDIOBJ_AllocObjWithHandle(ULONG ObjectType
)
488 PW32PROCESS W32Process
;
489 POBJ newObject
= NULL
;
490 HANDLE CurrentProcessId
, LockedProcessId
;
496 W32Process
= PsGetCurrentProcessWin32Process();
497 /* HACK HACK HACK: simplest-possible quota implementation - don't allow a process
498 to take too many GDI objects, itself. */
499 if (W32Process
&& W32Process
->GDIObjects
>= 0x2710)
502 ASSERT(ObjectType
!= GDI_OBJECT_TYPE_DONTCARE
);
504 TypeIndex
= GDI_OBJECT_GET_TYPE_INDEX(ObjectType
);
506 newObject
= GDIOBJ_AllocObj(TypeIndex
);
509 DPRINT1("Not enough memory to allocate gdi object!\n");
514 PGDI_TABLE_ENTRY Entry
;
517 CurrentProcessId
= PsGetCurrentProcessId();
518 LockedProcessId
= (HANDLE
)((ULONG_PTR
)CurrentProcessId
| 0x1);
520 // RtlZeroMemory(newObject, GetObjectSize(TypeIndex));
522 /* On Windows the higher 16 bit of the type field don't contain the
523 full type from the handle, but the base type.
524 (type = BRSUH, PEN, EXTPEN, basetype = BRUSH) */
525 TypeInfo
= (ObjectType
& GDI_HANDLE_BASETYPE_MASK
) | (ObjectType
>> GDI_ENTRY_UPPER_SHIFT
);
527 Index
= InterlockedPopFreeEntry();
532 Entry
= &GdiHandleTable
->Entries
[Index
];
535 PrevProcId
= _InterlockedCompareExchangePointer((PVOID
*)&Entry
->ProcessId
, LockedProcessId
, 0);
536 if (PrevProcId
== NULL
)
538 PW32THREAD Thread
= PsGetCurrentThreadWin32Thread();
541 Entry
->KernelData
= newObject
;
543 /* copy the reuse-counter */
544 TypeInfo
|= Entry
->Type
& GDI_ENTRY_REUSE_MASK
;
546 /* we found a free entry, no need to exchange this field atomically
547 since we're holding the lock */
548 Entry
->Type
= TypeInfo
;
550 /* Create a handle */
551 Handle
= (HGDIOBJ
)((Index
& 0xFFFF) | (TypeInfo
<< GDI_ENTRY_UPPER_SHIFT
));
553 /* Initialize BaseObject fields */
554 newObject
->hHmgr
= Handle
;
555 newObject
->ulShareCount
= 0;
556 newObject
->cExclusiveLock
= 1;
557 newObject
->Tid
= Thread
;
559 /* unlock the entry */
560 (void)_InterlockedExchangePointer((PVOID
*)&Entry
->ProcessId
, CurrentProcessId
);
562 GDIDBG_CAPTUREALLOCATOR(Index
);
564 if (W32Process
!= NULL
)
566 _InterlockedIncrement(&W32Process
->GDIObjects
);
569 DPRINT("GDIOBJ_AllocObj: 0x%x ob: 0x%x\n", Handle
, newObject
);
577 DPRINT1("[%d]Waiting on handle in index 0x%x\n", Attempts
, Index
);
580 /* damn, someone is trying to lock the object even though it doesn't
581 even exist anymore, wait a little and try again!
582 FIXME - we shouldn't loop forever! Give up after some time! */
589 GDIOBJ_FreeObj(newObject
, TypeIndex
);
591 DPRINT1("Failed to insert gdi object into the handle table, no handles left!\n");
592 GDIDBG_DUMPHANDLETABLE();
597 /* Wrapper for compatibility with old calls, will be removed later */
598 HGDIOBJ INTERNAL_CALL
599 GDIOBJ_AllocObjDepricated(ULONG ObjectType
)
604 pObject
= GDIOBJ_AllocObjWithHandle(ObjectType
);
605 hObject
= pObject
->hHmgr
;
606 GDIOBJ_UnlockObjByPtr(pObject
);
613 GDIOBJ_FreeObj(POBJ pObject
, UCHAR BaseType
)
615 /* Object must not have a handle! */
616 ASSERT(pObject
->hHmgr
== NULL
);
618 if (ObjTypeInfo
[BaseType
].bUseLookaside
)
620 PPAGED_LOOKASIDE_LIST LookasideList
;
622 LookasideList
= GdiHandleTable
->LookasideLists
+ BaseType
;
623 ExFreeToPagedLookasideList(LookasideList
, pObject
);
632 * Free memory allocated for the GDI object. For each object type this function calls the
633 * appropriate cleanup routine.
635 * \param hObj - handle of the object to be deleted.
637 * \return Returns TRUE if succesful.
638 * \return Returns FALSE if the cleanup routine returned FALSE or the object doesn't belong
639 * to the calling process.
642 GDIOBJ_FreeObjByHandle(HGDIOBJ hObj
, DWORD ExpectedType
)
644 PGDI_TABLE_ENTRY Entry
;
645 HANDLE ProcessId
, LockedProcessId
, PrevProcId
;
646 ULONG HandleType
, HandleUpper
, TypeIndex
;
652 DPRINT("GDIOBJ_FreeObj: hObj: 0x%08x\n", hObj
);
654 if (GDI_HANDLE_IS_STOCKOBJ(hObj
))
656 DPRINT1("GDIOBJ_FreeObj() failed, can't delete stock object handle: 0x%x !!!\n", hObj
);
657 GDIDBG_TRACECALLER();
661 ProcessId
= PsGetCurrentProcessId();
662 LockedProcessId
= (HANDLE
)((ULONG_PTR
)ProcessId
| 0x1);
664 Silent
= (ExpectedType
& GDI_OBJECT_TYPE_SILENT
);
665 ExpectedType
&= ~GDI_OBJECT_TYPE_SILENT
;
667 HandleType
= GDI_HANDLE_GET_TYPE(hObj
);
668 HandleUpper
= GDI_HANDLE_GET_UPPER(hObj
);
670 /* Check if we have the requested type */
671 if ( (ExpectedType
!= GDI_OBJECT_TYPE_DONTCARE
&&
672 HandleType
!= ExpectedType
) ||
675 DPRINT1("Attempted to free object 0x%x of wrong type (Handle: 0x%x, expected: 0x%x)\n",
676 hObj
, HandleType
, ExpectedType
);
677 GDIDBG_TRACECALLER();
681 Entry
= GDI_HANDLE_GET_ENTRY(GdiHandleTable
, hObj
);
684 /* lock the object, we must not delete global objects, so don't exchange the locking
685 process ID to zero when attempting to lock a global object... */
686 PrevProcId
= _InterlockedCompareExchangePointer((PVOID
*)&Entry
->ProcessId
, LockedProcessId
, ProcessId
);
687 if (PrevProcId
== ProcessId
)
689 if ( (Entry
->KernelData
!= NULL
) &&
690 ((Entry
->Type
<< GDI_ENTRY_UPPER_SHIFT
) == HandleUpper
) &&
691 ((Entry
->Type
& GDI_ENTRY_BASETYPE_MASK
) == (HandleUpper
& GDI_ENTRY_BASETYPE_MASK
)) )
695 Object
= Entry
->KernelData
;
697 if (Object
->cExclusiveLock
== 0)
700 PW32PROCESS W32Process
= PsGetCurrentProcessWin32Process();
702 /* Clear the basetype field so when unlocking the handle it gets finally deleted and increment reuse counter */
703 Entry
->Type
= (Entry
->Type
+ GDI_ENTRY_REUSE_INC
) & ~GDI_ENTRY_BASETYPE_MASK
;
705 /* unlock the handle slot */
706 (void)_InterlockedExchangePointer((PVOID
*)&Entry
->ProcessId
, NULL
);
708 /* push this entry to the free list */
709 InterlockedPushFreeEntry(GDI_ENTRY_TO_INDEX(GdiHandleTable
, Entry
));
711 Object
->hHmgr
= NULL
;
713 if (W32Process
!= NULL
)
715 _InterlockedDecrement(&W32Process
->GDIObjects
);
718 /* call the cleanup routine. */
719 TypeIndex
= GDI_OBJECT_GET_TYPE_INDEX(HandleType
);
720 Ret
= RunCleanupCallback(Object
, TypeIndex
);
722 /* Now it's time to free the memory */
723 GDIOBJ_FreeObj(Object
, TypeIndex
);
730 * The object is currently locked, so freeing is forbidden!
732 DPRINT1("Object->cExclusiveLock = %d\n", Object
->cExclusiveLock
);
733 GDIDBG_TRACELOCKER(GDI_HANDLE_GET_INDEX(hObj
));
739 LockErrorDebugOutput(hObj
, Entry
, "GDIOBJ_FreeObj");
740 (void)_InterlockedExchangePointer((PVOID
*)&Entry
->ProcessId
, PrevProcId
);
743 else if (PrevProcId
== LockedProcessId
)
748 DPRINT1("[%d]Waiting on 0x%x\n", Attempts
, hObj
);
751 /* the object is currently locked, wait some time and try again.
752 FIXME - we shouldn't loop forever! Give up after some time! */
761 if (((ULONG_PTR
)PrevProcId
& ~0x1) == 0)
763 DPRINT1("Attempted to free global gdi handle 0x%x, caller needs to get ownership first!!!\n", hObj
);
764 DPRINT1("Type = 0x%lx, KernelData = 0x%p, ProcessId = 0x%p\n", Entry
->Type
, Entry
->KernelData
, Entry
->ProcessId
);
768 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);
770 GDIDBG_TRACECALLER();
771 GDIDBG_TRACEALLOCATOR(GDI_HANDLE_GET_INDEX(hObj
));
780 IsObjectDead(HGDIOBJ hObject
)
782 INT Index
= GDI_HANDLE_GET_INDEX(hObject
);
783 PGDI_TABLE_ENTRY Entry
= &GdiHandleTable
->Entries
[Index
];
784 // We check to see if the objects are knocking on deaths door.
785 if ((Entry
->Type
& ~GDI_ENTRY_REUSE_MASK
) != 0 && Entry
->KernelData
!= NULL
)
789 DPRINT1("Object 0x%x currently being destroyed!!!\n",hObject
);
790 return TRUE
; // return true and move on.
797 * \param hObject object handle
798 * \return if the function fails the returned value is FALSE.
802 NtGdiDeleteObject(HGDIOBJ hObject
)
804 DPRINT("NtGdiDeleteObject handle 0x%08x\n", hObject
);
805 if (!IsObjectDead(hObject
))
807 return NULL
!= hObject
808 ? GDIOBJ_FreeObjByHandle(hObject
, GDI_OBJECT_TYPE_DONTCARE
) : FALSE
;
812 DPRINT1("Attempt DeleteObject 0x%x currently being destroyed!!!\n",hObject
);
813 return TRUE
; // return true and move on.
818 * Internal function. Called when the process is destroyed to free the remaining GDI handles.
819 * \param Process - PID of the process that will be destroyed.
822 GDI_CleanupForProcess(struct _EPROCESS
*Process
)
824 PGDI_TABLE_ENTRY Entry
, End
;
825 PEPROCESS CurrentProcess
;
826 PW32PROCESS W32Process
;
828 ULONG Index
= RESERVE_ENTRIES_COUNT
;
830 DPRINT("Starting CleanupForProcess prochandle %x Pid %d\n", Process
, Process
->UniqueProcessId
);
831 CurrentProcess
= PsGetCurrentProcess();
832 if (CurrentProcess
!= Process
)
834 KeAttachProcess(&Process
->Pcb
);
836 W32Process
= (PW32PROCESS
)Process
->Win32Process
;
839 if (W32Process
->GDIObjects
> 0)
841 /* FIXME - Instead of building the handle here and delete it using GDIOBJ_FreeObj
842 we should delete it directly here! */
843 ProcId
= Process
->UniqueProcessId
;
845 End
= &GdiHandleTable
->Entries
[GDI_HANDLE_COUNT
];
846 for (Entry
= &GdiHandleTable
->Entries
[RESERVE_ENTRIES_COUNT
];
850 /* ignore the lock bit */
851 if ( (HANDLE
)((ULONG_PTR
)Entry
->ProcessId
& ~0x1) == ProcId
&&
852 (Entry
->Type
& ~GDI_ENTRY_REUSE_MASK
) != 0 )
854 HGDIOBJ ObjectHandle
;
856 /* Create the object handle for the entry, the lower(!) 16 bit of the
857 Type field includes the type of the object including the stock
858 object flag - but since stock objects don't have a process id we can
859 simply ignore this fact here. */
860 ObjectHandle
= (HGDIOBJ
)(Index
| (Entry
->Type
<< GDI_ENTRY_UPPER_SHIFT
));
862 if (GDIOBJ_FreeObjByHandle(ObjectHandle
, GDI_OBJECT_TYPE_DONTCARE
) &&
863 W32Process
->GDIObjects
== 0)
865 /* there are no more gdi handles for this process, bail */
872 if (CurrentProcess
!= Process
)
877 DPRINT("Completed cleanup for process %d\n", Process
->UniqueProcessId
);
883 * Return pointer to the object by handle.
885 * \param hObj Object handle
886 * \return Pointer to the object.
888 * \note Process can only get pointer to the objects it created or global objects.
890 * \todo Get rid of the ExpectedType parameter!
892 PGDIOBJ INTERNAL_CALL
893 GDIOBJ_LockObj(HGDIOBJ hObj
, DWORD ExpectedType
)
896 PGDI_TABLE_ENTRY Entry
;
897 HANDLE ProcessId
, HandleProcessId
, LockedProcessId
, PrevProcId
;
899 ULONG HandleType
, HandleUpper
;
901 HandleIndex
= GDI_HANDLE_GET_INDEX(hObj
);
902 HandleType
= GDI_HANDLE_GET_TYPE(hObj
);
903 HandleUpper
= GDI_HANDLE_GET_UPPER(hObj
);
905 /* Check that the handle index is valid. */
906 if (HandleIndex
>= GDI_HANDLE_COUNT
)
909 Entry
= &GdiHandleTable
->Entries
[HandleIndex
];
911 /* Check if we have the requested type */
912 if ( (ExpectedType
!= GDI_OBJECT_TYPE_DONTCARE
&&
913 HandleType
!= ExpectedType
) ||
916 DPRINT1("Attempted to lock object 0x%x of wrong type (Handle: 0x%x, requested: 0x%x)\n",
917 hObj
, HandleType
, ExpectedType
);
918 GDIDBG_TRACECALLER();
919 GDIDBG_TRACEALLOCATOR(GDI_HANDLE_GET_INDEX(hObj
));
923 ProcessId
= (HANDLE
)((ULONG_PTR
)PsGetCurrentProcessId() & ~1);
924 HandleProcessId
= (HANDLE
)((ULONG_PTR
)Entry
->ProcessId
& ~1);
926 /* Check for invalid owner. */
927 if (ProcessId
!= HandleProcessId
&& HandleProcessId
!= NULL
)
929 DPRINT1("Tried to lock object (0x%p) of wrong owner! ProcessId = %p, HandleProcessId = %p\n", hObj
, ProcessId
, HandleProcessId
);
930 GDIDBG_TRACECALLER();
931 GDIDBG_TRACEALLOCATOR(GDI_HANDLE_GET_INDEX(hObj
));
936 * Prevent the thread from being terminated during the locking process.
937 * It would result in undesired effects and inconsistency of the global
941 KeEnterCriticalRegion();
944 * Loop until we either successfully lock the handle entry & object or
945 * fail some of the check.
950 /* Lock the handle table entry. */
951 LockedProcessId
= (HANDLE
)((ULONG_PTR
)HandleProcessId
| 0x1);
952 PrevProcId
= _InterlockedCompareExchangePointer((PVOID
*)&Entry
->ProcessId
,
956 if (PrevProcId
== HandleProcessId
)
959 * We're locking an object that belongs to our process or it's a
960 * global object if HandleProcessId is 0 here.
963 if ( (Entry
->KernelData
!= NULL
) &&
964 ((Entry
->Type
<< GDI_ENTRY_UPPER_SHIFT
) == HandleUpper
) )
966 PW32THREAD Thread
= PsGetCurrentThreadWin32Thread();
967 Object
= Entry
->KernelData
;
969 if (Object
->cExclusiveLock
== 0)
971 Object
->Tid
= Thread
;
972 Object
->cExclusiveLock
= 1;
973 GDIDBG_CAPTURELOCKER(GDI_HANDLE_GET_INDEX(hObj
))
977 if (Object
->Tid
!= Thread
)
979 /* Unlock the handle table entry. */
980 (void)_InterlockedExchangePointer((PVOID
*)&Entry
->ProcessId
, PrevProcId
);
985 _InterlockedIncrement((PLONG
)&Object
->cExclusiveLock
);
991 * Debugging code. Report attempts to lock deleted handles and
992 * locking type mismatches.
994 LockErrorDebugOutput(hObj
, Entry
, "GDIOBJ_LockObj");
997 /* Unlock the handle table entry. */
998 (void)_InterlockedExchangePointer((PVOID
*)&Entry
->ProcessId
, PrevProcId
);
1005 * The handle is currently locked, wait some time and try again.
1013 KeLeaveCriticalRegion();
1020 * Return pointer to the object by handle (and allow sharing of the handle
1023 * \param hObj Object handle
1024 * \return Pointer to the object.
1026 * \note Process can only get pointer to the objects it created or global objects.
1028 * \todo Get rid of the ExpectedType parameter!
1030 PGDIOBJ INTERNAL_CALL
1031 GDIOBJ_ShareLockObj(HGDIOBJ hObj
, DWORD ExpectedType
)
1034 PGDI_TABLE_ENTRY Entry
;
1035 HANDLE ProcessId
, HandleProcessId
, LockedProcessId
, PrevProcId
;
1037 ULONG_PTR HandleType
, HandleUpper
;
1039 HandleIndex
= GDI_HANDLE_GET_INDEX(hObj
);
1040 HandleType
= GDI_HANDLE_GET_TYPE(hObj
);
1041 HandleUpper
= GDI_HANDLE_GET_UPPER(hObj
);
1043 /* Check that the handle index is valid. */
1044 if (HandleIndex
>= GDI_HANDLE_COUNT
)
1047 /* Check if we have the requested type */
1048 if ( (ExpectedType
!= GDI_OBJECT_TYPE_DONTCARE
&&
1049 HandleType
!= ExpectedType
) ||
1052 DPRINT1("Attempted to lock object 0x%x of wrong type (Handle: 0x%x, requested: 0x%x)\n",
1053 hObj
, HandleType
, ExpectedType
);
1057 Entry
= &GdiHandleTable
->Entries
[HandleIndex
];
1059 ProcessId
= (HANDLE
)((ULONG_PTR
)PsGetCurrentProcessId() & ~1);
1060 HandleProcessId
= (HANDLE
)((ULONG_PTR
)Entry
->ProcessId
& ~1);
1062 /* Check for invalid owner. */
1063 if (ProcessId
!= HandleProcessId
&& HandleProcessId
!= NULL
)
1069 * Prevent the thread from being terminated during the locking process.
1070 * It would result in undesired effects and inconsistency of the global
1074 KeEnterCriticalRegion();
1077 * Loop until we either successfully lock the handle entry & object or
1078 * fail some of the check.
1083 /* Lock the handle table entry. */
1084 LockedProcessId
= (HANDLE
)((ULONG_PTR
)HandleProcessId
| 0x1);
1085 PrevProcId
= _InterlockedCompareExchangePointer((PVOID
*)&Entry
->ProcessId
,
1089 if (PrevProcId
== HandleProcessId
)
1092 * We're locking an object that belongs to our process or it's a
1093 * global object if HandleProcessId is 0 here.
1096 if ( (Entry
->KernelData
!= NULL
) &&
1097 (HandleUpper
== (Entry
->Type
<< GDI_ENTRY_UPPER_SHIFT
)) )
1099 Object
= (POBJ
)Entry
->KernelData
;
1102 if (_InterlockedIncrement((PLONG
)&Object
->ulShareCount
) == 1)
1104 memset(GDIHandleLocker
[HandleIndex
], 0x00, GDI_STACK_LEVELS
* sizeof(ULONG
));
1105 RtlCaptureStackBackTrace(1, GDI_STACK_LEVELS
, (PVOID
*)GDIHandleLocker
[HandleIndex
], NULL
);
1108 _InterlockedIncrement((PLONG
)&Object
->ulShareCount
);
1114 * Debugging code. Report attempts to lock deleted handles and
1115 * locking type mismatches.
1117 LockErrorDebugOutput(hObj
, Entry
, "GDIOBJ_ShareLockObj");
1120 /* Unlock the handle table entry. */
1121 (void)_InterlockedExchangePointer((PVOID
*)&Entry
->ProcessId
, PrevProcId
);
1128 * The handle is currently locked, wait some time and try again.
1136 KeLeaveCriticalRegion();
1143 * Release GDI object. Every object locked by GDIOBJ_LockObj() must be unlocked. You should unlock the object
1144 * as soon as you don't need to have access to it's data.
1146 * \param Object Object pointer (as returned by GDIOBJ_LockObj).
1149 GDIOBJ_UnlockObjByPtr(POBJ Object
)
1151 if (_InterlockedDecrement((PLONG
)&Object
->cExclusiveLock
) < 0)
1153 DPRINT1("Trying to unlock non-existant object\n");
1158 GDIOBJ_ShareUnlockObjByPtr(POBJ Object
)
1160 if (_InterlockedDecrement((PLONG
)&Object
->ulShareCount
) < 0)
1162 DPRINT1("Trying to unlock non-existant object\n");
1167 GDIOBJ_OwnedByCurrentProcess(HGDIOBJ ObjectHandle
)
1169 PGDI_TABLE_ENTRY Entry
;
1173 DPRINT("GDIOBJ_OwnedByCurrentProcess: ObjectHandle: 0x%08x\n", ObjectHandle
);
1175 if (!GDI_HANDLE_IS_STOCKOBJ(ObjectHandle
))
1177 ProcessId
= PsGetCurrentProcessId();
1179 Entry
= GDI_HANDLE_GET_ENTRY(GdiHandleTable
, ObjectHandle
);
1180 Ret
= Entry
->KernelData
!= NULL
&&
1181 (Entry
->Type
& ~GDI_ENTRY_REUSE_MASK
) != 0 &&
1182 (HANDLE
)((ULONG_PTR
)Entry
->ProcessId
& ~0x1) == ProcessId
;
1191 GDIOBJ_ConvertToStockObj(HGDIOBJ
*phObj
)
1194 * FIXME !!!!! THIS FUNCTION NEEDS TO BE FIXED - IT IS NOT SAFE WHEN OTHER THREADS
1195 * MIGHT ATTEMPT TO LOCK THE OBJECT DURING THIS CALL!!!
1197 PGDI_TABLE_ENTRY Entry
;
1198 HANDLE ProcessId
, LockedProcessId
, PrevProcId
;
1208 DPRINT("GDIOBJ_ConvertToStockObj: hObj: 0x%08x\n", hObj
);
1210 Thread
= PsGetCurrentThreadWin32Thread();
1212 if (!GDI_HANDLE_IS_STOCKOBJ(hObj
))
1214 ProcessId
= PsGetCurrentProcessId();
1215 LockedProcessId
= (HANDLE
)((ULONG_PTR
)ProcessId
| 0x1);
1217 Entry
= GDI_HANDLE_GET_ENTRY(GdiHandleTable
, hObj
);
1220 /* lock the object, we must not convert stock objects, so don't check!!! */
1221 PrevProcId
= _InterlockedCompareExchangePointer((PVOID
*)&Entry
->ProcessId
, LockedProcessId
, ProcessId
);
1222 if (PrevProcId
== ProcessId
)
1224 LONG NewType
, PrevType
, OldType
;
1226 /* we're locking an object that belongs to our process. First calculate
1227 the new object type including the stock object flag and then try to
1229 /* On Windows the higher 16 bit of the type field don't contain the
1230 full type from the handle, but the base type.
1231 (type = BRSUH, PEN, EXTPEN, basetype = BRUSH) */
1232 OldType
= ((ULONG
)hObj
& GDI_HANDLE_BASETYPE_MASK
) | ((ULONG
)hObj
>> GDI_ENTRY_UPPER_SHIFT
);
1233 /* We are currently not using bits 24..31 (flags) of the type field, but for compatibility
1234 we copy them as we can't get them from the handle */
1235 OldType
|= Entry
->Type
& GDI_ENTRY_FLAGS_MASK
;
1237 /* As the object should be a stock object, set it's flag, but only in the lower 16 bits */
1238 NewType
= OldType
| GDI_ENTRY_STOCK_MASK
;
1240 /* Try to exchange the type field - but only if the old (previous type) matches! */
1241 PrevType
= _InterlockedCompareExchange(&Entry
->Type
, NewType
, OldType
);
1242 if (PrevType
== OldType
&& Entry
->KernelData
!= NULL
)
1244 PW32THREAD PrevThread
;
1247 /* We successfully set the stock object flag.
1248 KernelData should never be NULL here!!! */
1249 ASSERT(Entry
->KernelData
);
1251 Object
= Entry
->KernelData
;
1253 PrevThread
= Object
->Tid
;
1254 if (Object
->cExclusiveLock
== 0 || PrevThread
== Thread
)
1256 /* dereference the process' object counter */
1257 if (PrevProcId
!= GDI_GLOBAL_PROCESS
)
1259 PEPROCESS OldProcess
;
1260 PW32PROCESS W32Process
;
1264 Status
= PsLookupProcessByProcessId((HANDLE
)((ULONG_PTR
)PrevProcId
& ~0x1), &OldProcess
);
1265 if (NT_SUCCESS(Status
))
1267 W32Process
= (PW32PROCESS
)OldProcess
->Win32Process
;
1268 if (W32Process
!= NULL
)
1270 _InterlockedDecrement(&W32Process
->GDIObjects
);
1272 ObDereferenceObject(OldProcess
);
1276 /* remove the process id lock and make it global */
1277 (void)_InterlockedExchangePointer((PVOID
*)&Entry
->ProcessId
, GDI_GLOBAL_PROCESS
);
1279 hObj
= (HGDIOBJ
)((ULONG
)(hObj
) | GDI_HANDLE_STOCK_MASK
);
1282 /* we're done, successfully converted the object */
1288 if (++Attempts
> 20)
1290 DPRINT1("[%d]Locked by 0x%x (we're 0x%x)\n", Attempts
, PrevThread
, Thread
);
1293 /* WTF?! The object is already locked by a different thread!
1294 Release the lock, wait a bit and try again!
1295 FIXME - we should give up after some time unless we want to wait forever! */
1296 (void)_InterlockedExchangePointer((PVOID
*)&Entry
->ProcessId
, PrevProcId
);
1304 DPRINT1("Attempted to convert object 0x%x that is deleted! Should never get here!!!\n", hObj
);
1305 DPRINT1("OldType = 0x%x, Entry->Type = 0x%x, NewType = 0x%x, Entry->KernelData = 0x%x\n", OldType
, Entry
->Type
, NewType
, Entry
->KernelData
);
1308 else if (PrevProcId
== LockedProcessId
)
1311 if (++Attempts
> 20)
1313 DPRINT1("[%d]Waiting on 0x%x\n", Attempts
, hObj
);
1316 /* the object is currently locked, wait some time and try again.
1317 FIXME - we shouldn't loop forever! Give up after some time! */
1324 DPRINT1("Attempted to convert invalid handle: 0x%x\n", hObj
);
1332 GDIOBJ_SetOwnership(HGDIOBJ ObjectHandle
, PEPROCESS NewOwner
)
1334 PGDI_TABLE_ENTRY Entry
;
1335 HANDLE ProcessId
, LockedProcessId
, PrevProcId
;
1341 DPRINT("GDIOBJ_SetOwnership: hObj: 0x%x, NewProcess: 0x%x\n", ObjectHandle
, (NewOwner
? PsGetProcessId(NewOwner
) : 0));
1343 Thread
= PsGetCurrentThreadWin32Thread();
1345 if (!GDI_HANDLE_IS_STOCKOBJ(ObjectHandle
))
1347 ProcessId
= PsGetCurrentProcessId();
1348 LockedProcessId
= (HANDLE
)((ULONG_PTR
)ProcessId
| 0x1);
1350 Entry
= GDI_HANDLE_GET_ENTRY(GdiHandleTable
, ObjectHandle
);
1353 /* lock the object, we must not convert stock objects, so don't check!!! */
1354 PrevProcId
= _InterlockedCompareExchangePointer((PVOID
*)&Entry
->ProcessId
, ProcessId
, LockedProcessId
);
1355 if (PrevProcId
== ProcessId
)
1357 PW32THREAD PrevThread
;
1359 if ((Entry
->Type
& ~GDI_ENTRY_REUSE_MASK
) != 0 && Entry
->KernelData
!= NULL
)
1361 POBJ Object
= Entry
->KernelData
;
1363 PrevThread
= Object
->Tid
;
1364 if (Object
->cExclusiveLock
== 0 || PrevThread
== Thread
)
1366 PEPROCESS OldProcess
;
1367 PW32PROCESS W32Process
;
1370 /* dereference the process' object counter */
1372 if ((ULONG_PTR
)PrevProcId
& ~0x1)
1374 Status
= PsLookupProcessByProcessId((HANDLE
)((ULONG_PTR
)PrevProcId
& ~0x1), &OldProcess
);
1375 if (NT_SUCCESS(Status
))
1377 W32Process
= (PW32PROCESS
)OldProcess
->Win32Process
;
1378 if (W32Process
!= NULL
)
1380 _InterlockedDecrement(&W32Process
->GDIObjects
);
1382 ObDereferenceObject(OldProcess
);
1386 if (NewOwner
!= NULL
)
1388 ProcessId
= PsGetProcessId(NewOwner
);
1390 /* Increase the new process' object counter */
1391 W32Process
= (PW32PROCESS
)NewOwner
->Win32Process
;
1392 if (W32Process
!= NULL
)
1394 _InterlockedIncrement(&W32Process
->GDIObjects
);
1400 /* remove the process id lock and change it to the new process id */
1401 (void)_InterlockedExchangePointer((PVOID
*)&Entry
->ProcessId
, ProcessId
);
1409 if (++Attempts
> 20)
1411 DPRINT1("[%d]Locked by 0x%x (we're 0x%x)\n", Attempts
, PrevThread
, Thread
);
1414 /* WTF?! The object is already locked by a different thread!
1415 Release the lock, wait a bit and try again! DO reset the pid lock
1416 so we make sure we don't access invalid memory in case the object is
1417 being deleted in the meantime (because we don't have aquired a reference
1419 FIXME - we should give up after some time unless we want to wait forever! */
1420 (void)_InterlockedExchangePointer((PVOID
*)&Entry
->ProcessId
, PrevProcId
);
1428 DPRINT1("Attempted to change ownership of an object 0x%x currently being destroyed!!!\n", ObjectHandle
);
1429 DPRINT1("Entry->Type = 0x%lx, Entry->KernelData = 0x%p\n", Entry
->Type
, Entry
->KernelData
);
1432 else if (PrevProcId
== LockedProcessId
)
1435 if (++Attempts
> 20)
1437 DPRINT1("[%d]Waiting on 0x%x\n", Attempts
, ObjectHandle
);
1440 /* the object is currently locked, wait some time and try again.
1441 FIXME - we shouldn't loop forever! Give up after some time! */
1446 else if (((ULONG_PTR
)PrevProcId
& ~0x1) == 0)
1448 /* allow changing ownership of global objects */
1450 LockedProcessId
= (HANDLE
)((ULONG_PTR
)ProcessId
| 0x1);
1453 else if ((HANDLE
)((ULONG_PTR
)PrevProcId
& ~0x1) != PsGetCurrentProcessId())
1455 DPRINT1("Attempted to change ownership of object 0x%x (pid: 0x%x) from pid 0x%x!!!\n", ObjectHandle
, (ULONG_PTR
)PrevProcId
& ~0x1, PsGetCurrentProcessId());
1459 DPRINT1("Attempted to change owner of invalid handle: 0x%x\n", ObjectHandle
);
1465 GDIOBJ_CopyOwnership(HGDIOBJ CopyFrom
, HGDIOBJ CopyTo
)
1467 PGDI_TABLE_ENTRY FromEntry
;
1469 HANDLE FromProcessId
, FromLockedProcessId
, FromPrevProcId
;
1474 DPRINT("GDIOBJ_CopyOwnership: from: 0x%x, to: 0x%x\n", CopyFrom
, CopyTo
);
1476 Thread
= PsGetCurrentThreadWin32Thread();
1478 if (!GDI_HANDLE_IS_STOCKOBJ(CopyFrom
) && !GDI_HANDLE_IS_STOCKOBJ(CopyTo
))
1480 FromEntry
= GDI_HANDLE_GET_ENTRY(GdiHandleTable
, CopyFrom
);
1482 FromProcessId
= (HANDLE
)((ULONG_PTR
)FromEntry
->ProcessId
& ~0x1);
1483 FromLockedProcessId
= (HANDLE
)((ULONG_PTR
)FromProcessId
| 0x1);
1486 /* lock the object, we must not convert stock objects, so don't check!!! */
1487 FromPrevProcId
= _InterlockedCompareExchangePointer((PVOID
*)&FromEntry
->ProcessId
, FromProcessId
, FromLockedProcessId
);
1488 if (FromPrevProcId
== FromProcessId
)
1490 PW32THREAD PrevThread
;
1493 if ((FromEntry
->Type
& ~GDI_ENTRY_REUSE_MASK
) != 0 && FromEntry
->KernelData
!= NULL
)
1495 Object
= FromEntry
->KernelData
;
1497 /* save the pointer to the calling thread so we know it was this thread
1498 that locked the object */
1499 PrevThread
= Object
->Tid
;
1500 if (Object
->cExclusiveLock
== 0 || PrevThread
== Thread
)
1502 /* now let's change the ownership of the target object */
1504 if (((ULONG_PTR
)FromPrevProcId
& ~0x1) != 0)
1506 PEPROCESS ProcessTo
;
1508 if (NT_SUCCESS(PsLookupProcessByProcessId((HANDLE
)((ULONG_PTR
)FromPrevProcId
& ~0x1), &ProcessTo
)))
1510 GDIOBJ_SetOwnership(CopyTo
, ProcessTo
);
1511 ObDereferenceObject(ProcessTo
);
1516 /* mark the object as global */
1517 GDIOBJ_SetOwnership(CopyTo
, NULL
);
1520 (void)_InterlockedExchangePointer((PVOID
*)&FromEntry
->ProcessId
, FromPrevProcId
);
1525 if (++Attempts
> 20)
1527 DPRINT1("[%d]Locked by 0x%x (we're 0x%x)\n", Attempts
, PrevThread
, Thread
);
1530 /* WTF?! The object is already locked by a different thread!
1531 Release the lock, wait a bit and try again! DO reset the pid lock
1532 so we make sure we don't access invalid memory in case the object is
1533 being deleted in the meantime (because we don't have aquired a reference
1535 FIXME - we should give up after some time unless we want to wait forever! */
1536 (void)_InterlockedExchangePointer((PVOID
*)&FromEntry
->ProcessId
, FromPrevProcId
);
1539 goto LockHandleFrom
;
1544 DPRINT1("Attempted to copy ownership from an object 0x%x currently being destroyed!!!\n", CopyFrom
);
1547 else if (FromPrevProcId
== FromLockedProcessId
)
1550 if (++Attempts
> 20)
1552 DPRINT1("[%d]Waiting on 0x%x\n", Attempts
, CopyFrom
);
1555 /* the object is currently locked, wait some time and try again.
1556 FIXME - we shouldn't loop forever! Give up after some time! */
1559 goto LockHandleFrom
;
1561 else if ((HANDLE
)((ULONG_PTR
)FromPrevProcId
& ~0x1) != PsGetCurrentProcessId())
1563 /* FIXME - should we really allow copying ownership from objects that we don't even own? */
1564 DPRINT1("WARNING! Changing copying ownership of object 0x%x (pid: 0x%x) to pid 0x%x!!!\n", CopyFrom
, (ULONG_PTR
)FromPrevProcId
& ~0x1, PsGetCurrentProcessId());
1565 FromProcessId
= (HANDLE
)((ULONG_PTR
)FromPrevProcId
& ~0x1);
1566 FromLockedProcessId
= (HANDLE
)((ULONG_PTR
)FromProcessId
| 0x1);
1567 goto LockHandleFrom
;
1571 DPRINT1("Attempted to copy ownership from invalid handle: 0x%x\n", CopyFrom
);
1577 GDI_MapHandleTable(PSECTION_OBJECT SectionObject
, PEPROCESS Process
)
1579 PVOID MappedView
= NULL
;
1581 LARGE_INTEGER Offset
;
1582 ULONG ViewSize
= sizeof(GDI_HANDLE_TABLE
);
1584 Offset
.QuadPart
= 0;
1586 ASSERT(SectionObject
!= NULL
);
1587 ASSERT(Process
!= NULL
);
1589 Status
= MmMapViewOfSection(SectionObject
,
1600 if (!NT_SUCCESS(Status
))
1609 NtGdiCreateClientObj(
1613 // ATM we use DC object for KernelData. This is wrong.
1614 // The real type consists of BASEOBJECT and a pointer.
1615 // The UserData is set in user mode, so it is always NULL.
1616 // HANDLE should be HGDIOBJ
1621 /* Mask out everything that would change the type in a wrong manner */
1622 ulType
&= (GDI_HANDLE_TYPE_MASK
& ~GDI_HANDLE_BASETYPE_MASK
);
1624 /* Allocate a new object */
1625 pObject
= GDIOBJ_AllocObjWithHandle(GDI_OBJECT_TYPE_CLIOBJ
| ulType
);
1631 /* get the handle */
1632 handle
= pObject
->hHmgr
;
1635 GDIOBJ_UnlockObjByPtr(pObject
);
1643 NtGdiDeleteClientObj(
1647 /* We first need to get the real type from the handle */
1648 ULONG type
= GDI_HANDLE_GET_TYPE(h
);
1650 /* Check if it's really a CLIENTOBJ */
1651 if ((type
& GDI_HANDLE_BASETYPE_MASK
) != GDILoObjType_LO_CLIENTOBJ_TYPE
)
1653 /* FIXME: SetLastError? */
1656 return GDIOBJ_FreeObjByHandle(h
, type
);
1661 IntGdiGetObject(IN HANDLE Handle
,
1669 pGdiObject
= GDIOBJ_LockObj(Handle
, GDI_OBJECT_TYPE_DONTCARE
);
1672 SetLastWin32Error(ERROR_INVALID_HANDLE
);
1676 dwObjectType
= GDIOBJ_GetObjectType(Handle
);
1677 switch (dwObjectType
)
1679 case GDI_OBJECT_TYPE_PEN
:
1680 case GDI_OBJECT_TYPE_EXTPEN
:
1681 Result
= PEN_GetObject((PGDIBRUSHOBJ
) pGdiObject
, cbCount
, (PLOGPEN
) lpBuffer
); // IntGdiCreatePenIndirect
1684 case GDI_OBJECT_TYPE_BRUSH
:
1685 Result
= BRUSH_GetObject((PGDIBRUSHOBJ
) pGdiObject
, cbCount
, (LPLOGBRUSH
)lpBuffer
);
1688 case GDI_OBJECT_TYPE_BITMAP
:
1689 Result
= BITMAP_GetObject((BITMAPOBJ
*) pGdiObject
, cbCount
, lpBuffer
);
1691 case GDI_OBJECT_TYPE_FONT
:
1692 Result
= FontGetObject((PTEXTOBJ
) pGdiObject
, cbCount
, lpBuffer
);
1694 // Fix the LOGFONT structure for the stock fonts
1695 if (FIRST_STOCK_HANDLE
<= Handle
&& Handle
<= LAST_STOCK_HANDLE
)
1697 FixStockFontSizeW(Handle
, cbCount
, lpBuffer
);
1702 case GDI_OBJECT_TYPE_PALETTE
:
1703 Result
= PALETTE_GetObject((PPALGDI
) pGdiObject
, cbCount
, lpBuffer
);
1707 DPRINT1("GDI object type 0x%08x not implemented\n", dwObjectType
);
1711 GDIOBJ_UnlockObjByPtr(pGdiObject
);
1718 NtGdiExtGetObjectW(IN HANDLE hGdiObj
,
1720 OUT LPVOID lpBuffer
)
1727 DIBSECTION dibsection
;
1731 EXTLOGFONTW extlogfontw
;
1732 ENUMLOGFONTEXDVW enumlogfontexdvw
;
1735 // Normalize to the largest supported object size
1736 cbCount
= min((UINT
)cbCount
, sizeof(Object
));
1738 // Now do the actual call
1739 iRetCount
= IntGdiGetObject(hGdiObj
, cbCount
, lpBuffer
? &Object
: NULL
);
1740 cbCopyCount
= min((UINT
)cbCount
, (UINT
)iRetCount
);
1742 // Make sure we have a buffer and a copy size
1743 if ((cbCopyCount
) && (lpBuffer
))
1745 // Enter SEH for buffer transfer
1748 // Probe the buffer and copy it
1749 ProbeForWrite(lpBuffer
, cbCopyCount
, sizeof(WORD
));
1750 RtlCopyMemory(lpBuffer
, &Object
, cbCopyCount
);
1754 // Clear the return value.
1755 // Do *NOT* set last error here!