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 #define GDI_ENTRY_TO_INDEX(ht, e) \
31 (((ULONG_PTR)(e) - (ULONG_PTR)&((ht)->Entries[0])) / sizeof(GDI_TABLE_ENTRY))
32 #define GDI_HANDLE_GET_ENTRY(HandleTable, h) \
33 (&(HandleTable)->Entries[GDI_HANDLE_GET_INDEX((h))])
35 #define GDIBdyToHdr(body) \
36 ((PGDIOBJHDR)(body) - 1)
37 #define GDIHdrToBdy(hdr) \
38 (PGDIOBJ)((PGDIOBJHDR)(hdr) + 1)
40 /* apparently the first 10 entries are never used in windows as they are empty */
41 #define RESERVE_ENTRIES_COUNT 10
47 GDICLEANUPPROC CleanupProc
;
48 } GDI_OBJ_INFO
, *PGDI_OBJ_INFO
;
51 * Dummy GDI Cleanup Callback
53 static BOOL INTERNAL_CALL
54 GDI_CleanupDummy(PVOID ObjectBody
)
59 /* Testing shows that regions are the most used GDIObj type,
60 so put that one first for performance */
62 GDI_OBJ_INFO ObjInfo
[] =
64 /* Type */ /* Size */ /* CleanupProc */
65 {GDI_OBJECT_TYPE_REGION
, sizeof(ROSRGNDATA
), RGNDATA_Cleanup
},
66 {GDI_OBJECT_TYPE_BITMAP
, sizeof(BITMAPOBJ
), BITMAP_Cleanup
},
67 {GDI_OBJECT_TYPE_DC
, sizeof(DC
), DC_Cleanup
},
68 {GDI_OBJECT_TYPE_PALETTE
, sizeof(PALGDI
), PALETTE_Cleanup
},
69 {GDI_OBJECT_TYPE_BRUSH
, sizeof(GDIBRUSHOBJ
), BRUSH_Cleanup
},
70 {GDI_OBJECT_TYPE_PEN
, sizeof(GDIBRUSHOBJ
), GDI_CleanupDummy
},
71 {GDI_OBJECT_TYPE_FONT
, sizeof(TEXTOBJ
), GDI_CleanupDummy
},
72 {GDI_OBJECT_TYPE_DCE
, sizeof(DCE
), DCE_Cleanup
},
73 {GDI_OBJECT_TYPE_DIRECTDRAW
, sizeof(DD_DIRECTDRAW
), DD_Cleanup
},
74 {GDI_OBJECT_TYPE_DD_SURFACE
, sizeof(DD_SURFACE
), DDSURF_Cleanup
},
75 {GDI_OBJECT_TYPE_EXTPEN
, 0, GDI_CleanupDummy
},
76 {GDI_OBJECT_TYPE_METADC
, 0, GDI_CleanupDummy
},
77 {GDI_OBJECT_TYPE_METAFILE
, 0, GDI_CleanupDummy
},
78 {GDI_OBJECT_TYPE_ENHMETAFILE
, 0, GDI_CleanupDummy
},
79 {GDI_OBJECT_TYPE_ENHMETADC
, 0, GDI_CleanupDummy
},
80 {GDI_OBJECT_TYPE_MEMDC
, 0, GDI_CleanupDummy
},
81 {GDI_OBJECT_TYPE_EMF
, 0, GDI_CleanupDummy
}
84 #define OBJTYPE_COUNT (sizeof(ObjInfo) / sizeof(ObjInfo[0]))
86 static LARGE_INTEGER ShortDelay
;
88 #define DelayExecution() \
89 DPRINT("%s:%i: Delay\n", __FILE__, __LINE__); \
90 KeDelayExecutionThread(KernelMode, FALSE, &ShortDelay)
93 BOOLEAN STDCALL
KiRosPrintAddress(PVOID Address
);
94 VOID STDCALL
KeRosDumpStackFrames(PULONG Frame
, ULONG FrameCount
);
95 ULONG STDCALL
KeRosGetStackFrames(PULONG Frames
, ULONG FrameCount
);
99 * Allocate GDI object table.
100 * \param Size - number of entries in the object table.
102 PGDI_HANDLE_TABLE INTERNAL_CALL
103 GDIOBJ_iAllocHandleTable(OUT PSECTION_OBJECT
*SectionObject
)
105 PGDI_HANDLE_TABLE HandleTable
= NULL
;
106 LARGE_INTEGER htSize
;
110 PGDI_TABLE_ENTRY Entry
;
113 ASSERT(SectionObject
!= NULL
);
115 htSize
.QuadPart
= sizeof(GDI_HANDLE_TABLE
);
117 Status
= MmCreateSection((PVOID
*)SectionObject
,
125 if (!NT_SUCCESS(Status
))
128 /* FIXME - use MmMapViewInSessionSpace once available! */
129 Status
= MmMapViewInSystemSpace(*SectionObject
,
130 (PVOID
*)&HandleTable
,
132 if (!NT_SUCCESS(Status
))
134 ObDereferenceObject(*SectionObject
);
135 *SectionObject
= NULL
;
139 RtlZeroMemory(HandleTable
, sizeof(GDI_HANDLE_TABLE
));
142 * initialize the free entry cache
144 InitializeSListHead(&HandleTable
->FreeEntriesHead
);
145 Entry
= &HandleTable
->Entries
[RESERVE_ENTRIES_COUNT
];
146 for(i
= GDI_HANDLE_COUNT
- 1; i
>= RESERVE_ENTRIES_COUNT
; i
--)
148 InterlockedPushEntrySList(&HandleTable
->FreeEntriesHead
, &HandleTable
->FreeEntries
[i
]);
151 HandleTable
->LookasideLists
= ExAllocatePoolWithTag(NonPagedPool
,
152 OBJTYPE_COUNT
* sizeof(PAGED_LOOKASIDE_LIST
),
154 if(HandleTable
->LookasideLists
== NULL
)
156 MmUnmapViewInSystemSpace(HandleTable
);
157 ObDereferenceObject(*SectionObject
);
158 *SectionObject
= NULL
;
162 for(ObjType
= 0; ObjType
< OBJTYPE_COUNT
; ObjType
++)
164 ExInitializePagedLookasideList(HandleTable
->LookasideLists
+ ObjType
, NULL
, NULL
, 0,
165 ObjInfo
[ObjType
].Size
+ sizeof(GDIOBJHDR
), TAG_GDIOBJ
, 0);
168 ShortDelay
.QuadPart
= -5000LL; /* FIXME - 0.5 ms? */
173 static __inline PPAGED_LOOKASIDE_LIST
174 FindLookasideList(PGDI_HANDLE_TABLE HandleTable
,
179 for (Index
= 0; Index
< OBJTYPE_COUNT
; Index
++)
181 if (ObjInfo
[Index
].Type
== ObjectType
)
183 return HandleTable
->LookasideLists
+ Index
;
187 DPRINT1("Can't find lookaside list for object type 0x%08x\n", ObjectType
);
193 RunCleanupCallback(PGDIOBJ pObj
, DWORD ObjectType
)
197 for (Index
= 0; Index
< OBJTYPE_COUNT
; Index
++)
199 if (ObjInfo
[Index
].Type
== ObjectType
)
201 return ((GDICLEANUPPROC
)ObjInfo
[Index
].CleanupProc
)(pObj
);
205 DPRINT1("Can't find cleanup callback for object type 0x%08x\n", ObjectType
);
209 static __inline ULONG
210 GetObjectSize(DWORD ObjectType
)
214 for (Index
= 0; Index
< OBJTYPE_COUNT
; Index
++)
216 if (ObjInfo
[Index
].Type
== ObjectType
)
218 return ObjInfo
[Index
].Size
;
222 DPRINT1("Can't find size for object type 0x%08x\n", ObjectType
);
228 static int leak_reported
= 0;
229 #define GDI_STACK_LEVELS 12
230 static ULONG GDIHandleAllocator
[GDI_HANDLE_COUNT
][GDI_STACK_LEVELS
];
231 struct DbgOpenGDIHandle
237 static struct DbgOpenGDIHandle h
[H
];
239 void IntDumpHandleTable(PGDI_HANDLE_TABLE HandleTable
)
241 int i
, n
= 0, j
, k
, J
;
245 DPRINT1("gdi handle abusers already reported!\n");
250 DPRINT1("reporting gdi handle abusers:\n");
252 /* step through GDI handle table and find out who our culprit is... */
253 for ( i
= RESERVE_ENTRIES_COUNT
; i
< GDI_HANDLE_COUNT
; i
++ )
255 for ( j
= 0; j
< n
; j
++ )
259 for ( k
= 0; k
< GDI_STACK_LEVELS
; k
++ )
261 if ( GDIHandleAllocator
[i
][k
]
262 != GDIHandleAllocator
[J
][k
] )
285 /* bubble sort time! weeeeee!! */
286 for ( i
= 0; i
< n
-1; i
++ )
288 if ( h
[i
].count
< h
[i
+1].count
)
290 struct DbgOpenGDIHandle t
;
294 while ( j
> 0 && h
[j
-1].count
< t
.count
)
299 /* print the worst offenders... */
300 DbgPrint ( "Worst GDI Handle leak offenders (out of %i unique locations):\n", n
);
301 for ( i
= 0; i
< n
&& h
[i
].count
> 1; i
++ )
304 DbgPrint ( " %i allocs: ", h
[i
].count
);
305 for ( j
= 0; j
< GDI_STACK_LEVELS
; j
++ )
307 ULONG Addr
= GDIHandleAllocator
[h
[i
].idx
][j
];
308 if ( !KiRosPrintAddress ( (PVOID
)Addr
) )
309 DbgPrint ( "<%X>", Addr
);
313 if ( i
< n
&& h
[i
].count
== 1 )
314 DbgPrint ( "(list terminated - the remaining entries have 1 allocation only)\n" );
316 #endif /* GDI_DEBUG */
319 * Allocate memory for GDI object and return handle to it.
321 * \param ObjectType - type of object \ref GDI object types
323 * \return Handle of the allocated object.
325 * \note Use GDIOBJ_Lock() to obtain pointer to the new object.
326 * \todo return the object pointer and lock it by default.
328 HGDIOBJ INTERNAL_CALL
330 GDIOBJ_AllocObjDbg(PGDI_HANDLE_TABLE HandleTable
, const char* file
, int line
, ULONG ObjectType
)
331 #else /* !GDI_DEBUG */
332 GDIOBJ_AllocObj(PGDI_HANDLE_TABLE HandleTable
, ULONG ObjectType
)
333 #endif /* GDI_DEBUG */
335 PW32PROCESS W32Process
;
336 PGDIOBJHDR newObject
;
337 PPAGED_LOOKASIDE_LIST LookasideList
;
338 HANDLE CurrentProcessId
, LockedProcessId
;
343 W32Process
= PsGetCurrentProcessWin32Process();
344 /* HACK HACK HACK: simplest-possible quota implementation - don't allow a process
345 to take too many GDI objects, itself. */
346 if ( W32Process
&& W32Process
->GDIObjects
>= 0x2710 )
349 ASSERT(ObjectType
!= GDI_OBJECT_TYPE_DONTCARE
);
351 LookasideList
= FindLookasideList(HandleTable
, ObjectType
);
352 if(LookasideList
!= NULL
)
354 newObject
= ExAllocateFromPagedLookasideList(LookasideList
);
355 if(newObject
!= NULL
)
357 PSLIST_ENTRY FreeEntry
;
358 PGDI_TABLE_ENTRY Entry
;
362 CurrentProcessId
= PsGetCurrentProcessId();
363 LockedProcessId
= (HANDLE
)((ULONG_PTR
)CurrentProcessId
| 0x1);
365 newObject
->LockingThread
= NULL
;
366 newObject
->Locks
= 0;
369 newObject
->createdfile
= file
;
370 newObject
->createdline
= line
;
371 newObject
->lockfile
= NULL
;
372 newObject
->lockline
= 0;
375 ObjectBody
= GDIHdrToBdy(newObject
);
377 RtlZeroMemory(ObjectBody
, GetObjectSize(ObjectType
));
379 TypeInfo
= (ObjectType
& GDI_HANDLE_TYPE_MASK
) | (ObjectType
>> 16);
381 FreeEntry
= InterlockedPopEntrySList(&HandleTable
->FreeEntriesHead
);
382 if(FreeEntry
!= NULL
)
387 /* calculate the entry from the address of the entry in the free slot array */
388 Index
= ((ULONG_PTR
)FreeEntry
- (ULONG_PTR
)&HandleTable
->FreeEntries
[0]) /
389 sizeof(HandleTable
->FreeEntries
[0]);
390 Entry
= &HandleTable
->Entries
[Index
];
393 PrevProcId
= InterlockedCompareExchangePointer(&Entry
->ProcessId
, LockedProcessId
, 0);
394 if(PrevProcId
== NULL
)
398 ASSERT(Entry
->KernelData
== NULL
);
400 Entry
->KernelData
= ObjectBody
;
402 /* copy the reuse-counter */
403 TypeInfo
|= Entry
->Type
& GDI_HANDLE_REUSE_MASK
;
405 /* we found a free entry, no need to exchange this field atomically
406 since we're holding the lock */
407 Entry
->Type
= TypeInfo
;
409 /* unlock the entry */
410 (void)InterlockedExchangePointer(&Entry
->ProcessId
, CurrentProcessId
);
413 memset ( GDIHandleAllocator
[Index
], 0xcd, GDI_STACK_LEVELS
* sizeof(ULONG
) );
414 KeRosGetStackFrames ( GDIHandleAllocator
[Index
], GDI_STACK_LEVELS
);
415 #endif /* GDI_DEBUG */
417 if(W32Process
!= NULL
)
419 InterlockedIncrement(&W32Process
->GDIObjects
);
421 Handle
= (HGDIOBJ
)((Index
& 0xFFFF) | (TypeInfo
& (GDI_HANDLE_TYPE_MASK
| GDI_HANDLE_REUSE_MASK
)));
423 DPRINT("GDIOBJ_AllocObj: 0x%x ob: 0x%x\n", Handle
, ObjectBody
);
431 DPRINT1("[%d]Waiting on handle in index 0x%x\n", Attempts
, Index
);
434 /* damn, someone is trying to lock the object even though it doesn't
435 eve nexist anymore, wait a little and try again!
436 FIXME - we shouldn't loop forever! Give up after some time! */
443 ExFreeToPagedLookasideList(LookasideList
, newObject
);
444 DPRINT1("Failed to insert gdi object into the handle table, no handles left!\n");
446 IntDumpHandleTable(HandleTable
);
447 #endif /* GDI_DEBUG */
451 DPRINT1("Not enough memory to allocate gdi object!\n");
456 DPRINT1("Failed to find lookaside list for object type 0x%x\n", ObjectType
);
462 * Free memory allocated for the GDI object. For each object type this function calls the
463 * appropriate cleanup routine.
465 * \param hObj - handle of the object to be deleted.
467 * \return Returns TRUE if succesful.
468 * \return Returns FALSE if the cleanup routine returned FALSE or the object doesn't belong
469 * to the calling process.
473 GDIOBJ_FreeObjDbg(PGDI_HANDLE_TABLE HandleTable
, const char* file
, int line
, HGDIOBJ hObj
, DWORD ObjectType
)
474 #else /* !GDI_DEBUG */
475 GDIOBJ_FreeObj(PGDI_HANDLE_TABLE HandleTable
, HGDIOBJ hObj
, DWORD ObjectType
)
476 #endif /* GDI_DEBUG */
478 PGDI_TABLE_ENTRY Entry
;
479 PPAGED_LOOKASIDE_LIST LookasideList
;
480 HANDLE ProcessId
, LockedProcessId
, PrevProcId
;
487 DPRINT("GDIOBJ_FreeObj: hObj: 0x%08x\n", hObj
);
489 if(GDI_HANDLE_IS_STOCKOBJ(hObj
))
491 DPRINT1("GDIOBJ_FreeObj() failed, can't delete stock object handle: 0x%x !!!\n", hObj
);
493 DPRINT1("-> called from %s:%i\n", file
, line
);
498 ProcessId
= PsGetCurrentProcessId();
499 LockedProcessId
= (HANDLE
)((ULONG_PTR
)ProcessId
| 0x1);
501 Silent
= (ObjectType
& GDI_OBJECT_TYPE_SILENT
);
502 ObjectType
&= ~GDI_OBJECT_TYPE_SILENT
;
504 ExpectedType
= ((ObjectType
!= GDI_OBJECT_TYPE_DONTCARE
) ? ObjectType
: 0);
506 Entry
= GDI_HANDLE_GET_ENTRY(HandleTable
, hObj
);
509 /* lock the object, we must not delete global objects, so don't exchange the locking
510 process ID to zero when attempting to lock a global object... */
511 PrevProcId
= InterlockedCompareExchangePointer(&Entry
->ProcessId
, LockedProcessId
, ProcessId
);
512 if(PrevProcId
== ProcessId
)
514 if(Entry
->Type
!= 0 && Entry
->KernelData
!= NULL
&&
515 (ExpectedType
== 0 || ((Entry
->Type
<< 16) == ExpectedType
)))
519 GdiHdr
= GDIBdyToHdr(Entry
->KernelData
);
521 if(GdiHdr
->Locks
== 0)
524 PW32PROCESS W32Process
= PsGetCurrentProcessWin32Process();
525 ULONG Type
= Entry
->Type
<< 16;
527 /* Clear the type field so when unlocking the handle it gets finally deleted and increment reuse counter */
528 Entry
->Type
= ((Entry
->Type
>> GDI_HANDLE_REUSECNT_SHIFT
) + 1) << GDI_HANDLE_REUSECNT_SHIFT
;
529 Entry
->KernelData
= NULL
;
531 /* unlock the handle slot */
532 (void)InterlockedExchangePointer(&Entry
->ProcessId
, NULL
);
534 /* push this entry to the free list */
535 InterlockedPushEntrySList(&HandleTable
->FreeEntriesHead
,
536 &HandleTable
->FreeEntries
[GDI_ENTRY_TO_INDEX(HandleTable
, Entry
)]);
538 if(W32Process
!= NULL
)
540 InterlockedDecrement(&W32Process
->GDIObjects
);
543 /* call the cleanup routine. */
544 Ret
= RunCleanupCallback(GDIHdrToBdy(GdiHdr
), Type
);
546 /* Now it's time to free the memory */
547 LookasideList
= FindLookasideList(HandleTable
, Type
);
548 if(LookasideList
!= NULL
)
550 ExFreeToPagedLookasideList(LookasideList
, GdiHdr
);
558 * The object is currently locked, so freeing is forbidden!
560 DPRINT1("GdiHdr->Locks: %d\n", GdiHdr
->Locks
);
562 DPRINT1("Locked from: %s:%d\n", GdiHdr
->lockfile
, GdiHdr
->lockline
);
569 if((Entry
->Type
& ~GDI_HANDLE_REUSE_MASK
) != 0)
571 DPRINT1("Attempted to delete object 0x%x, type mismatch (0x%x : 0x%x)\n", hObj
, ObjectType
, ExpectedType
);
575 DPRINT1("Attempted to delete object 0x%x which was already deleted!\n", hObj
);
577 (void)InterlockedExchangePointer(&Entry
->ProcessId
, PrevProcId
);
580 else if(PrevProcId
== LockedProcessId
)
585 DPRINT1("[%d]Waiting on 0x%x\n", Attempts
, hObj
);
588 /* the object is currently locked, wait some time and try again.
589 FIXME - we shouldn't loop forever! Give up after some time! */
598 if(((ULONG_PTR
)PrevProcId
& ~0x1) == 0)
600 DPRINT1("Attempted to free global gdi handle 0x%x, caller needs to get ownership first!!!\n", hObj
);
604 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);
607 DPRINT1("-> called from %s:%i\n", file
, line
);
617 * \param hObject object handle
618 * \return if the function fails the returned value is FALSE.
621 NtGdiDeleteObject(HGDIOBJ hObject
)
623 DPRINT("NtGdiDeleteObject handle 0x%08x\n", hObject
);
625 return NULL
!= hObject
626 ? GDIOBJ_FreeObj(GdiHandleTable
, hObject
, GDI_OBJECT_TYPE_DONTCARE
) : FALSE
;
630 * Internal function. Called when the process is destroyed to free the remaining GDI handles.
631 * \param Process - PID of the process that will be destroyed.
634 GDI_CleanupForProcess (PGDI_HANDLE_TABLE HandleTable
, struct _EPROCESS
*Process
)
636 PGDI_TABLE_ENTRY Entry
, End
;
637 PEPROCESS CurrentProcess
;
638 PW32PROCESS W32Process
;
640 ULONG Index
= RESERVE_ENTRIES_COUNT
;
642 DPRINT("Starting CleanupForProcess prochandle %x Pid %d\n", Process
, Process
->UniqueProcessId
);
643 CurrentProcess
= PsGetCurrentProcess();
644 if (CurrentProcess
!= Process
)
646 KeAttachProcess(&Process
->Pcb
);
648 W32Process
= (PW32PROCESS
)Process
->Win32Process
;
651 if(W32Process
->GDIObjects
> 0)
653 /* FIXME - Instead of building the handle here and delete it using GDIOBJ_FreeObj
654 we should delete it directly here! */
655 ProcId
= Process
->UniqueProcessId
;
657 End
= &HandleTable
->Entries
[GDI_HANDLE_COUNT
];
658 for(Entry
= &HandleTable
->Entries
[RESERVE_ENTRIES_COUNT
];
662 /* ignore the lock bit */
663 if((HANDLE
)((ULONG_PTR
)Entry
->ProcessId
& ~0x1) == ProcId
&& (Entry
->Type
& ~GDI_HANDLE_REUSE_MASK
) != 0)
665 HGDIOBJ ObjectHandle
;
667 /* Create the object handle for the entry, the upper 16 bit of the
668 Type field includes the type of the object including the stock
669 object flag - but since stock objects don't have a process id we can
670 simply ignore this fact here. */
671 ObjectHandle
= (HGDIOBJ
)(Index
| (Entry
->Type
& 0xFFFF0000));
673 if(GDIOBJ_FreeObj(HandleTable
, ObjectHandle
, GDI_OBJECT_TYPE_DONTCARE
) &&
674 W32Process
->GDIObjects
== 0)
676 /* there are no more gdi handles for this process, bail */
683 if (CurrentProcess
!= Process
)
688 DPRINT("Completed cleanup for process %d\n", Process
->UniqueProcessId
);
694 * Return pointer to the object by handle.
696 * \param hObj Object handle
697 * \return Pointer to the object.
699 * \note Process can only get pointer to the objects it created or global objects.
701 * \todo Get rid of the ObjectType parameter!
703 PGDIOBJ INTERNAL_CALL
705 GDIOBJ_LockObjDbg (PGDI_HANDLE_TABLE HandleTable
, const char* file
, int line
, HGDIOBJ hObj
, DWORD ObjectType
)
706 #else /* !GDI_DEBUG */
707 GDIOBJ_LockObj (PGDI_HANDLE_TABLE HandleTable
, HGDIOBJ hObj
, DWORD ObjectType
)
708 #endif /* GDI_DEBUG */
711 PGDI_TABLE_ENTRY HandleEntry
;
712 HANDLE ProcessId
, HandleProcessId
, LockedProcessId
, PrevProcId
;
713 PGDIOBJ Object
= NULL
;
715 HandleIndex
= GDI_HANDLE_GET_INDEX(hObj
);
717 /* Check that the handle index is valid. */
718 if (HandleIndex
>= GDI_HANDLE_COUNT
)
721 HandleEntry
= &HandleTable
->Entries
[HandleIndex
];
723 ProcessId
= (HANDLE
)((ULONG_PTR
)PsGetCurrentProcessId() & ~1);
724 HandleProcessId
= (HANDLE
)((ULONG_PTR
)HandleEntry
->ProcessId
& ~1);
726 /* Check for invalid owner. */
727 if (ProcessId
!= HandleProcessId
&& HandleProcessId
!= NULL
)
733 * Prevent the thread from being terminated during the locking process.
734 * It would result in undesired effects and inconsistency of the global
738 KeEnterCriticalRegion();
741 * Loop until we either successfully lock the handle entry & object or
742 * fail some of the check.
747 /* Lock the handle table entry. */
748 LockedProcessId
= (HANDLE
)((ULONG_PTR
)HandleProcessId
| 0x1);
749 PrevProcId
= InterlockedCompareExchangePointer(&HandleEntry
->ProcessId
,
753 if (PrevProcId
== HandleProcessId
)
755 LONG HandleType
= HandleEntry
->Type
<< 16;
758 * We're locking an object that belongs to our process or it's a
759 * global object if HandleProcessId is 0 here.
762 /* FIXME: Check the upper 16-bits of handle number! */
763 if (HandleType
!= 0 && HandleEntry
->KernelData
!= NULL
&&
764 (ObjectType
== GDI_OBJECT_TYPE_DONTCARE
||
765 HandleType
== ObjectType
))
767 PGDIOBJHDR GdiHdr
= GDIBdyToHdr(HandleEntry
->KernelData
);
768 PETHREAD Thread
= PsGetCurrentThread();
770 if (GdiHdr
->Locks
== 0)
772 GdiHdr
->LockingThread
= Thread
;
775 GdiHdr
->lockfile
= file
;
776 GdiHdr
->lockline
= line
;
778 Object
= HandleEntry
->KernelData
;
782 InterlockedIncrement((PLONG
)&GdiHdr
->Locks
);
783 if (GdiHdr
->LockingThread
!= Thread
)
785 InterlockedDecrement((PLONG
)&GdiHdr
->Locks
);
787 /* Unlock the handle table entry. */
788 (void)InterlockedExchangePointer(&HandleEntry
->ProcessId
, PrevProcId
);
793 Object
= HandleEntry
->KernelData
;
799 * Debugging code. Report attempts to lock deleted handles and
800 * locking type mismatches.
803 if ((HandleType
& ~GDI_HANDLE_REUSE_MASK
) == 0)
805 DPRINT1("Attempted to lock object 0x%x that is deleted!\n", hObj
);
807 KeRosDumpStackFrames(NULL
, 20);
812 DPRINT1("Attempted to lock object 0x%x, type mismatch (0x%x : 0x%x)\n",
813 hObj
, HandleType
& ~GDI_HANDLE_REUSE_MASK
, ObjectType
& ~GDI_HANDLE_REUSE_MASK
);
815 KeRosDumpStackFrames(NULL
, 20);
819 DPRINT1("-> called from %s:%i\n", file
, line
);
823 /* Unlock the handle table entry. */
824 (void)InterlockedExchangePointer(&HandleEntry
->ProcessId
, PrevProcId
);
831 * The handle is currently locked, wait some time and try again.
839 KeLeaveCriticalRegion();
846 * Return pointer to the object by handle (and allow sharing of the handle
849 * \param hObj Object handle
850 * \return Pointer to the object.
852 * \note Process can only get pointer to the objects it created or global objects.
854 * \todo Get rid of the ObjectType parameter!
856 PGDIOBJ INTERNAL_CALL
858 GDIOBJ_ShareLockObjDbg (PGDI_HANDLE_TABLE HandleTable
, const char* file
, int line
, HGDIOBJ hObj
, DWORD ObjectType
)
859 #else /* !GDI_DEBUG */
860 GDIOBJ_ShareLockObj (PGDI_HANDLE_TABLE HandleTable
, HGDIOBJ hObj
, DWORD ObjectType
)
861 #endif /* GDI_DEBUG */
864 PGDI_TABLE_ENTRY HandleEntry
;
865 HANDLE ProcessId
, HandleProcessId
, LockedProcessId
, PrevProcId
;
866 PGDIOBJ Object
= NULL
;
868 HandleIndex
= GDI_HANDLE_GET_INDEX(hObj
);
870 /* Check that the handle index is valid. */
871 if (HandleIndex
>= GDI_HANDLE_COUNT
)
874 HandleEntry
= &HandleTable
->Entries
[HandleIndex
];
876 ProcessId
= (HANDLE
)((ULONG_PTR
)PsGetCurrentProcessId() & ~1);
877 HandleProcessId
= (HANDLE
)((ULONG_PTR
)HandleEntry
->ProcessId
& ~1);
879 /* Check for invalid owner. */
880 if (ProcessId
!= HandleProcessId
&& HandleProcessId
!= NULL
)
886 * Prevent the thread from being terminated during the locking process.
887 * It would result in undesired effects and inconsistency of the global
891 KeEnterCriticalRegion();
894 * Loop until we either successfully lock the handle entry & object or
895 * fail some of the check.
900 /* Lock the handle table entry. */
901 LockedProcessId
= (HANDLE
)((ULONG_PTR
)HandleProcessId
| 0x1);
902 PrevProcId
= InterlockedCompareExchangePointer(&HandleEntry
->ProcessId
,
906 if (PrevProcId
== HandleProcessId
)
908 LONG HandleType
= HandleEntry
->Type
<< 16;
911 * We're locking an object that belongs to our process or it's a
912 * global object if HandleProcessId is 0 here.
915 /* FIXME: Check the upper 16-bits of handle number! */
916 if (HandleType
!= 0 && HandleEntry
->KernelData
!= NULL
&&
917 (ObjectType
== GDI_OBJECT_TYPE_DONTCARE
||
918 HandleType
== ObjectType
))
920 PGDIOBJHDR GdiHdr
= GDIBdyToHdr(HandleEntry
->KernelData
);
923 if (InterlockedIncrement((PLONG
)&GdiHdr
->Locks
) == 1)
925 GdiHdr
->lockfile
= file
;
926 GdiHdr
->lockline
= line
;
929 InterlockedIncrement((PLONG
)&GdiHdr
->Locks
);
931 Object
= HandleEntry
->KernelData
;
936 * Debugging code. Report attempts to lock deleted handles and
937 * locking type mismatches.
940 if ((HandleType
& ~GDI_HANDLE_REUSE_MASK
) == 0)
942 DPRINT1("Attempted to lock object 0x%x that is deleted!\n", hObj
);
944 KeRosDumpStackFrames(NULL
, 20);
949 DPRINT1("Attempted to lock object 0x%x, type mismatch (0x%x : 0x%x)\n",
950 hObj
, HandleType
& ~GDI_HANDLE_REUSE_MASK
, ObjectType
& ~GDI_HANDLE_REUSE_MASK
);
952 KeRosDumpStackFrames(NULL
, 20);
956 DPRINT1("-> called from %s:%i\n", file
, line
);
960 /* Unlock the handle table entry. */
961 (void)InterlockedExchangePointer(&HandleEntry
->ProcessId
, PrevProcId
);
968 * The handle is currently locked, wait some time and try again.
976 KeLeaveCriticalRegion();
983 * Release GDI object. Every object locked by GDIOBJ_LockObj() must be unlocked. You should unlock the object
984 * as soon as you don't need to have access to it's data.
986 * \param Object Object pointer (as returned by GDIOBJ_LockObj).
989 GDIOBJ_UnlockObjByPtr(PGDI_HANDLE_TABLE HandleTable
, PGDIOBJ Object
)
991 PGDIOBJHDR GdiHdr
= GDIBdyToHdr(Object
);
993 if (InterlockedDecrement((PLONG
)&GdiHdr
->Locks
) == 0)
995 GdiHdr
->lockfile
= NULL
;
996 GdiHdr
->lockline
= 0;
999 InterlockedDecrement((PLONG
)&GdiHdr
->Locks
);
1005 GDIOBJ_OwnedByCurrentProcess(PGDI_HANDLE_TABLE HandleTable
, HGDIOBJ ObjectHandle
)
1007 PGDI_TABLE_ENTRY Entry
;
1011 DPRINT("GDIOBJ_OwnedByCurrentProcess: ObjectHandle: 0x%08x\n", ObjectHandle
);
1013 if(!GDI_HANDLE_IS_STOCKOBJ(ObjectHandle
))
1015 ProcessId
= PsGetCurrentProcessId();
1017 Entry
= GDI_HANDLE_GET_ENTRY(HandleTable
, ObjectHandle
);
1018 Ret
= Entry
->KernelData
!= NULL
&&
1019 (Entry
->Type
& ~GDI_HANDLE_REUSE_MASK
) != 0 &&
1020 (HANDLE
)((ULONG_PTR
)Entry
->ProcessId
& ~0x1) == ProcessId
;
1029 GDIOBJ_ConvertToStockObj(PGDI_HANDLE_TABLE HandleTable
, HGDIOBJ
*hObj
)
1032 * FIXME !!!!! THIS FUNCTION NEEDS TO BE FIXED - IT IS NOT SAFE WHEN OTHER THREADS
1033 * MIGHT ATTEMPT TO LOCK THE OBJECT DURING THIS CALL!!!
1035 PGDI_TABLE_ENTRY Entry
;
1036 HANDLE ProcessId
, LockedProcessId
, PrevProcId
;
1044 DPRINT("GDIOBJ_ConvertToStockObj: hObj: 0x%08x\n", *hObj
);
1046 Thread
= PsGetCurrentThread();
1048 if(!GDI_HANDLE_IS_STOCKOBJ(*hObj
))
1050 ProcessId
= PsGetCurrentProcessId();
1051 LockedProcessId
= (HANDLE
)((ULONG_PTR
)ProcessId
| 0x1);
1053 Entry
= GDI_HANDLE_GET_ENTRY(HandleTable
, *hObj
);
1056 /* lock the object, we must not convert stock objects, so don't check!!! */
1057 PrevProcId
= InterlockedCompareExchangePointer(&Entry
->ProcessId
, LockedProcessId
, ProcessId
);
1058 if(PrevProcId
== ProcessId
)
1060 LONG NewType
, PrevType
, OldType
;
1062 /* we're locking an object that belongs to our process. First calculate
1063 the new object type including the stock object flag and then try to
1065 NewType
= GDI_HANDLE_GET_TYPE(*hObj
);
1066 NewType
|= NewType
>> 16;
1067 NewType
|= (ULONG_PTR
)(*hObj
) & GDI_HANDLE_REUSE_MASK
;
1069 /* This is the type that the object should have right now, save it */
1071 /* As the object should be a stock object, set it's flag, but only in the upper 16 bits */
1072 NewType
|= GDI_HANDLE_STOCK_MASK
;
1074 /* Try to exchange the type field - but only if the old (previous type) matches! */
1075 PrevType
= InterlockedCompareExchange(&Entry
->Type
, NewType
, OldType
);
1076 if(PrevType
== OldType
&& Entry
->KernelData
!= NULL
)
1078 PETHREAD PrevThread
;
1081 /* We successfully set the stock object flag.
1082 KernelData should never be NULL here!!! */
1083 ASSERT(Entry
->KernelData
);
1085 GdiHdr
= GDIBdyToHdr(Entry
->KernelData
);
1087 PrevThread
= GdiHdr
->LockingThread
;
1088 if(GdiHdr
->Locks
== 0 || PrevThread
== Thread
)
1090 /* dereference the process' object counter */
1091 if(PrevProcId
!= GDI_GLOBAL_PROCESS
)
1093 PEPROCESS OldProcess
;
1094 PW32PROCESS W32Process
;
1098 Status
= PsLookupProcessByProcessId((HANDLE
)((ULONG_PTR
)PrevProcId
& ~0x1), &OldProcess
);
1099 if(NT_SUCCESS(Status
))
1101 W32Process
= (PW32PROCESS
)OldProcess
->Win32Process
;
1102 if(W32Process
!= NULL
)
1104 InterlockedDecrement(&W32Process
->GDIObjects
);
1106 ObDereferenceObject(OldProcess
);
1110 /* remove the process id lock and make it global */
1111 (void)InterlockedExchangePointer(&Entry
->ProcessId
, GDI_GLOBAL_PROCESS
);
1113 *hObj
= (HGDIOBJ
)((ULONG
)(*hObj
) | GDI_HANDLE_STOCK_MASK
);
1115 /* we're done, successfully converted the object */
1123 if(GdiHdr
->lockfile
!= NULL
)
1125 DPRINT1("[%d]Locked %s:%i by 0x%x (we're 0x%x)\n", Attempts
, GdiHdr
->lockfile
, GdiHdr
->lockline
, PrevThread
, Thread
);
1129 /* WTF?! The object is already locked by a different thread!
1130 Release the lock, wait a bit and try again!
1131 FIXME - we should give up after some time unless we want to wait forever! */
1132 (void)InterlockedExchangePointer(&Entry
->ProcessId
, PrevProcId
);
1140 DPRINT1("Attempted to convert object 0x%x that is deleted! Should never get here!!!\n", hObj
);
1143 else if(PrevProcId
== LockedProcessId
)
1148 DPRINT1("[%d]Waiting on 0x%x\n", Attempts
, hObj
);
1151 /* the object is currently locked, wait some time and try again.
1152 FIXME - we shouldn't loop forever! Give up after some time! */
1159 DPRINT1("Attempted to convert invalid handle: 0x%x\n", hObj
);
1167 GDIOBJ_SetOwnership(PGDI_HANDLE_TABLE HandleTable
, HGDIOBJ ObjectHandle
, PEPROCESS NewOwner
)
1169 PGDI_TABLE_ENTRY Entry
;
1170 HANDLE ProcessId
, LockedProcessId
, PrevProcId
;
1176 DPRINT("GDIOBJ_SetOwnership: hObj: 0x%x, NewProcess: 0x%x\n", ObjectHandle
, (NewOwner
? PsGetProcessId(NewOwner
) : 0));
1178 Thread
= PsGetCurrentThread();
1180 if(!GDI_HANDLE_IS_STOCKOBJ(ObjectHandle
))
1182 ProcessId
= PsGetCurrentProcessId();
1183 LockedProcessId
= (HANDLE
)((ULONG_PTR
)ProcessId
| 0x1);
1185 Entry
= GDI_HANDLE_GET_ENTRY(HandleTable
, ObjectHandle
);
1188 /* lock the object, we must not convert stock objects, so don't check!!! */
1189 PrevProcId
= InterlockedCompareExchangePointer(&Entry
->ProcessId
, ProcessId
, LockedProcessId
);
1190 if(PrevProcId
== ProcessId
)
1192 PETHREAD PrevThread
;
1194 if((Entry
->Type
& ~GDI_HANDLE_REUSE_MASK
) != 0 && Entry
->KernelData
!= NULL
)
1196 PGDIOBJHDR GdiHdr
= GDIBdyToHdr(Entry
->KernelData
);
1198 PrevThread
= GdiHdr
->LockingThread
;
1199 if(GdiHdr
->Locks
== 0 || PrevThread
== Thread
)
1201 PEPROCESS OldProcess
;
1202 PW32PROCESS W32Process
;
1205 /* dereference the process' object counter */
1207 if((ULONG_PTR
)PrevProcId
& ~0x1)
1209 Status
= PsLookupProcessByProcessId((HANDLE
)((ULONG_PTR
)PrevProcId
& ~0x1), &OldProcess
);
1210 if(NT_SUCCESS(Status
))
1212 W32Process
= (PW32PROCESS
)OldProcess
->Win32Process
;
1213 if(W32Process
!= NULL
)
1215 InterlockedDecrement(&W32Process
->GDIObjects
);
1217 ObDereferenceObject(OldProcess
);
1221 if(NewOwner
!= NULL
)
1223 ProcessId
= PsGetProcessId(NewOwner
);
1225 /* Increase the new process' object counter */
1226 W32Process
= (PW32PROCESS
)NewOwner
->Win32Process
;
1227 if(W32Process
!= NULL
)
1229 InterlockedIncrement(&W32Process
->GDIObjects
);
1235 /* remove the process id lock and change it to the new process id */
1236 (void)InterlockedExchangePointer(&Entry
->ProcessId
, ProcessId
);
1246 if(GdiHdr
->lockfile
!= NULL
)
1248 DPRINT1("[%d]Locked from %s:%i by 0x%x (we're 0x%x)\n", Attempts
, GdiHdr
->lockfile
, GdiHdr
->lockline
, PrevThread
, Thread
);
1252 /* WTF?! The object is already locked by a different thread!
1253 Release the lock, wait a bit and try again! DO reset the pid lock
1254 so we make sure we don't access invalid memory in case the object is
1255 being deleted in the meantime (because we don't have aquired a reference
1257 FIXME - we should give up after some time unless we want to wait forever! */
1258 (void)InterlockedExchangePointer(&Entry
->ProcessId
, PrevProcId
);
1266 DPRINT1("Attempted to change ownership of an object 0x%x currently being destroyed!!!\n", ObjectHandle
);
1269 else if(PrevProcId
== LockedProcessId
)
1274 DPRINT1("[%d]Waiting on 0x%x\n", Attempts
, ObjectHandle
);
1277 /* the object is currently locked, wait some time and try again.
1278 FIXME - we shouldn't loop forever! Give up after some time! */
1283 else if(((ULONG_PTR
)PrevProcId
& ~0x1) == 0)
1285 /* allow changing ownership of global objects */
1287 LockedProcessId
= (HANDLE
)((ULONG_PTR
)ProcessId
| 0x1);
1290 else if((HANDLE
)((ULONG_PTR
)PrevProcId
& ~0x1) != PsGetCurrentProcessId())
1292 DPRINT1("Attempted to change ownership of object 0x%x (pid: 0x%x) from pid 0x%x!!!\n", ObjectHandle
, (ULONG_PTR
)PrevProcId
& ~0x1, PsGetCurrentProcessId());
1296 DPRINT1("Attempted to change owner of invalid handle: 0x%x\n", ObjectHandle
);
1302 GDIOBJ_CopyOwnership(PGDI_HANDLE_TABLE HandleTable
, HGDIOBJ CopyFrom
, HGDIOBJ CopyTo
)
1304 PGDI_TABLE_ENTRY FromEntry
;
1306 HANDLE FromProcessId
, FromLockedProcessId
, FromPrevProcId
;
1311 DPRINT("GDIOBJ_CopyOwnership: from: 0x%x, to: 0x%x\n", CopyFrom
, CopyTo
);
1313 Thread
= PsGetCurrentThread();
1315 if(!GDI_HANDLE_IS_STOCKOBJ(CopyFrom
) && !GDI_HANDLE_IS_STOCKOBJ(CopyTo
))
1317 FromEntry
= GDI_HANDLE_GET_ENTRY(HandleTable
, CopyFrom
);
1319 FromProcessId
= (HANDLE
)((ULONG_PTR
)FromEntry
->ProcessId
& ~0x1);
1320 FromLockedProcessId
= (HANDLE
)((ULONG_PTR
)FromProcessId
| 0x1);
1323 /* lock the object, we must not convert stock objects, so don't check!!! */
1324 FromPrevProcId
= InterlockedCompareExchangePointer(&FromEntry
->ProcessId
, FromProcessId
, FromLockedProcessId
);
1325 if(FromPrevProcId
== FromProcessId
)
1327 PETHREAD PrevThread
;
1330 if((FromEntry
->Type
& ~GDI_HANDLE_REUSE_MASK
) != 0 && FromEntry
->KernelData
!= NULL
)
1332 GdiHdr
= GDIBdyToHdr(FromEntry
->KernelData
);
1334 /* save the pointer to the calling thread so we know it was this thread
1335 that locked the object */
1336 PrevThread
= GdiHdr
->LockingThread
;
1337 if(GdiHdr
->Locks
== 0 || PrevThread
== Thread
)
1339 /* now let's change the ownership of the target object */
1341 if(((ULONG_PTR
)FromPrevProcId
& ~0x1) != 0)
1343 PEPROCESS ProcessTo
;
1345 if(NT_SUCCESS(PsLookupProcessByProcessId((HANDLE
)((ULONG_PTR
)FromPrevProcId
& ~0x1), &ProcessTo
)))
1347 GDIOBJ_SetOwnership(HandleTable
, CopyTo
, ProcessTo
);
1348 ObDereferenceObject(ProcessTo
);
1353 /* mark the object as global */
1354 GDIOBJ_SetOwnership(HandleTable
, CopyTo
, NULL
);
1357 (void)InterlockedExchangePointer(&FromEntry
->ProcessId
, FromPrevProcId
);
1364 if(GdiHdr
->lockfile
!= NULL
)
1366 DPRINT1("[%d]Locked from %s:%i by 0x%x (we're 0x%x)\n", Attempts
, GdiHdr
->lockfile
, GdiHdr
->lockline
, PrevThread
, Thread
);
1370 /* WTF?! The object is already locked by a different thread!
1371 Release the lock, wait a bit and try again! DO reset the pid lock
1372 so we make sure we don't access invalid memory in case the object is
1373 being deleted in the meantime (because we don't have aquired a reference
1375 FIXME - we should give up after some time unless we want to wait forever! */
1376 (void)InterlockedExchangePointer(&FromEntry
->ProcessId
, FromPrevProcId
);
1379 goto LockHandleFrom
;
1384 DPRINT1("Attempted to copy ownership from an object 0x%x currently being destroyed!!!\n", CopyFrom
);
1387 else if(FromPrevProcId
== FromLockedProcessId
)
1392 DPRINT1("[%d]Waiting on 0x%x\n", Attempts
, CopyFrom
);
1395 /* the object is currently locked, wait some time and try again.
1396 FIXME - we shouldn't loop forever! Give up after some time! */
1399 goto LockHandleFrom
;
1401 else if((HANDLE
)((ULONG_PTR
)FromPrevProcId
& ~0x1) != PsGetCurrentProcessId())
1403 /* FIXME - should we really allow copying ownership from objects that we don't even own? */
1404 DPRINT1("WARNING! Changing copying ownership of object 0x%x (pid: 0x%x) to pid 0x%x!!!\n", CopyFrom
, (ULONG_PTR
)FromPrevProcId
& ~0x1, PsGetCurrentProcessId());
1405 FromProcessId
= (HANDLE
)((ULONG_PTR
)FromPrevProcId
& ~0x1);
1406 FromLockedProcessId
= (HANDLE
)((ULONG_PTR
)FromProcessId
| 0x1);
1407 goto LockHandleFrom
;
1411 DPRINT1("Attempted to copy ownership from invalid handle: 0x%x\n", CopyFrom
);
1417 GDI_MapHandleTable(PSECTION_OBJECT SectionObject
, PEPROCESS Process
)
1419 PVOID MappedView
= NULL
;
1421 LARGE_INTEGER Offset
;
1422 ULONG ViewSize
= sizeof(GDI_HANDLE_TABLE
);
1424 Offset
.QuadPart
= 0;
1426 ASSERT(SectionObject
!= NULL
);
1427 ASSERT(Process
!= NULL
);
1429 Status
= MmMapViewOfSection(SectionObject
,
1440 if (!NT_SUCCESS(Status
))