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