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