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