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
22 * $Id: gdiobj.c,v 1.79 2004/12/18 21:41:17 royce Exp $
30 /* F*(&#$ header mess!!!! */
32 STDCALL
PsGetProcessId(
35 #endif /* __USE_W32API */
40 #define GDI_ENTRY_TO_INDEX(ht, e) \
41 (((ULONG_PTR)(e) - (ULONG_PTR)&((ht)->Entries[0])) / sizeof(GDI_TABLE_ENTRY))
42 #define GDI_HANDLE_GET_ENTRY(HandleTable, h) \
43 (&(HandleTable)->Entries[GDI_HANDLE_GET_INDEX((h))])
45 #define GDIBdyToHdr(body) \
46 ((PGDIOBJHDR)(body) - 1)
47 #define GDIHdrToBdy(hdr) \
48 (PGDIOBJ)((PGDIOBJHDR)(hdr) + 1)
50 /* apparently the first 10 entries are never used in windows as they are empty */
51 #define RESERVE_ENTRIES_COUNT 10
53 typedef struct _GDI_HANDLE_TABLE
55 PPAGED_LOOKASIDE_LIST LookasideLists
;
57 SLIST_HEADER FreeEntriesHead
;
58 SLIST_ENTRY FreeEntries
[((GDI_HANDLE_COUNT
* sizeof(GDI_TABLE_ENTRY
)) << 3) /
59 (sizeof(SLIST_ENTRY
) << 3)];
61 GDI_TABLE_ENTRY Entries
[GDI_HANDLE_COUNT
];
62 } GDI_HANDLE_TABLE
, *PGDI_HANDLE_TABLE
;
68 GDICLEANUPPROC CleanupProc
;
69 } GDI_OBJ_INFO
, *PGDI_OBJ_INFO
;
72 * Dummy GDI Cleanup Callback
75 GDI_CleanupDummy(PVOID ObjectBody
)
80 /* Testing shows that regions are the most used GDIObj type,
81 so put that one first for performance */
83 GDI_OBJ_INFO ObjInfo
[] =
85 /* Type */ /* Size */ /* CleanupProc */
86 {GDI_OBJECT_TYPE_REGION
, sizeof(ROSRGNDATA
), RGNDATA_Cleanup
},
87 {GDI_OBJECT_TYPE_BITMAP
, sizeof(BITMAPOBJ
), BITMAP_Cleanup
},
88 {GDI_OBJECT_TYPE_DC
, sizeof(DC
), DC_Cleanup
},
89 {GDI_OBJECT_TYPE_PALETTE
, sizeof(PALGDI
), PALETTE_Cleanup
},
90 {GDI_OBJECT_TYPE_BRUSH
, sizeof(GDIBRUSHOBJ
), BRUSH_Cleanup
},
91 {GDI_OBJECT_TYPE_PEN
, sizeof(GDIBRUSHOBJ
), GDI_CleanupDummy
},
92 {GDI_OBJECT_TYPE_FONT
, sizeof(TEXTOBJ
), GDI_CleanupDummy
},
93 {GDI_OBJECT_TYPE_DCE
, sizeof(DCE
), DCE_Cleanup
},
94 /*{GDI_OBJECT_TYPE_DIRECTDRAW, sizeof(DD_DIRECTDRAW), DD_Cleanup},
95 {GDI_OBJECT_TYPE_DD_SURFACE, sizeof(DD_SURFACE), DDSURF_Cleanup},*/
96 {GDI_OBJECT_TYPE_EXTPEN
, 0, GDI_CleanupDummy
},
97 {GDI_OBJECT_TYPE_METADC
, 0, GDI_CleanupDummy
},
98 {GDI_OBJECT_TYPE_METAFILE
, 0, GDI_CleanupDummy
},
99 {GDI_OBJECT_TYPE_ENHMETAFILE
, 0, GDI_CleanupDummy
},
100 {GDI_OBJECT_TYPE_ENHMETADC
, 0, GDI_CleanupDummy
},
101 {GDI_OBJECT_TYPE_MEMDC
, 0, GDI_CleanupDummy
},
102 {GDI_OBJECT_TYPE_EMF
, 0, GDI_CleanupDummy
}
105 #define OBJTYPE_COUNT (sizeof(ObjInfo) / sizeof(ObjInfo[0]))
107 static PGDI_HANDLE_TABLE HandleTable
= NULL
;
108 static LARGE_INTEGER ShortDelay
;
110 #define DelayExecution() \
111 DPRINT("%s:%i: Delay\n", __FILE__, __LINE__); \
112 KeDelayExecutionThread(KernelMode, FALSE, &ShortDelay)
115 * Allocate GDI object table.
116 * \param Size - number of entries in the object table.
118 static PGDI_HANDLE_TABLE INTERNAL_CALL
119 GDIOBJ_iAllocHandleTable(VOID
)
121 PGDI_HANDLE_TABLE handleTable
;
124 PGDI_TABLE_ENTRY Entry
;
126 handleTable
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(GDI_HANDLE_TABLE
), TAG_GDIHNDTBLE
);
127 ASSERT( handleTable
);
128 RtlZeroMemory(handleTable
, sizeof(GDI_HANDLE_TABLE
));
131 * initialize the free entry cache
133 InitializeSListHead(&handleTable
->FreeEntriesHead
);
134 Entry
= &HandleTable
->Entries
[RESERVE_ENTRIES_COUNT
];
135 for(i
= GDI_HANDLE_COUNT
- 1; i
>= RESERVE_ENTRIES_COUNT
; i
--)
137 InterlockedPushEntrySList(&handleTable
->FreeEntriesHead
, &handleTable
->FreeEntries
[i
]);
140 handleTable
->LookasideLists
= ExAllocatePoolWithTag(NonPagedPool
,
141 OBJTYPE_COUNT
* sizeof(PAGED_LOOKASIDE_LIST
),
143 if(handleTable
->LookasideLists
== NULL
)
145 ExFreePool(handleTable
);
149 for(ObjType
= 0; ObjType
< OBJTYPE_COUNT
; ObjType
++)
151 ExInitializePagedLookasideList(handleTable
->LookasideLists
+ ObjType
, NULL
, NULL
, 0,
152 ObjInfo
[ObjType
].Size
+ sizeof(GDIOBJHDR
), TAG_GDIOBJ
, 0);
155 ShortDelay
.QuadPart
= -5000LL; /* FIXME - 0.5 ms? */
160 static inline PPAGED_LOOKASIDE_LIST
161 FindLookasideList(DWORD ObjectType
)
165 for (Index
= 0; Index
< OBJTYPE_COUNT
; Index
++)
167 if (ObjInfo
[Index
].Type
== ObjectType
)
169 return HandleTable
->LookasideLists
+ Index
;
173 DPRINT1("Can't find lookaside list for object type 0x%08x\n", ObjectType
);
179 RunCleanupCallback(PGDIOBJ pObj
, DWORD ObjectType
)
183 for (Index
= 0; Index
< OBJTYPE_COUNT
; Index
++)
185 if (ObjInfo
[Index
].Type
== ObjectType
)
187 return ((GDICLEANUPPROC
)ObjInfo
[Index
].CleanupProc
)(pObj
);
191 DPRINT1("Can't find cleanup callback for object type 0x%08x\n", ObjectType
);
196 GetObjectSize(DWORD ObjectType
)
200 for (Index
= 0; Index
< OBJTYPE_COUNT
; Index
++)
202 if (ObjInfo
[Index
].Type
== ObjectType
)
204 return ObjInfo
[Index
].Size
;
208 DPRINT1("Can't find size for object type 0x%08x\n", ObjectType
);
214 static int leak_reported
= 0;
215 #define GDI_STACK_LEVELS 4
216 static ULONG GDIHandleAllocator
[GDI_STACK_LEVELS
][GDI_HANDLE_COUNT
];
217 struct DbgOpenGDIHandle
223 static struct DbgOpenGDIHandle h
[H
];
225 void IntDumpHandleTable ( int which
)
229 // step through GDI handle table and find out who our culprit is...
230 for ( i
= RESERVE_ENTRIES_COUNT
; i
< GDI_HANDLE_COUNT
; i
++ )
232 for ( j
= 0; j
< n
; j
++ )
234 if ( GDIHandleAllocator
[which
][i
] == h
[j
].loc
)
241 h
[j
].loc
= GDIHandleAllocator
[which
][i
];
249 // bubble sort time! weeeeee!!
250 for ( i
= 0; i
< n
-1; i
++ )
252 if ( h
[i
].count
< h
[i
+1].count
)
254 struct DbgOpenGDIHandle t
;
256 t
.count
= h
[i
+1].count
;
257 h
[i
+1].loc
= h
[i
].loc
;
258 h
[i
+1].count
= h
[i
].count
;
260 while ( j
> 0 && h
[j
-1].count
< t
.count
)
265 // print the first 30 offenders...
266 DbgPrint ( "Worst GDI Handle leak offenders - stack trace level %i (out of %i unique locations):\n", which
, n
);
267 for ( i
= 0; i
< 30 && i
< n
; i
++ )
270 if ( !KeRosPrintAddress ( (PVOID
)h
[i
].loc
) )
271 DbgPrint ( "<%X>", h
[i
].loc
);
272 DbgPrint ( " (%i allocations)\n", h
[i
].count
);
278 * Allocate memory for GDI object and return handle to it.
280 * \param ObjectType - type of object \ref GDI object types
282 * \return Handle of the allocated object.
284 * \note Use GDIOBJ_Lock() to obtain pointer to the new object.
285 * \todo return the object pointer and lock it by default.
287 HGDIOBJ INTERNAL_CALL
289 GDIOBJ_AllocObjDbg(const char* file
, int line
, ULONG ObjectType
)
290 #else /* !GDI_DEBUG */
291 GDIOBJ_AllocObj(ULONG ObjectType
)
292 #endif /* GDI_DEBUG */
294 PW32PROCESS W32Process
;
295 PGDIOBJHDR newObject
;
296 PPAGED_LOOKASIDE_LIST LookasideList
;
297 LONG CurrentProcessId
, LockedProcessId
;
302 ASSERT(ObjectType
!= GDI_OBJECT_TYPE_DONTCARE
);
304 LookasideList
= FindLookasideList(ObjectType
);
305 if(LookasideList
!= NULL
)
307 newObject
= ExAllocateFromPagedLookasideList(LookasideList
);
308 if(newObject
!= NULL
)
310 PSLIST_ENTRY FreeEntry
;
311 PGDI_TABLE_ENTRY Entry
;
315 /* shift the process id to the left so we can use the first bit to lock
317 FIXME - don't shift once ROS' PIDs match with nt! */
318 CurrentProcessId
= (LONG
)PsGetCurrentProcessId() << 1;
319 LockedProcessId
= CurrentProcessId
| 0x1;
320 W32Process
= PsGetWin32Process();
322 newObject
->LockingThread
= NULL
;
323 newObject
->Locks
= 0;
326 newObject
->createdfile
= file
;
327 newObject
->createdline
= line
;
328 newObject
->lockfile
= NULL
;
329 newObject
->lockline
= 0;
332 ObjectBody
= GDIHdrToBdy(newObject
);
334 RtlZeroMemory(ObjectBody
, GetObjectSize(ObjectType
));
336 TypeInfo
= (ObjectType
& 0xFFFF0000) | (ObjectType
>> 16);
338 FreeEntry
= InterlockedPopEntrySList(&HandleTable
->FreeEntriesHead
);
339 if(FreeEntry
!= NULL
)
345 /* calculate the entry from the address of the entry in the free slot array */
346 Index
= ((ULONG_PTR
)FreeEntry
- (ULONG_PTR
)&HandleTable
->FreeEntries
[0]) /
347 sizeof(HandleTable
->FreeEntries
[0]);
348 Entry
= &HandleTable
->Entries
[Index
];
349 Handle
= (HGDIOBJ
)((Index
& 0xFFFF) | (ObjectType
& 0xFFFF0000));
352 PrevProcId
= InterlockedCompareExchange(&Entry
->ProcessId
, LockedProcessId
, 0);
355 ASSERT(Entry
->KernelData
== NULL
);
357 Entry
->KernelData
= ObjectBody
;
359 /* we found a free entry, no need to exchange this field atomically
360 since we're holding the lock */
361 Entry
->Type
= TypeInfo
;
363 /* unlock the entry */
364 InterlockedExchange(&Entry
->ProcessId
, CurrentProcessId
);
371 __asm__("mov %%ebp, %%ebx" : "=b" (Frame
) : );
372 #elif defined(_MSC_VER)
373 __asm mov
[Frame
], ebp
375 Frame
= (PULONG
)Frame
[0]; // step out of AllocObj()
376 for ( which
= 0; which
< GDI_STACK_LEVELS
&& Frame
[1] != 0 && Frame
[1] != 0xDEADBEEF; which
++ )
378 GDIHandleAllocator
[which
][Index
] = Frame
[1]; // step out of AllocObj()
379 Frame
= ((PULONG
)Frame
[0]);
381 for ( ; which
< GDI_STACK_LEVELS
; which
++ )
382 GDIHandleAllocator
[which
][Index
] = 0xDEADBEEF;
385 //ExAllocatePool ( PagedPool, (ULONG)newObject ); // initiate red-zone verification of object we allocated
387 if(W32Process
!= NULL
)
389 InterlockedIncrement(&W32Process
->GDIObjects
);
392 DPRINT("GDIOBJ_AllocObj: 0x%x ob: 0x%x\n", Handle
, ObjectBody
);
400 DPRINT1("[%d]Waiting on 0x%x\n", Attempts
, Handle
);
403 /* damn, someone is trying to lock the object even though it doesn't
404 eve nexist anymore, wait a little and try again!
405 FIXME - we shouldn't loop forever! Give up after some time! */
412 ExFreeToPagedLookasideList(LookasideList
, newObject
);
413 DPRINT1("Failed to insert gdi object into the handle table, no handles left!\n");
415 if ( !leak_reported
)
417 DPRINT1("reporting gdi handle abusers:\n");
419 for ( which
= 0; which
< GDI_STACK_LEVELS
; which
++ )
420 IntDumpHandleTable(which
);
425 DPRINT1("gdi handle abusers already reported!\n");
431 DPRINT1("Not enough memory to allocate gdi object!\n");
436 DPRINT1("Failed to find lookaside list for object type 0x%x\n", ObjectType
);
442 * Free memory allocated for the GDI object. For each object type this function calls the
443 * appropriate cleanup routine.
445 * \param hObj - handle of the object to be deleted.
447 * \return Returns TRUE if succesful.
448 * \return Returns FALSE if the cleanup routine returned FALSE or the object doesn't belong
449 * to the calling process.
453 GDIOBJ_FreeObjDbg(const char* file
, int line
, HGDIOBJ hObj
, DWORD ObjectType
)
454 #else /* !GDI_DEBUG */
455 GDIOBJ_FreeObj(HGDIOBJ hObj
, DWORD ObjectType
)
456 #endif /* GDI_DEBUG */
458 PGDI_TABLE_ENTRY Entry
;
459 PPAGED_LOOKASIDE_LIST LookasideList
;
460 LONG ProcessId
, LockedProcessId
, PrevProcId
, ExpectedType
;
465 DPRINT("GDIOBJ_FreeObj: hObj: 0x%08x\n", hObj
);
467 if(GDI_HANDLE_IS_STOCKOBJ(hObj
))
469 DPRINT1("GDIOBJ_FreeObj() failed, can't delete stock object handle: 0x%x !!!\n", hObj
);
471 DPRINT1("-> called from %s:%i\n", file
, line
);
476 /* shift the process id to the left so we can use the first bit to lock the object.
477 FIXME - don't shift once ROS' PIDs match with nt! */
478 ProcessId
= (LONG
)PsGetCurrentProcessId() << 1;
479 LockedProcessId
= ProcessId
| 0x1;
481 ExpectedType
= ((ObjectType
!= GDI_OBJECT_TYPE_DONTCARE
) ? ObjectType
: 0);
483 Entry
= GDI_HANDLE_GET_ENTRY(HandleTable
, hObj
);
486 /* lock the object, we must not delete global objects, so don't exchange the locking
487 process ID to zero when attempting to lock a global object... */
488 PrevProcId
= InterlockedCompareExchange(&Entry
->ProcessId
, LockedProcessId
, ProcessId
);
489 if(PrevProcId
== ProcessId
)
491 if(Entry
->Type
!= 0 && Entry
->KernelData
!= NULL
&& (ExpectedType
== 0 || ((Entry
->Type
<< 16) == ExpectedType
)))
495 GdiHdr
= GDIBdyToHdr(Entry
->KernelData
);
497 if(GdiHdr
->LockingThread
== NULL
)
500 PW32PROCESS W32Process
= PsGetWin32Process();
501 ULONG Type
= Entry
->Type
<< 16;
503 /* Clear the type field so when unlocking the handle it gets finally deleted */
505 Entry
->KernelData
= NULL
;
507 /* unlock the handle slot */
508 InterlockedExchange(&Entry
->ProcessId
, 0);
510 /* push this entry to the free list */
511 InterlockedPushEntrySList(&HandleTable
->FreeEntriesHead
,
512 &HandleTable
->FreeEntries
[GDI_ENTRY_TO_INDEX(HandleTable
, Entry
)]);
514 if(W32Process
!= NULL
)
516 InterlockedDecrement(&W32Process
->GDIObjects
);
519 /* call the cleanup routine. */
520 Ret
= RunCleanupCallback(GDIHdrToBdy(GdiHdr
), Type
);
522 /* Now it's time to free the memory */
523 LookasideList
= FindLookasideList(Type
);
524 if(LookasideList
!= NULL
)
526 ExFreeToPagedLookasideList(LookasideList
, GdiHdr
);
533 /* the object is currently locked. just clear the type field so when the
534 object gets unlocked it will be finally deleted from the table. */
537 /* unlock the handle slot */
538 InterlockedExchange(&Entry
->ProcessId
, 0);
540 /* report a successful deletion as the object is actually removed from the table */
548 DPRINT1("Attempted to delete object 0x%x, type mismatch (0x%x : 0x%x)\n", hObj
, ObjectType
, ExpectedType
);
552 DPRINT1("Attempted to delete object 0x%x which was already deleted!\n", hObj
);
554 InterlockedExchange(&Entry
->ProcessId
, PrevProcId
);
557 else if(PrevProcId
== LockedProcessId
)
562 DPRINT1("[%d]Waiting on 0x%x\n", Attempts
, hObj
);
565 /* the object is currently locked, wait some time and try again.
566 FIXME - we shouldn't loop forever! Give up after some time! */
573 if((PrevProcId
>> 1) == 0)
575 DPRINT1("Attempted to free global gdi handle 0x%x, caller needs to get ownership first!!!", hObj
);
579 DPRINT1("Attempted to free foreign handle: 0x%x Owner: 0x%x from Caller: 0x%x\n", hObj
, PrevProcId
>> 1, ProcessId
>> 1);
582 DPRINT1("-> called from %s:%i\n", file
, line
);
590 * Lock multiple objects. Use this function when you need to lock multiple objects and some of them may be
591 * duplicates. You should use this function to avoid trying to lock the same object twice!
593 * \param pList pointer to the list that contains handles to the objects. You should set hObj and ObjectType fields.
594 * \param nObj number of objects to lock
595 * \return for each entry in pList this function sets pObj field to point to the object.
597 * \note this function uses an O(n^2) algoritm because we shouldn't need to call it with more than 3 or 4 objects.
600 GDIOBJ_LockMultipleObj(PGDIMULTILOCK pList
, INT nObj
)
604 /* FIXME - check for "invalid" handles */
605 /* go through the list checking for duplicate objects */
606 for (i
= 0; i
< nObj
; i
++)
608 pList
[i
].pObj
= NULL
;
609 for (j
= 0; j
< i
; j
++)
611 if (pList
[i
].hObj
== pList
[j
].hObj
)
613 /* already locked, so just copy the pointer to the object */
614 pList
[i
].pObj
= pList
[j
].pObj
;
619 if (NULL
== pList
[i
].pObj
)
621 /* object hasn't been locked, so lock it. */
622 if (NULL
!= pList
[i
].hObj
)
624 pList
[i
].pObj
= GDIOBJ_LockObj(pList
[i
].hObj
, pList
[i
].ObjectType
);
633 * Unlock multiple objects. Use this function when you need to unlock multiple objects and some of them may be
636 * \param pList pointer to the list that contains handles to the objects. You should set hObj and ObjectType fields.
637 * \param nObj number of objects to lock
639 * \note this function uses O(n^2) algoritm because we shouldn't need to call it with more than 3 or 4 objects.
642 GDIOBJ_UnlockMultipleObj(PGDIMULTILOCK pList
, INT nObj
)
647 /* go through the list checking for duplicate objects */
648 for (i
= 0; i
< nObj
; i
++)
650 if (NULL
!= pList
[i
].pObj
)
652 for (j
= i
+ 1; j
< nObj
; j
++)
654 if ((pList
[i
].pObj
== pList
[j
].pObj
))
656 /* set the pointer to zero for all duplicates */
657 pList
[j
].pObj
= NULL
;
660 GDIOBJ_UnlockObj(pList
[i
].hObj
);
661 pList
[i
].pObj
= NULL
;
669 * Initialization of the GDI object engine.
672 InitGdiObjectHandleTable (VOID
)
674 DPRINT("InitGdiObjectHandleTable\n");
676 HandleTable
= GDIOBJ_iAllocHandleTable();
677 DPRINT("HandleTable: %x\n", HandleTable
);
682 * \param hObject object handle
683 * \return if the function fails the returned value is FALSE.
686 NtGdiDeleteObject(HGDIOBJ hObject
)
688 DPRINT("NtGdiDeleteObject handle 0x%08x\n", hObject
);
690 return NULL
!= hObject
691 ? GDIOBJ_FreeObj(hObject
, GDI_OBJECT_TYPE_DONTCARE
) : FALSE
;
695 * Internal function. Called when the process is destroyed to free the remaining GDI handles.
696 * \param Process - PID of the process that will be destroyed.
699 GDI_CleanupForProcess (struct _EPROCESS
*Process
)
701 PGDI_TABLE_ENTRY Entry
, End
;
702 PEPROCESS CurrentProcess
;
703 PW32PROCESS W32Process
;
705 ULONG Index
= RESERVE_ENTRIES_COUNT
;
707 DPRINT("Starting CleanupForProcess prochandle %x Pid %d\n", Process
, Pid
);
708 CurrentProcess
= PsGetCurrentProcess();
709 if (CurrentProcess
!= Process
)
711 KeAttachProcess(Process
);
713 W32Process
= Process
->Win32Process
;
716 if(W32Process
->GDIObjects
> 0)
718 /* FIXME - Instead of building the handle here and delete it using GDIOBJ_FreeObj
719 we should delete it directly here! */
720 ProcId
= ((LONG
)Process
->UniqueProcessId
<< 1);
722 End
= &HandleTable
->Entries
[GDI_HANDLE_COUNT
];
723 for(Entry
= &HandleTable
->Entries
[RESERVE_ENTRIES_COUNT
];
727 /* ignore the lock bit */
728 if((Entry
->ProcessId
& ~0x1) == ProcId
&& Entry
->Type
!= 0)
730 HGDIOBJ ObjectHandle
;
732 /* Create the object handle for the entry, the upper 16 bit of the
733 Type field includes the type of the object including the stock
734 object flag - but since stock objects don't have a process id we can
735 simply ignore this fact here. */
736 ObjectHandle
= (HGDIOBJ
)(Index
| (Entry
->Type
& 0xFFFF0000));
738 if(GDIOBJ_FreeObj(ObjectHandle
, GDI_OBJECT_TYPE_DONTCARE
) &&
739 W32Process
->GDIObjects
== 0)
741 /* there are no more gdi handles for this process, bail */
748 if (CurrentProcess
!= Process
)
753 DPRINT("Completed cleanup for process %d\n", Pid
);
759 * Return pointer to the object by handle.
761 * \param hObj Object handle
762 * \return Pointer to the object.
764 * \note Process can only get pointer to the objects it created or global objects.
766 * \todo Get rid of the ObjectType parameter!
768 PGDIOBJ INTERNAL_CALL
770 GDIOBJ_LockObjDbg (const char* file
, int line
, HGDIOBJ hObj
, DWORD ObjectType
)
771 #else /* !GDI_DEBUG */
772 GDIOBJ_LockObj (HGDIOBJ hObj
, DWORD ObjectType
)
773 #endif /* GDI_DEBUG */
775 PGDI_TABLE_ENTRY Entry
;
777 LONG ProcessId
, LockedProcessId
, PrevProcId
, ExpectedType
;
782 DPRINT("GDIOBJ_LockObj: hObj: 0x%08x\n", hObj
);
784 Thread
= PsGetCurrentThread();
786 /* shift the process id to the left so we can use the first bit to lock the object.
787 FIXME - don't shift once ROS' PIDs match with nt! */
788 ProcessId
= (LONG
)PsGetCurrentProcessId() << 1;
789 LockedProcessId
= ProcessId
| 0x1;
791 ExpectedType
= ((ObjectType
!= GDI_OBJECT_TYPE_DONTCARE
) ? ObjectType
: 0);
793 Entry
= GDI_HANDLE_GET_ENTRY(HandleTable
, hObj
);
796 /* lock the object, we must not delete stock objects, so don't check!!! */
797 PrevProcId
= InterlockedCompareExchange(&Entry
->ProcessId
, LockedProcessId
, ProcessId
);
798 if(PrevProcId
== ProcessId
)
800 LONG EntryType
= Entry
->Type
<< 16;
802 /* we're locking an object that belongs to our process or it's a global
803 object if ProcessId == 0 here. ProcessId can only be 0 here if it previously
804 failed to lock the object and it turned out to be a global object. */
805 if(EntryType
!= 0 && Entry
->KernelData
!= NULL
&& (ExpectedType
== 0 || (EntryType
== ExpectedType
)))
810 GdiHdr
= GDIBdyToHdr(Entry
->KernelData
);
812 /* save the pointer to the calling thread so we know it was this thread
813 that locked the object. There's no need to do this atomically as we're
814 holding the lock of the handle slot, but this way it's easier ;) */
815 PrevThread
= InterlockedCompareExchangePointer(&GdiHdr
->LockingThread
, Thread
, NULL
);
817 if(PrevThread
== NULL
|| PrevThread
== Thread
)
819 if(++GdiHdr
->Locks
== 1)
822 GdiHdr
->lockfile
= file
;
823 GdiHdr
->lockline
= line
;
827 InterlockedExchange(&Entry
->ProcessId
, PrevProcId
);
829 /* we're done, return the object body */
830 return GDIHdrToBdy(GdiHdr
);
834 InterlockedExchange(&Entry
->ProcessId
, PrevProcId
);
839 DPRINT1("[%d]Waiting at %s:%i as 0x%x on 0x%x\n", Attempts
, file
, line
, Thread
, PrevThread
);
849 InterlockedExchange(&Entry
->ProcessId
, PrevProcId
);
853 DPRINT1("Attempted to lock object 0x%x that is deleted!\n", hObj
);
854 KeRosDumpStackFrames ( NULL
, 20 );
858 DPRINT1("Attempted to lock object 0x%x, type mismatch (0x%x : 0x%x)\n", hObj
, EntryType
, ExpectedType
);
859 KeRosDumpStackFrames ( NULL
, 20 );
862 DPRINT1("-> called from %s:%i\n", file
, line
);
866 else if(PrevProcId
== LockedProcessId
)
871 DPRINT1("[%d]Waiting from %s:%i on 0x%x\n", Attempts
, file
, line
, hObj
);
874 /* the handle is currently locked, wait some time and try again.
875 FIXME - we shouldn't loop forever! Give up after some time! */
880 else if((PrevProcId
& ~0x1) == 0)
882 /* we're trying to lock a global object, change the ProcessId to 0 and try again */
884 LockedProcessId
= ProcessId
|0x1;
890 DPRINT1("Attempted to lock foreign handle: 0x%x, Owner: 0x%x locked: 0x%x Caller: 0x%x, stockobj: 0x%x\n", hObj
, PrevProcId
>> 1, PrevProcId
& 0x1, PsGetCurrentProcessId(), GDI_HANDLE_IS_STOCKOBJ(hObj
));
891 KeRosDumpStackFrames ( NULL
, 20 );
893 DPRINT1("-> called from %s:%i\n", file
, line
);
902 * Release GDI object. Every object locked by GDIOBJ_LockObj() must be unlocked. You should unlock the object
903 * as soon as you don't need to have access to it's data.
905 * \param hObj Object handle
907 * \note This function performs delayed cleanup. If the object is locked when GDI_FreeObj() is called
908 * then \em this function frees the object when reference count is zero.
912 GDIOBJ_UnlockObjDbg (const char* file
, int line
, HGDIOBJ hObj
)
913 #else /* !GDI_DEBUG */
914 GDIOBJ_UnlockObj (HGDIOBJ hObj
)
915 #endif /* GDI_DEBUG */
917 PGDI_TABLE_ENTRY Entry
;
919 LONG ProcessId
, LockedProcessId
, PrevProcId
;
924 DPRINT("GDIOBJ_UnlockObj: hObj: 0x%08x\n", hObj
);
925 Thread
= PsGetCurrentThread();
927 /* shift the process id to the left so we can use the first bit to lock the object.
928 FIXME - don't shift once ROS' PIDs match with nt! */
929 ProcessId
= (LONG
)PsGetCurrentProcessId() << 1;
930 LockedProcessId
= ProcessId
| 0x1;
932 Entry
= GDI_HANDLE_GET_ENTRY(HandleTable
, hObj
);
935 /* lock the handle, we must not delete stock objects, so don't check!!! */
936 PrevProcId
= InterlockedCompareExchange(&Entry
->ProcessId
, LockedProcessId
, ProcessId
);
937 if(PrevProcId
== ProcessId
)
939 /* we're unlocking an object that belongs to our process or it's a global
940 object if ProcessId == 0 here. ProcessId can only be 0 here if it previously
941 failed to lock the object and it turned out to be a global object. */
942 if(Entry
->KernelData
!= NULL
)
947 GdiHdr
= GDIBdyToHdr(Entry
->KernelData
);
948 //ExAllocatePool ( PagedPool, (ULONG)GdiHdr ); // initiate red-zone validation on this block
950 PrevThread
= GdiHdr
->LockingThread
;
951 if(PrevThread
== Thread
)
955 if(--GdiHdr
->Locks
== 0)
957 GdiHdr
->LockingThread
= NULL
;
960 GdiHdr
->lockfile
= NULL
;
961 GdiHdr
->lockline
= 0;
965 if(Entry
->Type
== 0 && GdiHdr
->Locks
== 0)
967 PPAGED_LOOKASIDE_LIST LookasideList
;
968 PW32PROCESS W32Process
= PsGetWin32Process();
969 DWORD Type
= GDI_HANDLE_GET_TYPE(hObj
);
971 ASSERT(ProcessId
!= 0); /* must not delete a global handle!!!! */
973 /* we should delete the handle */
974 Entry
->KernelData
= NULL
;
975 InterlockedExchange(&Entry
->ProcessId
, 0);
977 InterlockedPushEntrySList(&HandleTable
->FreeEntriesHead
,
978 &HandleTable
->FreeEntries
[GDI_ENTRY_TO_INDEX(HandleTable
, Entry
)]);
980 if(W32Process
!= NULL
)
982 InterlockedDecrement(&W32Process
->GDIObjects
);
985 /* call the cleanup routine. */
986 Ret
= RunCleanupCallback(GDIHdrToBdy(GdiHdr
), Type
);
988 /* Now it's time to free the memory */
989 LookasideList
= FindLookasideList(Type
);
990 if(LookasideList
!= NULL
)
992 ExFreeToPagedLookasideList(LookasideList
, GdiHdr
);
997 /* remove the handle slot lock */
998 InterlockedExchange(&Entry
->ProcessId
, PrevProcId
);
1006 else if(PrevThread
!= NULL
)
1008 DPRINT1("Attempted to unlock object 0x%x, previously locked by other thread (0x%x) from %s:%i (called from %s:%i)\n",
1009 hObj
, PrevThread
, GdiHdr
->lockfile
, GdiHdr
->lockline
, file
, line
);
1010 InterlockedExchange(&Entry
->ProcessId
, PrevProcId
);
1018 DPRINT1("[%d]Waiting at %s:%i as 0x%x on 0x%x\n", Attempts
, file
, line
, Thread
, PrevThread
);
1021 /* FIXME - we should give up after some time unless we want to wait forever! */
1022 InterlockedExchange(&Entry
->ProcessId
, PrevProcId
);
1030 InterlockedExchange(&Entry
->ProcessId
, PrevProcId
);
1031 DPRINT1("Attempted to unlock object 0x%x that is deleted!\n", hObj
);
1034 else if(PrevProcId
== LockedProcessId
)
1039 DPRINT1("[%d]Waiting from %s:%i on 0x%x\n", Attempts
, file
, line
, hObj
);
1042 /* the handle is currently locked, wait some time and try again.
1043 FIXME - we shouldn't loop forever! Give up after some time! */
1048 else if((PrevProcId
& ~0x1) == 0)
1050 /* we're trying to unlock a global object, change the ProcessId to 0 and try again */
1052 LockedProcessId
= ProcessId
|0x1;
1058 DPRINT1("Attempted to unlock foreign handle: 0x%x, Owner: 0x%x locked: 0x%x Caller: 0x%x, stockobj: 0x%x\n", hObj
, PrevProcId
>> 1, PrevProcId
& 0x1, PsGetCurrentProcessId(), GDI_HANDLE_IS_STOCKOBJ(hObj
));
1065 GDIOBJ_OwnedByCurrentProcess(HGDIOBJ ObjectHandle
)
1067 PGDI_TABLE_ENTRY Entry
;
1071 DPRINT("GDIOBJ_OwnedByCurrentProcess: ObjectHandle: 0x%08x\n", ObjectHandle
);
1073 if(!GDI_HANDLE_IS_STOCKOBJ(ObjectHandle
))
1075 ProcessId
= (LONG
)PsGetCurrentProcessId() << 1;
1077 Entry
= GDI_HANDLE_GET_ENTRY(HandleTable
, ObjectHandle
);
1078 Ret
= Entry
->KernelData
!= NULL
&&
1080 (Entry
->ProcessId
& ~0x1) == ProcessId
;
1089 GDIOBJ_ConvertToStockObj(HGDIOBJ
*hObj
)
1092 * FIXME !!!!! THIS FUNCTION NEEDS TO BE FIXED - IT IS NOT SAFE WHEN OTHER THREADS
1093 * MIGHT ATTEMPT TO LOCK THE OBJECT DURING THIS CALL!!!
1095 PGDI_TABLE_ENTRY Entry
;
1096 LONG ProcessId
, LockedProcessId
, PrevProcId
;
1104 DPRINT("GDIOBJ_ConvertToStockObj: hObj: 0x%08x\n", *hObj
);
1106 Thread
= PsGetCurrentThread();
1108 if(!GDI_HANDLE_IS_STOCKOBJ(*hObj
))
1110 /* shift the process id to the left so we can use the first bit to lock the object.
1111 FIXME - don't shift once ROS' PIDs match with nt! */
1112 ProcessId
= (LONG
)PsGetCurrentProcessId() << 1;
1113 LockedProcessId
= ProcessId
| 0x1;
1115 Entry
= GDI_HANDLE_GET_ENTRY(HandleTable
, *hObj
);
1118 /* lock the object, we must not convert stock objects, so don't check!!! */
1119 PrevProcId
= InterlockedCompareExchange(&Entry
->ProcessId
, LockedProcessId
, ProcessId
);
1120 if(PrevProcId
== ProcessId
)
1122 LONG NewType
, PrevType
, OldType
;
1124 /* we're locking an object that belongs to our process. First calculate
1125 the new object type including the stock object flag and then try to
1127 NewType
= GDI_HANDLE_GET_TYPE(*hObj
);
1128 NewType
|= NewType
>> 16;
1129 /* This is the type that the object should have right now, save it */
1131 /* As the object should be a stock object, set it's flag, but only in the upper 16 bits */
1132 NewType
|= GDI_HANDLE_STOCK_MASK
;
1134 /* Try to exchange the type field - but only if the old (previous type) matches! */
1135 PrevType
= InterlockedCompareExchange(&Entry
->Type
, NewType
, OldType
);
1136 if(PrevType
== OldType
&& Entry
->KernelData
!= NULL
)
1138 PETHREAD PrevThread
;
1141 /* We successfully set the stock object flag.
1142 KernelData should never be NULL here!!! */
1143 ASSERT(Entry
->KernelData
);
1145 GdiHdr
= GDIBdyToHdr(Entry
->KernelData
);
1147 PrevThread
= GdiHdr
->LockingThread
;
1148 if(PrevThread
== NULL
|| PrevThread
== Thread
)
1150 /* dereference the process' object counter */
1151 if(PrevProcId
!= GDI_GLOBAL_PROCESS
)
1153 PEPROCESS OldProcess
;
1154 PW32PROCESS W32Process
;
1158 Status
= PsLookupProcessByProcessId((PVOID
)(PrevProcId
>> 1), &OldProcess
);
1159 if(NT_SUCCESS(Status
))
1161 W32Process
= OldProcess
->Win32Process
;
1162 if(W32Process
!= NULL
)
1164 InterlockedDecrement(&W32Process
->GDIObjects
);
1166 ObDereferenceObject(OldProcess
);
1170 /* remove the process id lock and make it global */
1171 InterlockedExchange(&Entry
->ProcessId
, GDI_GLOBAL_PROCESS
);
1173 *hObj
= (HGDIOBJ
)((ULONG
)(*hObj
) | GDI_HANDLE_STOCK_MASK
);
1175 /* we're done, successfully converted the object */
1183 if(GdiHdr
->lockfile
!= NULL
)
1185 DPRINT1("[%d]Locked %s:%i by 0x%x (we're 0x%x)\n", Attempts
, GdiHdr
->lockfile
, GdiHdr
->lockline
, PrevThread
, Thread
);
1189 /* WTF?! The object is already locked by a different thread!
1190 Release the lock, wait a bit and try again!
1191 FIXME - we should give up after some time unless we want to wait forever! */
1192 InterlockedExchange(&Entry
->ProcessId
, PrevProcId
);
1200 DPRINT1("Attempted to convert object 0x%x that is deleted! Should never get here!!!\n", hObj
);
1203 else if(PrevProcId
== LockedProcessId
)
1208 DPRINT1("[%d]Waiting on 0x%x\n", Attempts
, hObj
);
1211 /* the object is currently locked, wait some time and try again.
1212 FIXME - we shouldn't loop forever! Give up after some time! */
1219 DPRINT1("Attempted to convert invalid handle: 0x%x\n", hObj
);
1227 GDIOBJ_SetOwnership(HGDIOBJ ObjectHandle
, PEPROCESS NewOwner
)
1229 PGDI_TABLE_ENTRY Entry
;
1230 LONG ProcessId
, LockedProcessId
, PrevProcId
;
1236 DPRINT("GDIOBJ_SetOwnership: hObj: 0x%x, NewProcess: 0x%x\n", ObjectHandle
, (NewOwner
? PsGetProcessId(NewOwner
) : 0));
1238 Thread
= PsGetCurrentThread();
1240 if(!GDI_HANDLE_IS_STOCKOBJ(ObjectHandle
))
1242 /* shift the process id to the left so we can use the first bit to lock the object.
1243 FIXME - don't shift once ROS' PIDs match with nt! */
1244 ProcessId
= (LONG
)PsGetCurrentProcessId() << 1;
1245 LockedProcessId
= ProcessId
| 0x1;
1247 Entry
= GDI_HANDLE_GET_ENTRY(HandleTable
, ObjectHandle
);
1250 /* lock the object, we must not convert stock objects, so don't check!!! */
1251 PrevProcId
= InterlockedCompareExchange(&Entry
->ProcessId
, ProcessId
, LockedProcessId
);
1252 if(PrevProcId
== ProcessId
)
1254 PETHREAD PrevThread
;
1256 if(Entry
->Type
!= 0 && Entry
->KernelData
!= NULL
)
1258 PGDIOBJHDR GdiHdr
= GDIBdyToHdr(Entry
->KernelData
);
1260 PrevThread
= GdiHdr
->LockingThread
;
1261 if(PrevThread
== NULL
|| PrevThread
== Thread
)
1263 PEPROCESS OldProcess
;
1264 PW32PROCESS W32Process
;
1267 /* dereference the process' object counter */
1269 Status
= PsLookupProcessByProcessId((PVOID
)(PrevProcId
>> 1), &OldProcess
);
1270 if(NT_SUCCESS(Status
))
1272 W32Process
= OldProcess
->Win32Process
;
1273 if(W32Process
!= NULL
)
1275 InterlockedDecrement(&W32Process
->GDIObjects
);
1277 ObDereferenceObject(OldProcess
);
1280 if(NewOwner
!= NULL
)
1283 ProcessId
= (LONG
)PsGetProcessId(NewOwner
) << 1;
1285 /* Increase the new process' object counter */
1286 W32Process
= NewOwner
->Win32Process
;
1287 if(W32Process
!= NULL
)
1289 InterlockedIncrement(&W32Process
->GDIObjects
);
1295 /* remove the process id lock and change it to the new process id */
1296 InterlockedExchange(&Entry
->ProcessId
, ProcessId
);
1306 if(GdiHdr
->lockfile
!= NULL
)
1308 DPRINT1("[%d]Locked from %s:%i by 0x%x (we're 0x%x)\n", Attempts
, GdiHdr
->lockfile
, GdiHdr
->lockline
, PrevThread
, Thread
);
1312 /* WTF?! The object is already locked by a different thread!
1313 Release the lock, wait a bit and try again! DO reset the pid lock
1314 so we make sure we don't access invalid memory in case the object is
1315 being deleted in the meantime (because we don't have aquired a reference
1317 FIXME - we should give up after some time unless we want to wait forever! */
1318 InterlockedExchange(&Entry
->ProcessId
, PrevProcId
);
1326 DPRINT1("Attempted to change ownership of an object 0x%x currently being destroyed!!!\n", ObjectHandle
);
1329 else if(PrevProcId
== LockedProcessId
)
1334 DPRINT1("[%d]Waiting on 0x%x\n", Attempts
, ObjectHandle
);
1337 /* the object is currently locked, wait some time and try again.
1338 FIXME - we shouldn't loop forever! Give up after some time! */
1343 else if((PrevProcId
>> 1) == 0)
1345 /* allow changing ownership of global objects */
1347 LockedProcessId
= ProcessId
| 0x1;
1350 else if((PrevProcId
>> 1) != (LONG
)PsGetCurrentProcessId())
1352 DPRINT1("Attempted to change ownership of object 0x%x (pid: 0x%x) from pid 0x%x!!!\n", ObjectHandle
, PrevProcId
>> 1, PsGetCurrentProcessId());
1356 DPRINT1("Attempted to change owner of invalid handle: 0x%x\n", ObjectHandle
);
1362 GDIOBJ_CopyOwnership(HGDIOBJ CopyFrom
, HGDIOBJ CopyTo
)
1364 PGDI_TABLE_ENTRY FromEntry
;
1366 LONG FromProcessId
, FromLockedProcessId
, FromPrevProcId
;
1371 DPRINT("GDIOBJ_CopyOwnership: from: 0x%x, to: 0x%x\n", CopyFrom
, CopyTo
);
1373 Thread
= PsGetCurrentThread();
1375 if(!GDI_HANDLE_IS_STOCKOBJ(CopyFrom
) && !GDI_HANDLE_IS_STOCKOBJ(CopyTo
))
1377 FromEntry
= GDI_HANDLE_GET_ENTRY(HandleTable
, CopyFrom
);
1379 FromProcessId
= FromEntry
->ProcessId
& ~0x1;
1380 FromLockedProcessId
= FromProcessId
| 0x1;
1383 /* lock the object, we must not convert stock objects, so don't check!!! */
1384 FromPrevProcId
= InterlockedCompareExchange(&FromEntry
->ProcessId
, FromProcessId
, FromLockedProcessId
);
1385 if(FromPrevProcId
== FromProcessId
)
1387 PETHREAD PrevThread
;
1390 if(FromEntry
->Type
!= 0 && FromEntry
->KernelData
!= NULL
)
1392 GdiHdr
= GDIBdyToHdr(FromEntry
->KernelData
);
1394 /* save the pointer to the calling thread so we know it was this thread
1395 that locked the object */
1396 PrevThread
= GdiHdr
->LockingThread
;
1397 if(PrevThread
== NULL
|| PrevThread
== Thread
)
1399 /* now let's change the ownership of the target object */
1401 if((FromPrevProcId
& ~0x1) != 0)
1403 PEPROCESS ProcessTo
;
1405 if(NT_SUCCESS(PsLookupProcessByProcessId((PVOID
)(FromPrevProcId
>> 1), &ProcessTo
)))
1407 GDIOBJ_SetOwnership(CopyTo
, ProcessTo
);
1408 ObDereferenceObject(ProcessTo
);
1413 /* mark the object as global */
1414 GDIOBJ_SetOwnership(CopyTo
, NULL
);
1417 InterlockedExchange(&FromEntry
->ProcessId
, FromPrevProcId
);
1424 if(GdiHdr
->lockfile
!= NULL
)
1426 DPRINT1("[%d]Locked from %s:%i by 0x%x (we're 0x%x)\n", Attempts
, GdiHdr
->lockfile
, GdiHdr
->lockline
, PrevThread
, Thread
);
1430 /* WTF?! The object is already locked by a different thread!
1431 Release the lock, wait a bit and try again! DO reset the pid lock
1432 so we make sure we don't access invalid memory in case the object is
1433 being deleted in the meantime (because we don't have aquired a reference
1435 FIXME - we should give up after some time unless we want to wait forever! */
1436 InterlockedExchange(&FromEntry
->ProcessId
, FromPrevProcId
);
1439 goto LockHandleFrom
;
1444 DPRINT1("Attempted to copy ownership from an object 0x%x currently being destroyed!!!\n", CopyFrom
);
1447 else if(FromPrevProcId
== FromLockedProcessId
)
1452 DPRINT1("[%d]Waiting on 0x%x\n", Attempts
, CopyFrom
);
1455 /* the object is currently locked, wait some time and try again.
1456 FIXME - we shouldn't loop forever! Give up after some time! */
1459 goto LockHandleFrom
;
1461 else if((FromPrevProcId
>> 1) != (LONG
)PsGetCurrentProcessId())
1463 /* FIXME - should we really allow copying ownership from objects that we don't even own? */
1464 DPRINT1("WARNING! Changing copying ownership of object 0x%x (pid: 0x%x) to pid 0x%x!!!\n", CopyFrom
, FromPrevProcId
>> 1, PsGetCurrentProcessId());
1465 FromProcessId
= FromPrevProcId
& ~0x1;
1466 FromLockedProcessId
= FromProcessId
| 0x1;
1467 goto LockHandleFrom
;
1471 DPRINT1("Attempted to copy ownership from invalid handle: 0x%x\n", CopyFrom
);
1477 GDI_MapHandleTable(HANDLE hProcess
)
1479 DPRINT("%s:%i: %s(): FIXME - Map handle table into the process memory space!\n",
1480 __FILE__
, __LINE__
, __FUNCTION__
);
1481 /* FIXME - Map the entire gdi handle table read-only to userland into the
1482 scope of hProcess and return the pointer */