007e3faeaf5db4002fa25602aa607913410f4406
[reactos.git] / reactos / subsystems / win32 / win32k / objects / gdiobj.c
1 /*
2 * ReactOS W32 Subsystem
3 * Copyright (C) 1998 - 2004 ReactOS Team
4 *
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.
9 *
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.
14 *
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.
18 */
19 /*
20 * GDIOBJ.C - GDI object manipulation routines
21 *
22 * $Id$
23 */
24
25 #include <w32k.h>
26
27 #define NDEBUG
28 #include <debug.h>
29
30 #define GDI_ENTRY_TO_INDEX(ht, e) \
31 (((ULONG_PTR)(e) - (ULONG_PTR)&((ht)->Entries[0])) / sizeof(GDI_TABLE_ENTRY))
32 #define GDI_HANDLE_GET_ENTRY(HandleTable, h) \
33 (&(HandleTable)->Entries[GDI_HANDLE_GET_INDEX((h))])
34
35 #define GDIBdyToHdr(body) \
36 ((PGDIOBJHDR)(body) - 1)
37 #define GDIHdrToBdy(hdr) \
38 (PGDIOBJ)((PGDIOBJHDR)(hdr) + 1)
39
40 /* apparently the first 10 entries are never used in windows as they are empty */
41 #define RESERVE_ENTRIES_COUNT 10
42
43 typedef struct _GDI_HANDLE_TABLE
44 {
45 /* the table must be located at the beginning of this structure so it can be
46 properly mapped! */
47 GDI_TABLE_ENTRY Entries[GDI_HANDLE_COUNT];
48
49 PPAGED_LOOKASIDE_LIST LookasideLists;
50
51 SLIST_HEADER FreeEntriesHead;
52 SLIST_ENTRY FreeEntries[((GDI_HANDLE_COUNT * sizeof(GDI_TABLE_ENTRY)) << 3) /
53 (sizeof(SLIST_ENTRY) << 3)];
54 } GDI_HANDLE_TABLE, *PGDI_HANDLE_TABLE;
55
56 typedef struct
57 {
58 ULONG Type;
59 ULONG Size;
60 GDICLEANUPPROC CleanupProc;
61 } GDI_OBJ_INFO, *PGDI_OBJ_INFO;
62
63 /*
64 * Dummy GDI Cleanup Callback
65 */
66 BOOL INTERNAL_CALL
67 GDI_CleanupDummy(PVOID ObjectBody)
68 {
69 return TRUE;
70 }
71
72 /* Testing shows that regions are the most used GDIObj type,
73 so put that one first for performance */
74 const
75 GDI_OBJ_INFO ObjInfo[] =
76 {
77 /* Type */ /* Size */ /* CleanupProc */
78 {GDI_OBJECT_TYPE_REGION, sizeof(ROSRGNDATA), RGNDATA_Cleanup},
79 {GDI_OBJECT_TYPE_BITMAP, sizeof(BITMAPOBJ), BITMAP_Cleanup},
80 {GDI_OBJECT_TYPE_DC, sizeof(DC), DC_Cleanup},
81 {GDI_OBJECT_TYPE_PALETTE, sizeof(PALGDI), PALETTE_Cleanup},
82 {GDI_OBJECT_TYPE_BRUSH, sizeof(GDIBRUSHOBJ), BRUSH_Cleanup},
83 {GDI_OBJECT_TYPE_PEN, sizeof(GDIBRUSHOBJ), GDI_CleanupDummy},
84 {GDI_OBJECT_TYPE_FONT, sizeof(TEXTOBJ), GDI_CleanupDummy},
85 {GDI_OBJECT_TYPE_DCE, sizeof(DCE), DCE_Cleanup},
86 {GDI_OBJECT_TYPE_DIRECTDRAW, sizeof(DD_DIRECTDRAW), DD_Cleanup},
87 {GDI_OBJECT_TYPE_DD_SURFACE, sizeof(DD_SURFACE), DDSURF_Cleanup},
88 {GDI_OBJECT_TYPE_EXTPEN, 0, GDI_CleanupDummy},
89 {GDI_OBJECT_TYPE_METADC, 0, GDI_CleanupDummy},
90 {GDI_OBJECT_TYPE_METAFILE, 0, GDI_CleanupDummy},
91 {GDI_OBJECT_TYPE_ENHMETAFILE, 0, GDI_CleanupDummy},
92 {GDI_OBJECT_TYPE_ENHMETADC, 0, GDI_CleanupDummy},
93 {GDI_OBJECT_TYPE_MEMDC, 0, GDI_CleanupDummy},
94 {GDI_OBJECT_TYPE_EMF, 0, GDI_CleanupDummy}
95 };
96
97 #define OBJTYPE_COUNT (sizeof(ObjInfo) / sizeof(ObjInfo[0]))
98
99 static PGDI_HANDLE_TABLE HandleTable = NULL;
100 static LARGE_INTEGER ShortDelay;
101
102 #define DelayExecution() \
103 DPRINT("%s:%i: Delay\n", __FILE__, __LINE__); \
104 KeDelayExecutionThread(KernelMode, FALSE, &ShortDelay)
105
106 #ifdef GDI_DEBUG
107 BOOLEAN STDCALL KiRosPrintAddress(PVOID Address);
108 VOID STDCALL KeRosDumpStackFrames(PULONG Frame, ULONG FrameCount);
109 ULONG STDCALL KeRosGetStackFrames(PULONG Frames, ULONG FrameCount);
110 #endif
111
112 /*!
113 * Allocate GDI object table.
114 * \param Size - number of entries in the object table.
115 */
116 static PGDI_HANDLE_TABLE INTERNAL_CALL
117 GDIOBJ_iAllocHandleTable(VOID)
118 {
119 PGDI_HANDLE_TABLE handleTable;
120 ULONG htSize;
121 UINT ObjType;
122 UINT i;
123 PGDI_TABLE_ENTRY Entry;
124
125 handleTable = NULL;
126 htSize = sizeof(GDI_HANDLE_TABLE);
127
128 IntUserCreateSharedSection(SessionSharedSectionPool,
129 (PVOID*)&handleTable,
130 &htSize);
131 ASSERT( handleTable );
132 RtlZeroMemory(handleTable, sizeof(GDI_HANDLE_TABLE));
133
134 /*
135 * initialize the free entry cache
136 */
137 InitializeSListHead(&handleTable->FreeEntriesHead);
138 Entry = &HandleTable->Entries[RESERVE_ENTRIES_COUNT];
139 for(i = GDI_HANDLE_COUNT - 1; i >= RESERVE_ENTRIES_COUNT; i--)
140 {
141 InterlockedPushEntrySList(&handleTable->FreeEntriesHead, &handleTable->FreeEntries[i]);
142 }
143
144 handleTable->LookasideLists = ExAllocatePoolWithTag(NonPagedPool,
145 OBJTYPE_COUNT * sizeof(PAGED_LOOKASIDE_LIST),
146 TAG_GDIHNDTBLE);
147 if(handleTable->LookasideLists == NULL)
148 {
149 InUserDeleteSharedSection(SessionSharedSectionPool,
150 handleTable);
151 return NULL;
152 }
153
154 for(ObjType = 0; ObjType < OBJTYPE_COUNT; ObjType++)
155 {
156 ExInitializePagedLookasideList(handleTable->LookasideLists + ObjType, NULL, NULL, 0,
157 ObjInfo[ObjType].Size + sizeof(GDIOBJHDR), TAG_GDIOBJ, 0);
158 }
159
160 ShortDelay.QuadPart = -5000LL; /* FIXME - 0.5 ms? */
161
162 return handleTable;
163 }
164
165 static __inline PPAGED_LOOKASIDE_LIST
166 FindLookasideList(DWORD ObjectType)
167 {
168 int Index;
169
170 for (Index = 0; Index < OBJTYPE_COUNT; Index++)
171 {
172 if (ObjInfo[Index].Type == ObjectType)
173 {
174 return HandleTable->LookasideLists + Index;
175 }
176 }
177
178 DPRINT1("Can't find lookaside list for object type 0x%08x\n", ObjectType);
179
180 return NULL;
181 }
182
183 static __inline BOOL
184 RunCleanupCallback(PGDIOBJ pObj, DWORD ObjectType)
185 {
186 int Index;
187
188 for (Index = 0; Index < OBJTYPE_COUNT; Index++)
189 {
190 if (ObjInfo[Index].Type == ObjectType)
191 {
192 return ((GDICLEANUPPROC)ObjInfo[Index].CleanupProc)(pObj);
193 }
194 }
195
196 DPRINT1("Can't find cleanup callback for object type 0x%08x\n", ObjectType);
197 return TRUE;
198 }
199
200 static __inline ULONG
201 GetObjectSize(DWORD ObjectType)
202 {
203 int Index;
204
205 for (Index = 0; Index < OBJTYPE_COUNT; Index++)
206 {
207 if (ObjInfo[Index].Type == ObjectType)
208 {
209 return ObjInfo[Index].Size;
210 }
211 }
212
213 DPRINT1("Can't find size for object type 0x%08x\n", ObjectType);
214 return 0;
215 }
216
217 #ifdef GDI_DEBUG
218
219 static int leak_reported = 0;
220 #define GDI_STACK_LEVELS 12
221 static ULONG GDIHandleAllocator[GDI_HANDLE_COUNT][GDI_STACK_LEVELS];
222 struct DbgOpenGDIHandle
223 {
224 ULONG idx;
225 int count;
226 };
227 #define H 1024
228 static struct DbgOpenGDIHandle h[H];
229
230 void IntDumpHandleTable()
231 {
232 int i, n = 0, j, k, J;
233
234 if ( leak_reported )
235 {
236 DPRINT1("gdi handle abusers already reported!\n");
237 return;
238 }
239
240 leak_reported = 1;
241 DPRINT1("reporting gdi handle abusers:\n");
242
243 /* step through GDI handle table and find out who our culprit is... */
244 for ( i = RESERVE_ENTRIES_COUNT; i < GDI_HANDLE_COUNT; i++ )
245 {
246 for ( j = 0; j < n; j++ )
247 {
248 next:
249 J = h[j].idx;
250 for ( k = 0; k < GDI_STACK_LEVELS; k++ )
251 {
252 if ( GDIHandleAllocator[i][k]
253 != GDIHandleAllocator[J][k] )
254 {
255 if ( ++j == n )
256 goto done;
257 else
258 goto next;
259 }
260 }
261 goto done;
262 }
263 done:
264 if ( j < H )
265 {
266 if ( j == n )
267 {
268 h[j].idx = i;
269 h[j].count = 1;
270 n = n + 1;
271 }
272 else
273 h[j].count++;
274 }
275 }
276 /* bubble sort time! weeeeee!! */
277 for ( i = 0; i < n-1; i++ )
278 {
279 if ( h[i].count < h[i+1].count )
280 {
281 struct DbgOpenGDIHandle t;
282 t = h[i+1];
283 h[i+1] = h[i];
284 j = i;
285 while ( j > 0 && h[j-1].count < t.count )
286 j--;
287 h[j] = t;
288 }
289 }
290 /* print the worst offenders... */
291 DbgPrint ( "Worst GDI Handle leak offenders (out of %i unique locations):\n", n );
292 for ( i = 0; i < n && h[i].count > 1; i++ )
293 {
294 int j;
295 DbgPrint ( " %i allocs: ", h[i].count );
296 for ( j = 0; j < GDI_STACK_LEVELS; j++ )
297 {
298 ULONG Addr = GDIHandleAllocator[h[i].idx][j];
299 if ( !KiRosPrintAddress ( (PVOID)Addr ) )
300 DbgPrint ( "<%X>", Addr );
301 }
302 DbgPrint ( "\n" );
303 }
304 if ( i < n && h[i].count == 1 )
305 DbgPrint ( "(list terminated - the remaining entries have 1 allocation only)\n" );
306 }
307 #endif /* GDI_DEBUG */
308
309 /*!
310 * Allocate memory for GDI object and return handle to it.
311 *
312 * \param ObjectType - type of object \ref GDI object types
313 *
314 * \return Handle of the allocated object.
315 *
316 * \note Use GDIOBJ_Lock() to obtain pointer to the new object.
317 * \todo return the object pointer and lock it by default.
318 */
319 HGDIOBJ INTERNAL_CALL
320 #ifdef GDI_DEBUG
321 GDIOBJ_AllocObjDbg(const char* file, int line, ULONG ObjectType)
322 #else /* !GDI_DEBUG */
323 GDIOBJ_AllocObj(ULONG ObjectType)
324 #endif /* GDI_DEBUG */
325 {
326 PW32PROCESS W32Process;
327 PGDIOBJHDR newObject;
328 PPAGED_LOOKASIDE_LIST LookasideList;
329 HANDLE CurrentProcessId, LockedProcessId;
330 #ifdef GDI_DEBUG
331 ULONG Attempts = 0;
332 #endif
333
334 W32Process = PsGetWin32Process();
335 /* HACK HACK HACK: simplest-possible quota implementation - don't allow a process
336 to take too many GDI objects, itself. */
337 if ( W32Process && W32Process->GDIObjects >= 0x2710 )
338 return NULL;
339
340 ASSERT(ObjectType != GDI_OBJECT_TYPE_DONTCARE);
341
342 LookasideList = FindLookasideList(ObjectType);
343 if(LookasideList != NULL)
344 {
345 newObject = ExAllocateFromPagedLookasideList(LookasideList);
346 if(newObject != NULL)
347 {
348 PSLIST_ENTRY FreeEntry;
349 PGDI_TABLE_ENTRY Entry;
350 PGDIOBJ ObjectBody;
351 LONG TypeInfo;
352
353 CurrentProcessId = PsGetCurrentProcessId();
354 LockedProcessId = (HANDLE)((ULONG_PTR)CurrentProcessId | 0x1);
355
356 newObject->LockingThread = NULL;
357 newObject->Locks = 0;
358
359 #ifdef GDI_DEBUG
360 newObject->createdfile = file;
361 newObject->createdline = line;
362 newObject->lockfile = NULL;
363 newObject->lockline = 0;
364 #endif
365
366 ObjectBody = GDIHdrToBdy(newObject);
367
368 RtlZeroMemory(ObjectBody, GetObjectSize(ObjectType));
369
370 TypeInfo = (ObjectType & GDI_HANDLE_TYPE_MASK) | (ObjectType >> 16);
371
372 FreeEntry = InterlockedPopEntrySList(&HandleTable->FreeEntriesHead);
373 if(FreeEntry != NULL)
374 {
375 HANDLE PrevProcId;
376 UINT Index;
377
378 /* calculate the entry from the address of the entry in the free slot array */
379 Index = ((ULONG_PTR)FreeEntry - (ULONG_PTR)&HandleTable->FreeEntries[0]) /
380 sizeof(HandleTable->FreeEntries[0]);
381 Entry = &HandleTable->Entries[Index];
382
383 LockHandle:
384 PrevProcId = InterlockedCompareExchangePointer(&Entry->ProcessId, LockedProcessId, 0);
385 if(PrevProcId == NULL)
386 {
387 HGDIOBJ Handle;
388
389 ASSERT(Entry->KernelData == NULL);
390
391 Entry->KernelData = ObjectBody;
392
393 /* copy the reuse-counter */
394 TypeInfo |= Entry->Type & GDI_HANDLE_REUSE_MASK;
395
396 /* we found a free entry, no need to exchange this field atomically
397 since we're holding the lock */
398 Entry->Type = TypeInfo;
399
400 /* unlock the entry */
401 InterlockedExchangePointer(&Entry->ProcessId, CurrentProcessId);
402
403 #ifdef GDI_DEBUG
404 memset ( GDIHandleAllocator[Index], 0xcd, GDI_STACK_LEVELS * sizeof(ULONG) );
405 KeRosGetStackFrames ( GDIHandleAllocator[Index], GDI_STACK_LEVELS );
406 #endif /* GDI_DEBUG */
407
408 if(W32Process != NULL)
409 {
410 InterlockedIncrement(&W32Process->GDIObjects);
411 }
412 Handle = (HGDIOBJ)((Index & 0xFFFF) | (TypeInfo & (GDI_HANDLE_TYPE_MASK | GDI_HANDLE_REUSE_MASK)));
413
414 DPRINT("GDIOBJ_AllocObj: 0x%x ob: 0x%x\n", Handle, ObjectBody);
415 return Handle;
416 }
417 else
418 {
419 #ifdef GDI_DEBUG
420 if(++Attempts > 20)
421 {
422 DPRINT1("[%d]Waiting on handle in index 0x%x\n", Attempts, Index);
423 }
424 #endif
425 /* damn, someone is trying to lock the object even though it doesn't
426 eve nexist anymore, wait a little and try again!
427 FIXME - we shouldn't loop forever! Give up after some time! */
428 DelayExecution();
429 /* try again */
430 goto LockHandle;
431 }
432 }
433
434 ExFreeToPagedLookasideList(LookasideList, newObject);
435 DPRINT1("Failed to insert gdi object into the handle table, no handles left!\n");
436 #ifdef GDI_DEBUG
437 IntDumpHandleTable();
438 #endif /* GDI_DEBUG */
439 }
440 else
441 {
442 DPRINT1("Not enough memory to allocate gdi object!\n");
443 }
444 }
445 else
446 {
447 DPRINT1("Failed to find lookaside list for object type 0x%x\n", ObjectType);
448 }
449 return NULL;
450 }
451
452 /*!
453 * Free memory allocated for the GDI object. For each object type this function calls the
454 * appropriate cleanup routine.
455 *
456 * \param hObj - handle of the object to be deleted.
457 *
458 * \return Returns TRUE if succesful.
459 * \return Returns FALSE if the cleanup routine returned FALSE or the object doesn't belong
460 * to the calling process.
461 */
462 BOOL INTERNAL_CALL
463 #ifdef GDI_DEBUG
464 GDIOBJ_FreeObjDbg(const char* file, int line, HGDIOBJ hObj, DWORD ObjectType)
465 #else /* !GDI_DEBUG */
466 GDIOBJ_FreeObj(HGDIOBJ hObj, DWORD ObjectType)
467 #endif /* GDI_DEBUG */
468 {
469 PGDI_TABLE_ENTRY Entry;
470 PPAGED_LOOKASIDE_LIST LookasideList;
471 HANDLE ProcessId, LockedProcessId, PrevProcId;
472 LONG ExpectedType;
473 BOOL Silent;
474 #ifdef GDI_DEBUG
475 ULONG Attempts = 0;
476 #endif
477
478 DPRINT("GDIOBJ_FreeObj: hObj: 0x%08x\n", hObj);
479
480 if(GDI_HANDLE_IS_STOCKOBJ(hObj))
481 {
482 DPRINT1("GDIOBJ_FreeObj() failed, can't delete stock object handle: 0x%x !!!\n", hObj);
483 #ifdef GDI_DEBUG
484 DPRINT1("-> called from %s:%i\n", file, line);
485 #endif
486 return FALSE;
487 }
488
489 ProcessId = PsGetCurrentProcessId();
490 LockedProcessId = (HANDLE)((ULONG_PTR)ProcessId | 0x1);
491
492 Silent = (ObjectType & GDI_OBJECT_TYPE_SILENT);
493 ObjectType &= ~GDI_OBJECT_TYPE_SILENT;
494
495 ExpectedType = ((ObjectType != GDI_OBJECT_TYPE_DONTCARE) ? ObjectType : 0);
496
497 Entry = GDI_HANDLE_GET_ENTRY(HandleTable, hObj);
498
499 LockHandle:
500 /* lock the object, we must not delete global objects, so don't exchange the locking
501 process ID to zero when attempting to lock a global object... */
502 PrevProcId = InterlockedCompareExchangePointer(&Entry->ProcessId, LockedProcessId, ProcessId);
503 if(PrevProcId == ProcessId)
504 {
505 if(Entry->Type != 0 && Entry->KernelData != NULL &&
506 (ExpectedType == 0 || ((Entry->Type << 16) == ExpectedType)))
507 {
508 PGDIOBJHDR GdiHdr;
509
510 GdiHdr = GDIBdyToHdr(Entry->KernelData);
511
512 if(GdiHdr->Locks == 0)
513 {
514 BOOL Ret;
515 PW32PROCESS W32Process = PsGetWin32Process();
516 ULONG Type = Entry->Type << 16;
517
518 /* Clear the type field so when unlocking the handle it gets finally deleted and increment reuse counter */
519 Entry->Type = ((Entry->Type >> GDI_HANDLE_REUSECNT_SHIFT) + 1) << GDI_HANDLE_REUSECNT_SHIFT;
520 Entry->KernelData = NULL;
521
522 /* unlock the handle slot */
523 InterlockedExchangePointer(&Entry->ProcessId, NULL);
524
525 /* push this entry to the free list */
526 InterlockedPushEntrySList(&HandleTable->FreeEntriesHead,
527 &HandleTable->FreeEntries[GDI_ENTRY_TO_INDEX(HandleTable, Entry)]);
528
529 if(W32Process != NULL)
530 {
531 InterlockedDecrement(&W32Process->GDIObjects);
532 }
533
534 /* call the cleanup routine. */
535 Ret = RunCleanupCallback(GDIHdrToBdy(GdiHdr), Type);
536
537 /* Now it's time to free the memory */
538 LookasideList = FindLookasideList(Type);
539 if(LookasideList != NULL)
540 {
541 ExFreeToPagedLookasideList(LookasideList, GdiHdr);
542 }
543
544 return Ret;
545 }
546 else
547 {
548 /*
549 * The object is currently locked, so freeing is forbidden!
550 */
551 DPRINT1("GdiHdr->Locks: %d\n", GdiHdr->Locks);
552 #ifdef GDI_DEBUG
553 DPRINT1("Locked from: %s:%d\n", GdiHdr->lockfile, GdiHdr->lockline);
554 #endif
555 ASSERT(FALSE);
556 }
557 }
558 else
559 {
560 if((Entry->Type & ~GDI_HANDLE_REUSE_MASK) != 0)
561 {
562 DPRINT1("Attempted to delete object 0x%x, type mismatch (0x%x : 0x%x)\n", hObj, ObjectType, ExpectedType);
563 }
564 else
565 {
566 DPRINT1("Attempted to delete object 0x%x which was already deleted!\n", hObj);
567 }
568 InterlockedExchangePointer(&Entry->ProcessId, PrevProcId);
569 }
570 }
571 else if(PrevProcId == LockedProcessId)
572 {
573 #ifdef GDI_DEBUG
574 if(++Attempts > 20)
575 {
576 DPRINT1("[%d]Waiting on 0x%x\n", Attempts, hObj);
577 }
578 #endif
579 /* the object is currently locked, wait some time and try again.
580 FIXME - we shouldn't loop forever! Give up after some time! */
581 DelayExecution();
582 /* try again */
583 goto LockHandle;
584 }
585 else
586 {
587 if(!Silent)
588 {
589 if(((ULONG_PTR)PrevProcId & ~0x1) == 0)
590 {
591 DPRINT1("Attempted to free global gdi handle 0x%x, caller needs to get ownership first!!!\n", hObj);
592 }
593 else
594 {
595 DPRINT1("Attempted to free foreign handle: 0x%x Owner: 0x%x from Caller: 0x%x\n", hObj, (ULONG_PTR)PrevProcId & ~0x1, (ULONG_PTR)ProcessId & ~0x1);
596 }
597 #ifdef GDI_DEBUG
598 DPRINT1("-> called from %s:%i\n", file, line);
599 #endif
600 }
601 }
602
603 return FALSE;
604 }
605
606 /*!
607 * Initialization of the GDI object engine.
608 */
609 VOID INTERNAL_CALL
610 InitGdiObjectHandleTable (VOID)
611 {
612 DPRINT("InitGdiObjectHandleTable\n");
613
614 HandleTable = GDIOBJ_iAllocHandleTable();
615 DPRINT("HandleTable: %x\n", HandleTable);
616 }
617
618 /*!
619 * Delete GDI object
620 * \param hObject object handle
621 * \return if the function fails the returned value is FALSE.
622 */
623 BOOL STDCALL
624 NtGdiDeleteObject(HGDIOBJ hObject)
625 {
626 DPRINT("NtGdiDeleteObject handle 0x%08x\n", hObject);
627
628 return NULL != hObject
629 ? GDIOBJ_FreeObj(hObject, GDI_OBJECT_TYPE_DONTCARE) : FALSE;
630 }
631
632 /*!
633 * Internal function. Called when the process is destroyed to free the remaining GDI handles.
634 * \param Process - PID of the process that will be destroyed.
635 */
636 BOOL INTERNAL_CALL
637 GDI_CleanupForProcess (struct _EPROCESS *Process)
638 {
639 PGDI_TABLE_ENTRY Entry, End;
640 PEPROCESS CurrentProcess;
641 PW32PROCESS W32Process;
642 HANDLE ProcId;
643 ULONG Index = RESERVE_ENTRIES_COUNT;
644
645 DPRINT("Starting CleanupForProcess prochandle %x Pid %d\n", Process, Process->UniqueProcessId);
646 CurrentProcess = PsGetCurrentProcess();
647 if (CurrentProcess != Process)
648 {
649 KeAttachProcess(&Process->Pcb);
650 }
651 W32Process = (PW32PROCESS)Process->Win32Process;
652 ASSERT(W32Process);
653
654 if(W32Process->GDIObjects > 0)
655 {
656 /* FIXME - Instead of building the handle here and delete it using GDIOBJ_FreeObj
657 we should delete it directly here! */
658 ProcId = Process->UniqueProcessId;
659
660 End = &HandleTable->Entries[GDI_HANDLE_COUNT];
661 for(Entry = &HandleTable->Entries[RESERVE_ENTRIES_COUNT];
662 Entry != End;
663 Entry++, Index++)
664 {
665 /* ignore the lock bit */
666 if((HANDLE)((ULONG_PTR)Entry->ProcessId & ~0x1) == ProcId && (Entry->Type & ~GDI_HANDLE_REUSE_MASK) != 0)
667 {
668 HGDIOBJ ObjectHandle;
669
670 /* Create the object handle for the entry, the upper 16 bit of the
671 Type field includes the type of the object including the stock
672 object flag - but since stock objects don't have a process id we can
673 simply ignore this fact here. */
674 ObjectHandle = (HGDIOBJ)(Index | (Entry->Type & 0xFFFF0000));
675
676 if(GDIOBJ_FreeObj(ObjectHandle, GDI_OBJECT_TYPE_DONTCARE) &&
677 W32Process->GDIObjects == 0)
678 {
679 /* there are no more gdi handles for this process, bail */
680 break;
681 }
682 }
683 }
684 }
685
686 if (CurrentProcess != Process)
687 {
688 KeDetachProcess();
689 }
690
691 DPRINT("Completed cleanup for process %d\n", Process->UniqueProcessId);
692
693 return TRUE;
694 }
695
696 /*!
697 * Return pointer to the object by handle.
698 *
699 * \param hObj Object handle
700 * \return Pointer to the object.
701 *
702 * \note Process can only get pointer to the objects it created or global objects.
703 *
704 * \todo Get rid of the ObjectType parameter!
705 */
706 PGDIOBJ INTERNAL_CALL
707 #ifdef GDI_DEBUG
708 GDIOBJ_LockObjDbg (const char* file, int line, HGDIOBJ hObj, DWORD ObjectType)
709 #else /* !GDI_DEBUG */
710 GDIOBJ_LockObj (HGDIOBJ hObj, DWORD ObjectType)
711 #endif /* GDI_DEBUG */
712 {
713 USHORT HandleIndex;
714 PGDI_TABLE_ENTRY HandleEntry;
715 HANDLE ProcessId, HandleProcessId, LockedProcessId, PrevProcId;
716 PGDIOBJ Object = NULL;
717
718 HandleIndex = GDI_HANDLE_GET_INDEX(hObj);
719
720 /* Check that the handle index is valid. */
721 if (HandleIndex >= GDI_HANDLE_COUNT)
722 return NULL;
723
724 HandleEntry = &HandleTable->Entries[HandleIndex];
725
726 ProcessId = (HANDLE)((ULONG_PTR)PsGetCurrentProcessId() & ~1);
727 HandleProcessId = (HANDLE)((ULONG_PTR)HandleEntry->ProcessId & ~1);
728
729 /* Check for invalid owner. */
730 if (ProcessId != HandleProcessId && HandleProcessId != NULL)
731 {
732 return NULL;
733 }
734
735 /*
736 * Prevent the thread from being terminated during the locking process.
737 * It would result in undesired effects and inconsistency of the global
738 * handle table.
739 */
740
741 KeEnterCriticalRegion();
742
743 /*
744 * Loop until we either successfully lock the handle entry & object or
745 * fail some of the check.
746 */
747
748 for (;;)
749 {
750 /* Lock the handle table entry. */
751 LockedProcessId = (HANDLE)((ULONG_PTR)HandleProcessId | 0x1);
752 PrevProcId = InterlockedCompareExchangePointer(&HandleEntry->ProcessId,
753 LockedProcessId,
754 HandleProcessId);
755
756 if (PrevProcId == HandleProcessId)
757 {
758 LONG HandleType = HandleEntry->Type << 16;
759
760 /*
761 * We're locking an object that belongs to our process or it's a
762 * global object if HandleProcessId is 0 here.
763 */
764
765 /* FIXME: Check the upper 16-bits of handle number! */
766 if (HandleType != 0 && HandleEntry->KernelData != NULL &&
767 (ObjectType == GDI_OBJECT_TYPE_DONTCARE ||
768 HandleType == ObjectType))
769 {
770 PGDIOBJHDR GdiHdr = GDIBdyToHdr(HandleEntry->KernelData);
771 PETHREAD Thread = PsGetCurrentThread();
772
773 if (GdiHdr->Locks == 0)
774 {
775 GdiHdr->LockingThread = Thread;
776 GdiHdr->Locks = 1;
777 #ifdef GDI_DEBUG
778 GdiHdr->lockfile = file;
779 GdiHdr->lockline = line;
780 #endif
781 Object = HandleEntry->KernelData;
782 }
783 else
784 {
785 InterlockedIncrement((PLONG)&GdiHdr->Locks);
786 if (GdiHdr->LockingThread != Thread)
787 {
788 InterlockedDecrement((PLONG)&GdiHdr->Locks);
789
790 /* Unlock the handle table entry. */
791 InterlockedExchangePointer(&HandleEntry->ProcessId, PrevProcId);
792
793 DelayExecution();
794 continue;
795 }
796 Object = HandleEntry->KernelData;
797 }
798 }
799 else
800 {
801 /*
802 * Debugging code. Report attempts to lock deleted handles and
803 * locking type mismatches.
804 */
805
806 if ((HandleType & ~GDI_HANDLE_REUSE_MASK) == 0)
807 {
808 DPRINT1("Attempted to lock object 0x%x that is deleted!\n", hObj);
809 #ifdef GDI_DEBUG
810 KeRosDumpStackFrames(NULL, 20);
811 #endif
812 }
813 else
814 {
815 DPRINT1("Attempted to lock object 0x%x, type mismatch (0x%x : 0x%x)\n",
816 hObj, HandleType & ~GDI_HANDLE_REUSE_MASK, ObjectType & ~GDI_HANDLE_REUSE_MASK);
817 #ifdef GDI_DEBUG
818 KeRosDumpStackFrames(NULL, 20);
819 #endif
820 }
821 #ifdef GDI_DEBUG
822 DPRINT1("-> called from %s:%i\n", file, line);
823 #endif
824 }
825
826 /* Unlock the handle table entry. */
827 InterlockedExchangePointer(&HandleEntry->ProcessId, PrevProcId);
828
829 break;
830 }
831 else
832 {
833 /*
834 * The handle is currently locked, wait some time and try again.
835 */
836
837 DelayExecution();
838 continue;
839 }
840 }
841
842 KeLeaveCriticalRegion();
843
844 return Object;
845 }
846
847
848 /*!
849 * Return pointer to the object by handle (and allow sharing of the handle
850 * across threads).
851 *
852 * \param hObj Object handle
853 * \return Pointer to the object.
854 *
855 * \note Process can only get pointer to the objects it created or global objects.
856 *
857 * \todo Get rid of the ObjectType parameter!
858 */
859 PGDIOBJ INTERNAL_CALL
860 #ifdef GDI_DEBUG
861 GDIOBJ_ShareLockObjDbg (const char* file, int line, HGDIOBJ hObj, DWORD ObjectType)
862 #else /* !GDI_DEBUG */
863 GDIOBJ_ShareLockObj (HGDIOBJ hObj, DWORD ObjectType)
864 #endif /* GDI_DEBUG */
865 {
866 USHORT HandleIndex;
867 PGDI_TABLE_ENTRY HandleEntry;
868 HANDLE ProcessId, HandleProcessId, LockedProcessId, PrevProcId;
869 PGDIOBJ Object = NULL;
870
871 HandleIndex = GDI_HANDLE_GET_INDEX(hObj);
872
873 /* Check that the handle index is valid. */
874 if (HandleIndex >= GDI_HANDLE_COUNT)
875 return NULL;
876
877 HandleEntry = &HandleTable->Entries[HandleIndex];
878
879 ProcessId = (HANDLE)((ULONG_PTR)PsGetCurrentProcessId() & ~1);
880 HandleProcessId = (HANDLE)((ULONG_PTR)HandleEntry->ProcessId & ~1);
881
882 /* Check for invalid owner. */
883 if (ProcessId != HandleProcessId && HandleProcessId != NULL)
884 {
885 return NULL;
886 }
887
888 /*
889 * Prevent the thread from being terminated during the locking process.
890 * It would result in undesired effects and inconsistency of the global
891 * handle table.
892 */
893
894 KeEnterCriticalRegion();
895
896 /*
897 * Loop until we either successfully lock the handle entry & object or
898 * fail some of the check.
899 */
900
901 for (;;)
902 {
903 /* Lock the handle table entry. */
904 LockedProcessId = (HANDLE)((ULONG_PTR)HandleProcessId | 0x1);
905 PrevProcId = InterlockedCompareExchangePointer(&HandleEntry->ProcessId,
906 LockedProcessId,
907 HandleProcessId);
908
909 if (PrevProcId == HandleProcessId)
910 {
911 LONG HandleType = HandleEntry->Type << 16;
912
913 /*
914 * We're locking an object that belongs to our process or it's a
915 * global object if HandleProcessId is 0 here.
916 */
917
918 /* FIXME: Check the upper 16-bits of handle number! */
919 if (HandleType != 0 && HandleEntry->KernelData != NULL &&
920 (ObjectType == GDI_OBJECT_TYPE_DONTCARE ||
921 HandleType == ObjectType))
922 {
923 PGDIOBJHDR GdiHdr = GDIBdyToHdr(HandleEntry->KernelData);
924
925 #ifdef GDI_DEBUG
926 if (InterlockedIncrement((PLONG)&GdiHdr->Locks) == 1)
927 {
928 GdiHdr->lockfile = file;
929 GdiHdr->lockline = line;
930 }
931 #else
932 InterlockedIncrement((PLONG)&GdiHdr->Locks);
933 #endif
934 Object = HandleEntry->KernelData;
935 }
936 else
937 {
938 /*
939 * Debugging code. Report attempts to lock deleted handles and
940 * locking type mismatches.
941 */
942
943 if ((HandleType & ~GDI_HANDLE_REUSE_MASK) == 0)
944 {
945 DPRINT1("Attempted to lock object 0x%x that is deleted!\n", hObj);
946 #ifdef GDI_DEBUG
947 KeRosDumpStackFrames(NULL, 20);
948 #endif
949 }
950 else
951 {
952 DPRINT1("Attempted to lock object 0x%x, type mismatch (0x%x : 0x%x)\n",
953 hObj, HandleType & ~GDI_HANDLE_REUSE_MASK, ObjectType & ~GDI_HANDLE_REUSE_MASK);
954 #ifdef GDI_DEBUG
955 KeRosDumpStackFrames(NULL, 20);
956 #endif
957 }
958 #ifdef GDI_DEBUG
959 DPRINT1("-> called from %s:%i\n", file, line);
960 #endif
961 }
962
963 /* Unlock the handle table entry. */
964 InterlockedExchangePointer(&HandleEntry->ProcessId, PrevProcId);
965
966 break;
967 }
968 else
969 {
970 /*
971 * The handle is currently locked, wait some time and try again.
972 */
973
974 DelayExecution();
975 continue;
976 }
977 }
978
979 KeLeaveCriticalRegion();
980
981 return Object;
982 }
983
984
985 /*!
986 * Release GDI object. Every object locked by GDIOBJ_LockObj() must be unlocked. You should unlock the object
987 * as soon as you don't need to have access to it's data.
988
989 * \param Object Object pointer (as returned by GDIOBJ_LockObj).
990 */
991 VOID INTERNAL_CALL
992 GDIOBJ_UnlockObjByPtr(PGDIOBJ Object)
993 {
994 PGDIOBJHDR GdiHdr = GDIBdyToHdr(Object);
995 #ifdef GDI_DEBUG
996 if (InterlockedDecrement((PLONG)&GdiHdr->Locks) == 0)
997 {
998 GdiHdr->lockfile = NULL;
999 GdiHdr->lockline = 0;
1000 }
1001 #else
1002 InterlockedDecrement((PLONG)&GdiHdr->Locks);
1003 #endif
1004 }
1005
1006
1007 BOOL INTERNAL_CALL
1008 GDIOBJ_OwnedByCurrentProcess(HGDIOBJ ObjectHandle)
1009 {
1010 PGDI_TABLE_ENTRY Entry;
1011 HANDLE ProcessId;
1012 BOOL Ret;
1013
1014 DPRINT("GDIOBJ_OwnedByCurrentProcess: ObjectHandle: 0x%08x\n", ObjectHandle);
1015
1016 if(!GDI_HANDLE_IS_STOCKOBJ(ObjectHandle))
1017 {
1018 ProcessId = PsGetCurrentProcessId();
1019
1020 Entry = GDI_HANDLE_GET_ENTRY(HandleTable, ObjectHandle);
1021 Ret = Entry->KernelData != NULL &&
1022 (Entry->Type & ~GDI_HANDLE_REUSE_MASK) != 0 &&
1023 (HANDLE)((ULONG_PTR)Entry->ProcessId & ~0x1) == ProcessId;
1024
1025 return Ret;
1026 }
1027
1028 return FALSE;
1029 }
1030
1031 BOOL INTERNAL_CALL
1032 GDIOBJ_ConvertToStockObj(HGDIOBJ *hObj)
1033 {
1034 /*
1035 * FIXME !!!!! THIS FUNCTION NEEDS TO BE FIXED - IT IS NOT SAFE WHEN OTHER THREADS
1036 * MIGHT ATTEMPT TO LOCK THE OBJECT DURING THIS CALL!!!
1037 */
1038 PGDI_TABLE_ENTRY Entry;
1039 HANDLE ProcessId, LockedProcessId, PrevProcId;
1040 PETHREAD Thread;
1041 #ifdef GDI_DEBUG
1042 ULONG Attempts = 0;
1043 #endif
1044
1045 ASSERT(hObj);
1046
1047 DPRINT("GDIOBJ_ConvertToStockObj: hObj: 0x%08x\n", *hObj);
1048
1049 Thread = PsGetCurrentThread();
1050
1051 if(!GDI_HANDLE_IS_STOCKOBJ(*hObj))
1052 {
1053 ProcessId = PsGetCurrentProcessId();
1054 LockedProcessId = (HANDLE)((ULONG_PTR)ProcessId | 0x1);
1055
1056 Entry = GDI_HANDLE_GET_ENTRY(HandleTable, *hObj);
1057
1058 LockHandle:
1059 /* lock the object, we must not convert stock objects, so don't check!!! */
1060 PrevProcId = InterlockedCompareExchangePointer(&Entry->ProcessId, LockedProcessId, ProcessId);
1061 if(PrevProcId == ProcessId)
1062 {
1063 LONG NewType, PrevType, OldType;
1064
1065 /* we're locking an object that belongs to our process. First calculate
1066 the new object type including the stock object flag and then try to
1067 exchange it.*/
1068 NewType = GDI_HANDLE_GET_TYPE(*hObj);
1069 NewType |= NewType >> 16;
1070 NewType |= (ULONG_PTR)(*hObj) & GDI_HANDLE_REUSE_MASK;
1071
1072 /* This is the type that the object should have right now, save it */
1073 OldType = NewType;
1074 /* As the object should be a stock object, set it's flag, but only in the upper 16 bits */
1075 NewType |= GDI_HANDLE_STOCK_MASK;
1076
1077 /* Try to exchange the type field - but only if the old (previous type) matches! */
1078 PrevType = InterlockedCompareExchange(&Entry->Type, NewType, OldType);
1079 if(PrevType == OldType && Entry->KernelData != NULL)
1080 {
1081 PETHREAD PrevThread;
1082 PGDIOBJHDR GdiHdr;
1083
1084 /* We successfully set the stock object flag.
1085 KernelData should never be NULL here!!! */
1086 ASSERT(Entry->KernelData);
1087
1088 GdiHdr = GDIBdyToHdr(Entry->KernelData);
1089
1090 PrevThread = GdiHdr->LockingThread;
1091 if(GdiHdr->Locks == 0 || PrevThread == Thread)
1092 {
1093 /* dereference the process' object counter */
1094 if(PrevProcId != GDI_GLOBAL_PROCESS)
1095 {
1096 PEPROCESS OldProcess;
1097 PW32PROCESS W32Process;
1098 NTSTATUS Status;
1099
1100 /* FIXME */
1101 Status = PsLookupProcessByProcessId((HANDLE)((ULONG_PTR)PrevProcId & ~0x1), &OldProcess);
1102 if(NT_SUCCESS(Status))
1103 {
1104 W32Process = (PW32PROCESS)OldProcess->Win32Process;
1105 if(W32Process != NULL)
1106 {
1107 InterlockedDecrement(&W32Process->GDIObjects);
1108 }
1109 ObDereferenceObject(OldProcess);
1110 }
1111 }
1112
1113 /* remove the process id lock and make it global */
1114 InterlockedExchangePointer(&Entry->ProcessId, GDI_GLOBAL_PROCESS);
1115
1116 *hObj = (HGDIOBJ)((ULONG)(*hObj) | GDI_HANDLE_STOCK_MASK);
1117
1118 /* we're done, successfully converted the object */
1119 return TRUE;
1120 }
1121 else
1122 {
1123 #ifdef GDI_DEBUG
1124 if(++Attempts > 20)
1125 {
1126 if(GdiHdr->lockfile != NULL)
1127 {
1128 DPRINT1("[%d]Locked %s:%i by 0x%x (we're 0x%x)\n", Attempts, GdiHdr->lockfile, GdiHdr->lockline, PrevThread, Thread);
1129 }
1130 }
1131 #endif
1132 /* WTF?! The object is already locked by a different thread!
1133 Release the lock, wait a bit and try again!
1134 FIXME - we should give up after some time unless we want to wait forever! */
1135 InterlockedExchangePointer(&Entry->ProcessId, PrevProcId);
1136
1137 DelayExecution();
1138 goto LockHandle;
1139 }
1140 }
1141 else
1142 {
1143 DPRINT1("Attempted to convert object 0x%x that is deleted! Should never get here!!!\n", hObj);
1144 }
1145 }
1146 else if(PrevProcId == LockedProcessId)
1147 {
1148 #ifdef GDI_DEBUG
1149 if(++Attempts > 20)
1150 {
1151 DPRINT1("[%d]Waiting on 0x%x\n", Attempts, hObj);
1152 }
1153 #endif
1154 /* the object is currently locked, wait some time and try again.
1155 FIXME - we shouldn't loop forever! Give up after some time! */
1156 DelayExecution();
1157 /* try again */
1158 goto LockHandle;
1159 }
1160 else
1161 {
1162 DPRINT1("Attempted to convert invalid handle: 0x%x\n", hObj);
1163 }
1164 }
1165
1166 return FALSE;
1167 }
1168
1169 void INTERNAL_CALL
1170 GDIOBJ_SetOwnership(HGDIOBJ ObjectHandle, PEPROCESS NewOwner)
1171 {
1172 PGDI_TABLE_ENTRY Entry;
1173 HANDLE ProcessId, LockedProcessId, PrevProcId;
1174 PETHREAD Thread;
1175 #ifdef GDI_DEBUG
1176 ULONG Attempts = 0;
1177 #endif
1178
1179 DPRINT("GDIOBJ_SetOwnership: hObj: 0x%x, NewProcess: 0x%x\n", ObjectHandle, (NewOwner ? PsGetProcessId(NewOwner) : 0));
1180
1181 Thread = PsGetCurrentThread();
1182
1183 if(!GDI_HANDLE_IS_STOCKOBJ(ObjectHandle))
1184 {
1185 ProcessId = PsGetCurrentProcessId();
1186 LockedProcessId = (HANDLE)((ULONG_PTR)ProcessId | 0x1);
1187
1188 Entry = GDI_HANDLE_GET_ENTRY(HandleTable, ObjectHandle);
1189
1190 LockHandle:
1191 /* lock the object, we must not convert stock objects, so don't check!!! */
1192 PrevProcId = InterlockedCompareExchangePointer(&Entry->ProcessId, ProcessId, LockedProcessId);
1193 if(PrevProcId == ProcessId)
1194 {
1195 PETHREAD PrevThread;
1196
1197 if((Entry->Type & ~GDI_HANDLE_REUSE_MASK) != 0 && Entry->KernelData != NULL)
1198 {
1199 PGDIOBJHDR GdiHdr = GDIBdyToHdr(Entry->KernelData);
1200
1201 PrevThread = GdiHdr->LockingThread;
1202 if(GdiHdr->Locks == 0 || PrevThread == Thread)
1203 {
1204 PEPROCESS OldProcess;
1205 PW32PROCESS W32Process;
1206 NTSTATUS Status;
1207
1208 /* dereference the process' object counter */
1209 /* FIXME */
1210 if((ULONG_PTR)PrevProcId & ~0x1)
1211 {
1212 Status = PsLookupProcessByProcessId((HANDLE)((ULONG_PTR)PrevProcId & ~0x1), &OldProcess);
1213 if(NT_SUCCESS(Status))
1214 {
1215 W32Process = (PW32PROCESS)OldProcess->Win32Process;
1216 if(W32Process != NULL)
1217 {
1218 InterlockedDecrement(&W32Process->GDIObjects);
1219 }
1220 ObDereferenceObject(OldProcess);
1221 }
1222 }
1223
1224 if(NewOwner != NULL)
1225 {
1226 ProcessId = PsGetProcessId(NewOwner);
1227
1228 /* Increase the new process' object counter */
1229 W32Process = (PW32PROCESS)NewOwner->Win32Process;
1230 if(W32Process != NULL)
1231 {
1232 InterlockedIncrement(&W32Process->GDIObjects);
1233 }
1234 }
1235 else
1236 ProcessId = 0;
1237
1238 /* remove the process id lock and change it to the new process id */
1239 InterlockedExchangePointer(&Entry->ProcessId, ProcessId);
1240
1241 /* we're done! */
1242 return;
1243 }
1244 else
1245 {
1246 #ifdef GDI_DEBUG
1247 if(++Attempts > 20)
1248 {
1249 if(GdiHdr->lockfile != NULL)
1250 {
1251 DPRINT1("[%d]Locked from %s:%i by 0x%x (we're 0x%x)\n", Attempts, GdiHdr->lockfile, GdiHdr->lockline, PrevThread, Thread);
1252 }
1253 }
1254 #endif
1255 /* WTF?! The object is already locked by a different thread!
1256 Release the lock, wait a bit and try again! DO reset the pid lock
1257 so we make sure we don't access invalid memory in case the object is
1258 being deleted in the meantime (because we don't have aquired a reference
1259 at this point).
1260 FIXME - we should give up after some time unless we want to wait forever! */
1261 InterlockedExchangePointer(&Entry->ProcessId, PrevProcId);
1262
1263 DelayExecution();
1264 goto LockHandle;
1265 }
1266 }
1267 else
1268 {
1269 DPRINT1("Attempted to change ownership of an object 0x%x currently being destroyed!!!\n", ObjectHandle);
1270 }
1271 }
1272 else if(PrevProcId == LockedProcessId)
1273 {
1274 #ifdef GDI_DEBUG
1275 if(++Attempts > 20)
1276 {
1277 DPRINT1("[%d]Waiting on 0x%x\n", Attempts, ObjectHandle);
1278 }
1279 #endif
1280 /* the object is currently locked, wait some time and try again.
1281 FIXME - we shouldn't loop forever! Give up after some time! */
1282 DelayExecution();
1283 /* try again */
1284 goto LockHandle;
1285 }
1286 else if(((ULONG_PTR)PrevProcId & ~0x1) == 0)
1287 {
1288 /* allow changing ownership of global objects */
1289 ProcessId = NULL;
1290 LockedProcessId = (HANDLE)((ULONG_PTR)ProcessId | 0x1);
1291 goto LockHandle;
1292 }
1293 else if((HANDLE)((ULONG_PTR)PrevProcId & ~0x1) != PsGetCurrentProcessId())
1294 {
1295 DPRINT1("Attempted to change ownership of object 0x%x (pid: 0x%x) from pid 0x%x!!!\n", ObjectHandle, (ULONG_PTR)PrevProcId & ~0x1, PsGetCurrentProcessId());
1296 }
1297 else
1298 {
1299 DPRINT1("Attempted to change owner of invalid handle: 0x%x\n", ObjectHandle);
1300 }
1301 }
1302 }
1303
1304 void INTERNAL_CALL
1305 GDIOBJ_CopyOwnership(HGDIOBJ CopyFrom, HGDIOBJ CopyTo)
1306 {
1307 PGDI_TABLE_ENTRY FromEntry;
1308 PETHREAD Thread;
1309 HANDLE FromProcessId, FromLockedProcessId, FromPrevProcId;
1310 #ifdef GDI_DEBUG
1311 ULONG Attempts = 0;
1312 #endif
1313
1314 DPRINT("GDIOBJ_CopyOwnership: from: 0x%x, to: 0x%x\n", CopyFrom, CopyTo);
1315
1316 Thread = PsGetCurrentThread();
1317
1318 if(!GDI_HANDLE_IS_STOCKOBJ(CopyFrom) && !GDI_HANDLE_IS_STOCKOBJ(CopyTo))
1319 {
1320 FromEntry = GDI_HANDLE_GET_ENTRY(HandleTable, CopyFrom);
1321
1322 FromProcessId = (HANDLE)((ULONG_PTR)FromEntry->ProcessId & ~0x1);
1323 FromLockedProcessId = (HANDLE)((ULONG_PTR)FromProcessId | 0x1);
1324
1325 LockHandleFrom:
1326 /* lock the object, we must not convert stock objects, so don't check!!! */
1327 FromPrevProcId = InterlockedCompareExchangePointer(&FromEntry->ProcessId, FromProcessId, FromLockedProcessId);
1328 if(FromPrevProcId == FromProcessId)
1329 {
1330 PETHREAD PrevThread;
1331 PGDIOBJHDR GdiHdr;
1332
1333 if((FromEntry->Type & ~GDI_HANDLE_REUSE_MASK) != 0 && FromEntry->KernelData != NULL)
1334 {
1335 GdiHdr = GDIBdyToHdr(FromEntry->KernelData);
1336
1337 /* save the pointer to the calling thread so we know it was this thread
1338 that locked the object */
1339 PrevThread = GdiHdr->LockingThread;
1340 if(GdiHdr->Locks == 0 || PrevThread == Thread)
1341 {
1342 /* now let's change the ownership of the target object */
1343
1344 if(((ULONG_PTR)FromPrevProcId & ~0x1) != 0)
1345 {
1346 PEPROCESS ProcessTo;
1347 /* FIXME */
1348 if(NT_SUCCESS(PsLookupProcessByProcessId((HANDLE)((ULONG_PTR)FromPrevProcId & ~0x1), &ProcessTo)))
1349 {
1350 GDIOBJ_SetOwnership(CopyTo, ProcessTo);
1351 ObDereferenceObject(ProcessTo);
1352 }
1353 }
1354 else
1355 {
1356 /* mark the object as global */
1357 GDIOBJ_SetOwnership(CopyTo, NULL);
1358 }
1359
1360 InterlockedExchangePointer(&FromEntry->ProcessId, FromPrevProcId);
1361 }
1362 else
1363 {
1364 #ifdef GDI_DEBUG
1365 if(++Attempts > 20)
1366 {
1367 if(GdiHdr->lockfile != NULL)
1368 {
1369 DPRINT1("[%d]Locked from %s:%i by 0x%x (we're 0x%x)\n", Attempts, GdiHdr->lockfile, GdiHdr->lockline, PrevThread, Thread);
1370 }
1371 }
1372 #endif
1373 /* WTF?! The object is already locked by a different thread!
1374 Release the lock, wait a bit and try again! DO reset the pid lock
1375 so we make sure we don't access invalid memory in case the object is
1376 being deleted in the meantime (because we don't have aquired a reference
1377 at this point).
1378 FIXME - we should give up after some time unless we want to wait forever! */
1379 InterlockedExchangePointer(&FromEntry->ProcessId, FromPrevProcId);
1380
1381 DelayExecution();
1382 goto LockHandleFrom;
1383 }
1384 }
1385 else
1386 {
1387 DPRINT1("Attempted to copy ownership from an object 0x%x currently being destroyed!!!\n", CopyFrom);
1388 }
1389 }
1390 else if(FromPrevProcId == FromLockedProcessId)
1391 {
1392 #ifdef GDI_DEBUG
1393 if(++Attempts > 20)
1394 {
1395 DPRINT1("[%d]Waiting on 0x%x\n", Attempts, CopyFrom);
1396 }
1397 #endif
1398 /* the object is currently locked, wait some time and try again.
1399 FIXME - we shouldn't loop forever! Give up after some time! */
1400 DelayExecution();
1401 /* try again */
1402 goto LockHandleFrom;
1403 }
1404 else if((HANDLE)((ULONG_PTR)FromPrevProcId & ~0x1) != PsGetCurrentProcessId())
1405 {
1406 /* FIXME - should we really allow copying ownership from objects that we don't even own? */
1407 DPRINT1("WARNING! Changing copying ownership of object 0x%x (pid: 0x%x) to pid 0x%x!!!\n", CopyFrom, (ULONG_PTR)FromPrevProcId & ~0x1, PsGetCurrentProcessId());
1408 FromProcessId = (HANDLE)((ULONG_PTR)FromPrevProcId & ~0x1);
1409 FromLockedProcessId = (HANDLE)((ULONG_PTR)FromProcessId | 0x1);
1410 goto LockHandleFrom;
1411 }
1412 else
1413 {
1414 DPRINT1("Attempted to copy ownership from invalid handle: 0x%x\n", CopyFrom);
1415 }
1416 }
1417 }
1418
1419 PVOID INTERNAL_CALL
1420 GDI_MapHandleTable(PEPROCESS Process)
1421 {
1422 ULONG TableSize = sizeof(HandleTable->Entries);
1423 PVOID MappedGdiTable = NULL; /* FIXME - try preferred GDI_HANDLE_TABLE_BASE_ADDRESS? */
1424 NTSTATUS Status = IntUserMapSharedSection(SessionSharedSectionPool,
1425 Process,
1426 HandleTable,
1427 NULL,
1428 &MappedGdiTable,
1429 &TableSize,
1430 TRUE);
1431 if(NT_SUCCESS(Status))
1432 {
1433 return MappedGdiTable;
1434 }
1435
1436 return NULL;
1437 }
1438
1439 /* EOF */