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.80 2004/12/19 00:03:56 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 W32Process
= PsGetWin32Process();
303 /* HACK HACK HACK: simplest-possible quota implementation - don't allow a process
304 to take too many GDI objects, itself. */
305 if ( W32Process
&& W32Process
->GDIObjects
>= 0x2710 )
308 ASSERT(ObjectType
!= GDI_OBJECT_TYPE_DONTCARE
);
310 LookasideList
= FindLookasideList(ObjectType
);
311 if(LookasideList
!= NULL
)
313 newObject
= ExAllocateFromPagedLookasideList(LookasideList
);
314 if(newObject
!= NULL
)
316 PSLIST_ENTRY FreeEntry
;
317 PGDI_TABLE_ENTRY Entry
;
321 /* shift the process id to the left so we can use the first bit to lock
323 FIXME - don't shift once ROS' PIDs match with nt! */
324 CurrentProcessId
= (LONG
)PsGetCurrentProcessId() << 1;
325 LockedProcessId
= CurrentProcessId
| 0x1;
327 newObject
->LockingThread
= NULL
;
328 newObject
->Locks
= 0;
331 newObject
->createdfile
= file
;
332 newObject
->createdline
= line
;
333 newObject
->lockfile
= NULL
;
334 newObject
->lockline
= 0;
337 ObjectBody
= GDIHdrToBdy(newObject
);
339 RtlZeroMemory(ObjectBody
, GetObjectSize(ObjectType
));
341 TypeInfo
= (ObjectType
& 0xFFFF0000) | (ObjectType
>> 16);
343 FreeEntry
= InterlockedPopEntrySList(&HandleTable
->FreeEntriesHead
);
344 if(FreeEntry
!= NULL
)
350 /* calculate the entry from the address of the entry in the free slot array */
351 Index
= ((ULONG_PTR
)FreeEntry
- (ULONG_PTR
)&HandleTable
->FreeEntries
[0]) /
352 sizeof(HandleTable
->FreeEntries
[0]);
353 Entry
= &HandleTable
->Entries
[Index
];
354 Handle
= (HGDIOBJ
)((Index
& 0xFFFF) | (ObjectType
& 0xFFFF0000));
357 PrevProcId
= InterlockedCompareExchange(&Entry
->ProcessId
, LockedProcessId
, 0);
360 ASSERT(Entry
->KernelData
== NULL
);
362 Entry
->KernelData
= ObjectBody
;
364 /* we found a free entry, no need to exchange this field atomically
365 since we're holding the lock */
366 Entry
->Type
= TypeInfo
;
368 /* unlock the entry */
369 InterlockedExchange(&Entry
->ProcessId
, CurrentProcessId
);
376 __asm__("mov %%ebp, %%ebx" : "=b" (Frame
) : );
377 #elif defined(_MSC_VER)
378 __asm mov
[Frame
], ebp
380 Frame
= (PULONG
)Frame
[0]; /* step out of AllocObj() */
381 for ( which
= 0; which
< GDI_STACK_LEVELS
&& Frame
[1] != 0 && Frame
[1] != 0xDEADBEEF; which
++ )
383 GDIHandleAllocator
[which
][Index
] = Frame
[1]; /* step out of AllocObj() */
384 Frame
= ((PULONG
)Frame
[0]);
386 for ( ; which
< GDI_STACK_LEVELS
; which
++ )
387 GDIHandleAllocator
[which
][Index
] = 0xDEADBEEF;
391 if(W32Process
!= NULL
)
393 InterlockedIncrement(&W32Process
->GDIObjects
);
396 DPRINT("GDIOBJ_AllocObj: 0x%x ob: 0x%x\n", Handle
, ObjectBody
);
404 DPRINT1("[%d]Waiting on 0x%x\n", Attempts
, Handle
);
407 /* damn, someone is trying to lock the object even though it doesn't
408 eve nexist anymore, wait a little and try again!
409 FIXME - we shouldn't loop forever! Give up after some time! */
416 ExFreeToPagedLookasideList(LookasideList
, newObject
);
417 DPRINT1("Failed to insert gdi object into the handle table, no handles left!\n");
419 if ( !leak_reported
)
421 DPRINT1("reporting gdi handle abusers:\n");
423 for ( which
= 0; which
< GDI_STACK_LEVELS
; which
++ )
424 IntDumpHandleTable(which
);
429 DPRINT1("gdi handle abusers already reported!\n");
435 DPRINT1("Not enough memory to allocate gdi object!\n");
440 DPRINT1("Failed to find lookaside list for object type 0x%x\n", ObjectType
);
446 * Free memory allocated for the GDI object. For each object type this function calls the
447 * appropriate cleanup routine.
449 * \param hObj - handle of the object to be deleted.
451 * \return Returns TRUE if succesful.
452 * \return Returns FALSE if the cleanup routine returned FALSE or the object doesn't belong
453 * to the calling process.
457 GDIOBJ_FreeObjDbg(const char* file
, int line
, HGDIOBJ hObj
, DWORD ObjectType
)
458 #else /* !GDI_DEBUG */
459 GDIOBJ_FreeObj(HGDIOBJ hObj
, DWORD ObjectType
)
460 #endif /* GDI_DEBUG */
462 PGDI_TABLE_ENTRY Entry
;
463 PPAGED_LOOKASIDE_LIST LookasideList
;
464 LONG ProcessId
, LockedProcessId
, PrevProcId
, ExpectedType
;
469 DPRINT("GDIOBJ_FreeObj: hObj: 0x%08x\n", hObj
);
471 if(GDI_HANDLE_IS_STOCKOBJ(hObj
))
473 DPRINT1("GDIOBJ_FreeObj() failed, can't delete stock object handle: 0x%x !!!\n", hObj
);
475 DPRINT1("-> called from %s:%i\n", file
, line
);
480 /* shift the process id to the left so we can use the first bit to lock the object.
481 FIXME - don't shift once ROS' PIDs match with nt! */
482 ProcessId
= (LONG
)PsGetCurrentProcessId() << 1;
483 LockedProcessId
= ProcessId
| 0x1;
485 ExpectedType
= ((ObjectType
!= GDI_OBJECT_TYPE_DONTCARE
) ? ObjectType
: 0);
487 Entry
= GDI_HANDLE_GET_ENTRY(HandleTable
, hObj
);
490 /* lock the object, we must not delete global objects, so don't exchange the locking
491 process ID to zero when attempting to lock a global object... */
492 PrevProcId
= InterlockedCompareExchange(&Entry
->ProcessId
, LockedProcessId
, ProcessId
);
493 if(PrevProcId
== ProcessId
)
495 if(Entry
->Type
!= 0 && Entry
->KernelData
!= NULL
&& (ExpectedType
== 0 || ((Entry
->Type
<< 16) == ExpectedType
)))
499 GdiHdr
= GDIBdyToHdr(Entry
->KernelData
);
501 if(GdiHdr
->LockingThread
== NULL
)
504 PW32PROCESS W32Process
= PsGetWin32Process();
505 ULONG Type
= Entry
->Type
<< 16;
507 /* Clear the type field so when unlocking the handle it gets finally deleted */
509 Entry
->KernelData
= NULL
;
511 /* unlock the handle slot */
512 InterlockedExchange(&Entry
->ProcessId
, 0);
514 /* push this entry to the free list */
515 InterlockedPushEntrySList(&HandleTable
->FreeEntriesHead
,
516 &HandleTable
->FreeEntries
[GDI_ENTRY_TO_INDEX(HandleTable
, Entry
)]);
518 if(W32Process
!= NULL
)
520 InterlockedDecrement(&W32Process
->GDIObjects
);
523 /* call the cleanup routine. */
524 Ret
= RunCleanupCallback(GDIHdrToBdy(GdiHdr
), Type
);
526 /* Now it's time to free the memory */
527 LookasideList
= FindLookasideList(Type
);
528 if(LookasideList
!= NULL
)
530 ExFreeToPagedLookasideList(LookasideList
, GdiHdr
);
537 /* the object is currently locked. just clear the type field so when the
538 object gets unlocked it will be finally deleted from the table. */
541 /* unlock the handle slot */
542 InterlockedExchange(&Entry
->ProcessId
, 0);
544 /* report a successful deletion as the object is actually removed from the table */
552 DPRINT1("Attempted to delete object 0x%x, type mismatch (0x%x : 0x%x)\n", hObj
, ObjectType
, ExpectedType
);
556 DPRINT1("Attempted to delete object 0x%x which was already deleted!\n", hObj
);
558 InterlockedExchange(&Entry
->ProcessId
, PrevProcId
);
561 else if(PrevProcId
== LockedProcessId
)
566 DPRINT1("[%d]Waiting on 0x%x\n", Attempts
, hObj
);
569 /* the object is currently locked, wait some time and try again.
570 FIXME - we shouldn't loop forever! Give up after some time! */
577 if((PrevProcId
>> 1) == 0)
579 DPRINT1("Attempted to free global gdi handle 0x%x, caller needs to get ownership first!!!", hObj
);
583 DPRINT1("Attempted to free foreign handle: 0x%x Owner: 0x%x from Caller: 0x%x\n", hObj
, PrevProcId
>> 1, ProcessId
>> 1);
586 DPRINT1("-> called from %s:%i\n", file
, line
);
594 * Lock multiple objects. Use this function when you need to lock multiple objects and some of them may be
595 * duplicates. You should use this function to avoid trying to lock the same object twice!
597 * \param pList pointer to the list that contains handles to the objects. You should set hObj and ObjectType fields.
598 * \param nObj number of objects to lock
599 * \return for each entry in pList this function sets pObj field to point to the object.
601 * \note this function uses an O(n^2) algoritm because we shouldn't need to call it with more than 3 or 4 objects.
604 GDIOBJ_LockMultipleObj(PGDIMULTILOCK pList
, INT nObj
)
608 /* FIXME - check for "invalid" handles */
609 /* go through the list checking for duplicate objects */
610 for (i
= 0; i
< nObj
; i
++)
612 pList
[i
].pObj
= NULL
;
613 for (j
= 0; j
< i
; j
++)
615 if (pList
[i
].hObj
== pList
[j
].hObj
)
617 /* already locked, so just copy the pointer to the object */
618 pList
[i
].pObj
= pList
[j
].pObj
;
623 if (NULL
== pList
[i
].pObj
)
625 /* object hasn't been locked, so lock it. */
626 if (NULL
!= pList
[i
].hObj
)
628 pList
[i
].pObj
= GDIOBJ_LockObj(pList
[i
].hObj
, pList
[i
].ObjectType
);
637 * Unlock multiple objects. Use this function when you need to unlock multiple objects and some of them may be
640 * \param pList pointer to the list that contains handles to the objects. You should set hObj and ObjectType fields.
641 * \param nObj number of objects to lock
643 * \note this function uses O(n^2) algoritm because we shouldn't need to call it with more than 3 or 4 objects.
646 GDIOBJ_UnlockMultipleObj(PGDIMULTILOCK pList
, INT nObj
)
651 /* go through the list checking for duplicate objects */
652 for (i
= 0; i
< nObj
; i
++)
654 if (NULL
!= pList
[i
].pObj
)
656 for (j
= i
+ 1; j
< nObj
; j
++)
658 if ((pList
[i
].pObj
== pList
[j
].pObj
))
660 /* set the pointer to zero for all duplicates */
661 pList
[j
].pObj
= NULL
;
664 GDIOBJ_UnlockObj(pList
[i
].hObj
);
665 pList
[i
].pObj
= NULL
;
673 * Initialization of the GDI object engine.
676 InitGdiObjectHandleTable (VOID
)
678 DPRINT("InitGdiObjectHandleTable\n");
680 HandleTable
= GDIOBJ_iAllocHandleTable();
681 DPRINT("HandleTable: %x\n", HandleTable
);
686 * \param hObject object handle
687 * \return if the function fails the returned value is FALSE.
690 NtGdiDeleteObject(HGDIOBJ hObject
)
692 DPRINT("NtGdiDeleteObject handle 0x%08x\n", hObject
);
694 return NULL
!= hObject
695 ? GDIOBJ_FreeObj(hObject
, GDI_OBJECT_TYPE_DONTCARE
) : FALSE
;
699 * Internal function. Called when the process is destroyed to free the remaining GDI handles.
700 * \param Process - PID of the process that will be destroyed.
703 GDI_CleanupForProcess (struct _EPROCESS
*Process
)
705 PGDI_TABLE_ENTRY Entry
, End
;
706 PEPROCESS CurrentProcess
;
707 PW32PROCESS W32Process
;
709 ULONG Index
= RESERVE_ENTRIES_COUNT
;
711 DPRINT("Starting CleanupForProcess prochandle %x Pid %d\n", Process
, Pid
);
712 CurrentProcess
= PsGetCurrentProcess();
713 if (CurrentProcess
!= Process
)
715 KeAttachProcess(Process
);
717 W32Process
= Process
->Win32Process
;
720 if(W32Process
->GDIObjects
> 0)
722 /* FIXME - Instead of building the handle here and delete it using GDIOBJ_FreeObj
723 we should delete it directly here! */
724 ProcId
= ((LONG
)Process
->UniqueProcessId
<< 1);
726 End
= &HandleTable
->Entries
[GDI_HANDLE_COUNT
];
727 for(Entry
= &HandleTable
->Entries
[RESERVE_ENTRIES_COUNT
];
731 /* ignore the lock bit */
732 if((Entry
->ProcessId
& ~0x1) == ProcId
&& Entry
->Type
!= 0)
734 HGDIOBJ ObjectHandle
;
736 /* Create the object handle for the entry, the upper 16 bit of the
737 Type field includes the type of the object including the stock
738 object flag - but since stock objects don't have a process id we can
739 simply ignore this fact here. */
740 ObjectHandle
= (HGDIOBJ
)(Index
| (Entry
->Type
& 0xFFFF0000));
742 if(GDIOBJ_FreeObj(ObjectHandle
, GDI_OBJECT_TYPE_DONTCARE
) &&
743 W32Process
->GDIObjects
== 0)
745 /* there are no more gdi handles for this process, bail */
752 if (CurrentProcess
!= Process
)
757 DPRINT("Completed cleanup for process %d\n", Pid
);
763 * Return pointer to the object by handle.
765 * \param hObj Object handle
766 * \return Pointer to the object.
768 * \note Process can only get pointer to the objects it created or global objects.
770 * \todo Get rid of the ObjectType parameter!
772 PGDIOBJ INTERNAL_CALL
774 GDIOBJ_LockObjDbg (const char* file
, int line
, HGDIOBJ hObj
, DWORD ObjectType
)
775 #else /* !GDI_DEBUG */
776 GDIOBJ_LockObj (HGDIOBJ hObj
, DWORD ObjectType
)
777 #endif /* GDI_DEBUG */
779 PGDI_TABLE_ENTRY Entry
;
781 LONG ProcessId
, LockedProcessId
, PrevProcId
, ExpectedType
;
786 DPRINT("GDIOBJ_LockObj: hObj: 0x%08x\n", hObj
);
788 Thread
= PsGetCurrentThread();
790 /* shift the process id to the left so we can use the first bit to lock the object.
791 FIXME - don't shift once ROS' PIDs match with nt! */
792 ProcessId
= (LONG
)PsGetCurrentProcessId() << 1;
793 LockedProcessId
= ProcessId
| 0x1;
795 ExpectedType
= ((ObjectType
!= GDI_OBJECT_TYPE_DONTCARE
) ? ObjectType
: 0);
797 Entry
= GDI_HANDLE_GET_ENTRY(HandleTable
, hObj
);
800 /* lock the object, we must not delete stock objects, so don't check!!! */
801 PrevProcId
= InterlockedCompareExchange(&Entry
->ProcessId
, LockedProcessId
, ProcessId
);
802 if(PrevProcId
== ProcessId
)
804 LONG EntryType
= Entry
->Type
<< 16;
806 /* we're locking an object that belongs to our process or it's a global
807 object if ProcessId == 0 here. ProcessId can only be 0 here if it previously
808 failed to lock the object and it turned out to be a global object. */
809 if(EntryType
!= 0 && Entry
->KernelData
!= NULL
&& (ExpectedType
== 0 || (EntryType
== ExpectedType
)))
814 GdiHdr
= GDIBdyToHdr(Entry
->KernelData
);
816 /* save the pointer to the calling thread so we know it was this thread
817 that locked the object. There's no need to do this atomically as we're
818 holding the lock of the handle slot, but this way it's easier ;) */
819 PrevThread
= InterlockedCompareExchangePointer(&GdiHdr
->LockingThread
, Thread
, NULL
);
821 if(PrevThread
== NULL
|| PrevThread
== Thread
)
823 if(++GdiHdr
->Locks
== 1)
826 GdiHdr
->lockfile
= file
;
827 GdiHdr
->lockline
= line
;
831 InterlockedExchange(&Entry
->ProcessId
, PrevProcId
);
833 /* we're done, return the object body */
834 return GDIHdrToBdy(GdiHdr
);
838 InterlockedExchange(&Entry
->ProcessId
, PrevProcId
);
843 DPRINT1("[%d]Waiting at %s:%i as 0x%x on 0x%x\n", Attempts
, file
, line
, Thread
, PrevThread
);
853 InterlockedExchange(&Entry
->ProcessId
, PrevProcId
);
857 DPRINT1("Attempted to lock object 0x%x that is deleted!\n", hObj
);
858 KeRosDumpStackFrames ( NULL
, 20 );
862 DPRINT1("Attempted to lock object 0x%x, type mismatch (0x%x : 0x%x)\n", hObj
, EntryType
, ExpectedType
);
863 KeRosDumpStackFrames ( NULL
, 20 );
866 DPRINT1("-> called from %s:%i\n", file
, line
);
870 else if(PrevProcId
== LockedProcessId
)
875 DPRINT1("[%d]Waiting from %s:%i on 0x%x\n", Attempts
, file
, line
, hObj
);
878 /* the handle is currently locked, wait some time and try again.
879 FIXME - we shouldn't loop forever! Give up after some time! */
884 else if((PrevProcId
& ~0x1) == 0)
886 /* we're trying to lock a global object, change the ProcessId to 0 and try again */
888 LockedProcessId
= ProcessId
|0x1;
894 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
));
895 KeRosDumpStackFrames ( NULL
, 20 );
897 DPRINT1("-> called from %s:%i\n", file
, line
);
906 * Release GDI object. Every object locked by GDIOBJ_LockObj() must be unlocked. You should unlock the object
907 * as soon as you don't need to have access to it's data.
909 * \param hObj Object handle
911 * \note This function performs delayed cleanup. If the object is locked when GDI_FreeObj() is called
912 * then \em this function frees the object when reference count is zero.
916 GDIOBJ_UnlockObjDbg (const char* file
, int line
, HGDIOBJ hObj
)
917 #else /* !GDI_DEBUG */
918 GDIOBJ_UnlockObj (HGDIOBJ hObj
)
919 #endif /* GDI_DEBUG */
921 PGDI_TABLE_ENTRY Entry
;
923 LONG ProcessId
, LockedProcessId
, PrevProcId
;
928 DPRINT("GDIOBJ_UnlockObj: hObj: 0x%08x\n", hObj
);
929 Thread
= PsGetCurrentThread();
931 /* shift the process id to the left so we can use the first bit to lock the object.
932 FIXME - don't shift once ROS' PIDs match with nt! */
933 ProcessId
= (LONG
)PsGetCurrentProcessId() << 1;
934 LockedProcessId
= ProcessId
| 0x1;
936 Entry
= GDI_HANDLE_GET_ENTRY(HandleTable
, hObj
);
939 /* lock the handle, we must not delete stock objects, so don't check!!! */
940 PrevProcId
= InterlockedCompareExchange(&Entry
->ProcessId
, LockedProcessId
, ProcessId
);
941 if(PrevProcId
== ProcessId
)
943 /* we're unlocking an object that belongs to our process or it's a global
944 object if ProcessId == 0 here. ProcessId can only be 0 here if it previously
945 failed to lock the object and it turned out to be a global object. */
946 if(Entry
->KernelData
!= NULL
)
951 GdiHdr
= GDIBdyToHdr(Entry
->KernelData
);
953 PrevThread
= GdiHdr
->LockingThread
;
954 if(PrevThread
== Thread
)
958 if(--GdiHdr
->Locks
== 0)
960 GdiHdr
->LockingThread
= NULL
;
963 GdiHdr
->lockfile
= NULL
;
964 GdiHdr
->lockline
= 0;
968 if(Entry
->Type
== 0 && GdiHdr
->Locks
== 0)
970 PPAGED_LOOKASIDE_LIST LookasideList
;
971 PW32PROCESS W32Process
= PsGetWin32Process();
972 DWORD Type
= GDI_HANDLE_GET_TYPE(hObj
);
974 ASSERT(ProcessId
!= 0); /* must not delete a global handle!!!! */
976 /* we should delete the handle */
977 Entry
->KernelData
= NULL
;
978 InterlockedExchange(&Entry
->ProcessId
, 0);
980 InterlockedPushEntrySList(&HandleTable
->FreeEntriesHead
,
981 &HandleTable
->FreeEntries
[GDI_ENTRY_TO_INDEX(HandleTable
, Entry
)]);
983 if(W32Process
!= NULL
)
985 InterlockedDecrement(&W32Process
->GDIObjects
);
988 /* call the cleanup routine. */
989 Ret
= RunCleanupCallback(GDIHdrToBdy(GdiHdr
), Type
);
991 /* Now it's time to free the memory */
992 LookasideList
= FindLookasideList(Type
);
993 if(LookasideList
!= NULL
)
995 ExFreeToPagedLookasideList(LookasideList
, GdiHdr
);
1000 /* remove the handle slot lock */
1001 InterlockedExchange(&Entry
->ProcessId
, PrevProcId
);
1009 else if(PrevThread
!= NULL
)
1011 DPRINT1("Attempted to unlock object 0x%x, previously locked by other thread (0x%x) from %s:%i (called from %s:%i)\n",
1012 hObj
, PrevThread
, GdiHdr
->lockfile
, GdiHdr
->lockline
, file
, line
);
1013 InterlockedExchange(&Entry
->ProcessId
, PrevProcId
);
1021 DPRINT1("[%d]Waiting at %s:%i as 0x%x on 0x%x\n", Attempts
, file
, line
, Thread
, PrevThread
);
1024 /* FIXME - we should give up after some time unless we want to wait forever! */
1025 InterlockedExchange(&Entry
->ProcessId
, PrevProcId
);
1033 InterlockedExchange(&Entry
->ProcessId
, PrevProcId
);
1034 DPRINT1("Attempted to unlock object 0x%x that is deleted!\n", hObj
);
1037 else if(PrevProcId
== LockedProcessId
)
1042 DPRINT1("[%d]Waiting from %s:%i on 0x%x\n", Attempts
, file
, line
, hObj
);
1045 /* the handle is currently locked, wait some time and try again.
1046 FIXME - we shouldn't loop forever! Give up after some time! */
1051 else if((PrevProcId
& ~0x1) == 0)
1053 /* we're trying to unlock a global object, change the ProcessId to 0 and try again */
1055 LockedProcessId
= ProcessId
|0x1;
1061 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
));
1068 GDIOBJ_OwnedByCurrentProcess(HGDIOBJ ObjectHandle
)
1070 PGDI_TABLE_ENTRY Entry
;
1074 DPRINT("GDIOBJ_OwnedByCurrentProcess: ObjectHandle: 0x%08x\n", ObjectHandle
);
1076 if(!GDI_HANDLE_IS_STOCKOBJ(ObjectHandle
))
1078 ProcessId
= (LONG
)PsGetCurrentProcessId() << 1;
1080 Entry
= GDI_HANDLE_GET_ENTRY(HandleTable
, ObjectHandle
);
1081 Ret
= Entry
->KernelData
!= NULL
&&
1083 (Entry
->ProcessId
& ~0x1) == ProcessId
;
1092 GDIOBJ_ConvertToStockObj(HGDIOBJ
*hObj
)
1095 * FIXME !!!!! THIS FUNCTION NEEDS TO BE FIXED - IT IS NOT SAFE WHEN OTHER THREADS
1096 * MIGHT ATTEMPT TO LOCK THE OBJECT DURING THIS CALL!!!
1098 PGDI_TABLE_ENTRY Entry
;
1099 LONG ProcessId
, LockedProcessId
, PrevProcId
;
1107 DPRINT("GDIOBJ_ConvertToStockObj: hObj: 0x%08x\n", *hObj
);
1109 Thread
= PsGetCurrentThread();
1111 if(!GDI_HANDLE_IS_STOCKOBJ(*hObj
))
1113 /* shift the process id to the left so we can use the first bit to lock the object.
1114 FIXME - don't shift once ROS' PIDs match with nt! */
1115 ProcessId
= (LONG
)PsGetCurrentProcessId() << 1;
1116 LockedProcessId
= ProcessId
| 0x1;
1118 Entry
= GDI_HANDLE_GET_ENTRY(HandleTable
, *hObj
);
1121 /* lock the object, we must not convert stock objects, so don't check!!! */
1122 PrevProcId
= InterlockedCompareExchange(&Entry
->ProcessId
, LockedProcessId
, ProcessId
);
1123 if(PrevProcId
== ProcessId
)
1125 LONG NewType
, PrevType
, OldType
;
1127 /* we're locking an object that belongs to our process. First calculate
1128 the new object type including the stock object flag and then try to
1130 NewType
= GDI_HANDLE_GET_TYPE(*hObj
);
1131 NewType
|= NewType
>> 16;
1132 /* This is the type that the object should have right now, save it */
1134 /* As the object should be a stock object, set it's flag, but only in the upper 16 bits */
1135 NewType
|= GDI_HANDLE_STOCK_MASK
;
1137 /* Try to exchange the type field - but only if the old (previous type) matches! */
1138 PrevType
= InterlockedCompareExchange(&Entry
->Type
, NewType
, OldType
);
1139 if(PrevType
== OldType
&& Entry
->KernelData
!= NULL
)
1141 PETHREAD PrevThread
;
1144 /* We successfully set the stock object flag.
1145 KernelData should never be NULL here!!! */
1146 ASSERT(Entry
->KernelData
);
1148 GdiHdr
= GDIBdyToHdr(Entry
->KernelData
);
1150 PrevThread
= GdiHdr
->LockingThread
;
1151 if(PrevThread
== NULL
|| PrevThread
== Thread
)
1153 /* dereference the process' object counter */
1154 if(PrevProcId
!= GDI_GLOBAL_PROCESS
)
1156 PEPROCESS OldProcess
;
1157 PW32PROCESS W32Process
;
1161 Status
= PsLookupProcessByProcessId((PVOID
)(PrevProcId
>> 1), &OldProcess
);
1162 if(NT_SUCCESS(Status
))
1164 W32Process
= OldProcess
->Win32Process
;
1165 if(W32Process
!= NULL
)
1167 InterlockedDecrement(&W32Process
->GDIObjects
);
1169 ObDereferenceObject(OldProcess
);
1173 /* remove the process id lock and make it global */
1174 InterlockedExchange(&Entry
->ProcessId
, GDI_GLOBAL_PROCESS
);
1176 *hObj
= (HGDIOBJ
)((ULONG
)(*hObj
) | GDI_HANDLE_STOCK_MASK
);
1178 /* we're done, successfully converted the object */
1186 if(GdiHdr
->lockfile
!= NULL
)
1188 DPRINT1("[%d]Locked %s:%i by 0x%x (we're 0x%x)\n", Attempts
, GdiHdr
->lockfile
, GdiHdr
->lockline
, PrevThread
, Thread
);
1192 /* WTF?! The object is already locked by a different thread!
1193 Release the lock, wait a bit and try again!
1194 FIXME - we should give up after some time unless we want to wait forever! */
1195 InterlockedExchange(&Entry
->ProcessId
, PrevProcId
);
1203 DPRINT1("Attempted to convert object 0x%x that is deleted! Should never get here!!!\n", hObj
);
1206 else if(PrevProcId
== LockedProcessId
)
1211 DPRINT1("[%d]Waiting on 0x%x\n", Attempts
, hObj
);
1214 /* the object is currently locked, wait some time and try again.
1215 FIXME - we shouldn't loop forever! Give up after some time! */
1222 DPRINT1("Attempted to convert invalid handle: 0x%x\n", hObj
);
1230 GDIOBJ_SetOwnership(HGDIOBJ ObjectHandle
, PEPROCESS NewOwner
)
1232 PGDI_TABLE_ENTRY Entry
;
1233 LONG ProcessId
, LockedProcessId
, PrevProcId
;
1239 DPRINT("GDIOBJ_SetOwnership: hObj: 0x%x, NewProcess: 0x%x\n", ObjectHandle
, (NewOwner
? PsGetProcessId(NewOwner
) : 0));
1241 Thread
= PsGetCurrentThread();
1243 if(!GDI_HANDLE_IS_STOCKOBJ(ObjectHandle
))
1245 /* shift the process id to the left so we can use the first bit to lock the object.
1246 FIXME - don't shift once ROS' PIDs match with nt! */
1247 ProcessId
= (LONG
)PsGetCurrentProcessId() << 1;
1248 LockedProcessId
= ProcessId
| 0x1;
1250 Entry
= GDI_HANDLE_GET_ENTRY(HandleTable
, ObjectHandle
);
1253 /* lock the object, we must not convert stock objects, so don't check!!! */
1254 PrevProcId
= InterlockedCompareExchange(&Entry
->ProcessId
, ProcessId
, LockedProcessId
);
1255 if(PrevProcId
== ProcessId
)
1257 PETHREAD PrevThread
;
1259 if(Entry
->Type
!= 0 && Entry
->KernelData
!= NULL
)
1261 PGDIOBJHDR GdiHdr
= GDIBdyToHdr(Entry
->KernelData
);
1263 PrevThread
= GdiHdr
->LockingThread
;
1264 if(PrevThread
== NULL
|| PrevThread
== Thread
)
1266 PEPROCESS OldProcess
;
1267 PW32PROCESS W32Process
;
1270 /* dereference the process' object counter */
1272 Status
= PsLookupProcessByProcessId((PVOID
)(PrevProcId
>> 1), &OldProcess
);
1273 if(NT_SUCCESS(Status
))
1275 W32Process
= OldProcess
->Win32Process
;
1276 if(W32Process
!= NULL
)
1278 InterlockedDecrement(&W32Process
->GDIObjects
);
1280 ObDereferenceObject(OldProcess
);
1283 if(NewOwner
!= NULL
)
1286 ProcessId
= (LONG
)PsGetProcessId(NewOwner
) << 1;
1288 /* Increase the new process' object counter */
1289 W32Process
= NewOwner
->Win32Process
;
1290 if(W32Process
!= NULL
)
1292 InterlockedIncrement(&W32Process
->GDIObjects
);
1298 /* remove the process id lock and change it to the new process id */
1299 InterlockedExchange(&Entry
->ProcessId
, ProcessId
);
1309 if(GdiHdr
->lockfile
!= NULL
)
1311 DPRINT1("[%d]Locked from %s:%i by 0x%x (we're 0x%x)\n", Attempts
, GdiHdr
->lockfile
, GdiHdr
->lockline
, PrevThread
, Thread
);
1315 /* WTF?! The object is already locked by a different thread!
1316 Release the lock, wait a bit and try again! DO reset the pid lock
1317 so we make sure we don't access invalid memory in case the object is
1318 being deleted in the meantime (because we don't have aquired a reference
1320 FIXME - we should give up after some time unless we want to wait forever! */
1321 InterlockedExchange(&Entry
->ProcessId
, PrevProcId
);
1329 DPRINT1("Attempted to change ownership of an object 0x%x currently being destroyed!!!\n", ObjectHandle
);
1332 else if(PrevProcId
== LockedProcessId
)
1337 DPRINT1("[%d]Waiting on 0x%x\n", Attempts
, ObjectHandle
);
1340 /* the object is currently locked, wait some time and try again.
1341 FIXME - we shouldn't loop forever! Give up after some time! */
1346 else if((PrevProcId
>> 1) == 0)
1348 /* allow changing ownership of global objects */
1350 LockedProcessId
= ProcessId
| 0x1;
1353 else if((PrevProcId
>> 1) != (LONG
)PsGetCurrentProcessId())
1355 DPRINT1("Attempted to change ownership of object 0x%x (pid: 0x%x) from pid 0x%x!!!\n", ObjectHandle
, PrevProcId
>> 1, PsGetCurrentProcessId());
1359 DPRINT1("Attempted to change owner of invalid handle: 0x%x\n", ObjectHandle
);
1365 GDIOBJ_CopyOwnership(HGDIOBJ CopyFrom
, HGDIOBJ CopyTo
)
1367 PGDI_TABLE_ENTRY FromEntry
;
1369 LONG FromProcessId
, FromLockedProcessId
, FromPrevProcId
;
1374 DPRINT("GDIOBJ_CopyOwnership: from: 0x%x, to: 0x%x\n", CopyFrom
, CopyTo
);
1376 Thread
= PsGetCurrentThread();
1378 if(!GDI_HANDLE_IS_STOCKOBJ(CopyFrom
) && !GDI_HANDLE_IS_STOCKOBJ(CopyTo
))
1380 FromEntry
= GDI_HANDLE_GET_ENTRY(HandleTable
, CopyFrom
);
1382 FromProcessId
= FromEntry
->ProcessId
& ~0x1;
1383 FromLockedProcessId
= FromProcessId
| 0x1;
1386 /* lock the object, we must not convert stock objects, so don't check!!! */
1387 FromPrevProcId
= InterlockedCompareExchange(&FromEntry
->ProcessId
, FromProcessId
, FromLockedProcessId
);
1388 if(FromPrevProcId
== FromProcessId
)
1390 PETHREAD PrevThread
;
1393 if(FromEntry
->Type
!= 0 && FromEntry
->KernelData
!= NULL
)
1395 GdiHdr
= GDIBdyToHdr(FromEntry
->KernelData
);
1397 /* save the pointer to the calling thread so we know it was this thread
1398 that locked the object */
1399 PrevThread
= GdiHdr
->LockingThread
;
1400 if(PrevThread
== NULL
|| PrevThread
== Thread
)
1402 /* now let's change the ownership of the target object */
1404 if((FromPrevProcId
& ~0x1) != 0)
1406 PEPROCESS ProcessTo
;
1408 if(NT_SUCCESS(PsLookupProcessByProcessId((PVOID
)(FromPrevProcId
>> 1), &ProcessTo
)))
1410 GDIOBJ_SetOwnership(CopyTo
, ProcessTo
);
1411 ObDereferenceObject(ProcessTo
);
1416 /* mark the object as global */
1417 GDIOBJ_SetOwnership(CopyTo
, NULL
);
1420 InterlockedExchange(&FromEntry
->ProcessId
, FromPrevProcId
);
1427 if(GdiHdr
->lockfile
!= NULL
)
1429 DPRINT1("[%d]Locked from %s:%i by 0x%x (we're 0x%x)\n", Attempts
, GdiHdr
->lockfile
, GdiHdr
->lockline
, PrevThread
, Thread
);
1433 /* WTF?! The object is already locked by a different thread!
1434 Release the lock, wait a bit and try again! DO reset the pid lock
1435 so we make sure we don't access invalid memory in case the object is
1436 being deleted in the meantime (because we don't have aquired a reference
1438 FIXME - we should give up after some time unless we want to wait forever! */
1439 InterlockedExchange(&FromEntry
->ProcessId
, FromPrevProcId
);
1442 goto LockHandleFrom
;
1447 DPRINT1("Attempted to copy ownership from an object 0x%x currently being destroyed!!!\n", CopyFrom
);
1450 else if(FromPrevProcId
== FromLockedProcessId
)
1455 DPRINT1("[%d]Waiting on 0x%x\n", Attempts
, CopyFrom
);
1458 /* the object is currently locked, wait some time and try again.
1459 FIXME - we shouldn't loop forever! Give up after some time! */
1462 goto LockHandleFrom
;
1464 else if((FromPrevProcId
>> 1) != (LONG
)PsGetCurrentProcessId())
1466 /* FIXME - should we really allow copying ownership from objects that we don't even own? */
1467 DPRINT1("WARNING! Changing copying ownership of object 0x%x (pid: 0x%x) to pid 0x%x!!!\n", CopyFrom
, FromPrevProcId
>> 1, PsGetCurrentProcessId());
1468 FromProcessId
= FromPrevProcId
& ~0x1;
1469 FromLockedProcessId
= FromProcessId
| 0x1;
1470 goto LockHandleFrom
;
1474 DPRINT1("Attempted to copy ownership from invalid handle: 0x%x\n", CopyFrom
);
1480 GDI_MapHandleTable(HANDLE hProcess
)
1482 DPRINT("%s:%i: %s(): FIXME - Map handle table into the process memory space!\n",
1483 __FILE__
, __LINE__
, __FUNCTION__
);
1484 /* FIXME - Map the entire gdi handle table read-only to userland into the
1485 scope of hProcess and return the pointer */