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.78 2004/12/17 15:12:37 navaraf 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
);
213 * Allocate memory for GDI object and return handle to it.
215 * \param ObjectType - type of object \ref GDI object types
217 * \return Handle of the allocated object.
219 * \note Use GDIOBJ_Lock() to obtain pointer to the new object.
220 * \todo return the object pointer and lock it by default.
222 HGDIOBJ INTERNAL_CALL
224 GDIOBJ_AllocObjDbg(const char* file
, int line
, ULONG ObjectType
)
225 #else /* !GDI_DEBUG */
226 GDIOBJ_AllocObj(ULONG ObjectType
)
227 #endif /* GDI_DEBUG */
229 PW32PROCESS W32Process
;
230 PGDIOBJHDR newObject
;
231 PPAGED_LOOKASIDE_LIST LookasideList
;
232 LONG CurrentProcessId
, LockedProcessId
;
237 ASSERT(ObjectType
!= GDI_OBJECT_TYPE_DONTCARE
);
239 LookasideList
= FindLookasideList(ObjectType
);
240 if(LookasideList
!= NULL
)
242 newObject
= ExAllocateFromPagedLookasideList(LookasideList
);
243 if(newObject
!= NULL
)
245 PSLIST_ENTRY FreeEntry
;
246 PGDI_TABLE_ENTRY Entry
;
250 /* shift the process id to the left so we can use the first bit to lock
252 FIXME - don't shift once ROS' PIDs match with nt! */
253 CurrentProcessId
= (LONG
)PsGetCurrentProcessId() << 1;
254 LockedProcessId
= CurrentProcessId
| 0x1;
255 W32Process
= PsGetWin32Process();
257 newObject
->LockingThread
= NULL
;
258 newObject
->Locks
= 0;
261 newObject
->createdfile
= file
;
262 newObject
->createdline
= line
;
263 newObject
->lockfile
= NULL
;
264 newObject
->lockline
= 0;
267 ObjectBody
= GDIHdrToBdy(newObject
);
269 RtlZeroMemory(ObjectBody
, GetObjectSize(ObjectType
));
271 TypeInfo
= (ObjectType
& 0xFFFF0000) | (ObjectType
>> 16);
273 FreeEntry
= InterlockedPopEntrySList(&HandleTable
->FreeEntriesHead
);
274 if(FreeEntry
!= NULL
)
280 /* calculate the entry from the address of the entry in the free slot array */
281 Index
= ((ULONG_PTR
)FreeEntry
- (ULONG_PTR
)&HandleTable
->FreeEntries
[0]) /
282 sizeof(HandleTable
->FreeEntries
[0]);
283 Entry
= &HandleTable
->Entries
[Index
];
284 Handle
= (HGDIOBJ
)((Index
& 0xFFFF) | (ObjectType
& 0xFFFF0000));
287 PrevProcId
= InterlockedCompareExchange(&Entry
->ProcessId
, LockedProcessId
, 0);
290 ASSERT(Entry
->KernelData
== NULL
);
292 Entry
->KernelData
= ObjectBody
;
294 /* we found a free entry, no need to exchange this field atomically
295 since we're holding the lock */
296 Entry
->Type
= TypeInfo
;
298 /* unlock the entry */
299 InterlockedExchange(&Entry
->ProcessId
, CurrentProcessId
);
301 if(W32Process
!= NULL
)
303 InterlockedIncrement(&W32Process
->GDIObjects
);
306 DPRINT("GDIOBJ_AllocObj: 0x%x ob: 0x%x\n", Handle
, ObjectBody
);
314 DPRINT1("[%d]Waiting on 0x%x\n", Attempts
, Handle
);
317 /* damn, someone is trying to lock the object even though it doesn't
318 eve nexist anymore, wait a little and try again!
319 FIXME - we shouldn't loop forever! Give up after some time! */
326 ExFreeToPagedLookasideList(LookasideList
, newObject
);
327 DPRINT1("Failed to insert gdi object into the handle table, no handles left!\n");
331 DPRINT1("Not enough memory to allocate gdi object!\n");
336 DPRINT1("Failed to find lookaside list for object type 0x%x\n", ObjectType
);
342 * Free memory allocated for the GDI object. For each object type this function calls the
343 * appropriate cleanup routine.
345 * \param hObj - handle of the object to be deleted.
347 * \return Returns TRUE if succesful.
348 * \return Returns FALSE if the cleanup routine returned FALSE or the object doesn't belong
349 * to the calling process.
353 GDIOBJ_FreeObjDbg(const char* file
, int line
, HGDIOBJ hObj
, DWORD ObjectType
)
354 #else /* !GDI_DEBUG */
355 GDIOBJ_FreeObj(HGDIOBJ hObj
, DWORD ObjectType
)
356 #endif /* GDI_DEBUG */
358 PGDI_TABLE_ENTRY Entry
;
359 PPAGED_LOOKASIDE_LIST LookasideList
;
360 LONG ProcessId
, LockedProcessId
, PrevProcId
, ExpectedType
;
365 DPRINT("GDIOBJ_FreeObj: hObj: 0x%08x\n", hObj
);
367 if(GDI_HANDLE_IS_STOCKOBJ(hObj
))
369 DPRINT1("GDIOBJ_FreeObj() failed, can't delete stock object handle: 0x%x !!!\n", hObj
);
371 DPRINT1("-> called from %s:%i\n", file
, line
);
376 /* shift the process id to the left so we can use the first bit to lock the object.
377 FIXME - don't shift once ROS' PIDs match with nt! */
378 ProcessId
= (LONG
)PsGetCurrentProcessId() << 1;
379 LockedProcessId
= ProcessId
| 0x1;
381 ExpectedType
= ((ObjectType
!= GDI_OBJECT_TYPE_DONTCARE
) ? ObjectType
: 0);
383 Entry
= GDI_HANDLE_GET_ENTRY(HandleTable
, hObj
);
386 /* lock the object, we must not delete global objects, so don't exchange the locking
387 process ID to zero when attempting to lock a global object... */
388 PrevProcId
= InterlockedCompareExchange(&Entry
->ProcessId
, LockedProcessId
, ProcessId
);
389 if(PrevProcId
== ProcessId
)
391 if(Entry
->Type
!= 0 && Entry
->KernelData
!= NULL
&& (ExpectedType
== 0 || ((Entry
->Type
<< 16) == ExpectedType
)))
395 GdiHdr
= GDIBdyToHdr(Entry
->KernelData
);
397 if(GdiHdr
->LockingThread
== NULL
)
400 PW32PROCESS W32Process
= PsGetWin32Process();
401 ULONG Type
= Entry
->Type
<< 16;
403 /* Clear the type field so when unlocking the handle it gets finally deleted */
405 Entry
->KernelData
= NULL
;
407 /* unlock the handle slot */
408 InterlockedExchange(&Entry
->ProcessId
, 0);
410 /* push this entry to the free list */
411 InterlockedPushEntrySList(&HandleTable
->FreeEntriesHead
,
412 &HandleTable
->FreeEntries
[GDI_ENTRY_TO_INDEX(HandleTable
, Entry
)]);
414 if(W32Process
!= NULL
)
416 InterlockedDecrement(&W32Process
->GDIObjects
);
419 /* call the cleanup routine. */
420 Ret
= RunCleanupCallback(GDIHdrToBdy(GdiHdr
), Type
);
422 /* Now it's time to free the memory */
423 LookasideList
= FindLookasideList(Type
);
424 if(LookasideList
!= NULL
)
426 ExFreeToPagedLookasideList(LookasideList
, GdiHdr
);
433 /* the object is currently locked. just clear the type field so when the
434 object gets unlocked it will be finally deleted from the table. */
437 /* unlock the handle slot */
438 InterlockedExchange(&Entry
->ProcessId
, 0);
440 /* report a successful deletion as the object is actually removed from the table */
448 DPRINT1("Attempted to delete object 0x%x, type mismatch (0x%x : 0x%x)\n", hObj
, ObjectType
, ExpectedType
);
452 DPRINT1("Attempted to delete object 0x%x which was already deleted!\n", hObj
);
454 InterlockedExchange(&Entry
->ProcessId
, PrevProcId
);
457 else if(PrevProcId
== LockedProcessId
)
462 DPRINT1("[%d]Waiting on 0x%x\n", Attempts
, hObj
);
465 /* the object is currently locked, wait some time and try again.
466 FIXME - we shouldn't loop forever! Give up after some time! */
473 if((PrevProcId
>> 1) == 0)
475 DPRINT1("Attempted to free global gdi handle 0x%x, caller needs to get ownership first!!!", hObj
);
479 DPRINT1("Attempted to free foreign handle: 0x%x Owner: 0x%x from Caller: 0x%x\n", hObj
, PrevProcId
>> 1, ProcessId
>> 1);
482 DPRINT1("-> called from %s:%i\n", file
, line
);
490 * Lock multiple objects. Use this function when you need to lock multiple objects and some of them may be
491 * duplicates. You should use this function to avoid trying to lock the same object twice!
493 * \param pList pointer to the list that contains handles to the objects. You should set hObj and ObjectType fields.
494 * \param nObj number of objects to lock
495 * \return for each entry in pList this function sets pObj field to point to the object.
497 * \note this function uses an O(n^2) algoritm because we shouldn't need to call it with more than 3 or 4 objects.
500 GDIOBJ_LockMultipleObj(PGDIMULTILOCK pList
, INT nObj
)
504 /* FIXME - check for "invalid" handles */
505 /* go through the list checking for duplicate objects */
506 for (i
= 0; i
< nObj
; i
++)
508 pList
[i
].pObj
= NULL
;
509 for (j
= 0; j
< i
; j
++)
511 if (pList
[i
].hObj
== pList
[j
].hObj
)
513 /* already locked, so just copy the pointer to the object */
514 pList
[i
].pObj
= pList
[j
].pObj
;
519 if (NULL
== pList
[i
].pObj
)
521 /* object hasn't been locked, so lock it. */
522 if (NULL
!= pList
[i
].hObj
)
524 pList
[i
].pObj
= GDIOBJ_LockObj(pList
[i
].hObj
, pList
[i
].ObjectType
);
533 * Unlock multiple objects. Use this function when you need to unlock multiple objects and some of them may be
536 * \param pList pointer to the list that contains handles to the objects. You should set hObj and ObjectType fields.
537 * \param nObj number of objects to lock
539 * \note this function uses O(n^2) algoritm because we shouldn't need to call it with more than 3 or 4 objects.
542 GDIOBJ_UnlockMultipleObj(PGDIMULTILOCK pList
, INT nObj
)
547 /* go through the list checking for duplicate objects */
548 for (i
= 0; i
< nObj
; i
++)
550 if (NULL
!= pList
[i
].pObj
)
552 for (j
= i
+ 1; j
< nObj
; j
++)
554 if ((pList
[i
].pObj
== pList
[j
].pObj
))
556 /* set the pointer to zero for all duplicates */
557 pList
[j
].pObj
= NULL
;
560 GDIOBJ_UnlockObj(pList
[i
].hObj
);
561 pList
[i
].pObj
= NULL
;
569 * Initialization of the GDI object engine.
572 InitGdiObjectHandleTable (VOID
)
574 DPRINT("InitGdiObjectHandleTable\n");
576 HandleTable
= GDIOBJ_iAllocHandleTable();
577 DPRINT("HandleTable: %x\n", HandleTable
);
582 * \param hObject object handle
583 * \return if the function fails the returned value is FALSE.
586 NtGdiDeleteObject(HGDIOBJ hObject
)
588 DPRINT("NtGdiDeleteObject handle 0x%08x\n", hObject
);
590 return NULL
!= hObject
591 ? GDIOBJ_FreeObj(hObject
, GDI_OBJECT_TYPE_DONTCARE
) : FALSE
;
595 * Internal function. Called when the process is destroyed to free the remaining GDI handles.
596 * \param Process - PID of the process that will be destroyed.
599 GDI_CleanupForProcess (struct _EPROCESS
*Process
)
601 PGDI_TABLE_ENTRY Entry
, End
;
602 PEPROCESS CurrentProcess
;
603 PW32PROCESS W32Process
;
605 ULONG Index
= RESERVE_ENTRIES_COUNT
;
607 DPRINT("Starting CleanupForProcess prochandle %x Pid %d\n", Process
, Pid
);
608 CurrentProcess
= PsGetCurrentProcess();
609 if (CurrentProcess
!= Process
)
611 KeAttachProcess(Process
);
613 W32Process
= Process
->Win32Process
;
616 if(W32Process
->GDIObjects
> 0)
618 /* FIXME - Instead of building the handle here and delete it using GDIOBJ_FreeObj
619 we should delete it directly here! */
620 ProcId
= ((LONG
)Process
->UniqueProcessId
<< 1);
622 End
= &HandleTable
->Entries
[GDI_HANDLE_COUNT
];
623 for(Entry
= &HandleTable
->Entries
[RESERVE_ENTRIES_COUNT
];
627 /* ignore the lock bit */
628 if((Entry
->ProcessId
& ~0x1) == ProcId
&& Entry
->Type
!= 0)
630 HGDIOBJ ObjectHandle
;
632 /* Create the object handle for the entry, the upper 16 bit of the
633 Type field includes the type of the object including the stock
634 object flag - but since stock objects don't have a process id we can
635 simply ignore this fact here. */
636 ObjectHandle
= (HGDIOBJ
)(Index
| (Entry
->Type
& 0xFFFF0000));
638 if(GDIOBJ_FreeObj(ObjectHandle
, GDI_OBJECT_TYPE_DONTCARE
) &&
639 W32Process
->GDIObjects
== 0)
641 /* there are no more gdi handles for this process, bail */
648 if (CurrentProcess
!= Process
)
653 DPRINT("Completed cleanup for process %d\n", Pid
);
659 * Return pointer to the object by handle.
661 * \param hObj Object handle
662 * \return Pointer to the object.
664 * \note Process can only get pointer to the objects it created or global objects.
666 * \todo Get rid of the ObjectType parameter!
668 PGDIOBJ INTERNAL_CALL
670 GDIOBJ_LockObjDbg (const char* file
, int line
, HGDIOBJ hObj
, DWORD ObjectType
)
671 #else /* !GDI_DEBUG */
672 GDIOBJ_LockObj (HGDIOBJ hObj
, DWORD ObjectType
)
673 #endif /* GDI_DEBUG */
675 PGDI_TABLE_ENTRY Entry
;
677 LONG ProcessId
, LockedProcessId
, PrevProcId
, ExpectedType
;
682 DPRINT("GDIOBJ_LockObj: hObj: 0x%08x\n", hObj
);
684 Thread
= PsGetCurrentThread();
686 /* shift the process id to the left so we can use the first bit to lock the object.
687 FIXME - don't shift once ROS' PIDs match with nt! */
688 ProcessId
= (LONG
)PsGetCurrentProcessId() << 1;
689 LockedProcessId
= ProcessId
| 0x1;
691 ExpectedType
= ((ObjectType
!= GDI_OBJECT_TYPE_DONTCARE
) ? ObjectType
: 0);
693 Entry
= GDI_HANDLE_GET_ENTRY(HandleTable
, hObj
);
696 /* lock the object, we must not delete stock objects, so don't check!!! */
697 PrevProcId
= InterlockedCompareExchange(&Entry
->ProcessId
, LockedProcessId
, ProcessId
);
698 if(PrevProcId
== ProcessId
)
700 LONG EntryType
= Entry
->Type
<< 16;
702 /* we're locking an object that belongs to our process or it's a global
703 object if ProcessId == 0 here. ProcessId can only be 0 here if it previously
704 failed to lock the object and it turned out to be a global object. */
705 if(EntryType
!= 0 && Entry
->KernelData
!= NULL
&& (ExpectedType
== 0 || (EntryType
== ExpectedType
)))
710 GdiHdr
= GDIBdyToHdr(Entry
->KernelData
);
712 /* save the pointer to the calling thread so we know it was this thread
713 that locked the object. There's no need to do this atomically as we're
714 holding the lock of the handle slot, but this way it's easier ;) */
715 PrevThread
= InterlockedCompareExchangePointer(&GdiHdr
->LockingThread
, Thread
, NULL
);
717 if(PrevThread
== NULL
|| PrevThread
== Thread
)
719 if(++GdiHdr
->Locks
== 1)
722 GdiHdr
->lockfile
= file
;
723 GdiHdr
->lockline
= line
;
727 InterlockedExchange(&Entry
->ProcessId
, PrevProcId
);
729 /* we're done, return the object body */
730 return GDIHdrToBdy(GdiHdr
);
734 InterlockedExchange(&Entry
->ProcessId
, PrevProcId
);
739 DPRINT1("[%d]Waiting at %s:%i as 0x%x on 0x%x\n", Attempts
, file
, line
, Thread
, PrevThread
);
749 InterlockedExchange(&Entry
->ProcessId
, PrevProcId
);
753 DPRINT1("Attempted to lock object 0x%x that is deleted!\n", hObj
);
757 DPRINT1("Attempted to lock object 0x%x, type mismatch (0x%x : 0x%x)\n", hObj
, EntryType
, ExpectedType
);
760 DPRINT1("-> called from %s:%i\n", file
, line
);
764 else if(PrevProcId
== LockedProcessId
)
769 DPRINT1("[%d]Waiting from %s:%i on 0x%x\n", Attempts
, file
, line
, hObj
);
772 /* the handle is currently locked, wait some time and try again.
773 FIXME - we shouldn't loop forever! Give up after some time! */
778 else if((PrevProcId
& ~0x1) == 0)
780 /* we're trying to lock a global object, change the ProcessId to 0 and try again */
782 LockedProcessId
= ProcessId
|0x1;
788 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
));
790 DPRINT1("-> called from %s:%i\n", file
, line
);
799 * Release GDI object. Every object locked by GDIOBJ_LockObj() must be unlocked. You should unlock the object
800 * as soon as you don't need to have access to it's data.
802 * \param hObj Object handle
804 * \note This function performs delayed cleanup. If the object is locked when GDI_FreeObj() is called
805 * then \em this function frees the object when reference count is zero.
809 GDIOBJ_UnlockObjDbg (const char* file
, int line
, HGDIOBJ hObj
)
810 #else /* !GDI_DEBUG */
811 GDIOBJ_UnlockObj (HGDIOBJ hObj
)
812 #endif /* GDI_DEBUG */
814 PGDI_TABLE_ENTRY Entry
;
816 LONG ProcessId
, LockedProcessId
, PrevProcId
;
821 DPRINT("GDIOBJ_UnlockObj: hObj: 0x%08x\n", hObj
);
822 Thread
= PsGetCurrentThread();
824 /* shift the process id to the left so we can use the first bit to lock the object.
825 FIXME - don't shift once ROS' PIDs match with nt! */
826 ProcessId
= (LONG
)PsGetCurrentProcessId() << 1;
827 LockedProcessId
= ProcessId
| 0x1;
829 Entry
= GDI_HANDLE_GET_ENTRY(HandleTable
, hObj
);
832 /* lock the handle, we must not delete stock objects, so don't check!!! */
833 PrevProcId
= InterlockedCompareExchange(&Entry
->ProcessId
, LockedProcessId
, ProcessId
);
834 if(PrevProcId
== ProcessId
)
836 /* we're unlocking an object that belongs to our process or it's a global
837 object if ProcessId == 0 here. ProcessId can only be 0 here if it previously
838 failed to lock the object and it turned out to be a global object. */
839 if(Entry
->KernelData
!= NULL
)
844 GdiHdr
= GDIBdyToHdr(Entry
->KernelData
);
846 PrevThread
= GdiHdr
->LockingThread
;
847 if(PrevThread
== Thread
)
851 if(--GdiHdr
->Locks
== 0)
853 GdiHdr
->LockingThread
= NULL
;
856 GdiHdr
->lockfile
= NULL
;
857 GdiHdr
->lockline
= 0;
861 if(Entry
->Type
== 0 && GdiHdr
->Locks
== 0)
863 PPAGED_LOOKASIDE_LIST LookasideList
;
864 PW32PROCESS W32Process
= PsGetWin32Process();
865 DWORD Type
= GDI_HANDLE_GET_TYPE(hObj
);
867 ASSERT(ProcessId
!= 0); /* must not delete a global handle!!!! */
869 /* we should delete the handle */
870 Entry
->KernelData
= NULL
;
871 InterlockedExchange(&Entry
->ProcessId
, 0);
873 InterlockedPushEntrySList(&HandleTable
->FreeEntriesHead
,
874 &HandleTable
->FreeEntries
[GDI_ENTRY_TO_INDEX(HandleTable
, Entry
)]);
876 if(W32Process
!= NULL
)
878 InterlockedDecrement(&W32Process
->GDIObjects
);
881 /* call the cleanup routine. */
882 Ret
= RunCleanupCallback(GDIHdrToBdy(GdiHdr
), Type
);
884 /* Now it's time to free the memory */
885 LookasideList
= FindLookasideList(Type
);
886 if(LookasideList
!= NULL
)
888 ExFreeToPagedLookasideList(LookasideList
, GdiHdr
);
893 /* remove the handle slot lock */
894 InterlockedExchange(&Entry
->ProcessId
, PrevProcId
);
902 else if(PrevThread
!= NULL
)
904 DPRINT1("Attempted to unlock object 0x%x, previously locked by other thread (0x%x) from %s:%i (called from %s:%i)\n",
905 hObj
, PrevThread
, GdiHdr
->lockfile
, GdiHdr
->lockline
, file
, line
);
906 InterlockedExchange(&Entry
->ProcessId
, PrevProcId
);
914 DPRINT1("[%d]Waiting at %s:%i as 0x%x on 0x%x\n", Attempts
, file
, line
, Thread
, PrevThread
);
917 /* FIXME - we should give up after some time unless we want to wait forever! */
918 InterlockedExchange(&Entry
->ProcessId
, PrevProcId
);
926 InterlockedExchange(&Entry
->ProcessId
, PrevProcId
);
927 DPRINT1("Attempted to unlock object 0x%x that is deleted!\n", hObj
);
930 else if(PrevProcId
== LockedProcessId
)
935 DPRINT1("[%d]Waiting from %s:%i on 0x%x\n", Attempts
, file
, line
, hObj
);
938 /* the handle is currently locked, wait some time and try again.
939 FIXME - we shouldn't loop forever! Give up after some time! */
944 else if((PrevProcId
& ~0x1) == 0)
946 /* we're trying to unlock a global object, change the ProcessId to 0 and try again */
948 LockedProcessId
= ProcessId
|0x1;
954 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
));
961 GDIOBJ_OwnedByCurrentProcess(HGDIOBJ ObjectHandle
)
963 PGDI_TABLE_ENTRY Entry
;
967 DPRINT("GDIOBJ_OwnedByCurrentProcess: ObjectHandle: 0x%08x\n", ObjectHandle
);
969 if(!GDI_HANDLE_IS_STOCKOBJ(ObjectHandle
))
971 ProcessId
= (LONG
)PsGetCurrentProcessId() << 1;
973 Entry
= GDI_HANDLE_GET_ENTRY(HandleTable
, ObjectHandle
);
974 Ret
= Entry
->KernelData
!= NULL
&&
976 (Entry
->ProcessId
& ~0x1) == ProcessId
;
985 GDIOBJ_ConvertToStockObj(HGDIOBJ
*hObj
)
988 * FIXME !!!!! THIS FUNCTION NEEDS TO BE FIXED - IT IS NOT SAFE WHEN OTHER THREADS
989 * MIGHT ATTEMPT TO LOCK THE OBJECT DURING THIS CALL!!!
991 PGDI_TABLE_ENTRY Entry
;
992 LONG ProcessId
, LockedProcessId
, PrevProcId
;
1000 DPRINT("GDIOBJ_ConvertToStockObj: hObj: 0x%08x\n", *hObj
);
1002 Thread
= PsGetCurrentThread();
1004 if(!GDI_HANDLE_IS_STOCKOBJ(*hObj
))
1006 /* shift the process id to the left so we can use the first bit to lock the object.
1007 FIXME - don't shift once ROS' PIDs match with nt! */
1008 ProcessId
= (LONG
)PsGetCurrentProcessId() << 1;
1009 LockedProcessId
= ProcessId
| 0x1;
1011 Entry
= GDI_HANDLE_GET_ENTRY(HandleTable
, *hObj
);
1014 /* lock the object, we must not convert stock objects, so don't check!!! */
1015 PrevProcId
= InterlockedCompareExchange(&Entry
->ProcessId
, LockedProcessId
, ProcessId
);
1016 if(PrevProcId
== ProcessId
)
1018 LONG NewType
, PrevType
, OldType
;
1020 /* we're locking an object that belongs to our process. First calculate
1021 the new object type including the stock object flag and then try to
1023 NewType
= GDI_HANDLE_GET_TYPE(*hObj
);
1024 NewType
|= NewType
>> 16;
1025 /* This is the type that the object should have right now, save it */
1027 /* As the object should be a stock object, set it's flag, but only in the upper 16 bits */
1028 NewType
|= GDI_HANDLE_STOCK_MASK
;
1030 /* Try to exchange the type field - but only if the old (previous type) matches! */
1031 PrevType
= InterlockedCompareExchange(&Entry
->Type
, NewType
, OldType
);
1032 if(PrevType
== OldType
&& Entry
->KernelData
!= NULL
)
1034 PETHREAD PrevThread
;
1037 /* We successfully set the stock object flag.
1038 KernelData should never be NULL here!!! */
1039 ASSERT(Entry
->KernelData
);
1041 GdiHdr
= GDIBdyToHdr(Entry
->KernelData
);
1043 PrevThread
= GdiHdr
->LockingThread
;
1044 if(PrevThread
== NULL
|| PrevThread
== Thread
)
1046 /* dereference the process' object counter */
1047 if(PrevProcId
!= GDI_GLOBAL_PROCESS
)
1049 PEPROCESS OldProcess
;
1050 PW32PROCESS W32Process
;
1054 Status
= PsLookupProcessByProcessId((PVOID
)(PrevProcId
>> 1), &OldProcess
);
1055 if(NT_SUCCESS(Status
))
1057 W32Process
= OldProcess
->Win32Process
;
1058 if(W32Process
!= NULL
)
1060 InterlockedDecrement(&W32Process
->GDIObjects
);
1062 ObDereferenceObject(OldProcess
);
1066 /* remove the process id lock and make it global */
1067 InterlockedExchange(&Entry
->ProcessId
, GDI_GLOBAL_PROCESS
);
1069 *hObj
= (HGDIOBJ
)((ULONG
)(*hObj
) | GDI_HANDLE_STOCK_MASK
);
1071 /* we're done, successfully converted the object */
1079 if(GdiHdr
->lockfile
!= NULL
)
1081 DPRINT1("[%d]Locked %s:%i by 0x%x (we're 0x%x)\n", Attempts
, GdiHdr
->lockfile
, GdiHdr
->lockline
, PrevThread
, Thread
);
1085 /* WTF?! The object is already locked by a different thread!
1086 Release the lock, wait a bit and try again!
1087 FIXME - we should give up after some time unless we want to wait forever! */
1088 InterlockedExchange(&Entry
->ProcessId
, PrevProcId
);
1096 DPRINT1("Attempted to convert object 0x%x that is deleted! Should never get here!!!\n", hObj
);
1099 else if(PrevProcId
== LockedProcessId
)
1104 DPRINT1("[%d]Waiting on 0x%x\n", Attempts
, hObj
);
1107 /* the object is currently locked, wait some time and try again.
1108 FIXME - we shouldn't loop forever! Give up after some time! */
1115 DPRINT1("Attempted to convert invalid handle: 0x%x\n", hObj
);
1123 GDIOBJ_SetOwnership(HGDIOBJ ObjectHandle
, PEPROCESS NewOwner
)
1125 PGDI_TABLE_ENTRY Entry
;
1126 LONG ProcessId
, LockedProcessId
, PrevProcId
;
1132 DPRINT("GDIOBJ_SetOwnership: hObj: 0x%x, NewProcess: 0x%x\n", ObjectHandle
, (NewOwner
? PsGetProcessId(NewOwner
) : 0));
1134 Thread
= PsGetCurrentThread();
1136 if(!GDI_HANDLE_IS_STOCKOBJ(ObjectHandle
))
1138 /* shift the process id to the left so we can use the first bit to lock the object.
1139 FIXME - don't shift once ROS' PIDs match with nt! */
1140 ProcessId
= (LONG
)PsGetCurrentProcessId() << 1;
1141 LockedProcessId
= ProcessId
| 0x1;
1143 Entry
= GDI_HANDLE_GET_ENTRY(HandleTable
, ObjectHandle
);
1146 /* lock the object, we must not convert stock objects, so don't check!!! */
1147 PrevProcId
= InterlockedCompareExchange(&Entry
->ProcessId
, ProcessId
, LockedProcessId
);
1148 if(PrevProcId
== ProcessId
)
1150 PETHREAD PrevThread
;
1152 if(Entry
->Type
!= 0 && Entry
->KernelData
!= NULL
)
1154 PGDIOBJHDR GdiHdr
= GDIBdyToHdr(Entry
->KernelData
);
1156 PrevThread
= GdiHdr
->LockingThread
;
1157 if(PrevThread
== NULL
|| PrevThread
== Thread
)
1159 PEPROCESS OldProcess
;
1160 PW32PROCESS W32Process
;
1163 /* dereference the process' object counter */
1165 Status
= PsLookupProcessByProcessId((PVOID
)(PrevProcId
>> 1), &OldProcess
);
1166 if(NT_SUCCESS(Status
))
1168 W32Process
= OldProcess
->Win32Process
;
1169 if(W32Process
!= NULL
)
1171 InterlockedDecrement(&W32Process
->GDIObjects
);
1173 ObDereferenceObject(OldProcess
);
1176 if(NewOwner
!= NULL
)
1179 ProcessId
= (LONG
)PsGetProcessId(NewOwner
) << 1;
1181 /* Increase the new process' object counter */
1182 W32Process
= NewOwner
->Win32Process
;
1183 if(W32Process
!= NULL
)
1185 InterlockedIncrement(&W32Process
->GDIObjects
);
1191 /* remove the process id lock and change it to the new process id */
1192 InterlockedExchange(&Entry
->ProcessId
, ProcessId
);
1202 if(GdiHdr
->lockfile
!= NULL
)
1204 DPRINT1("[%d]Locked from %s:%i by 0x%x (we're 0x%x)\n", Attempts
, GdiHdr
->lockfile
, GdiHdr
->lockline
, PrevThread
, Thread
);
1208 /* WTF?! The object is already locked by a different thread!
1209 Release the lock, wait a bit and try again! DO reset the pid lock
1210 so we make sure we don't access invalid memory in case the object is
1211 being deleted in the meantime (because we don't have aquired a reference
1213 FIXME - we should give up after some time unless we want to wait forever! */
1214 InterlockedExchange(&Entry
->ProcessId
, PrevProcId
);
1222 DPRINT1("Attempted to change ownership of an object 0x%x currently being destroyed!!!\n", ObjectHandle
);
1225 else if(PrevProcId
== LockedProcessId
)
1230 DPRINT1("[%d]Waiting on 0x%x\n", Attempts
, ObjectHandle
);
1233 /* the object is currently locked, wait some time and try again.
1234 FIXME - we shouldn't loop forever! Give up after some time! */
1239 else if((PrevProcId
>> 1) == 0)
1241 /* allow changing ownership of global objects */
1243 LockedProcessId
= ProcessId
| 0x1;
1246 else if((PrevProcId
>> 1) != (LONG
)PsGetCurrentProcessId())
1248 DPRINT1("Attempted to change ownership of object 0x%x (pid: 0x%x) from pid 0x%x!!!\n", ObjectHandle
, PrevProcId
>> 1, PsGetCurrentProcessId());
1252 DPRINT1("Attempted to change owner of invalid handle: 0x%x\n", ObjectHandle
);
1258 GDIOBJ_CopyOwnership(HGDIOBJ CopyFrom
, HGDIOBJ CopyTo
)
1260 PGDI_TABLE_ENTRY FromEntry
;
1262 LONG FromProcessId
, FromLockedProcessId
, FromPrevProcId
;
1267 DPRINT("GDIOBJ_CopyOwnership: from: 0x%x, to: 0x%x\n", CopyFrom
, CopyTo
);
1269 Thread
= PsGetCurrentThread();
1271 if(!GDI_HANDLE_IS_STOCKOBJ(CopyFrom
) && !GDI_HANDLE_IS_STOCKOBJ(CopyTo
))
1273 FromEntry
= GDI_HANDLE_GET_ENTRY(HandleTable
, CopyFrom
);
1275 FromProcessId
= FromEntry
->ProcessId
& ~0x1;
1276 FromLockedProcessId
= FromProcessId
| 0x1;
1279 /* lock the object, we must not convert stock objects, so don't check!!! */
1280 FromPrevProcId
= InterlockedCompareExchange(&FromEntry
->ProcessId
, FromProcessId
, FromLockedProcessId
);
1281 if(FromPrevProcId
== FromProcessId
)
1283 PETHREAD PrevThread
;
1286 if(FromEntry
->Type
!= 0 && FromEntry
->KernelData
!= NULL
)
1288 GdiHdr
= GDIBdyToHdr(FromEntry
->KernelData
);
1290 /* save the pointer to the calling thread so we know it was this thread
1291 that locked the object */
1292 PrevThread
= GdiHdr
->LockingThread
;
1293 if(PrevThread
== NULL
|| PrevThread
== Thread
)
1295 /* now let's change the ownership of the target object */
1297 if((FromPrevProcId
& ~0x1) != 0)
1299 PEPROCESS ProcessTo
;
1301 if(NT_SUCCESS(PsLookupProcessByProcessId((PVOID
)(FromPrevProcId
>> 1), &ProcessTo
)))
1303 GDIOBJ_SetOwnership(CopyTo
, ProcessTo
);
1304 ObDereferenceObject(ProcessTo
);
1309 /* mark the object as global */
1310 GDIOBJ_SetOwnership(CopyTo
, NULL
);
1313 InterlockedExchange(&FromEntry
->ProcessId
, FromPrevProcId
);
1320 if(GdiHdr
->lockfile
!= NULL
)
1322 DPRINT1("[%d]Locked from %s:%i by 0x%x (we're 0x%x)\n", Attempts
, GdiHdr
->lockfile
, GdiHdr
->lockline
, PrevThread
, Thread
);
1326 /* WTF?! The object is already locked by a different thread!
1327 Release the lock, wait a bit and try again! DO reset the pid lock
1328 so we make sure we don't access invalid memory in case the object is
1329 being deleted in the meantime (because we don't have aquired a reference
1331 FIXME - we should give up after some time unless we want to wait forever! */
1332 InterlockedExchange(&FromEntry
->ProcessId
, FromPrevProcId
);
1335 goto LockHandleFrom
;
1340 DPRINT1("Attempted to copy ownership from an object 0x%x currently being destroyed!!!\n", CopyFrom
);
1343 else if(FromPrevProcId
== FromLockedProcessId
)
1348 DPRINT1("[%d]Waiting on 0x%x\n", Attempts
, CopyFrom
);
1351 /* the object is currently locked, wait some time and try again.
1352 FIXME - we shouldn't loop forever! Give up after some time! */
1355 goto LockHandleFrom
;
1357 else if((FromPrevProcId
>> 1) != (LONG
)PsGetCurrentProcessId())
1359 /* FIXME - should we really allow copying ownership from objects that we don't even own? */
1360 DPRINT1("WARNING! Changing copying ownership of object 0x%x (pid: 0x%x) to pid 0x%x!!!\n", CopyFrom
, FromPrevProcId
>> 1, PsGetCurrentProcessId());
1361 FromProcessId
= FromPrevProcId
& ~0x1;
1362 FromLockedProcessId
= FromProcessId
| 0x1;
1363 goto LockHandleFrom
;
1367 DPRINT1("Attempted to copy ownership from invalid handle: 0x%x\n", CopyFrom
);
1373 GDI_MapHandleTable(HANDLE hProcess
)
1375 DPRINT("%s:%i: %s(): FIXME - Map handle table into the process memory space!\n",
1376 __FILE__
, __LINE__
, __FUNCTION__
);
1377 /* FIXME - Map the entire gdi handle table read-only to userland into the
1378 scope of hProcess and return the pointer */