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