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
43 typedef struct _GDI_HANDLE_TABLE
45 /* the table must be located at the beginning of this structure so it can be
47 GDI_TABLE_ENTRY Entries
[GDI_HANDLE_COUNT
];
49 PPAGED_LOOKASIDE_LIST LookasideLists
;
51 SLIST_HEADER FreeEntriesHead
;
52 SLIST_ENTRY FreeEntries
[((GDI_HANDLE_COUNT
* sizeof(GDI_TABLE_ENTRY
)) << 3) /
53 (sizeof(SLIST_ENTRY
) << 3)];
54 } GDI_HANDLE_TABLE
, *PGDI_HANDLE_TABLE
;
60 GDICLEANUPPROC CleanupProc
;
61 } GDI_OBJ_INFO
, *PGDI_OBJ_INFO
;
64 * Dummy GDI Cleanup Callback
67 GDI_CleanupDummy(PVOID ObjectBody
)
72 /* Testing shows that regions are the most used GDIObj type,
73 so put that one first for performance */
75 GDI_OBJ_INFO ObjInfo
[] =
77 /* Type */ /* Size */ /* CleanupProc */
78 {GDI_OBJECT_TYPE_REGION
, sizeof(ROSRGNDATA
), RGNDATA_Cleanup
},
79 {GDI_OBJECT_TYPE_BITMAP
, sizeof(BITMAPOBJ
), BITMAP_Cleanup
},
80 {GDI_OBJECT_TYPE_DC
, sizeof(DC
), DC_Cleanup
},
81 {GDI_OBJECT_TYPE_PALETTE
, sizeof(PALGDI
), PALETTE_Cleanup
},
82 {GDI_OBJECT_TYPE_BRUSH
, sizeof(GDIBRUSHOBJ
), BRUSH_Cleanup
},
83 {GDI_OBJECT_TYPE_PEN
, sizeof(GDIBRUSHOBJ
), GDI_CleanupDummy
},
84 {GDI_OBJECT_TYPE_FONT
, sizeof(TEXTOBJ
), GDI_CleanupDummy
},
85 {GDI_OBJECT_TYPE_DCE
, sizeof(DCE
), DCE_Cleanup
},
86 {GDI_OBJECT_TYPE_DIRECTDRAW
, sizeof(DD_DIRECTDRAW
), DD_Cleanup
},
87 {GDI_OBJECT_TYPE_DD_SURFACE
, sizeof(DD_SURFACE
), DDSURF_Cleanup
},
88 {GDI_OBJECT_TYPE_EXTPEN
, 0, GDI_CleanupDummy
},
89 {GDI_OBJECT_TYPE_METADC
, 0, GDI_CleanupDummy
},
90 {GDI_OBJECT_TYPE_METAFILE
, 0, GDI_CleanupDummy
},
91 {GDI_OBJECT_TYPE_ENHMETAFILE
, 0, GDI_CleanupDummy
},
92 {GDI_OBJECT_TYPE_ENHMETADC
, 0, GDI_CleanupDummy
},
93 {GDI_OBJECT_TYPE_MEMDC
, 0, GDI_CleanupDummy
},
94 {GDI_OBJECT_TYPE_EMF
, 0, GDI_CleanupDummy
}
97 #define OBJTYPE_COUNT (sizeof(ObjInfo) / sizeof(ObjInfo[0]))
99 static PGDI_HANDLE_TABLE HandleTable
= NULL
;
100 static LARGE_INTEGER ShortDelay
;
102 #define DelayExecution() \
103 DPRINT("%s:%i: Delay\n", __FILE__, __LINE__); \
104 KeDelayExecutionThread(KernelMode, FALSE, &ShortDelay)
107 BOOLEAN STDCALL
KiRosPrintAddress(PVOID Address
);
108 VOID STDCALL
KeRosDumpStackFrames(PULONG Frame
, ULONG FrameCount
);
109 ULONG STDCALL
KeRosGetStackFrames(PULONG Frames
, ULONG FrameCount
);
113 * Allocate GDI object table.
114 * \param Size - number of entries in the object table.
116 static PGDI_HANDLE_TABLE INTERNAL_CALL
117 GDIOBJ_iAllocHandleTable(VOID
)
119 PGDI_HANDLE_TABLE handleTable
;
123 PGDI_TABLE_ENTRY Entry
;
126 htSize
= sizeof(GDI_HANDLE_TABLE
);
128 IntUserCreateSharedSection(SessionSharedSectionPool
,
129 (PVOID
*)&handleTable
,
131 ASSERT( handleTable
);
132 RtlZeroMemory(handleTable
, sizeof(GDI_HANDLE_TABLE
));
135 * initialize the free entry cache
137 InitializeSListHead(&handleTable
->FreeEntriesHead
);
138 Entry
= &HandleTable
->Entries
[RESERVE_ENTRIES_COUNT
];
139 for(i
= GDI_HANDLE_COUNT
- 1; i
>= RESERVE_ENTRIES_COUNT
; i
--)
141 InterlockedPushEntrySList(&handleTable
->FreeEntriesHead
, &handleTable
->FreeEntries
[i
]);
144 handleTable
->LookasideLists
= ExAllocatePoolWithTag(NonPagedPool
,
145 OBJTYPE_COUNT
* sizeof(PAGED_LOOKASIDE_LIST
),
147 if(handleTable
->LookasideLists
== NULL
)
149 InUserDeleteSharedSection(SessionSharedSectionPool
,
154 for(ObjType
= 0; ObjType
< OBJTYPE_COUNT
; ObjType
++)
156 ExInitializePagedLookasideList(handleTable
->LookasideLists
+ ObjType
, NULL
, NULL
, 0,
157 ObjInfo
[ObjType
].Size
+ sizeof(GDIOBJHDR
), TAG_GDIOBJ
, 0);
160 ShortDelay
.QuadPart
= -5000LL; /* FIXME - 0.5 ms? */
165 static __inline PPAGED_LOOKASIDE_LIST
166 FindLookasideList(DWORD ObjectType
)
170 for (Index
= 0; Index
< OBJTYPE_COUNT
; Index
++)
172 if (ObjInfo
[Index
].Type
== ObjectType
)
174 return HandleTable
->LookasideLists
+ Index
;
178 DPRINT1("Can't find lookaside list for object type 0x%08x\n", ObjectType
);
184 RunCleanupCallback(PGDIOBJ pObj
, DWORD ObjectType
)
188 for (Index
= 0; Index
< OBJTYPE_COUNT
; Index
++)
190 if (ObjInfo
[Index
].Type
== ObjectType
)
192 return ((GDICLEANUPPROC
)ObjInfo
[Index
].CleanupProc
)(pObj
);
196 DPRINT1("Can't find cleanup callback for object type 0x%08x\n", ObjectType
);
200 static __inline ULONG
201 GetObjectSize(DWORD ObjectType
)
205 for (Index
= 0; Index
< OBJTYPE_COUNT
; Index
++)
207 if (ObjInfo
[Index
].Type
== ObjectType
)
209 return ObjInfo
[Index
].Size
;
213 DPRINT1("Can't find size for object type 0x%08x\n", ObjectType
);
219 static int leak_reported
= 0;
220 #define GDI_STACK_LEVELS 12
221 static ULONG GDIHandleAllocator
[GDI_HANDLE_COUNT
][GDI_STACK_LEVELS
];
222 struct DbgOpenGDIHandle
228 static struct DbgOpenGDIHandle h
[H
];
230 void IntDumpHandleTable()
232 int i
, n
= 0, j
, k
, J
;
236 DPRINT1("gdi handle abusers already reported!\n");
241 DPRINT1("reporting gdi handle abusers:\n");
243 /* step through GDI handle table and find out who our culprit is... */
244 for ( i
= RESERVE_ENTRIES_COUNT
; i
< GDI_HANDLE_COUNT
; i
++ )
246 for ( j
= 0; j
< n
; j
++ )
250 for ( k
= 0; k
< GDI_STACK_LEVELS
; k
++ )
252 if ( GDIHandleAllocator
[i
][k
]
253 != GDIHandleAllocator
[J
][k
] )
276 /* bubble sort time! weeeeee!! */
277 for ( i
= 0; i
< n
-1; i
++ )
279 if ( h
[i
].count
< h
[i
+1].count
)
281 struct DbgOpenGDIHandle t
;
285 while ( j
> 0 && h
[j
-1].count
< t
.count
)
290 /* print the worst offenders... */
291 DbgPrint ( "Worst GDI Handle leak offenders (out of %i unique locations):\n", n
);
292 for ( i
= 0; i
< n
&& h
[i
].count
> 1; i
++ )
295 DbgPrint ( " %i allocs: ", h
[i
].count
);
296 for ( j
= 0; j
< GDI_STACK_LEVELS
; j
++ )
298 ULONG Addr
= GDIHandleAllocator
[h
[i
].idx
][j
];
299 if ( !KiRosPrintAddress ( (PVOID
)Addr
) )
300 DbgPrint ( "<%X>", Addr
);
304 if ( i
< n
&& h
[i
].count
== 1 )
305 DbgPrint ( "(list terminated - the remaining entries have 1 allocation only)\n" );
307 #endif /* GDI_DEBUG */
310 * Allocate memory for GDI object and return handle to it.
312 * \param ObjectType - type of object \ref GDI object types
314 * \return Handle of the allocated object.
316 * \note Use GDIOBJ_Lock() to obtain pointer to the new object.
317 * \todo return the object pointer and lock it by default.
319 HGDIOBJ INTERNAL_CALL
321 GDIOBJ_AllocObjDbg(const char* file
, int line
, ULONG ObjectType
)
322 #else /* !GDI_DEBUG */
323 GDIOBJ_AllocObj(ULONG ObjectType
)
324 #endif /* GDI_DEBUG */
326 PW32PROCESS W32Process
;
327 PGDIOBJHDR newObject
;
328 PPAGED_LOOKASIDE_LIST LookasideList
;
329 HANDLE CurrentProcessId
, LockedProcessId
;
334 W32Process
= PsGetWin32Process();
335 /* HACK HACK HACK: simplest-possible quota implementation - don't allow a process
336 to take too many GDI objects, itself. */
337 if ( W32Process
&& W32Process
->GDIObjects
>= 0x2710 )
340 ASSERT(ObjectType
!= GDI_OBJECT_TYPE_DONTCARE
);
342 LookasideList
= FindLookasideList(ObjectType
);
343 if(LookasideList
!= NULL
)
345 newObject
= ExAllocateFromPagedLookasideList(LookasideList
);
346 if(newObject
!= NULL
)
348 PSLIST_ENTRY FreeEntry
;
349 PGDI_TABLE_ENTRY Entry
;
353 CurrentProcessId
= PsGetCurrentProcessId();
354 LockedProcessId
= (HANDLE
)((ULONG_PTR
)CurrentProcessId
| 0x1);
356 newObject
->LockingThread
= NULL
;
357 newObject
->Locks
= 0;
360 newObject
->createdfile
= file
;
361 newObject
->createdline
= line
;
362 newObject
->lockfile
= NULL
;
363 newObject
->lockline
= 0;
366 ObjectBody
= GDIHdrToBdy(newObject
);
368 RtlZeroMemory(ObjectBody
, GetObjectSize(ObjectType
));
370 TypeInfo
= (ObjectType
& GDI_HANDLE_TYPE_MASK
) | (ObjectType
>> 16);
372 FreeEntry
= InterlockedPopEntrySList(&HandleTable
->FreeEntriesHead
);
373 if(FreeEntry
!= NULL
)
378 /* calculate the entry from the address of the entry in the free slot array */
379 Index
= ((ULONG_PTR
)FreeEntry
- (ULONG_PTR
)&HandleTable
->FreeEntries
[0]) /
380 sizeof(HandleTable
->FreeEntries
[0]);
381 Entry
= &HandleTable
->Entries
[Index
];
384 PrevProcId
= InterlockedCompareExchangePointer(&Entry
->ProcessId
, LockedProcessId
, 0);
385 if(PrevProcId
== NULL
)
389 ASSERT(Entry
->KernelData
== NULL
);
391 Entry
->KernelData
= ObjectBody
;
393 /* copy the reuse-counter */
394 TypeInfo
|= Entry
->Type
& GDI_HANDLE_REUSE_MASK
;
396 /* we found a free entry, no need to exchange this field atomically
397 since we're holding the lock */
398 Entry
->Type
= TypeInfo
;
400 /* unlock the entry */
401 InterlockedExchangePointer(&Entry
->ProcessId
, CurrentProcessId
);
404 memset ( GDIHandleAllocator
[Index
], 0xcd, GDI_STACK_LEVELS
* sizeof(ULONG
) );
405 KeRosGetStackFrames ( GDIHandleAllocator
[Index
], GDI_STACK_LEVELS
);
406 #endif /* GDI_DEBUG */
408 if(W32Process
!= NULL
)
410 InterlockedIncrement(&W32Process
->GDIObjects
);
412 Handle
= (HGDIOBJ
)((Index
& 0xFFFF) | (TypeInfo
& (GDI_HANDLE_TYPE_MASK
| GDI_HANDLE_REUSE_MASK
)));
414 DPRINT("GDIOBJ_AllocObj: 0x%x ob: 0x%x\n", Handle
, ObjectBody
);
422 DPRINT1("[%d]Waiting on handle in index 0x%x\n", Attempts
, Index
);
425 /* damn, someone is trying to lock the object even though it doesn't
426 eve nexist anymore, wait a little and try again!
427 FIXME - we shouldn't loop forever! Give up after some time! */
434 ExFreeToPagedLookasideList(LookasideList
, newObject
);
435 DPRINT1("Failed to insert gdi object into the handle table, no handles left!\n");
437 IntDumpHandleTable();
438 #endif /* GDI_DEBUG */
442 DPRINT1("Not enough memory to allocate gdi object!\n");
447 DPRINT1("Failed to find lookaside list for object type 0x%x\n", ObjectType
);
453 * Free memory allocated for the GDI object. For each object type this function calls the
454 * appropriate cleanup routine.
456 * \param hObj - handle of the object to be deleted.
458 * \return Returns TRUE if succesful.
459 * \return Returns FALSE if the cleanup routine returned FALSE or the object doesn't belong
460 * to the calling process.
464 GDIOBJ_FreeObjDbg(const char* file
, int line
, HGDIOBJ hObj
, DWORD ObjectType
)
465 #else /* !GDI_DEBUG */
466 GDIOBJ_FreeObj(HGDIOBJ hObj
, DWORD ObjectType
)
467 #endif /* GDI_DEBUG */
469 PGDI_TABLE_ENTRY Entry
;
470 PPAGED_LOOKASIDE_LIST LookasideList
;
471 HANDLE ProcessId
, LockedProcessId
, PrevProcId
;
478 DPRINT("GDIOBJ_FreeObj: hObj: 0x%08x\n", hObj
);
480 if(GDI_HANDLE_IS_STOCKOBJ(hObj
))
482 DPRINT1("GDIOBJ_FreeObj() failed, can't delete stock object handle: 0x%x !!!\n", hObj
);
484 DPRINT1("-> called from %s:%i\n", file
, line
);
489 ProcessId
= PsGetCurrentProcessId();
490 LockedProcessId
= (HANDLE
)((ULONG_PTR
)ProcessId
| 0x1);
492 Silent
= (ObjectType
& GDI_OBJECT_TYPE_SILENT
);
493 ObjectType
&= ~GDI_OBJECT_TYPE_SILENT
;
495 ExpectedType
= ((ObjectType
!= GDI_OBJECT_TYPE_DONTCARE
) ? ObjectType
: 0);
497 Entry
= GDI_HANDLE_GET_ENTRY(HandleTable
, hObj
);
500 /* lock the object, we must not delete global objects, so don't exchange the locking
501 process ID to zero when attempting to lock a global object... */
502 PrevProcId
= InterlockedCompareExchangePointer(&Entry
->ProcessId
, LockedProcessId
, ProcessId
);
503 if(PrevProcId
== ProcessId
)
505 if(Entry
->Type
!= 0 && Entry
->KernelData
!= NULL
&&
506 (ExpectedType
== 0 || ((Entry
->Type
<< 16) == ExpectedType
)))
510 GdiHdr
= GDIBdyToHdr(Entry
->KernelData
);
512 if(GdiHdr
->Locks
== 0)
515 PW32PROCESS W32Process
= PsGetWin32Process();
516 ULONG Type
= Entry
->Type
<< 16;
518 /* Clear the type field so when unlocking the handle it gets finally deleted and increment reuse counter */
519 Entry
->Type
= ((Entry
->Type
>> GDI_HANDLE_REUSECNT_SHIFT
) + 1) << GDI_HANDLE_REUSECNT_SHIFT
;
520 Entry
->KernelData
= NULL
;
522 /* unlock the handle slot */
523 InterlockedExchangePointer(&Entry
->ProcessId
, NULL
);
525 /* push this entry to the free list */
526 InterlockedPushEntrySList(&HandleTable
->FreeEntriesHead
,
527 &HandleTable
->FreeEntries
[GDI_ENTRY_TO_INDEX(HandleTable
, Entry
)]);
529 if(W32Process
!= NULL
)
531 InterlockedDecrement(&W32Process
->GDIObjects
);
534 /* call the cleanup routine. */
535 Ret
= RunCleanupCallback(GDIHdrToBdy(GdiHdr
), Type
);
537 /* Now it's time to free the memory */
538 LookasideList
= FindLookasideList(Type
);
539 if(LookasideList
!= NULL
)
541 ExFreeToPagedLookasideList(LookasideList
, GdiHdr
);
549 * The object is currently locked, so freeing is forbidden!
551 DPRINT1("GdiHdr->Locks: %d\n", GdiHdr
->Locks
);
553 DPRINT1("Locked from: %s:%d\n", GdiHdr
->lockfile
, GdiHdr
->lockline
);
560 if((Entry
->Type
& ~GDI_HANDLE_REUSE_MASK
) != 0)
562 DPRINT1("Attempted to delete object 0x%x, type mismatch (0x%x : 0x%x)\n", hObj
, ObjectType
, ExpectedType
);
566 DPRINT1("Attempted to delete object 0x%x which was already deleted!\n", hObj
);
568 InterlockedExchangePointer(&Entry
->ProcessId
, PrevProcId
);
571 else if(PrevProcId
== LockedProcessId
)
576 DPRINT1("[%d]Waiting on 0x%x\n", Attempts
, hObj
);
579 /* the object is currently locked, wait some time and try again.
580 FIXME - we shouldn't loop forever! Give up after some time! */
589 if(((ULONG_PTR
)PrevProcId
& ~0x1) == 0)
591 DPRINT1("Attempted to free global gdi handle 0x%x, caller needs to get ownership first!!!\n", hObj
);
595 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);
598 DPRINT1("-> called from %s:%i\n", file
, line
);
607 * Initialization of the GDI object engine.
610 InitGdiObjectHandleTable (VOID
)
612 DPRINT("InitGdiObjectHandleTable\n");
614 HandleTable
= GDIOBJ_iAllocHandleTable();
615 DPRINT("HandleTable: %x\n", HandleTable
);
620 * \param hObject object handle
621 * \return if the function fails the returned value is FALSE.
624 NtGdiDeleteObject(HGDIOBJ hObject
)
626 DPRINT("NtGdiDeleteObject handle 0x%08x\n", hObject
);
628 return NULL
!= hObject
629 ? GDIOBJ_FreeObj(hObject
, GDI_OBJECT_TYPE_DONTCARE
) : FALSE
;
633 * Internal function. Called when the process is destroyed to free the remaining GDI handles.
634 * \param Process - PID of the process that will be destroyed.
637 GDI_CleanupForProcess (struct _EPROCESS
*Process
)
639 PGDI_TABLE_ENTRY Entry
, End
;
640 PEPROCESS CurrentProcess
;
641 PW32PROCESS W32Process
;
643 ULONG Index
= RESERVE_ENTRIES_COUNT
;
645 DPRINT("Starting CleanupForProcess prochandle %x Pid %d\n", Process
, Process
->UniqueProcessId
);
646 CurrentProcess
= PsGetCurrentProcess();
647 if (CurrentProcess
!= Process
)
649 KeAttachProcess(&Process
->Pcb
);
651 W32Process
= (PW32PROCESS
)Process
->Win32Process
;
654 if(W32Process
->GDIObjects
> 0)
656 /* FIXME - Instead of building the handle here and delete it using GDIOBJ_FreeObj
657 we should delete it directly here! */
658 ProcId
= Process
->UniqueProcessId
;
660 End
= &HandleTable
->Entries
[GDI_HANDLE_COUNT
];
661 for(Entry
= &HandleTable
->Entries
[RESERVE_ENTRIES_COUNT
];
665 /* ignore the lock bit */
666 if((HANDLE
)((ULONG_PTR
)Entry
->ProcessId
& ~0x1) == ProcId
&& (Entry
->Type
& ~GDI_HANDLE_REUSE_MASK
) != 0)
668 HGDIOBJ ObjectHandle
;
670 /* Create the object handle for the entry, the upper 16 bit of the
671 Type field includes the type of the object including the stock
672 object flag - but since stock objects don't have a process id we can
673 simply ignore this fact here. */
674 ObjectHandle
= (HGDIOBJ
)(Index
| (Entry
->Type
& 0xFFFF0000));
676 if(GDIOBJ_FreeObj(ObjectHandle
, GDI_OBJECT_TYPE_DONTCARE
) &&
677 W32Process
->GDIObjects
== 0)
679 /* there are no more gdi handles for this process, bail */
686 if (CurrentProcess
!= Process
)
691 DPRINT("Completed cleanup for process %d\n", Process
->UniqueProcessId
);
697 * Return pointer to the object by handle.
699 * \param hObj Object handle
700 * \return Pointer to the object.
702 * \note Process can only get pointer to the objects it created or global objects.
704 * \todo Get rid of the ObjectType parameter!
706 PGDIOBJ INTERNAL_CALL
708 GDIOBJ_LockObjDbg (const char* file
, int line
, HGDIOBJ hObj
, DWORD ObjectType
)
709 #else /* !GDI_DEBUG */
710 GDIOBJ_LockObj (HGDIOBJ hObj
, DWORD ObjectType
)
711 #endif /* GDI_DEBUG */
714 PGDI_TABLE_ENTRY HandleEntry
;
715 HANDLE ProcessId
, HandleProcessId
, LockedProcessId
, PrevProcId
;
716 PGDIOBJ Object
= NULL
;
718 HandleIndex
= GDI_HANDLE_GET_INDEX(hObj
);
720 /* Check that the handle index is valid. */
721 if (HandleIndex
>= GDI_HANDLE_COUNT
)
724 HandleEntry
= &HandleTable
->Entries
[HandleIndex
];
726 ProcessId
= (HANDLE
)((ULONG_PTR
)PsGetCurrentProcessId() & ~1);
727 HandleProcessId
= (HANDLE
)((ULONG_PTR
)HandleEntry
->ProcessId
& ~1);
729 /* Check for invalid owner. */
730 if (ProcessId
!= HandleProcessId
&& HandleProcessId
!= NULL
)
736 * Prevent the thread from being terminated during the locking process.
737 * It would result in undesired effects and inconsistency of the global
741 KeEnterCriticalRegion();
744 * Loop until we either successfully lock the handle entry & object or
745 * fail some of the check.
750 /* Lock the handle table entry. */
751 LockedProcessId
= (HANDLE
)((ULONG_PTR
)HandleProcessId
| 0x1);
752 PrevProcId
= InterlockedCompareExchangePointer(&HandleEntry
->ProcessId
,
756 if (PrevProcId
== HandleProcessId
)
758 LONG HandleType
= HandleEntry
->Type
<< 16;
761 * We're locking an object that belongs to our process or it's a
762 * global object if HandleProcessId is 0 here.
765 /* FIXME: Check the upper 16-bits of handle number! */
766 if (HandleType
!= 0 && HandleEntry
->KernelData
!= NULL
&&
767 (ObjectType
== GDI_OBJECT_TYPE_DONTCARE
||
768 HandleType
== ObjectType
))
770 PGDIOBJHDR GdiHdr
= GDIBdyToHdr(HandleEntry
->KernelData
);
771 PETHREAD Thread
= PsGetCurrentThread();
773 if (GdiHdr
->Locks
== 0)
775 GdiHdr
->LockingThread
= Thread
;
778 GdiHdr
->lockfile
= file
;
779 GdiHdr
->lockline
= line
;
781 Object
= HandleEntry
->KernelData
;
785 InterlockedIncrement((PLONG
)&GdiHdr
->Locks
);
786 if (GdiHdr
->LockingThread
!= Thread
)
788 InterlockedDecrement((PLONG
)&GdiHdr
->Locks
);
790 /* Unlock the handle table entry. */
791 InterlockedExchangePointer(&HandleEntry
->ProcessId
, PrevProcId
);
796 Object
= HandleEntry
->KernelData
;
802 * Debugging code. Report attempts to lock deleted handles and
803 * locking type mismatches.
806 if ((HandleType
& ~GDI_HANDLE_REUSE_MASK
) == 0)
808 DPRINT1("Attempted to lock object 0x%x that is deleted!\n", hObj
);
810 KeRosDumpStackFrames(NULL
, 20);
815 DPRINT1("Attempted to lock object 0x%x, type mismatch (0x%x : 0x%x)\n",
816 hObj
, HandleType
& ~GDI_HANDLE_REUSE_MASK
, ObjectType
& ~GDI_HANDLE_REUSE_MASK
);
818 KeRosDumpStackFrames(NULL
, 20);
822 DPRINT1("-> called from %s:%i\n", file
, line
);
826 /* Unlock the handle table entry. */
827 InterlockedExchangePointer(&HandleEntry
->ProcessId
, PrevProcId
);
834 * The handle is currently locked, wait some time and try again.
842 KeLeaveCriticalRegion();
849 * Return pointer to the object by handle (and allow sharing of the handle
852 * \param hObj Object handle
853 * \return Pointer to the object.
855 * \note Process can only get pointer to the objects it created or global objects.
857 * \todo Get rid of the ObjectType parameter!
859 PGDIOBJ INTERNAL_CALL
861 GDIOBJ_ShareLockObjDbg (const char* file
, int line
, HGDIOBJ hObj
, DWORD ObjectType
)
862 #else /* !GDI_DEBUG */
863 GDIOBJ_ShareLockObj (HGDIOBJ hObj
, DWORD ObjectType
)
864 #endif /* GDI_DEBUG */
867 PGDI_TABLE_ENTRY HandleEntry
;
868 HANDLE ProcessId
, HandleProcessId
, LockedProcessId
, PrevProcId
;
869 PGDIOBJ Object
= NULL
;
871 HandleIndex
= GDI_HANDLE_GET_INDEX(hObj
);
873 /* Check that the handle index is valid. */
874 if (HandleIndex
>= GDI_HANDLE_COUNT
)
877 HandleEntry
= &HandleTable
->Entries
[HandleIndex
];
879 ProcessId
= (HANDLE
)((ULONG_PTR
)PsGetCurrentProcessId() & ~1);
880 HandleProcessId
= (HANDLE
)((ULONG_PTR
)HandleEntry
->ProcessId
& ~1);
882 /* Check for invalid owner. */
883 if (ProcessId
!= HandleProcessId
&& HandleProcessId
!= NULL
)
889 * Prevent the thread from being terminated during the locking process.
890 * It would result in undesired effects and inconsistency of the global
894 KeEnterCriticalRegion();
897 * Loop until we either successfully lock the handle entry & object or
898 * fail some of the check.
903 /* Lock the handle table entry. */
904 LockedProcessId
= (HANDLE
)((ULONG_PTR
)HandleProcessId
| 0x1);
905 PrevProcId
= InterlockedCompareExchangePointer(&HandleEntry
->ProcessId
,
909 if (PrevProcId
== HandleProcessId
)
911 LONG HandleType
= HandleEntry
->Type
<< 16;
914 * We're locking an object that belongs to our process or it's a
915 * global object if HandleProcessId is 0 here.
918 /* FIXME: Check the upper 16-bits of handle number! */
919 if (HandleType
!= 0 && HandleEntry
->KernelData
!= NULL
&&
920 (ObjectType
== GDI_OBJECT_TYPE_DONTCARE
||
921 HandleType
== ObjectType
))
923 PGDIOBJHDR GdiHdr
= GDIBdyToHdr(HandleEntry
->KernelData
);
926 if (InterlockedIncrement((PLONG
)&GdiHdr
->Locks
) == 1)
928 GdiHdr
->lockfile
= file
;
929 GdiHdr
->lockline
= line
;
932 InterlockedIncrement((PLONG
)&GdiHdr
->Locks
);
934 Object
= HandleEntry
->KernelData
;
939 * Debugging code. Report attempts to lock deleted handles and
940 * locking type mismatches.
943 if ((HandleType
& ~GDI_HANDLE_REUSE_MASK
) == 0)
945 DPRINT1("Attempted to lock object 0x%x that is deleted!\n", hObj
);
947 KeRosDumpStackFrames(NULL
, 20);
952 DPRINT1("Attempted to lock object 0x%x, type mismatch (0x%x : 0x%x)\n",
953 hObj
, HandleType
& ~GDI_HANDLE_REUSE_MASK
, ObjectType
& ~GDI_HANDLE_REUSE_MASK
);
955 KeRosDumpStackFrames(NULL
, 20);
959 DPRINT1("-> called from %s:%i\n", file
, line
);
963 /* Unlock the handle table entry. */
964 InterlockedExchangePointer(&HandleEntry
->ProcessId
, PrevProcId
);
971 * The handle is currently locked, wait some time and try again.
979 KeLeaveCriticalRegion();
986 * Release GDI object. Every object locked by GDIOBJ_LockObj() must be unlocked. You should unlock the object
987 * as soon as you don't need to have access to it's data.
989 * \param Object Object pointer (as returned by GDIOBJ_LockObj).
992 GDIOBJ_UnlockObjByPtr(PGDIOBJ Object
)
994 PGDIOBJHDR GdiHdr
= GDIBdyToHdr(Object
);
996 if (InterlockedDecrement((PLONG
)&GdiHdr
->Locks
) == 0)
998 GdiHdr
->lockfile
= NULL
;
999 GdiHdr
->lockline
= 0;
1002 InterlockedDecrement((PLONG
)&GdiHdr
->Locks
);
1008 GDIOBJ_OwnedByCurrentProcess(HGDIOBJ ObjectHandle
)
1010 PGDI_TABLE_ENTRY Entry
;
1014 DPRINT("GDIOBJ_OwnedByCurrentProcess: ObjectHandle: 0x%08x\n", ObjectHandle
);
1016 if(!GDI_HANDLE_IS_STOCKOBJ(ObjectHandle
))
1018 ProcessId
= PsGetCurrentProcessId();
1020 Entry
= GDI_HANDLE_GET_ENTRY(HandleTable
, ObjectHandle
);
1021 Ret
= Entry
->KernelData
!= NULL
&&
1022 (Entry
->Type
& ~GDI_HANDLE_REUSE_MASK
) != 0 &&
1023 (HANDLE
)((ULONG_PTR
)Entry
->ProcessId
& ~0x1) == ProcessId
;
1032 GDIOBJ_ConvertToStockObj(HGDIOBJ
*hObj
)
1035 * FIXME !!!!! THIS FUNCTION NEEDS TO BE FIXED - IT IS NOT SAFE WHEN OTHER THREADS
1036 * MIGHT ATTEMPT TO LOCK THE OBJECT DURING THIS CALL!!!
1038 PGDI_TABLE_ENTRY Entry
;
1039 HANDLE ProcessId
, LockedProcessId
, PrevProcId
;
1047 DPRINT("GDIOBJ_ConvertToStockObj: hObj: 0x%08x\n", *hObj
);
1049 Thread
= PsGetCurrentThread();
1051 if(!GDI_HANDLE_IS_STOCKOBJ(*hObj
))
1053 ProcessId
= PsGetCurrentProcessId();
1054 LockedProcessId
= (HANDLE
)((ULONG_PTR
)ProcessId
| 0x1);
1056 Entry
= GDI_HANDLE_GET_ENTRY(HandleTable
, *hObj
);
1059 /* lock the object, we must not convert stock objects, so don't check!!! */
1060 PrevProcId
= InterlockedCompareExchangePointer(&Entry
->ProcessId
, LockedProcessId
, ProcessId
);
1061 if(PrevProcId
== ProcessId
)
1063 LONG NewType
, PrevType
, OldType
;
1065 /* we're locking an object that belongs to our process. First calculate
1066 the new object type including the stock object flag and then try to
1068 NewType
= GDI_HANDLE_GET_TYPE(*hObj
);
1069 NewType
|= NewType
>> 16;
1070 NewType
|= (ULONG_PTR
)(*hObj
) & GDI_HANDLE_REUSE_MASK
;
1072 /* This is the type that the object should have right now, save it */
1074 /* As the object should be a stock object, set it's flag, but only in the upper 16 bits */
1075 NewType
|= GDI_HANDLE_STOCK_MASK
;
1077 /* Try to exchange the type field - but only if the old (previous type) matches! */
1078 PrevType
= InterlockedCompareExchange(&Entry
->Type
, NewType
, OldType
);
1079 if(PrevType
== OldType
&& Entry
->KernelData
!= NULL
)
1081 PETHREAD PrevThread
;
1084 /* We successfully set the stock object flag.
1085 KernelData should never be NULL here!!! */
1086 ASSERT(Entry
->KernelData
);
1088 GdiHdr
= GDIBdyToHdr(Entry
->KernelData
);
1090 PrevThread
= GdiHdr
->LockingThread
;
1091 if(GdiHdr
->Locks
== 0 || PrevThread
== Thread
)
1093 /* dereference the process' object counter */
1094 if(PrevProcId
!= GDI_GLOBAL_PROCESS
)
1096 PEPROCESS OldProcess
;
1097 PW32PROCESS W32Process
;
1101 Status
= PsLookupProcessByProcessId((HANDLE
)((ULONG_PTR
)PrevProcId
& ~0x1), &OldProcess
);
1102 if(NT_SUCCESS(Status
))
1104 W32Process
= (PW32PROCESS
)OldProcess
->Win32Process
;
1105 if(W32Process
!= NULL
)
1107 InterlockedDecrement(&W32Process
->GDIObjects
);
1109 ObDereferenceObject(OldProcess
);
1113 /* remove the process id lock and make it global */
1114 InterlockedExchangePointer(&Entry
->ProcessId
, GDI_GLOBAL_PROCESS
);
1116 *hObj
= (HGDIOBJ
)((ULONG
)(*hObj
) | GDI_HANDLE_STOCK_MASK
);
1118 /* we're done, successfully converted the object */
1126 if(GdiHdr
->lockfile
!= NULL
)
1128 DPRINT1("[%d]Locked %s:%i by 0x%x (we're 0x%x)\n", Attempts
, GdiHdr
->lockfile
, GdiHdr
->lockline
, PrevThread
, Thread
);
1132 /* WTF?! The object is already locked by a different thread!
1133 Release the lock, wait a bit and try again!
1134 FIXME - we should give up after some time unless we want to wait forever! */
1135 InterlockedExchangePointer(&Entry
->ProcessId
, PrevProcId
);
1143 DPRINT1("Attempted to convert object 0x%x that is deleted! Should never get here!!!\n", hObj
);
1146 else if(PrevProcId
== LockedProcessId
)
1151 DPRINT1("[%d]Waiting on 0x%x\n", Attempts
, hObj
);
1154 /* the object is currently locked, wait some time and try again.
1155 FIXME - we shouldn't loop forever! Give up after some time! */
1162 DPRINT1("Attempted to convert invalid handle: 0x%x\n", hObj
);
1170 GDIOBJ_SetOwnership(HGDIOBJ ObjectHandle
, PEPROCESS NewOwner
)
1172 PGDI_TABLE_ENTRY Entry
;
1173 HANDLE ProcessId
, LockedProcessId
, PrevProcId
;
1179 DPRINT("GDIOBJ_SetOwnership: hObj: 0x%x, NewProcess: 0x%x\n", ObjectHandle
, (NewOwner
? PsGetProcessId(NewOwner
) : 0));
1181 Thread
= PsGetCurrentThread();
1183 if(!GDI_HANDLE_IS_STOCKOBJ(ObjectHandle
))
1185 ProcessId
= PsGetCurrentProcessId();
1186 LockedProcessId
= (HANDLE
)((ULONG_PTR
)ProcessId
| 0x1);
1188 Entry
= GDI_HANDLE_GET_ENTRY(HandleTable
, ObjectHandle
);
1191 /* lock the object, we must not convert stock objects, so don't check!!! */
1192 PrevProcId
= InterlockedCompareExchangePointer(&Entry
->ProcessId
, ProcessId
, LockedProcessId
);
1193 if(PrevProcId
== ProcessId
)
1195 PETHREAD PrevThread
;
1197 if((Entry
->Type
& ~GDI_HANDLE_REUSE_MASK
) != 0 && Entry
->KernelData
!= NULL
)
1199 PGDIOBJHDR GdiHdr
= GDIBdyToHdr(Entry
->KernelData
);
1201 PrevThread
= GdiHdr
->LockingThread
;
1202 if(GdiHdr
->Locks
== 0 || PrevThread
== Thread
)
1204 PEPROCESS OldProcess
;
1205 PW32PROCESS W32Process
;
1208 /* dereference the process' object counter */
1210 if((ULONG_PTR
)PrevProcId
& ~0x1)
1212 Status
= PsLookupProcessByProcessId((HANDLE
)((ULONG_PTR
)PrevProcId
& ~0x1), &OldProcess
);
1213 if(NT_SUCCESS(Status
))
1215 W32Process
= (PW32PROCESS
)OldProcess
->Win32Process
;
1216 if(W32Process
!= NULL
)
1218 InterlockedDecrement(&W32Process
->GDIObjects
);
1220 ObDereferenceObject(OldProcess
);
1224 if(NewOwner
!= NULL
)
1226 ProcessId
= PsGetProcessId(NewOwner
);
1228 /* Increase the new process' object counter */
1229 W32Process
= (PW32PROCESS
)NewOwner
->Win32Process
;
1230 if(W32Process
!= NULL
)
1232 InterlockedIncrement(&W32Process
->GDIObjects
);
1238 /* remove the process id lock and change it to the new process id */
1239 InterlockedExchangePointer(&Entry
->ProcessId
, ProcessId
);
1249 if(GdiHdr
->lockfile
!= NULL
)
1251 DPRINT1("[%d]Locked from %s:%i by 0x%x (we're 0x%x)\n", Attempts
, GdiHdr
->lockfile
, GdiHdr
->lockline
, PrevThread
, Thread
);
1255 /* WTF?! The object is already locked by a different thread!
1256 Release the lock, wait a bit and try again! DO reset the pid lock
1257 so we make sure we don't access invalid memory in case the object is
1258 being deleted in the meantime (because we don't have aquired a reference
1260 FIXME - we should give up after some time unless we want to wait forever! */
1261 InterlockedExchangePointer(&Entry
->ProcessId
, PrevProcId
);
1269 DPRINT1("Attempted to change ownership of an object 0x%x currently being destroyed!!!\n", ObjectHandle
);
1272 else if(PrevProcId
== LockedProcessId
)
1277 DPRINT1("[%d]Waiting on 0x%x\n", Attempts
, ObjectHandle
);
1280 /* the object is currently locked, wait some time and try again.
1281 FIXME - we shouldn't loop forever! Give up after some time! */
1286 else if(((ULONG_PTR
)PrevProcId
& ~0x1) == 0)
1288 /* allow changing ownership of global objects */
1290 LockedProcessId
= (HANDLE
)((ULONG_PTR
)ProcessId
| 0x1);
1293 else if((HANDLE
)((ULONG_PTR
)PrevProcId
& ~0x1) != PsGetCurrentProcessId())
1295 DPRINT1("Attempted to change ownership of object 0x%x (pid: 0x%x) from pid 0x%x!!!\n", ObjectHandle
, (ULONG_PTR
)PrevProcId
& ~0x1, PsGetCurrentProcessId());
1299 DPRINT1("Attempted to change owner of invalid handle: 0x%x\n", ObjectHandle
);
1305 GDIOBJ_CopyOwnership(HGDIOBJ CopyFrom
, HGDIOBJ CopyTo
)
1307 PGDI_TABLE_ENTRY FromEntry
;
1309 HANDLE FromProcessId
, FromLockedProcessId
, FromPrevProcId
;
1314 DPRINT("GDIOBJ_CopyOwnership: from: 0x%x, to: 0x%x\n", CopyFrom
, CopyTo
);
1316 Thread
= PsGetCurrentThread();
1318 if(!GDI_HANDLE_IS_STOCKOBJ(CopyFrom
) && !GDI_HANDLE_IS_STOCKOBJ(CopyTo
))
1320 FromEntry
= GDI_HANDLE_GET_ENTRY(HandleTable
, CopyFrom
);
1322 FromProcessId
= (HANDLE
)((ULONG_PTR
)FromEntry
->ProcessId
& ~0x1);
1323 FromLockedProcessId
= (HANDLE
)((ULONG_PTR
)FromProcessId
| 0x1);
1326 /* lock the object, we must not convert stock objects, so don't check!!! */
1327 FromPrevProcId
= InterlockedCompareExchangePointer(&FromEntry
->ProcessId
, FromProcessId
, FromLockedProcessId
);
1328 if(FromPrevProcId
== FromProcessId
)
1330 PETHREAD PrevThread
;
1333 if((FromEntry
->Type
& ~GDI_HANDLE_REUSE_MASK
) != 0 && FromEntry
->KernelData
!= NULL
)
1335 GdiHdr
= GDIBdyToHdr(FromEntry
->KernelData
);
1337 /* save the pointer to the calling thread so we know it was this thread
1338 that locked the object */
1339 PrevThread
= GdiHdr
->LockingThread
;
1340 if(GdiHdr
->Locks
== 0 || PrevThread
== Thread
)
1342 /* now let's change the ownership of the target object */
1344 if(((ULONG_PTR
)FromPrevProcId
& ~0x1) != 0)
1346 PEPROCESS ProcessTo
;
1348 if(NT_SUCCESS(PsLookupProcessByProcessId((HANDLE
)((ULONG_PTR
)FromPrevProcId
& ~0x1), &ProcessTo
)))
1350 GDIOBJ_SetOwnership(CopyTo
, ProcessTo
);
1351 ObDereferenceObject(ProcessTo
);
1356 /* mark the object as global */
1357 GDIOBJ_SetOwnership(CopyTo
, NULL
);
1360 InterlockedExchangePointer(&FromEntry
->ProcessId
, FromPrevProcId
);
1367 if(GdiHdr
->lockfile
!= NULL
)
1369 DPRINT1("[%d]Locked from %s:%i by 0x%x (we're 0x%x)\n", Attempts
, GdiHdr
->lockfile
, GdiHdr
->lockline
, PrevThread
, Thread
);
1373 /* WTF?! The object is already locked by a different thread!
1374 Release the lock, wait a bit and try again! DO reset the pid lock
1375 so we make sure we don't access invalid memory in case the object is
1376 being deleted in the meantime (because we don't have aquired a reference
1378 FIXME - we should give up after some time unless we want to wait forever! */
1379 InterlockedExchangePointer(&FromEntry
->ProcessId
, FromPrevProcId
);
1382 goto LockHandleFrom
;
1387 DPRINT1("Attempted to copy ownership from an object 0x%x currently being destroyed!!!\n", CopyFrom
);
1390 else if(FromPrevProcId
== FromLockedProcessId
)
1395 DPRINT1("[%d]Waiting on 0x%x\n", Attempts
, CopyFrom
);
1398 /* the object is currently locked, wait some time and try again.
1399 FIXME - we shouldn't loop forever! Give up after some time! */
1402 goto LockHandleFrom
;
1404 else if((HANDLE
)((ULONG_PTR
)FromPrevProcId
& ~0x1) != PsGetCurrentProcessId())
1406 /* FIXME - should we really allow copying ownership from objects that we don't even own? */
1407 DPRINT1("WARNING! Changing copying ownership of object 0x%x (pid: 0x%x) to pid 0x%x!!!\n", CopyFrom
, (ULONG_PTR
)FromPrevProcId
& ~0x1, PsGetCurrentProcessId());
1408 FromProcessId
= (HANDLE
)((ULONG_PTR
)FromPrevProcId
& ~0x1);
1409 FromLockedProcessId
= (HANDLE
)((ULONG_PTR
)FromProcessId
| 0x1);
1410 goto LockHandleFrom
;
1414 DPRINT1("Attempted to copy ownership from invalid handle: 0x%x\n", CopyFrom
);
1420 GDI_MapHandleTable(PEPROCESS Process
)
1422 ULONG TableSize
= sizeof(HandleTable
->Entries
);
1423 PVOID MappedGdiTable
= NULL
; /* FIXME - try preferred GDI_HANDLE_TABLE_BASE_ADDRESS? */
1424 NTSTATUS Status
= IntUserMapSharedSection(SessionSharedSectionPool
,
1431 if(NT_SUCCESS(Status
))
1433 return MappedGdiTable
;