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 */
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
55 GDICLEANUPPROC CleanupProc
;
56 } GDI_OBJ_INFO
, *PGDI_OBJ_INFO
;
59 * Dummy GDI Cleanup Callback
61 static BOOL INTERNAL_CALL
62 GDI_CleanupDummy(PVOID ObjectBody
)
67 /* Testing shows that regions are the most used GDIObj type,
68 so put that one first for performance */
70 GDI_OBJ_INFO ObjInfo
[] =
72 /* Type */ /* Size */ /* CleanupProc */
73 {GDI_OBJECT_TYPE_REGION
, sizeof(ROSRGNDATA
), RGNDATA_Cleanup
},
74 {GDI_OBJECT_TYPE_BITMAP
, sizeof(BITMAPOBJ
), BITMAP_Cleanup
},
75 {GDI_OBJECT_TYPE_DC
, sizeof(DC
), DC_Cleanup
},
76 {GDI_OBJECT_TYPE_PALETTE
, sizeof(PALGDI
), PALETTE_Cleanup
},
77 {GDI_OBJECT_TYPE_BRUSH
, sizeof(GDIBRUSHOBJ
), BRUSH_Cleanup
},
78 {GDI_OBJECT_TYPE_PEN
, sizeof(GDIBRUSHOBJ
), GDI_CleanupDummy
},
79 {GDI_OBJECT_TYPE_FONT
, sizeof(TEXTOBJ
), GDI_CleanupDummy
},
80 {GDI_OBJECT_TYPE_DCE
, sizeof(DCE
), DCE_Cleanup
},
81 {GDI_OBJECT_TYPE_DIRECTDRAW
, sizeof(DD_DIRECTDRAW
), DD_Cleanup
},
82 {GDI_OBJECT_TYPE_DD_SURFACE
, sizeof(DD_SURFACE
), DDSURF_Cleanup
},
83 {GDI_OBJECT_TYPE_EXTPEN
, sizeof(GDIBRUSHOBJ
), EXTPEN_Cleanup
},
84 /* FIXME do not use normal DC struct for this */
85 {GDI_OBJECT_TYPE_METADC
, sizeof(DC
), GDI_CleanupDummy
},
86 {GDI_OBJECT_TYPE_METAFILE
, sizeof(DC
), GDI_CleanupDummy
},
87 {GDI_OBJECT_TYPE_ENHMETAFILE
, 0, GDI_CleanupDummy
},
88 {GDI_OBJECT_TYPE_ENHMETADC
, 0, GDI_CleanupDummy
},
89 {GDI_OBJECT_TYPE_MEMDC
, 0, GDI_CleanupDummy
},
90 {GDI_OBJECT_TYPE_EMF
, 0, GDI_CleanupDummy
}
93 #define OBJTYPE_COUNT (sizeof(ObjInfo) / sizeof(ObjInfo[0]))
95 static LARGE_INTEGER ShortDelay
;
97 #define DelayExecution() \
98 DPRINT("%s:%i: Delay\n", __FILE__, __LINE__); \
99 KeDelayExecutionThread(KernelMode, FALSE, &ShortDelay)
102 BOOLEAN STDCALL
KiRosPrintAddress(PVOID Address
);
103 VOID STDCALL
KeRosDumpStackFrames(PULONG Frame
, ULONG FrameCount
);
104 ULONG STDCALL
KeRosGetStackFrames(PULONG Frames
, ULONG FrameCount
);
108 * Allocate GDI object table.
109 * \param Size - number of entries in the object table.
111 PGDI_HANDLE_TABLE INTERNAL_CALL
112 GDIOBJ_iAllocHandleTable(OUT PSECTION_OBJECT
*SectionObject
)
114 PGDI_HANDLE_TABLE HandleTable
= NULL
;
115 LARGE_INTEGER htSize
;
119 PGDI_TABLE_ENTRY Entry
;
122 ASSERT(SectionObject
!= NULL
);
124 htSize
.QuadPart
= sizeof(GDI_HANDLE_TABLE
);
126 Status
= MmCreateSection((PVOID
*)SectionObject
,
134 if (!NT_SUCCESS(Status
))
137 /* FIXME - use MmMapViewInSessionSpace once available! */
138 Status
= MmMapViewInSystemSpace(*SectionObject
,
139 (PVOID
*)&HandleTable
,
141 if (!NT_SUCCESS(Status
))
143 ObDereferenceObject(*SectionObject
);
144 *SectionObject
= NULL
;
148 RtlZeroMemory(HandleTable
, sizeof(GDI_HANDLE_TABLE
));
151 * initialize the free entry cache
153 InitializeSListHead(&HandleTable
->FreeEntriesHead
);
154 Entry
= &HandleTable
->Entries
[RESERVE_ENTRIES_COUNT
];
155 for(i
= GDI_HANDLE_COUNT
- 1; i
>= RESERVE_ENTRIES_COUNT
; i
--)
157 InterlockedPushEntrySList(&HandleTable
->FreeEntriesHead
, &HandleTable
->FreeEntries
[i
]);
160 HandleTable
->LookasideLists
= ExAllocatePoolWithTag(NonPagedPool
,
161 OBJTYPE_COUNT
* sizeof(PAGED_LOOKASIDE_LIST
),
163 if(HandleTable
->LookasideLists
== NULL
)
165 MmUnmapViewInSystemSpace(HandleTable
);
166 ObDereferenceObject(*SectionObject
);
167 *SectionObject
= NULL
;
171 for(ObjType
= 0; ObjType
< OBJTYPE_COUNT
; ObjType
++)
173 ExInitializePagedLookasideList(HandleTable
->LookasideLists
+ ObjType
, NULL
, NULL
, 0,
174 ObjInfo
[ObjType
].Size
+ sizeof(GDIOBJHDR
), TAG_GDIOBJ
, 0);
177 ShortDelay
.QuadPart
= -5000LL; /* FIXME - 0.5 ms? */
182 static __inline PPAGED_LOOKASIDE_LIST
183 FindLookasideList(PGDI_HANDLE_TABLE HandleTable
,
188 for (Index
= 0; Index
< OBJTYPE_COUNT
; Index
++)
190 if (ObjInfo
[Index
].Type
== ObjectType
)
192 return HandleTable
->LookasideLists
+ Index
;
196 DPRINT1("Can't find lookaside list for object type 0x%08x\n", ObjectType
);
202 RunCleanupCallback(PGDIOBJ pObj
, DWORD ObjectType
)
206 for (Index
= 0; Index
< OBJTYPE_COUNT
; Index
++)
208 if (ObjInfo
[Index
].Type
== ObjectType
)
210 return ((GDICLEANUPPROC
)ObjInfo
[Index
].CleanupProc
)(pObj
);
214 DPRINT1("Can't find cleanup callback for object type 0x%08x\n", ObjectType
);
218 static __inline ULONG
219 GetObjectSize(DWORD ObjectType
)
223 for (Index
= 0; Index
< OBJTYPE_COUNT
; Index
++)
225 if (ObjInfo
[Index
].Type
== ObjectType
)
227 return ObjInfo
[Index
].Size
;
231 DPRINT1("Can't find size for object type 0x%08x\n", ObjectType
);
237 static int leak_reported
= 0;
238 #define GDI_STACK_LEVELS 12
239 static ULONG GDIHandleAllocator
[GDI_HANDLE_COUNT
][GDI_STACK_LEVELS
];
240 struct DbgOpenGDIHandle
246 static struct DbgOpenGDIHandle h
[H
];
248 void IntDumpHandleTable(PGDI_HANDLE_TABLE HandleTable
)
250 int i
, n
= 0, j
, k
, J
;
254 DPRINT1("gdi handle abusers already reported!\n");
259 DPRINT1("reporting gdi handle abusers:\n");
261 /* step through GDI handle table and find out who our culprit is... */
262 for ( i
= RESERVE_ENTRIES_COUNT
; i
< GDI_HANDLE_COUNT
; i
++ )
264 for ( j
= 0; j
< n
; j
++ )
268 for ( k
= 0; k
< GDI_STACK_LEVELS
; k
++ )
270 if ( GDIHandleAllocator
[i
][k
]
271 != GDIHandleAllocator
[J
][k
] )
294 /* bubble sort time! weeeeee!! */
295 for ( i
= 0; i
< n
-1; i
++ )
297 if ( h
[i
].count
< h
[i
+1].count
)
299 struct DbgOpenGDIHandle t
;
303 while ( j
> 0 && h
[j
-1].count
< t
.count
)
308 /* print the worst offenders... */
309 DbgPrint ( "Worst GDI Handle leak offenders (out of %i unique locations):\n", n
);
310 for ( i
= 0; i
< n
&& h
[i
].count
> 1; i
++ )
313 DbgPrint ( " %i allocs: ", h
[i
].count
);
314 for ( j
= 0; j
< GDI_STACK_LEVELS
; j
++ )
316 ULONG Addr
= GDIHandleAllocator
[h
[i
].idx
][j
];
317 if ( !KiRosPrintAddress ( (PVOID
)Addr
) )
318 DbgPrint ( "<%X>", Addr
);
322 if ( i
< n
&& h
[i
].count
== 1 )
323 DbgPrint ( "(list terminated - the remaining entries have 1 allocation only)\n" );
325 #endif /* GDI_DEBUG */
328 * Allocate memory for GDI object and return handle to it.
330 * \param ObjectType - type of object \ref GDI object types
332 * \return Handle of the allocated object.
334 * \note Use GDIOBJ_Lock() to obtain pointer to the new object.
335 * \todo return the object pointer and lock it by default.
337 HGDIOBJ INTERNAL_CALL
339 GDIOBJ_AllocObjDbg(PGDI_HANDLE_TABLE HandleTable
, const char* file
, int line
, ULONG ObjectType
)
340 #else /* !GDI_DEBUG */
341 GDIOBJ_AllocObj(PGDI_HANDLE_TABLE HandleTable
, ULONG ObjectType
)
342 #endif /* GDI_DEBUG */
344 PW32PROCESS W32Process
;
345 PGDIOBJHDR newObject
;
346 PPAGED_LOOKASIDE_LIST LookasideList
;
347 HANDLE CurrentProcessId
, LockedProcessId
;
352 W32Process
= PsGetCurrentProcessWin32Process();
353 /* HACK HACK HACK: simplest-possible quota implementation - don't allow a process
354 to take too many GDI objects, itself. */
355 if ( W32Process
&& W32Process
->GDIObjects
>= 0x2710 )
358 ASSERT(ObjectType
!= GDI_OBJECT_TYPE_DONTCARE
);
360 LookasideList
= FindLookasideList(HandleTable
, ObjectType
);
361 if(LookasideList
!= NULL
)
363 newObject
= ExAllocateFromPagedLookasideList(LookasideList
);
364 if(newObject
!= NULL
)
366 PSLIST_ENTRY FreeEntry
;
367 PGDI_TABLE_ENTRY Entry
;
371 CurrentProcessId
= PsGetCurrentProcessId();
372 LockedProcessId
= (HANDLE
)((ULONG_PTR
)CurrentProcessId
| 0x1);
374 newObject
->LockingThread
= NULL
;
375 newObject
->Locks
= 0;
378 newObject
->createdfile
= file
;
379 newObject
->createdline
= line
;
380 newObject
->lockfile
= NULL
;
381 newObject
->lockline
= 0;
384 ObjectBody
= GDIHdrToBdy(newObject
);
386 RtlZeroMemory(ObjectBody
, GetObjectSize(ObjectType
));
388 TypeInfo
= (ObjectType
& GDI_HANDLE_TYPE_MASK
) | (ObjectType
>> 16);
390 FreeEntry
= InterlockedPopEntrySList(&HandleTable
->FreeEntriesHead
);
391 if(FreeEntry
!= NULL
)
396 /* calculate the entry from the address of the entry in the free slot array */
397 Index
= ((ULONG_PTR
)FreeEntry
- (ULONG_PTR
)&HandleTable
->FreeEntries
[0]) /
398 sizeof(HandleTable
->FreeEntries
[0]);
399 Entry
= &HandleTable
->Entries
[Index
];
402 PrevProcId
= InterlockedCompareExchangePointer(&Entry
->ProcessId
, LockedProcessId
, 0);
403 if(PrevProcId
== NULL
)
407 ASSERT(Entry
->KernelData
== NULL
);
409 Entry
->KernelData
= ObjectBody
;
411 /* copy the reuse-counter */
412 TypeInfo
|= Entry
->Type
& GDI_HANDLE_REUSE_MASK
;
414 /* we found a free entry, no need to exchange this field atomically
415 since we're holding the lock */
416 Entry
->Type
= TypeInfo
;
418 /* unlock the entry */
419 (void)InterlockedExchangePointer(&Entry
->ProcessId
, CurrentProcessId
);
422 memset ( GDIHandleAllocator
[Index
], 0xcd, GDI_STACK_LEVELS
* sizeof(ULONG
) );
423 KeRosGetStackFrames ( GDIHandleAllocator
[Index
], GDI_STACK_LEVELS
);
424 #endif /* GDI_DEBUG */
426 if(W32Process
!= NULL
)
428 InterlockedIncrement(&W32Process
->GDIObjects
);
430 Handle
= (HGDIOBJ
)((Index
& 0xFFFF) | (TypeInfo
& (GDI_HANDLE_TYPE_MASK
| GDI_HANDLE_REUSE_MASK
)));
432 DPRINT("GDIOBJ_AllocObj: 0x%x ob: 0x%x\n", Handle
, ObjectBody
);
440 DPRINT1("[%d]Waiting on handle in index 0x%x\n", Attempts
, Index
);
443 /* damn, someone is trying to lock the object even though it doesn't
444 eve nexist anymore, wait a little and try again!
445 FIXME - we shouldn't loop forever! Give up after some time! */
452 ExFreeToPagedLookasideList(LookasideList
, newObject
);
453 DPRINT1("Failed to insert gdi object into the handle table, no handles left!\n");
455 IntDumpHandleTable(HandleTable
);
456 #endif /* GDI_DEBUG */
460 DPRINT1("Not enough memory to allocate gdi object!\n");
465 DPRINT1("Failed to find lookaside list for object type 0x%x\n", ObjectType
);
471 * Free memory allocated for the GDI object. For each object type this function calls the
472 * appropriate cleanup routine.
474 * \param hObj - handle of the object to be deleted.
476 * \return Returns TRUE if succesful.
477 * \return Returns FALSE if the cleanup routine returned FALSE or the object doesn't belong
478 * to the calling process.
482 GDIOBJ_FreeObjDbg(PGDI_HANDLE_TABLE HandleTable
, const char* file
, int line
, HGDIOBJ hObj
, DWORD ObjectType
)
483 #else /* !GDI_DEBUG */
484 GDIOBJ_FreeObj(PGDI_HANDLE_TABLE HandleTable
, HGDIOBJ hObj
, DWORD ObjectType
)
485 #endif /* GDI_DEBUG */
487 PGDI_TABLE_ENTRY Entry
;
488 PPAGED_LOOKASIDE_LIST LookasideList
;
489 HANDLE ProcessId
, LockedProcessId
, PrevProcId
;
496 DPRINT("GDIOBJ_FreeObj: hObj: 0x%08x\n", hObj
);
498 if(GDI_HANDLE_IS_STOCKOBJ(hObj
))
500 DPRINT1("GDIOBJ_FreeObj() failed, can't delete stock object handle: 0x%x !!!\n", hObj
);
502 DPRINT1("-> called from %s:%i\n", file
, line
);
507 ProcessId
= PsGetCurrentProcessId();
508 LockedProcessId
= (HANDLE
)((ULONG_PTR
)ProcessId
| 0x1);
510 Silent
= (ObjectType
& GDI_OBJECT_TYPE_SILENT
);
511 ObjectType
&= ~GDI_OBJECT_TYPE_SILENT
;
513 ExpectedType
= ((ObjectType
!= GDI_OBJECT_TYPE_DONTCARE
) ? ObjectType
: 0);
515 Entry
= GDI_HANDLE_GET_ENTRY(HandleTable
, hObj
);
518 /* lock the object, we must not delete global objects, so don't exchange the locking
519 process ID to zero when attempting to lock a global object... */
520 PrevProcId
= InterlockedCompareExchangePointer(&Entry
->ProcessId
, LockedProcessId
, ProcessId
);
521 if(PrevProcId
== ProcessId
)
523 if(Entry
->Type
!= 0 && Entry
->KernelData
!= NULL
&&
524 (ExpectedType
== 0 || ((Entry
->Type
<< 16) == ExpectedType
)) &&
525 (Entry
->Type
& (GDI_HANDLE_TYPE_MASK
| GDI_HANDLE_REUSE_MASK
)) ==
526 ((ULONG_PTR
)hObj
& (GDI_HANDLE_TYPE_MASK
| GDI_HANDLE_REUSE_MASK
)))
530 GdiHdr
= GDIBdyToHdr(Entry
->KernelData
);
532 if(GdiHdr
->Locks
== 0)
535 PW32PROCESS W32Process
= PsGetCurrentProcessWin32Process();
536 ULONG Type
= Entry
->Type
<< 16;
538 /* Clear the type field so when unlocking the handle it gets finally deleted and increment reuse counter */
539 Entry
->Type
= ((Entry
->Type
>> GDI_HANDLE_REUSECNT_SHIFT
) + 1) << GDI_HANDLE_REUSECNT_SHIFT
;
540 Entry
->KernelData
= NULL
;
542 /* unlock the handle slot */
543 (void)InterlockedExchangePointer(&Entry
->ProcessId
, NULL
);
545 /* push this entry to the free list */
546 InterlockedPushEntrySList(&HandleTable
->FreeEntriesHead
,
547 &HandleTable
->FreeEntries
[GDI_ENTRY_TO_INDEX(HandleTable
, Entry
)]);
549 if(W32Process
!= NULL
)
551 InterlockedDecrement(&W32Process
->GDIObjects
);
554 /* call the cleanup routine. */
555 Ret
= RunCleanupCallback(GDIHdrToBdy(GdiHdr
), Type
);
557 /* Now it's time to free the memory */
558 LookasideList
= FindLookasideList(HandleTable
, Type
);
559 if(LookasideList
!= NULL
)
561 ExFreeToPagedLookasideList(LookasideList
, GdiHdr
);
569 * The object is currently locked, so freeing is forbidden!
571 DPRINT1("GdiHdr->Locks: %d\n", GdiHdr
->Locks
);
573 DPRINT1("Locked from: %s:%d\n", GdiHdr
->lockfile
, GdiHdr
->lockline
);
580 if((Entry
->Type
& ~GDI_HANDLE_REUSE_MASK
) != 0)
582 DPRINT1("Attempted to delete object 0x%x, type mismatch (0x%x : 0x%x)\n", hObj
, ObjectType
, ExpectedType
);
583 KeRosDumpStackFrames(NULL
, 20);
587 DPRINT1("Attempted to delete object 0x%x which was already deleted!\n", hObj
);
588 KeRosDumpStackFrames(NULL
, 20);
590 (void)InterlockedExchangePointer(&Entry
->ProcessId
, PrevProcId
);
593 else if(PrevProcId
== LockedProcessId
)
598 DPRINT1("[%d]Waiting on 0x%x\n", Attempts
, hObj
);
601 /* the object is currently locked, wait some time and try again.
602 FIXME - we shouldn't loop forever! Give up after some time! */
611 if(((ULONG_PTR
)PrevProcId
& ~0x1) == 0)
613 DPRINT1("Attempted to free global gdi handle 0x%x, caller needs to get ownership first!!!\n", hObj
);
614 KeRosDumpStackFrames(NULL
, 20);
618 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);
619 KeRosDumpStackFrames(NULL
, 20);
622 DPRINT1("-> called from %s:%i\n", file
, line
);
632 * \param hObject object handle
633 * \return if the function fails the returned value is FALSE.
636 NtGdiDeleteObject(HGDIOBJ hObject
)
638 DPRINT("NtGdiDeleteObject handle 0x%08x\n", hObject
);
640 return NULL
!= hObject
641 ? GDIOBJ_FreeObj(GdiHandleTable
, hObject
, GDI_OBJECT_TYPE_DONTCARE
) : FALSE
;
645 * Internal function. Called when the process is destroyed to free the remaining GDI handles.
646 * \param Process - PID of the process that will be destroyed.
649 GDI_CleanupForProcess (PGDI_HANDLE_TABLE HandleTable
, struct _EPROCESS
*Process
)
651 PGDI_TABLE_ENTRY Entry
, End
;
652 PEPROCESS CurrentProcess
;
653 PW32PROCESS W32Process
;
655 ULONG Index
= RESERVE_ENTRIES_COUNT
;
657 DPRINT("Starting CleanupForProcess prochandle %x Pid %d\n", Process
, Process
->UniqueProcessId
);
658 CurrentProcess
= PsGetCurrentProcess();
659 if (CurrentProcess
!= Process
)
661 KeAttachProcess(&Process
->Pcb
);
663 W32Process
= (PW32PROCESS
)Process
->Win32Process
;
666 if(W32Process
->GDIObjects
> 0)
668 /* FIXME - Instead of building the handle here and delete it using GDIOBJ_FreeObj
669 we should delete it directly here! */
670 ProcId
= Process
->UniqueProcessId
;
672 End
= &HandleTable
->Entries
[GDI_HANDLE_COUNT
];
673 for(Entry
= &HandleTable
->Entries
[RESERVE_ENTRIES_COUNT
];
677 /* ignore the lock bit */
678 if((HANDLE
)((ULONG_PTR
)Entry
->ProcessId
& ~0x1) == ProcId
&& (Entry
->Type
& ~GDI_HANDLE_REUSE_MASK
) != 0)
680 HGDIOBJ ObjectHandle
;
682 /* Create the object handle for the entry, the upper 16 bit of the
683 Type field includes the type of the object including the stock
684 object flag - but since stock objects don't have a process id we can
685 simply ignore this fact here. */
686 ObjectHandle
= (HGDIOBJ
)(Index
| (Entry
->Type
& 0xFFFF0000));
688 if(GDIOBJ_FreeObj(HandleTable
, ObjectHandle
, GDI_OBJECT_TYPE_DONTCARE
) &&
689 W32Process
->GDIObjects
== 0)
691 /* there are no more gdi handles for this process, bail */
698 if (CurrentProcess
!= Process
)
703 DPRINT("Completed cleanup for process %d\n", Process
->UniqueProcessId
);
709 * Return pointer to the object by handle.
711 * \param hObj Object handle
712 * \return Pointer to the object.
714 * \note Process can only get pointer to the objects it created or global objects.
716 * \todo Get rid of the ObjectType parameter!
718 PGDIOBJ INTERNAL_CALL
720 GDIOBJ_LockObjDbg (PGDI_HANDLE_TABLE HandleTable
, const char* file
, int line
, HGDIOBJ hObj
, DWORD ObjectType
)
721 #else /* !GDI_DEBUG */
722 GDIOBJ_LockObj (PGDI_HANDLE_TABLE HandleTable
, HGDIOBJ hObj
, DWORD ObjectType
)
723 #endif /* GDI_DEBUG */
726 PGDI_TABLE_ENTRY HandleEntry
;
727 HANDLE ProcessId
, HandleProcessId
, LockedProcessId
, PrevProcId
;
728 PGDIOBJ Object
= NULL
;
730 HandleIndex
= GDI_HANDLE_GET_INDEX(hObj
);
732 /* Check that the handle index is valid. */
733 if (HandleIndex
>= GDI_HANDLE_COUNT
)
736 HandleEntry
= &HandleTable
->Entries
[HandleIndex
];
738 ProcessId
= (HANDLE
)((ULONG_PTR
)PsGetCurrentProcessId() & ~1);
739 HandleProcessId
= (HANDLE
)((ULONG_PTR
)HandleEntry
->ProcessId
& ~1);
741 /* Check for invalid owner. */
742 if (ProcessId
!= HandleProcessId
&& HandleProcessId
!= NULL
)
748 * Prevent the thread from being terminated during the locking process.
749 * It would result in undesired effects and inconsistency of the global
753 KeEnterCriticalRegion();
756 * Loop until we either successfully lock the handle entry & object or
757 * fail some of the check.
762 /* Lock the handle table entry. */
763 LockedProcessId
= (HANDLE
)((ULONG_PTR
)HandleProcessId
| 0x1);
764 PrevProcId
= InterlockedCompareExchangePointer(&HandleEntry
->ProcessId
,
768 if (PrevProcId
== HandleProcessId
)
770 LONG HandleType
= HandleEntry
->Type
<< 16;
773 * We're locking an object that belongs to our process or it's a
774 * global object if HandleProcessId is 0 here.
777 /* FIXME: Check the upper 16-bits of handle number! */
778 if (HandleType
!= 0 && HandleEntry
->KernelData
!= NULL
&&
779 (ObjectType
== GDI_OBJECT_TYPE_DONTCARE
||
780 HandleType
== ObjectType
))
782 PGDIOBJHDR GdiHdr
= GDIBdyToHdr(HandleEntry
->KernelData
);
783 PETHREAD Thread
= PsGetCurrentThread();
785 if (GdiHdr
->Locks
== 0)
787 GdiHdr
->LockingThread
= Thread
;
790 GdiHdr
->lockfile
= file
;
791 GdiHdr
->lockline
= line
;
793 Object
= HandleEntry
->KernelData
;
797 InterlockedIncrement((PLONG
)&GdiHdr
->Locks
);
798 if (GdiHdr
->LockingThread
!= Thread
)
800 InterlockedDecrement((PLONG
)&GdiHdr
->Locks
);
802 /* Unlock the handle table entry. */
803 (void)InterlockedExchangePointer(&HandleEntry
->ProcessId
, PrevProcId
);
808 Object
= HandleEntry
->KernelData
;
814 * Debugging code. Report attempts to lock deleted handles and
815 * locking type mismatches.
818 if ((HandleType
& ~GDI_HANDLE_REUSE_MASK
) == 0)
820 DPRINT1("Attempted to lock object 0x%x that is deleted!\n", hObj
);
821 KeRosDumpStackFrames(NULL
, 20);
825 DPRINT1("Attempted to lock object 0x%x, type mismatch (0x%x : 0x%x)\n",
826 hObj
, HandleType
& ~GDI_HANDLE_REUSE_MASK
, ObjectType
& ~GDI_HANDLE_REUSE_MASK
);
828 KeRosDumpStackFrames(NULL
, 20);
831 DPRINT1("-> called from %s:%i\n", file
, line
);
835 /* Unlock the handle table entry. */
836 (void)InterlockedExchangePointer(&HandleEntry
->ProcessId
, PrevProcId
);
843 * The handle is currently locked, wait some time and try again.
851 KeLeaveCriticalRegion();
858 * Return pointer to the object by handle (and allow sharing of the handle
861 * \param hObj Object handle
862 * \return Pointer to the object.
864 * \note Process can only get pointer to the objects it created or global objects.
866 * \todo Get rid of the ObjectType parameter!
868 PGDIOBJ INTERNAL_CALL
870 GDIOBJ_ShareLockObjDbg (PGDI_HANDLE_TABLE HandleTable
, const char* file
, int line
, HGDIOBJ hObj
, DWORD ObjectType
)
871 #else /* !GDI_DEBUG */
872 GDIOBJ_ShareLockObj (PGDI_HANDLE_TABLE HandleTable
, HGDIOBJ hObj
, DWORD ObjectType
)
873 #endif /* GDI_DEBUG */
876 PGDI_TABLE_ENTRY HandleEntry
;
877 HANDLE ProcessId
, HandleProcessId
, LockedProcessId
, PrevProcId
;
878 PGDIOBJ Object
= NULL
;
880 HandleIndex
= GDI_HANDLE_GET_INDEX(hObj
);
882 /* Check that the handle index is valid. */
883 if (HandleIndex
>= GDI_HANDLE_COUNT
)
886 HandleEntry
= &HandleTable
->Entries
[HandleIndex
];
888 ProcessId
= (HANDLE
)((ULONG_PTR
)PsGetCurrentProcessId() & ~1);
889 HandleProcessId
= (HANDLE
)((ULONG_PTR
)HandleEntry
->ProcessId
& ~1);
891 /* Check for invalid owner. */
892 if (ProcessId
!= HandleProcessId
&& HandleProcessId
!= NULL
)
898 * Prevent the thread from being terminated during the locking process.
899 * It would result in undesired effects and inconsistency of the global
903 KeEnterCriticalRegion();
906 * Loop until we either successfully lock the handle entry & object or
907 * fail some of the check.
912 /* Lock the handle table entry. */
913 LockedProcessId
= (HANDLE
)((ULONG_PTR
)HandleProcessId
| 0x1);
914 PrevProcId
= InterlockedCompareExchangePointer(&HandleEntry
->ProcessId
,
918 if (PrevProcId
== HandleProcessId
)
920 LONG HandleType
= HandleEntry
->Type
<< 16;
923 * We're locking an object that belongs to our process or it's a
924 * global object if HandleProcessId is 0 here.
927 /* FIXME: Check the upper 16-bits of handle number! */
928 if (HandleType
!= 0 && HandleEntry
->KernelData
!= NULL
&&
929 (ObjectType
== GDI_OBJECT_TYPE_DONTCARE
||
930 HandleType
== ObjectType
))
932 PGDIOBJHDR GdiHdr
= GDIBdyToHdr(HandleEntry
->KernelData
);
935 if (InterlockedIncrement((PLONG
)&GdiHdr
->Locks
) == 1)
937 GdiHdr
->lockfile
= file
;
938 GdiHdr
->lockline
= line
;
941 InterlockedIncrement((PLONG
)&GdiHdr
->Locks
);
943 Object
= HandleEntry
->KernelData
;
948 * Debugging code. Report attempts to lock deleted handles and
949 * locking type mismatches.
952 if ((HandleType
& ~GDI_HANDLE_REUSE_MASK
) == 0)
954 DPRINT1("Attempted to lock object 0x%x that is deleted!\n", hObj
);
955 KeRosDumpStackFrames(NULL
, 20);
959 DPRINT1("Attempted to lock object 0x%x, type mismatch (0x%x : 0x%x)\n",
960 hObj
, HandleType
& ~GDI_HANDLE_REUSE_MASK
, ObjectType
& ~GDI_HANDLE_REUSE_MASK
);
962 KeRosDumpStackFrames(NULL
, 20);
965 DPRINT1("-> called from %s:%i\n", file
, line
);
969 /* Unlock the handle table entry. */
970 (void)InterlockedExchangePointer(&HandleEntry
->ProcessId
, PrevProcId
);
977 * The handle is currently locked, wait some time and try again.
985 KeLeaveCriticalRegion();
992 * Release GDI object. Every object locked by GDIOBJ_LockObj() must be unlocked. You should unlock the object
993 * as soon as you don't need to have access to it's data.
995 * \param Object Object pointer (as returned by GDIOBJ_LockObj).
998 GDIOBJ_UnlockObjByPtr(PGDI_HANDLE_TABLE HandleTable
, PGDIOBJ Object
)
1000 PGDIOBJHDR GdiHdr
= GDIBdyToHdr(Object
);
1002 if (InterlockedDecrement((PLONG
)&GdiHdr
->Locks
) == 0)
1004 GdiHdr
->lockfile
= NULL
;
1005 GdiHdr
->lockline
= 0;
1008 InterlockedDecrement((PLONG
)&GdiHdr
->Locks
);
1013 GDIOBJ_OwnedByCurrentProcess(PGDI_HANDLE_TABLE HandleTable
, HGDIOBJ ObjectHandle
)
1015 PGDI_TABLE_ENTRY Entry
;
1019 DPRINT("GDIOBJ_OwnedByCurrentProcess: ObjectHandle: 0x%08x\n", ObjectHandle
);
1021 if(!GDI_HANDLE_IS_STOCKOBJ(ObjectHandle
))
1023 ProcessId
= PsGetCurrentProcessId();
1025 Entry
= GDI_HANDLE_GET_ENTRY(HandleTable
, ObjectHandle
);
1026 Ret
= Entry
->KernelData
!= NULL
&&
1027 (Entry
->Type
& ~GDI_HANDLE_REUSE_MASK
) != 0 &&
1028 (HANDLE
)((ULONG_PTR
)Entry
->ProcessId
& ~0x1) == ProcessId
;
1037 GDIOBJ_ConvertToStockObj(PGDI_HANDLE_TABLE HandleTable
, HGDIOBJ
*hObj
)
1040 * FIXME !!!!! THIS FUNCTION NEEDS TO BE FIXED - IT IS NOT SAFE WHEN OTHER THREADS
1041 * MIGHT ATTEMPT TO LOCK THE OBJECT DURING THIS CALL!!!
1043 PGDI_TABLE_ENTRY Entry
;
1044 HANDLE ProcessId
, LockedProcessId
, PrevProcId
;
1052 DPRINT("GDIOBJ_ConvertToStockObj: hObj: 0x%08x\n", *hObj
);
1054 Thread
= PsGetCurrentThread();
1056 if(!GDI_HANDLE_IS_STOCKOBJ(*hObj
))
1058 ProcessId
= PsGetCurrentProcessId();
1059 LockedProcessId
= (HANDLE
)((ULONG_PTR
)ProcessId
| 0x1);
1061 Entry
= GDI_HANDLE_GET_ENTRY(HandleTable
, *hObj
);
1064 /* lock the object, we must not convert stock objects, so don't check!!! */
1065 PrevProcId
= InterlockedCompareExchangePointer(&Entry
->ProcessId
, LockedProcessId
, ProcessId
);
1066 if(PrevProcId
== ProcessId
)
1068 LONG NewType
, PrevType
, OldType
;
1070 /* we're locking an object that belongs to our process. First calculate
1071 the new object type including the stock object flag and then try to
1073 NewType
= GDI_HANDLE_GET_TYPE(*hObj
);
1074 NewType
|= NewType
>> 16;
1075 NewType
|= (ULONG_PTR
)(*hObj
) & GDI_HANDLE_REUSE_MASK
;
1077 /* This is the type that the object should have right now, save it */
1079 /* As the object should be a stock object, set it's flag, but only in the upper 16 bits */
1080 NewType
|= GDI_HANDLE_STOCK_MASK
;
1082 /* Try to exchange the type field - but only if the old (previous type) matches! */
1083 PrevType
= InterlockedCompareExchange(&Entry
->Type
, NewType
, OldType
);
1084 if(PrevType
== OldType
&& Entry
->KernelData
!= NULL
)
1086 PETHREAD PrevThread
;
1089 /* We successfully set the stock object flag.
1090 KernelData should never be NULL here!!! */
1091 ASSERT(Entry
->KernelData
);
1093 GdiHdr
= GDIBdyToHdr(Entry
->KernelData
);
1095 PrevThread
= GdiHdr
->LockingThread
;
1096 if(GdiHdr
->Locks
== 0 || PrevThread
== Thread
)
1098 /* dereference the process' object counter */
1099 if(PrevProcId
!= GDI_GLOBAL_PROCESS
)
1101 PEPROCESS OldProcess
;
1102 PW32PROCESS W32Process
;
1106 Status
= PsLookupProcessByProcessId((HANDLE
)((ULONG_PTR
)PrevProcId
& ~0x1), &OldProcess
);
1107 if(NT_SUCCESS(Status
))
1109 W32Process
= (PW32PROCESS
)OldProcess
->Win32Process
;
1110 if(W32Process
!= NULL
)
1112 InterlockedDecrement(&W32Process
->GDIObjects
);
1114 ObDereferenceObject(OldProcess
);
1118 /* remove the process id lock and make it global */
1119 (void)InterlockedExchangePointer(&Entry
->ProcessId
, GDI_GLOBAL_PROCESS
);
1121 *hObj
= (HGDIOBJ
)((ULONG
)(*hObj
) | GDI_HANDLE_STOCK_MASK
);
1123 /* we're done, successfully converted the object */
1131 if(GdiHdr
->lockfile
!= NULL
)
1133 DPRINT1("[%d]Locked %s:%i by 0x%x (we're 0x%x)\n", Attempts
, GdiHdr
->lockfile
, GdiHdr
->lockline
, PrevThread
, Thread
);
1137 /* WTF?! The object is already locked by a different thread!
1138 Release the lock, wait a bit and try again!
1139 FIXME - we should give up after some time unless we want to wait forever! */
1140 (void)InterlockedExchangePointer(&Entry
->ProcessId
, PrevProcId
);
1148 DPRINT1("Attempted to convert object 0x%x that is deleted! Should never get here!!!\n", hObj
);
1151 else if(PrevProcId
== LockedProcessId
)
1156 DPRINT1("[%d]Waiting on 0x%x\n", Attempts
, hObj
);
1159 /* the object is currently locked, wait some time and try again.
1160 FIXME - we shouldn't loop forever! Give up after some time! */
1167 DPRINT1("Attempted to convert invalid handle: 0x%x\n", hObj
);
1175 GDIOBJ_SetOwnership(PGDI_HANDLE_TABLE HandleTable
, HGDIOBJ ObjectHandle
, PEPROCESS NewOwner
)
1177 PGDI_TABLE_ENTRY Entry
;
1178 HANDLE ProcessId
, LockedProcessId
, PrevProcId
;
1184 DPRINT("GDIOBJ_SetOwnership: hObj: 0x%x, NewProcess: 0x%x\n", ObjectHandle
, (NewOwner
? PsGetProcessId(NewOwner
) : 0));
1186 Thread
= PsGetCurrentThread();
1188 if(!GDI_HANDLE_IS_STOCKOBJ(ObjectHandle
))
1190 ProcessId
= PsGetCurrentProcessId();
1191 LockedProcessId
= (HANDLE
)((ULONG_PTR
)ProcessId
| 0x1);
1193 Entry
= GDI_HANDLE_GET_ENTRY(HandleTable
, ObjectHandle
);
1196 /* lock the object, we must not convert stock objects, so don't check!!! */
1197 PrevProcId
= InterlockedCompareExchangePointer(&Entry
->ProcessId
, ProcessId
, LockedProcessId
);
1198 if(PrevProcId
== ProcessId
)
1200 PETHREAD PrevThread
;
1202 if((Entry
->Type
& ~GDI_HANDLE_REUSE_MASK
) != 0 && Entry
->KernelData
!= NULL
)
1204 PGDIOBJHDR GdiHdr
= GDIBdyToHdr(Entry
->KernelData
);
1206 PrevThread
= GdiHdr
->LockingThread
;
1207 if(GdiHdr
->Locks
== 0 || PrevThread
== Thread
)
1209 PEPROCESS OldProcess
;
1210 PW32PROCESS W32Process
;
1213 /* dereference the process' object counter */
1215 if((ULONG_PTR
)PrevProcId
& ~0x1)
1217 Status
= PsLookupProcessByProcessId((HANDLE
)((ULONG_PTR
)PrevProcId
& ~0x1), &OldProcess
);
1218 if(NT_SUCCESS(Status
))
1220 W32Process
= (PW32PROCESS
)OldProcess
->Win32Process
;
1221 if(W32Process
!= NULL
)
1223 InterlockedDecrement(&W32Process
->GDIObjects
);
1225 ObDereferenceObject(OldProcess
);
1229 if(NewOwner
!= NULL
)
1231 ProcessId
= PsGetProcessId(NewOwner
);
1233 /* Increase the new process' object counter */
1234 W32Process
= (PW32PROCESS
)NewOwner
->Win32Process
;
1235 if(W32Process
!= NULL
)
1237 InterlockedIncrement(&W32Process
->GDIObjects
);
1243 /* remove the process id lock and change it to the new process id */
1244 (void)InterlockedExchangePointer(&Entry
->ProcessId
, ProcessId
);
1254 if(GdiHdr
->lockfile
!= NULL
)
1256 DPRINT1("[%d]Locked from %s:%i by 0x%x (we're 0x%x)\n", Attempts
, GdiHdr
->lockfile
, GdiHdr
->lockline
, PrevThread
, Thread
);
1260 /* WTF?! The object is already locked by a different thread!
1261 Release the lock, wait a bit and try again! DO reset the pid lock
1262 so we make sure we don't access invalid memory in case the object is
1263 being deleted in the meantime (because we don't have aquired a reference
1265 FIXME - we should give up after some time unless we want to wait forever! */
1266 (void)InterlockedExchangePointer(&Entry
->ProcessId
, PrevProcId
);
1274 DPRINT1("Attempted to change ownership of an object 0x%x currently being destroyed!!!\n", ObjectHandle
);
1277 else if(PrevProcId
== LockedProcessId
)
1282 DPRINT1("[%d]Waiting on 0x%x\n", Attempts
, ObjectHandle
);
1285 /* the object is currently locked, wait some time and try again.
1286 FIXME - we shouldn't loop forever! Give up after some time! */
1291 else if(((ULONG_PTR
)PrevProcId
& ~0x1) == 0)
1293 /* allow changing ownership of global objects */
1295 LockedProcessId
= (HANDLE
)((ULONG_PTR
)ProcessId
| 0x1);
1298 else if((HANDLE
)((ULONG_PTR
)PrevProcId
& ~0x1) != PsGetCurrentProcessId())
1300 DPRINT1("Attempted to change ownership of object 0x%x (pid: 0x%x) from pid 0x%x!!!\n", ObjectHandle
, (ULONG_PTR
)PrevProcId
& ~0x1, PsGetCurrentProcessId());
1304 DPRINT1("Attempted to change owner of invalid handle: 0x%x\n", ObjectHandle
);
1310 GDIOBJ_CopyOwnership(PGDI_HANDLE_TABLE HandleTable
, HGDIOBJ CopyFrom
, HGDIOBJ CopyTo
)
1312 PGDI_TABLE_ENTRY FromEntry
;
1314 HANDLE FromProcessId
, FromLockedProcessId
, FromPrevProcId
;
1319 DPRINT("GDIOBJ_CopyOwnership: from: 0x%x, to: 0x%x\n", CopyFrom
, CopyTo
);
1321 Thread
= PsGetCurrentThread();
1323 if(!GDI_HANDLE_IS_STOCKOBJ(CopyFrom
) && !GDI_HANDLE_IS_STOCKOBJ(CopyTo
))
1325 FromEntry
= GDI_HANDLE_GET_ENTRY(HandleTable
, CopyFrom
);
1327 FromProcessId
= (HANDLE
)((ULONG_PTR
)FromEntry
->ProcessId
& ~0x1);
1328 FromLockedProcessId
= (HANDLE
)((ULONG_PTR
)FromProcessId
| 0x1);
1331 /* lock the object, we must not convert stock objects, so don't check!!! */
1332 FromPrevProcId
= InterlockedCompareExchangePointer(&FromEntry
->ProcessId
, FromProcessId
, FromLockedProcessId
);
1333 if(FromPrevProcId
== FromProcessId
)
1335 PETHREAD PrevThread
;
1338 if((FromEntry
->Type
& ~GDI_HANDLE_REUSE_MASK
) != 0 && FromEntry
->KernelData
!= NULL
)
1340 GdiHdr
= GDIBdyToHdr(FromEntry
->KernelData
);
1342 /* save the pointer to the calling thread so we know it was this thread
1343 that locked the object */
1344 PrevThread
= GdiHdr
->LockingThread
;
1345 if(GdiHdr
->Locks
== 0 || PrevThread
== Thread
)
1347 /* now let's change the ownership of the target object */
1349 if(((ULONG_PTR
)FromPrevProcId
& ~0x1) != 0)
1351 PEPROCESS ProcessTo
;
1353 if(NT_SUCCESS(PsLookupProcessByProcessId((HANDLE
)((ULONG_PTR
)FromPrevProcId
& ~0x1), &ProcessTo
)))
1355 GDIOBJ_SetOwnership(HandleTable
, CopyTo
, ProcessTo
);
1356 ObDereferenceObject(ProcessTo
);
1361 /* mark the object as global */
1362 GDIOBJ_SetOwnership(HandleTable
, CopyTo
, NULL
);
1365 (void)InterlockedExchangePointer(&FromEntry
->ProcessId
, FromPrevProcId
);
1372 if(GdiHdr
->lockfile
!= NULL
)
1374 DPRINT1("[%d]Locked from %s:%i by 0x%x (we're 0x%x)\n", Attempts
, GdiHdr
->lockfile
, GdiHdr
->lockline
, PrevThread
, Thread
);
1378 /* WTF?! The object is already locked by a different thread!
1379 Release the lock, wait a bit and try again! DO reset the pid lock
1380 so we make sure we don't access invalid memory in case the object is
1381 being deleted in the meantime (because we don't have aquired a reference
1383 FIXME - we should give up after some time unless we want to wait forever! */
1384 (void)InterlockedExchangePointer(&FromEntry
->ProcessId
, FromPrevProcId
);
1387 goto LockHandleFrom
;
1392 DPRINT1("Attempted to copy ownership from an object 0x%x currently being destroyed!!!\n", CopyFrom
);
1395 else if(FromPrevProcId
== FromLockedProcessId
)
1400 DPRINT1("[%d]Waiting on 0x%x\n", Attempts
, CopyFrom
);
1403 /* the object is currently locked, wait some time and try again.
1404 FIXME - we shouldn't loop forever! Give up after some time! */
1407 goto LockHandleFrom
;
1409 else if((HANDLE
)((ULONG_PTR
)FromPrevProcId
& ~0x1) != PsGetCurrentProcessId())
1411 /* FIXME - should we really allow copying ownership from objects that we don't even own? */
1412 DPRINT1("WARNING! Changing copying ownership of object 0x%x (pid: 0x%x) to pid 0x%x!!!\n", CopyFrom
, (ULONG_PTR
)FromPrevProcId
& ~0x1, PsGetCurrentProcessId());
1413 FromProcessId
= (HANDLE
)((ULONG_PTR
)FromPrevProcId
& ~0x1);
1414 FromLockedProcessId
= (HANDLE
)((ULONG_PTR
)FromProcessId
| 0x1);
1415 goto LockHandleFrom
;
1419 DPRINT1("Attempted to copy ownership from invalid handle: 0x%x\n", CopyFrom
);
1425 GDI_MapHandleTable(PSECTION_OBJECT SectionObject
, PEPROCESS Process
)
1427 PVOID MappedView
= NULL
;
1429 LARGE_INTEGER Offset
;
1430 ULONG ViewSize
= sizeof(GDI_HANDLE_TABLE
);
1432 Offset
.QuadPart
= 0;
1434 ASSERT(SectionObject
!= NULL
);
1435 ASSERT(Process
!= NULL
);
1437 Status
= MmMapViewOfSection(SectionObject
,
1448 if (!NT_SUCCESS(Status
))