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