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