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 * Allocate GDI object table.
108 * \param Size - number of entries in the object table.
110 static PGDI_HANDLE_TABLE INTERNAL_CALL
111 GDIOBJ_iAllocHandleTable(VOID
)
113 PGDI_HANDLE_TABLE handleTable
;
117 PGDI_TABLE_ENTRY Entry
;
120 htSize
= sizeof(GDI_HANDLE_TABLE
);
122 IntUserCreateSharedSection(SessionSharedSectionPool
,
123 (PVOID
*)&handleTable
,
125 ASSERT( handleTable
);
126 RtlZeroMemory(handleTable
, sizeof(GDI_HANDLE_TABLE
));
129 * initialize the free entry cache
131 InitializeSListHead(&handleTable
->FreeEntriesHead
);
132 Entry
= &HandleTable
->Entries
[RESERVE_ENTRIES_COUNT
];
133 for(i
= GDI_HANDLE_COUNT
- 1; i
>= RESERVE_ENTRIES_COUNT
; i
--)
135 InterlockedPushEntrySList(&handleTable
->FreeEntriesHead
, &handleTable
->FreeEntries
[i
]);
138 handleTable
->LookasideLists
= ExAllocatePoolWithTag(NonPagedPool
,
139 OBJTYPE_COUNT
* sizeof(PAGED_LOOKASIDE_LIST
),
141 if(handleTable
->LookasideLists
== NULL
)
143 InUserDeleteSharedSection(SessionSharedSectionPool
,
148 for(ObjType
= 0; ObjType
< OBJTYPE_COUNT
; ObjType
++)
150 ExInitializePagedLookasideList(handleTable
->LookasideLists
+ ObjType
, NULL
, NULL
, 0,
151 ObjInfo
[ObjType
].Size
+ sizeof(GDIOBJHDR
), TAG_GDIOBJ
, 0);
154 ShortDelay
.QuadPart
= -5000LL; /* FIXME - 0.5 ms? */
159 static __inline PPAGED_LOOKASIDE_LIST
160 FindLookasideList(DWORD ObjectType
)
164 for (Index
= 0; Index
< OBJTYPE_COUNT
; Index
++)
166 if (ObjInfo
[Index
].Type
== ObjectType
)
168 return HandleTable
->LookasideLists
+ Index
;
172 DPRINT1("Can't find lookaside list for object type 0x%08x\n", ObjectType
);
178 RunCleanupCallback(PGDIOBJ pObj
, DWORD ObjectType
)
182 for (Index
= 0; Index
< OBJTYPE_COUNT
; Index
++)
184 if (ObjInfo
[Index
].Type
== ObjectType
)
186 return ((GDICLEANUPPROC
)ObjInfo
[Index
].CleanupProc
)(pObj
);
190 DPRINT1("Can't find cleanup callback for object type 0x%08x\n", ObjectType
);
194 static __inline ULONG
195 GetObjectSize(DWORD ObjectType
)
199 for (Index
= 0; Index
< OBJTYPE_COUNT
; Index
++)
201 if (ObjInfo
[Index
].Type
== ObjectType
)
203 return ObjInfo
[Index
].Size
;
207 DPRINT1("Can't find size for object type 0x%08x\n", ObjectType
);
213 static int leak_reported
= 0;
214 #define GDI_STACK_LEVELS 12
215 static ULONG GDIHandleAllocator
[GDI_HANDLE_COUNT
][GDI_STACK_LEVELS
];
216 struct DbgOpenGDIHandle
222 static struct DbgOpenGDIHandle h
[H
];
224 void IntDumpHandleTable()
226 int i
, n
= 0, j
, k
, J
;
230 DPRINT1("gdi handle abusers already reported!\n");
235 DPRINT1("reporting gdi handle abusers:\n");
237 /* step through GDI handle table and find out who our culprit is... */
238 for ( i
= RESERVE_ENTRIES_COUNT
; i
< GDI_HANDLE_COUNT
; i
++ )
240 for ( j
= 0; j
< n
; j
++ )
244 for ( k
= 0; k
< GDI_STACK_LEVELS
; k
++ )
246 if ( GDIHandleAllocator
[i
][k
]
247 != GDIHandleAllocator
[J
][k
] )
270 /* bubble sort time! weeeeee!! */
271 for ( i
= 0; i
< n
-1; i
++ )
273 if ( h
[i
].count
< h
[i
+1].count
)
275 struct DbgOpenGDIHandle t
;
279 while ( j
> 0 && h
[j
-1].count
< t
.count
)
284 /* print the worst offenders... */
285 DbgPrint ( "Worst GDI Handle leak offenders (out of %i unique locations):\n", n
);
286 for ( i
= 0; i
< n
&& h
[i
].count
> 1; i
++ )
289 DbgPrint ( " %i allocs: ", h
[i
].count
);
290 for ( j
= 0; j
< GDI_STACK_LEVELS
; j
++ )
292 ULONG Addr
= GDIHandleAllocator
[h
[i
].idx
][j
];
293 if ( !KiRosPrintAddress ( (PVOID
)Addr
) )
294 DbgPrint ( "<%X>", Addr
);
298 if ( i
< n
&& h
[i
].count
== 1 )
299 DbgPrint ( "(list terminated - the remaining entries have 1 allocation only)\n" );
301 #endif /* GDI_DEBUG */
304 * Allocate memory for GDI object and return handle to it.
306 * \param ObjectType - type of object \ref GDI object types
308 * \return Handle of the allocated object.
310 * \note Use GDIOBJ_Lock() to obtain pointer to the new object.
311 * \todo return the object pointer and lock it by default.
313 HGDIOBJ INTERNAL_CALL
315 GDIOBJ_AllocObjDbg(const char* file
, int line
, ULONG ObjectType
)
316 #else /* !GDI_DEBUG */
317 GDIOBJ_AllocObj(ULONG ObjectType
)
318 #endif /* GDI_DEBUG */
320 PW32PROCESS W32Process
;
321 PGDIOBJHDR newObject
;
322 PPAGED_LOOKASIDE_LIST LookasideList
;
323 HANDLE CurrentProcessId
, LockedProcessId
;
328 W32Process
= PsGetWin32Process();
329 /* HACK HACK HACK: simplest-possible quota implementation - don't allow a process
330 to take too many GDI objects, itself. */
331 if ( W32Process
&& W32Process
->GDIObjects
>= 0x2710 )
334 ASSERT(ObjectType
!= GDI_OBJECT_TYPE_DONTCARE
);
336 LookasideList
= FindLookasideList(ObjectType
);
337 if(LookasideList
!= NULL
)
339 newObject
= ExAllocateFromPagedLookasideList(LookasideList
);
340 if(newObject
!= NULL
)
342 PSLIST_ENTRY FreeEntry
;
343 PGDI_TABLE_ENTRY Entry
;
347 CurrentProcessId
= PsGetCurrentProcessId();
348 LockedProcessId
= (HANDLE
)((ULONG_PTR
)CurrentProcessId
| 0x1);
350 newObject
->LockingThread
= NULL
;
351 newObject
->Locks
= 0;
354 newObject
->createdfile
= file
;
355 newObject
->createdline
= line
;
356 newObject
->lockfile
= NULL
;
357 newObject
->lockline
= 0;
360 ObjectBody
= GDIHdrToBdy(newObject
);
362 RtlZeroMemory(ObjectBody
, GetObjectSize(ObjectType
));
364 TypeInfo
= (ObjectType
& GDI_HANDLE_TYPE_MASK
) | (ObjectType
>> 16);
366 FreeEntry
= InterlockedPopEntrySList(&HandleTable
->FreeEntriesHead
);
367 if(FreeEntry
!= NULL
)
372 /* calculate the entry from the address of the entry in the free slot array */
373 Index
= ((ULONG_PTR
)FreeEntry
- (ULONG_PTR
)&HandleTable
->FreeEntries
[0]) /
374 sizeof(HandleTable
->FreeEntries
[0]);
375 Entry
= &HandleTable
->Entries
[Index
];
378 PrevProcId
= InterlockedCompareExchangePointer(&Entry
->ProcessId
, LockedProcessId
, 0);
379 if(PrevProcId
== NULL
)
383 ASSERT(Entry
->KernelData
== NULL
);
385 Entry
->KernelData
= ObjectBody
;
387 /* copy the reuse-counter */
388 TypeInfo
|= Entry
->Type
& GDI_HANDLE_REUSE_MASK
;
390 /* we found a free entry, no need to exchange this field atomically
391 since we're holding the lock */
392 Entry
->Type
= TypeInfo
;
394 /* unlock the entry */
395 InterlockedExchangePointer(&Entry
->ProcessId
, CurrentProcessId
);
398 memset ( GDIHandleAllocator
[Index
], 0xcd, GDI_STACK_LEVELS
* sizeof(ULONG
) );
399 KeRosGetStackFrames ( GDIHandleAllocator
[Index
], GDI_STACK_LEVELS
);
400 #endif /* GDI_DEBUG */
402 if(W32Process
!= NULL
)
404 InterlockedIncrement(&W32Process
->GDIObjects
);
406 Handle
= (HGDIOBJ
)((Index
& 0xFFFF) | (TypeInfo
& (GDI_HANDLE_TYPE_MASK
| GDI_HANDLE_REUSE_MASK
)));
408 DPRINT("GDIOBJ_AllocObj: 0x%x ob: 0x%x\n", Handle
, ObjectBody
);
416 DPRINT1("[%d]Waiting on handle in index 0x%x\n", Attempts
, Index
);
419 /* damn, someone is trying to lock the object even though it doesn't
420 eve nexist anymore, wait a little and try again!
421 FIXME - we shouldn't loop forever! Give up after some time! */
428 ExFreeToPagedLookasideList(LookasideList
, newObject
);
429 DPRINT1("Failed to insert gdi object into the handle table, no handles left!\n");
431 IntDumpHandleTable();
432 #endif /* GDI_DEBUG */
436 DPRINT1("Not enough memory to allocate gdi object!\n");
441 DPRINT1("Failed to find lookaside list for object type 0x%x\n", ObjectType
);
447 * Free memory allocated for the GDI object. For each object type this function calls the
448 * appropriate cleanup routine.
450 * \param hObj - handle of the object to be deleted.
452 * \return Returns TRUE if succesful.
453 * \return Returns FALSE if the cleanup routine returned FALSE or the object doesn't belong
454 * to the calling process.
458 GDIOBJ_FreeObjDbg(const char* file
, int line
, HGDIOBJ hObj
, DWORD ObjectType
)
459 #else /* !GDI_DEBUG */
460 GDIOBJ_FreeObj(HGDIOBJ hObj
, DWORD ObjectType
)
461 #endif /* GDI_DEBUG */
463 PGDI_TABLE_ENTRY Entry
;
464 PPAGED_LOOKASIDE_LIST LookasideList
;
465 HANDLE ProcessId
, LockedProcessId
, PrevProcId
;
472 DPRINT("GDIOBJ_FreeObj: hObj: 0x%08x\n", hObj
);
474 if(GDI_HANDLE_IS_STOCKOBJ(hObj
))
476 DPRINT1("GDIOBJ_FreeObj() failed, can't delete stock object handle: 0x%x !!!\n", hObj
);
478 DPRINT1("-> called from %s:%i\n", file
, line
);
483 ProcessId
= PsGetCurrentProcessId();
484 LockedProcessId
= (HANDLE
)((ULONG_PTR
)ProcessId
| 0x1);
486 Silent
= (ObjectType
& GDI_OBJECT_TYPE_SILENT
);
487 ObjectType
&= ~GDI_OBJECT_TYPE_SILENT
;
489 ExpectedType
= ((ObjectType
!= GDI_OBJECT_TYPE_DONTCARE
) ? ObjectType
: 0);
491 Entry
= GDI_HANDLE_GET_ENTRY(HandleTable
, hObj
);
494 /* lock the object, we must not delete global objects, so don't exchange the locking
495 process ID to zero when attempting to lock a global object... */
496 PrevProcId
= InterlockedCompareExchangePointer(&Entry
->ProcessId
, LockedProcessId
, ProcessId
);
497 if(PrevProcId
== ProcessId
)
499 if(Entry
->Type
!= 0 && Entry
->KernelData
!= NULL
&&
500 (ExpectedType
== 0 || ((Entry
->Type
<< 16) == ExpectedType
)))
504 GdiHdr
= GDIBdyToHdr(Entry
->KernelData
);
506 if(GdiHdr
->Locks
== 0)
509 PW32PROCESS W32Process
= PsGetWin32Process();
510 ULONG Type
= Entry
->Type
<< 16;
512 /* Clear the type field so when unlocking the handle it gets finally deleted and increment reuse counter */
513 Entry
->Type
= ((Entry
->Type
>> GDI_HANDLE_REUSECNT_SHIFT
) + 1) << GDI_HANDLE_REUSECNT_SHIFT
;
514 Entry
->KernelData
= NULL
;
516 /* unlock the handle slot */
517 InterlockedExchangePointer(&Entry
->ProcessId
, NULL
);
519 /* push this entry to the free list */
520 InterlockedPushEntrySList(&HandleTable
->FreeEntriesHead
,
521 &HandleTable
->FreeEntries
[GDI_ENTRY_TO_INDEX(HandleTable
, Entry
)]);
523 if(W32Process
!= NULL
)
525 InterlockedDecrement(&W32Process
->GDIObjects
);
528 /* call the cleanup routine. */
529 Ret
= RunCleanupCallback(GDIHdrToBdy(GdiHdr
), Type
);
531 /* Now it's time to free the memory */
532 LookasideList
= FindLookasideList(Type
);
533 if(LookasideList
!= NULL
)
535 ExFreeToPagedLookasideList(LookasideList
, GdiHdr
);
543 * The object is currently locked, so freeing is forbidden!
545 DPRINT1("GdiHdr->Locks: %d\n", GdiHdr
->Locks
);
547 DPRINT1("Locked from: %s:%d\n", GdiHdr
->lockfile
, GdiHdr
->lockline
);
554 if((Entry
->Type
& ~GDI_HANDLE_REUSE_MASK
) != 0)
556 DPRINT1("Attempted to delete object 0x%x, type mismatch (0x%x : 0x%x)\n", hObj
, ObjectType
, ExpectedType
);
560 DPRINT1("Attempted to delete object 0x%x which was already deleted!\n", hObj
);
562 InterlockedExchangePointer(&Entry
->ProcessId
, PrevProcId
);
565 else if(PrevProcId
== LockedProcessId
)
570 DPRINT1("[%d]Waiting on 0x%x\n", Attempts
, hObj
);
573 /* the object is currently locked, wait some time and try again.
574 FIXME - we shouldn't loop forever! Give up after some time! */
583 if(((ULONG_PTR
)PrevProcId
& ~0x1) == 0)
585 DPRINT1("Attempted to free global gdi handle 0x%x, caller needs to get ownership first!!!\n", hObj
);
589 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);
592 DPRINT1("-> called from %s:%i\n", file
, line
);
601 * Initialization of the GDI object engine.
604 InitGdiObjectHandleTable (VOID
)
606 DPRINT("InitGdiObjectHandleTable\n");
608 HandleTable
= GDIOBJ_iAllocHandleTable();
609 DPRINT("HandleTable: %x\n", HandleTable
);
614 * \param hObject object handle
615 * \return if the function fails the returned value is FALSE.
618 NtGdiDeleteObject(HGDIOBJ hObject
)
620 DPRINT("NtGdiDeleteObject handle 0x%08x\n", hObject
);
622 return NULL
!= hObject
623 ? GDIOBJ_FreeObj(hObject
, GDI_OBJECT_TYPE_DONTCARE
) : FALSE
;
627 * Internal function. Called when the process is destroyed to free the remaining GDI handles.
628 * \param Process - PID of the process that will be destroyed.
631 GDI_CleanupForProcess (struct _EPROCESS
*Process
)
633 PGDI_TABLE_ENTRY Entry
, End
;
634 PEPROCESS CurrentProcess
;
635 PW32PROCESS W32Process
;
637 ULONG Index
= RESERVE_ENTRIES_COUNT
;
639 DPRINT("Starting CleanupForProcess prochandle %x Pid %d\n", Process
, Process
->UniqueProcessId
);
640 CurrentProcess
= PsGetCurrentProcess();
641 if (CurrentProcess
!= Process
)
643 KeAttachProcess(&Process
->Pcb
);
645 W32Process
= (PW32PROCESS
)Process
->Win32Process
;
648 if(W32Process
->GDIObjects
> 0)
650 /* FIXME - Instead of building the handle here and delete it using GDIOBJ_FreeObj
651 we should delete it directly here! */
652 ProcId
= Process
->UniqueProcessId
;
654 End
= &HandleTable
->Entries
[GDI_HANDLE_COUNT
];
655 for(Entry
= &HandleTable
->Entries
[RESERVE_ENTRIES_COUNT
];
659 /* ignore the lock bit */
660 if((HANDLE
)((ULONG_PTR
)Entry
->ProcessId
& ~0x1) == ProcId
&& (Entry
->Type
& ~GDI_HANDLE_REUSE_MASK
) != 0)
662 HGDIOBJ ObjectHandle
;
664 /* Create the object handle for the entry, the upper 16 bit of the
665 Type field includes the type of the object including the stock
666 object flag - but since stock objects don't have a process id we can
667 simply ignore this fact here. */
668 ObjectHandle
= (HGDIOBJ
)(Index
| (Entry
->Type
& 0xFFFF0000));
670 if(GDIOBJ_FreeObj(ObjectHandle
, GDI_OBJECT_TYPE_DONTCARE
) &&
671 W32Process
->GDIObjects
== 0)
673 /* there are no more gdi handles for this process, bail */
680 if (CurrentProcess
!= Process
)
685 DPRINT("Completed cleanup for process %d\n", Process
->UniqueProcessId
);
691 * Return pointer to the object by handle.
693 * \param hObj Object handle
694 * \return Pointer to the object.
696 * \note Process can only get pointer to the objects it created or global objects.
698 * \todo Get rid of the ObjectType parameter!
700 PGDIOBJ INTERNAL_CALL
702 GDIOBJ_LockObjDbg (const char* file
, int line
, HGDIOBJ hObj
, DWORD ObjectType
)
703 #else /* !GDI_DEBUG */
704 GDIOBJ_LockObj (HGDIOBJ hObj
, DWORD ObjectType
)
705 #endif /* GDI_DEBUG */
708 PGDI_TABLE_ENTRY HandleEntry
;
709 HANDLE ProcessId
, HandleProcessId
, LockedProcessId
, PrevProcId
;
710 PGDIOBJ Object
= NULL
;
712 HandleIndex
= GDI_HANDLE_GET_INDEX(hObj
);
714 /* Check that the handle index is valid. */
715 if (HandleIndex
>= GDI_HANDLE_COUNT
)
718 HandleEntry
= &HandleTable
->Entries
[HandleIndex
];
720 ProcessId
= (HANDLE
)((ULONG_PTR
)PsGetCurrentProcessId() & ~1);
721 HandleProcessId
= (HANDLE
)((ULONG_PTR
)HandleEntry
->ProcessId
& ~1);
723 /* Check for invalid owner. */
724 if (ProcessId
!= HandleProcessId
&& HandleProcessId
!= NULL
)
730 * Prevent the thread from being terminated during the locking process.
731 * It would result in undesired effects and inconsistency of the global
735 KeEnterCriticalRegion();
738 * Loop until we either successfully lock the handle entry & object or
739 * fail some of the check.
744 /* Lock the handle table entry. */
745 LockedProcessId
= (HANDLE
)((ULONG_PTR
)HandleProcessId
| 0x1);
746 PrevProcId
= InterlockedCompareExchangePointer(&HandleEntry
->ProcessId
,
750 if (PrevProcId
== HandleProcessId
)
752 LONG HandleType
= HandleEntry
->Type
<< 16;
755 * We're locking an object that belongs to our process or it's a
756 * global object if HandleProcessId is 0 here.
759 /* FIXME: Check the upper 16-bits of handle number! */
760 if (HandleType
!= 0 && HandleEntry
->KernelData
!= NULL
&&
761 (ObjectType
== GDI_OBJECT_TYPE_DONTCARE
||
762 HandleType
== ObjectType
))
764 PGDIOBJHDR GdiHdr
= GDIBdyToHdr(HandleEntry
->KernelData
);
765 PETHREAD Thread
= PsGetCurrentThread();
767 if (GdiHdr
->Locks
== 0)
769 GdiHdr
->LockingThread
= Thread
;
772 GdiHdr
->lockfile
= file
;
773 GdiHdr
->lockline
= line
;
775 Object
= HandleEntry
->KernelData
;
779 InterlockedIncrement((PLONG
)&GdiHdr
->Locks
);
780 if (GdiHdr
->LockingThread
!= Thread
)
782 InterlockedDecrement((PLONG
)&GdiHdr
->Locks
);
784 /* Unlock the handle table entry. */
785 InterlockedExchangePointer(&HandleEntry
->ProcessId
, PrevProcId
);
790 Object
= HandleEntry
->KernelData
;
796 * Debugging code. Report attempts to lock deleted handles and
797 * locking type mismatches.
800 if ((HandleType
& ~GDI_HANDLE_REUSE_MASK
) == 0)
802 DPRINT1("Attempted to lock object 0x%x that is deleted!\n", hObj
);
804 KeRosDumpStackFrames(NULL
, 20);
809 DPRINT1("Attempted to lock object 0x%x, type mismatch (0x%x : 0x%x)\n",
810 hObj
, HandleType
& ~GDI_HANDLE_REUSE_MASK
, ObjectType
& ~GDI_HANDLE_REUSE_MASK
);
812 KeRosDumpStackFrames(NULL
, 20);
816 DPRINT1("-> called from %s:%i\n", file
, line
);
820 /* Unlock the handle table entry. */
821 InterlockedExchangePointer(&HandleEntry
->ProcessId
, PrevProcId
);
828 * The handle is currently locked, wait some time and try again.
836 KeLeaveCriticalRegion();
843 * Return pointer to the object by handle (and allow sharing of the handle
846 * \param hObj Object handle
847 * \return Pointer to the object.
849 * \note Process can only get pointer to the objects it created or global objects.
851 * \todo Get rid of the ObjectType parameter!
853 PGDIOBJ INTERNAL_CALL
855 GDIOBJ_ShareLockObjDbg (const char* file
, int line
, HGDIOBJ hObj
, DWORD ObjectType
)
856 #else /* !GDI_DEBUG */
857 GDIOBJ_ShareLockObj (HGDIOBJ hObj
, DWORD ObjectType
)
858 #endif /* GDI_DEBUG */
861 PGDI_TABLE_ENTRY HandleEntry
;
862 HANDLE ProcessId
, HandleProcessId
, LockedProcessId
, PrevProcId
;
863 PGDIOBJ Object
= NULL
;
865 HandleIndex
= GDI_HANDLE_GET_INDEX(hObj
);
867 /* Check that the handle index is valid. */
868 if (HandleIndex
>= GDI_HANDLE_COUNT
)
871 HandleEntry
= &HandleTable
->Entries
[HandleIndex
];
873 ProcessId
= (HANDLE
)((ULONG_PTR
)PsGetCurrentProcessId() & ~1);
874 HandleProcessId
= (HANDLE
)((ULONG_PTR
)HandleEntry
->ProcessId
& ~1);
876 /* Check for invalid owner. */
877 if (ProcessId
!= HandleProcessId
&& HandleProcessId
!= NULL
)
883 * Prevent the thread from being terminated during the locking process.
884 * It would result in undesired effects and inconsistency of the global
888 KeEnterCriticalRegion();
891 * Loop until we either successfully lock the handle entry & object or
892 * fail some of the check.
897 /* Lock the handle table entry. */
898 LockedProcessId
= (HANDLE
)((ULONG_PTR
)HandleProcessId
| 0x1);
899 PrevProcId
= InterlockedCompareExchangePointer(&HandleEntry
->ProcessId
,
903 if (PrevProcId
== HandleProcessId
)
905 LONG HandleType
= HandleEntry
->Type
<< 16;
908 * We're locking an object that belongs to our process or it's a
909 * global object if HandleProcessId is 0 here.
912 /* FIXME: Check the upper 16-bits of handle number! */
913 if (HandleType
!= 0 && HandleEntry
->KernelData
!= NULL
&&
914 (ObjectType
== GDI_OBJECT_TYPE_DONTCARE
||
915 HandleType
== ObjectType
))
917 PGDIOBJHDR GdiHdr
= GDIBdyToHdr(HandleEntry
->KernelData
);
920 if (InterlockedIncrement((PLONG
)&GdiHdr
->Locks
) == 1)
922 GdiHdr
->lockfile
= file
;
923 GdiHdr
->lockline
= line
;
926 InterlockedIncrement((PLONG
)&GdiHdr
->Locks
);
928 Object
= HandleEntry
->KernelData
;
933 * Debugging code. Report attempts to lock deleted handles and
934 * locking type mismatches.
937 if ((HandleType
& ~GDI_HANDLE_REUSE_MASK
) == 0)
939 DPRINT1("Attempted to lock object 0x%x that is deleted!\n", hObj
);
941 KeRosDumpStackFrames(NULL
, 20);
946 DPRINT1("Attempted to lock object 0x%x, type mismatch (0x%x : 0x%x)\n",
947 hObj
, HandleType
& ~GDI_HANDLE_REUSE_MASK
, ObjectType
& ~GDI_HANDLE_REUSE_MASK
);
949 KeRosDumpStackFrames(NULL
, 20);
953 DPRINT1("-> called from %s:%i\n", file
, line
);
957 /* Unlock the handle table entry. */
958 InterlockedExchangePointer(&HandleEntry
->ProcessId
, PrevProcId
);
965 * The handle is currently locked, wait some time and try again.
973 KeLeaveCriticalRegion();
980 * Release GDI object. Every object locked by GDIOBJ_LockObj() must be unlocked. You should unlock the object
981 * as soon as you don't need to have access to it's data.
983 * \param Object Object pointer (as returned by GDIOBJ_LockObj).
986 GDIOBJ_UnlockObjByPtr(PGDIOBJ Object
)
988 PGDIOBJHDR GdiHdr
= GDIBdyToHdr(Object
);
990 if (InterlockedDecrement((PLONG
)&GdiHdr
->Locks
) == 0)
992 GdiHdr
->lockfile
= NULL
;
993 GdiHdr
->lockline
= 0;
996 InterlockedDecrement((PLONG
)&GdiHdr
->Locks
);
1002 GDIOBJ_OwnedByCurrentProcess(HGDIOBJ ObjectHandle
)
1004 PGDI_TABLE_ENTRY Entry
;
1008 DPRINT("GDIOBJ_OwnedByCurrentProcess: ObjectHandle: 0x%08x\n", ObjectHandle
);
1010 if(!GDI_HANDLE_IS_STOCKOBJ(ObjectHandle
))
1012 ProcessId
= PsGetCurrentProcessId();
1014 Entry
= GDI_HANDLE_GET_ENTRY(HandleTable
, ObjectHandle
);
1015 Ret
= Entry
->KernelData
!= NULL
&&
1016 (Entry
->Type
& ~GDI_HANDLE_REUSE_MASK
) != 0 &&
1017 (HANDLE
)((ULONG_PTR
)Entry
->ProcessId
& ~0x1) == ProcessId
;
1026 GDIOBJ_ConvertToStockObj(HGDIOBJ
*hObj
)
1029 * FIXME !!!!! THIS FUNCTION NEEDS TO BE FIXED - IT IS NOT SAFE WHEN OTHER THREADS
1030 * MIGHT ATTEMPT TO LOCK THE OBJECT DURING THIS CALL!!!
1032 PGDI_TABLE_ENTRY Entry
;
1033 HANDLE ProcessId
, LockedProcessId
, PrevProcId
;
1041 DPRINT("GDIOBJ_ConvertToStockObj: hObj: 0x%08x\n", *hObj
);
1043 Thread
= PsGetCurrentThread();
1045 if(!GDI_HANDLE_IS_STOCKOBJ(*hObj
))
1047 ProcessId
= PsGetCurrentProcessId();
1048 LockedProcessId
= (HANDLE
)((ULONG_PTR
)ProcessId
| 0x1);
1050 Entry
= GDI_HANDLE_GET_ENTRY(HandleTable
, *hObj
);
1053 /* lock the object, we must not convert stock objects, so don't check!!! */
1054 PrevProcId
= InterlockedCompareExchangePointer(&Entry
->ProcessId
, LockedProcessId
, ProcessId
);
1055 if(PrevProcId
== ProcessId
)
1057 LONG NewType
, PrevType
, OldType
;
1059 /* we're locking an object that belongs to our process. First calculate
1060 the new object type including the stock object flag and then try to
1062 NewType
= GDI_HANDLE_GET_TYPE(*hObj
);
1063 NewType
|= NewType
>> 16;
1064 NewType
|= (ULONG_PTR
)(*hObj
) & GDI_HANDLE_REUSE_MASK
;
1066 /* This is the type that the object should have right now, save it */
1068 /* As the object should be a stock object, set it's flag, but only in the upper 16 bits */
1069 NewType
|= GDI_HANDLE_STOCK_MASK
;
1071 /* Try to exchange the type field - but only if the old (previous type) matches! */
1072 PrevType
= InterlockedCompareExchange(&Entry
->Type
, NewType
, OldType
);
1073 if(PrevType
== OldType
&& Entry
->KernelData
!= NULL
)
1075 PETHREAD PrevThread
;
1078 /* We successfully set the stock object flag.
1079 KernelData should never be NULL here!!! */
1080 ASSERT(Entry
->KernelData
);
1082 GdiHdr
= GDIBdyToHdr(Entry
->KernelData
);
1084 PrevThread
= GdiHdr
->LockingThread
;
1085 if(GdiHdr
->Locks
== 0 || PrevThread
== Thread
)
1087 /* dereference the process' object counter */
1088 if(PrevProcId
!= GDI_GLOBAL_PROCESS
)
1090 PEPROCESS OldProcess
;
1091 PW32PROCESS W32Process
;
1095 Status
= PsLookupProcessByProcessId((HANDLE
)((ULONG_PTR
)PrevProcId
& ~0x1), &OldProcess
);
1096 if(NT_SUCCESS(Status
))
1098 W32Process
= (PW32PROCESS
)OldProcess
->Win32Process
;
1099 if(W32Process
!= NULL
)
1101 InterlockedDecrement(&W32Process
->GDIObjects
);
1103 ObDereferenceObject(OldProcess
);
1107 /* remove the process id lock and make it global */
1108 InterlockedExchangePointer(&Entry
->ProcessId
, GDI_GLOBAL_PROCESS
);
1110 *hObj
= (HGDIOBJ
)((ULONG
)(*hObj
) | GDI_HANDLE_STOCK_MASK
);
1112 /* we're done, successfully converted the object */
1120 if(GdiHdr
->lockfile
!= NULL
)
1122 DPRINT1("[%d]Locked %s:%i by 0x%x (we're 0x%x)\n", Attempts
, GdiHdr
->lockfile
, GdiHdr
->lockline
, PrevThread
, Thread
);
1126 /* WTF?! The object is already locked by a different thread!
1127 Release the lock, wait a bit and try again!
1128 FIXME - we should give up after some time unless we want to wait forever! */
1129 InterlockedExchangePointer(&Entry
->ProcessId
, PrevProcId
);
1137 DPRINT1("Attempted to convert object 0x%x that is deleted! Should never get here!!!\n", hObj
);
1140 else if(PrevProcId
== LockedProcessId
)
1145 DPRINT1("[%d]Waiting on 0x%x\n", Attempts
, hObj
);
1148 /* the object is currently locked, wait some time and try again.
1149 FIXME - we shouldn't loop forever! Give up after some time! */
1156 DPRINT1("Attempted to convert invalid handle: 0x%x\n", hObj
);
1164 GDIOBJ_SetOwnership(HGDIOBJ ObjectHandle
, PEPROCESS NewOwner
)
1166 PGDI_TABLE_ENTRY Entry
;
1167 HANDLE ProcessId
, LockedProcessId
, PrevProcId
;
1173 DPRINT("GDIOBJ_SetOwnership: hObj: 0x%x, NewProcess: 0x%x\n", ObjectHandle
, (NewOwner
? PsGetProcessId(NewOwner
) : 0));
1175 Thread
= PsGetCurrentThread();
1177 if(!GDI_HANDLE_IS_STOCKOBJ(ObjectHandle
))
1179 ProcessId
= PsGetCurrentProcessId();
1180 LockedProcessId
= (HANDLE
)((ULONG_PTR
)ProcessId
| 0x1);
1182 Entry
= GDI_HANDLE_GET_ENTRY(HandleTable
, ObjectHandle
);
1185 /* lock the object, we must not convert stock objects, so don't check!!! */
1186 PrevProcId
= InterlockedCompareExchangePointer(&Entry
->ProcessId
, ProcessId
, LockedProcessId
);
1187 if(PrevProcId
== ProcessId
)
1189 PETHREAD PrevThread
;
1191 if((Entry
->Type
& ~GDI_HANDLE_REUSE_MASK
) != 0 && Entry
->KernelData
!= NULL
)
1193 PGDIOBJHDR GdiHdr
= GDIBdyToHdr(Entry
->KernelData
);
1195 PrevThread
= GdiHdr
->LockingThread
;
1196 if(GdiHdr
->Locks
== 0 || PrevThread
== Thread
)
1198 PEPROCESS OldProcess
;
1199 PW32PROCESS W32Process
;
1202 /* dereference the process' object counter */
1204 if((ULONG_PTR
)PrevProcId
& ~0x1)
1206 Status
= PsLookupProcessByProcessId((HANDLE
)((ULONG_PTR
)PrevProcId
& ~0x1), &OldProcess
);
1207 if(NT_SUCCESS(Status
))
1209 W32Process
= (PW32PROCESS
)OldProcess
->Win32Process
;
1210 if(W32Process
!= NULL
)
1212 InterlockedDecrement(&W32Process
->GDIObjects
);
1214 ObDereferenceObject(OldProcess
);
1218 if(NewOwner
!= NULL
)
1220 ProcessId
= PsGetProcessId(NewOwner
);
1222 /* Increase the new process' object counter */
1223 W32Process
= (PW32PROCESS
)NewOwner
->Win32Process
;
1224 if(W32Process
!= NULL
)
1226 InterlockedIncrement(&W32Process
->GDIObjects
);
1232 /* remove the process id lock and change it to the new process id */
1233 InterlockedExchangePointer(&Entry
->ProcessId
, ProcessId
);
1243 if(GdiHdr
->lockfile
!= NULL
)
1245 DPRINT1("[%d]Locked from %s:%i by 0x%x (we're 0x%x)\n", Attempts
, GdiHdr
->lockfile
, GdiHdr
->lockline
, PrevThread
, Thread
);
1249 /* WTF?! The object is already locked by a different thread!
1250 Release the lock, wait a bit and try again! DO reset the pid lock
1251 so we make sure we don't access invalid memory in case the object is
1252 being deleted in the meantime (because we don't have aquired a reference
1254 FIXME - we should give up after some time unless we want to wait forever! */
1255 InterlockedExchangePointer(&Entry
->ProcessId
, PrevProcId
);
1263 DPRINT1("Attempted to change ownership of an object 0x%x currently being destroyed!!!\n", ObjectHandle
);
1266 else if(PrevProcId
== LockedProcessId
)
1271 DPRINT1("[%d]Waiting on 0x%x\n", Attempts
, ObjectHandle
);
1274 /* the object is currently locked, wait some time and try again.
1275 FIXME - we shouldn't loop forever! Give up after some time! */
1280 else if(((ULONG_PTR
)PrevProcId
& ~0x1) == 0)
1282 /* allow changing ownership of global objects */
1284 LockedProcessId
= (HANDLE
)((ULONG_PTR
)ProcessId
| 0x1);
1287 else if((HANDLE
)((ULONG_PTR
)PrevProcId
& ~0x1) != PsGetCurrentProcessId())
1289 DPRINT1("Attempted to change ownership of object 0x%x (pid: 0x%x) from pid 0x%x!!!\n", ObjectHandle
, (ULONG_PTR
)PrevProcId
& ~0x1, PsGetCurrentProcessId());
1293 DPRINT1("Attempted to change owner of invalid handle: 0x%x\n", ObjectHandle
);
1299 GDIOBJ_CopyOwnership(HGDIOBJ CopyFrom
, HGDIOBJ CopyTo
)
1301 PGDI_TABLE_ENTRY FromEntry
;
1303 HANDLE FromProcessId
, FromLockedProcessId
, FromPrevProcId
;
1308 DPRINT("GDIOBJ_CopyOwnership: from: 0x%x, to: 0x%x\n", CopyFrom
, CopyTo
);
1310 Thread
= PsGetCurrentThread();
1312 if(!GDI_HANDLE_IS_STOCKOBJ(CopyFrom
) && !GDI_HANDLE_IS_STOCKOBJ(CopyTo
))
1314 FromEntry
= GDI_HANDLE_GET_ENTRY(HandleTable
, CopyFrom
);
1316 FromProcessId
= (HANDLE
)((ULONG_PTR
)FromEntry
->ProcessId
& ~0x1);
1317 FromLockedProcessId
= (HANDLE
)((ULONG_PTR
)FromProcessId
| 0x1);
1320 /* lock the object, we must not convert stock objects, so don't check!!! */
1321 FromPrevProcId
= InterlockedCompareExchangePointer(&FromEntry
->ProcessId
, FromProcessId
, FromLockedProcessId
);
1322 if(FromPrevProcId
== FromProcessId
)
1324 PETHREAD PrevThread
;
1327 if((FromEntry
->Type
& ~GDI_HANDLE_REUSE_MASK
) != 0 && FromEntry
->KernelData
!= NULL
)
1329 GdiHdr
= GDIBdyToHdr(FromEntry
->KernelData
);
1331 /* save the pointer to the calling thread so we know it was this thread
1332 that locked the object */
1333 PrevThread
= GdiHdr
->LockingThread
;
1334 if(GdiHdr
->Locks
== 0 || PrevThread
== Thread
)
1336 /* now let's change the ownership of the target object */
1338 if(((ULONG_PTR
)FromPrevProcId
& ~0x1) != 0)
1340 PEPROCESS ProcessTo
;
1342 if(NT_SUCCESS(PsLookupProcessByProcessId((HANDLE
)((ULONG_PTR
)FromPrevProcId
& ~0x1), &ProcessTo
)))
1344 GDIOBJ_SetOwnership(CopyTo
, ProcessTo
);
1345 ObDereferenceObject(ProcessTo
);
1350 /* mark the object as global */
1351 GDIOBJ_SetOwnership(CopyTo
, NULL
);
1354 InterlockedExchangePointer(&FromEntry
->ProcessId
, FromPrevProcId
);
1361 if(GdiHdr
->lockfile
!= NULL
)
1363 DPRINT1("[%d]Locked from %s:%i by 0x%x (we're 0x%x)\n", Attempts
, GdiHdr
->lockfile
, GdiHdr
->lockline
, PrevThread
, Thread
);
1367 /* WTF?! The object is already locked by a different thread!
1368 Release the lock, wait a bit and try again! DO reset the pid lock
1369 so we make sure we don't access invalid memory in case the object is
1370 being deleted in the meantime (because we don't have aquired a reference
1372 FIXME - we should give up after some time unless we want to wait forever! */
1373 InterlockedExchangePointer(&FromEntry
->ProcessId
, FromPrevProcId
);
1376 goto LockHandleFrom
;
1381 DPRINT1("Attempted to copy ownership from an object 0x%x currently being destroyed!!!\n", CopyFrom
);
1384 else if(FromPrevProcId
== FromLockedProcessId
)
1389 DPRINT1("[%d]Waiting on 0x%x\n", Attempts
, CopyFrom
);
1392 /* the object is currently locked, wait some time and try again.
1393 FIXME - we shouldn't loop forever! Give up after some time! */
1396 goto LockHandleFrom
;
1398 else if((HANDLE
)((ULONG_PTR
)FromPrevProcId
& ~0x1) != PsGetCurrentProcessId())
1400 /* FIXME - should we really allow copying ownership from objects that we don't even own? */
1401 DPRINT1("WARNING! Changing copying ownership of object 0x%x (pid: 0x%x) to pid 0x%x!!!\n", CopyFrom
, (ULONG_PTR
)FromPrevProcId
& ~0x1, PsGetCurrentProcessId());
1402 FromProcessId
= (HANDLE
)((ULONG_PTR
)FromPrevProcId
& ~0x1);
1403 FromLockedProcessId
= (HANDLE
)((ULONG_PTR
)FromProcessId
| 0x1);
1404 goto LockHandleFrom
;
1408 DPRINT1("Attempted to copy ownership from invalid handle: 0x%x\n", CopyFrom
);
1414 GDI_MapHandleTable(PEPROCESS Process
)
1416 ULONG TableSize
= sizeof(HandleTable
->Entries
);
1417 PVOID MappedGdiTable
= NULL
; /* FIXME - try preferred GDI_HANDLE_TABLE_BASE_ADDRESS? */
1418 NTSTATUS Status
= IntUserMapSharedSection(SessionSharedSectionPool
,
1425 if(NT_SUCCESS(Status
))
1427 return MappedGdiTable
;