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