- Rename GDIOBJ_AllocObj to GDIOBJ_allocObjWithHandle and make it return a pointer...
[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, REGION_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 GDIDBG_TRACECALLER();
365 }
366
367 ULONG
368 FASTCALL
369 InterlockedPopFreeEntry()
370 {
371 ULONG idxFirstFree, idxNextFree, idxPrev;
372 PGDI_TABLE_ENTRY pFreeEntry;
373
374 DPRINT("Enter InterLockedPopFreeEntry\n");
375
376 do
377 {
378 idxFirstFree = GdiHandleTable->FirstFree;
379 if (idxFirstFree)
380 {
381 pFreeEntry = GdiHandleTable->Entries + idxFirstFree;
382 ASSERT(((ULONG)pFreeEntry->KernelData & ~GDI_HANDLE_INDEX_MASK) == 0);
383 idxNextFree = (ULONG)pFreeEntry->KernelData;
384 idxPrev = (ULONG)_InterlockedCompareExchange((LONG*)&GdiHandleTable->FirstFree, idxNextFree, idxFirstFree);
385 }
386 else
387 {
388 idxFirstFree = GdiHandleTable->FirstUnused;
389 idxNextFree = idxFirstFree + 1;
390 if (idxNextFree >= GDI_HANDLE_COUNT)
391 {
392 DPRINT1("No more gdi handles left!\n");
393 return 0;
394 }
395 idxPrev = (ULONG)_InterlockedCompareExchange((LONG*)&GdiHandleTable->FirstUnused, idxNextFree, idxFirstFree);
396 }
397 }
398 while (idxPrev != idxFirstFree);
399
400 return idxFirstFree;
401 }
402
403 /* Pushes an entry of the handle table to the free list,
404 The entry must be unlocked and the base type field must be 0 */
405 VOID
406 FASTCALL
407 InterlockedPushFreeEntry(ULONG idxToFree)
408 {
409 ULONG idxFirstFree, idxPrev;
410 PGDI_TABLE_ENTRY pFreeEntry;
411
412 DPRINT("Enter InterlockedPushFreeEntry\n");
413
414 pFreeEntry = GdiHandleTable->Entries + idxToFree;
415 ASSERT((pFreeEntry->Type & GDI_ENTRY_BASETYPE_MASK) == 0);
416 ASSERT(pFreeEntry->ProcessId == 0);
417 pFreeEntry->UserData = NULL;
418
419 do
420 {
421 idxFirstFree = GdiHandleTable->FirstFree;
422 pFreeEntry->KernelData = (PVOID)idxFirstFree;
423
424 idxPrev = (ULONG)_InterlockedCompareExchange((LONG*)&GdiHandleTable->FirstFree, idxToFree, idxFirstFree);
425 }
426 while (idxPrev != idxFirstFree);
427 }
428
429
430 BOOL
431 INTERNAL_CALL
432 GDIOBJ_ValidateHandle(HGDIOBJ hObj, ULONG ObjectType)
433 {
434 PGDI_TABLE_ENTRY Entry = GDI_HANDLE_GET_ENTRY(GdiHandleTable, hObj);
435 if ((((ULONG_PTR)hObj & GDI_HANDLE_TYPE_MASK) == ObjectType) &&
436 (Entry->Type << GDI_ENTRY_UPPER_SHIFT) == GDI_HANDLE_GET_UPPER(hObj))
437 {
438 HANDLE pid = (HANDLE)((ULONG_PTR)Entry->ProcessId & ~0x1);
439 if (pid == NULL || pid == PsGetCurrentProcessId())
440 {
441 return TRUE;
442 }
443 }
444 return FALSE;
445 }
446
447 POBJ INTERNAL_CALL
448 GDIOBJ_AllocObj(UCHAR BaseType)
449 {
450 POBJ pObject;
451
452 ASSERT((BaseType & ~GDIObjTypeTotal) == 0);
453 // BaseType &= GDI_HANDLE_BASETYPE_MASK;
454
455 if (ObjTypeInfo[BaseType].bUseLookaside)
456 {
457 PPAGED_LOOKASIDE_LIST LookasideList;
458
459 LookasideList = GdiHandleTable->LookasideLists + BaseType;
460 pObject = ExAllocateFromPagedLookasideList(LookasideList);
461 }
462 else
463 {
464 pObject = ExAllocatePoolWithTag(PagedPool,
465 ObjTypeInfo[BaseType].ulBodySize,
466 ObjTypeInfo[BaseType].Tag);
467 }
468
469 if (pObject)
470 {
471 RtlZeroMemory(pObject, ObjTypeInfo[BaseType].ulBodySize);
472 }
473
474 return pObject;
475 }
476
477
478 /*!
479 * Allocate memory for GDI object and return handle to it.
480 *
481 * \param ObjectType - type of object \ref GDI object types
482 *
483 * \return Pointer to the allocated object, which is locked.
484 */
485 POBJ INTERNAL_CALL
486 GDIOBJ_AllocObjWithHandle(ULONG ObjectType)
487 {
488 PW32PROCESS W32Process;
489 POBJ newObject = NULL;
490 HANDLE CurrentProcessId, LockedProcessId;
491 UCHAR TypeIndex;
492 #ifdef GDI_DEBUG
493 ULONG Attempts = 0;
494 #endif
495
496 W32Process = PsGetCurrentProcessWin32Process();
497 /* HACK HACK HACK: simplest-possible quota implementation - don't allow a process
498 to take too many GDI objects, itself. */
499 if (W32Process && W32Process->GDIObjects >= 0x2710)
500 return NULL;
501
502 ASSERT(ObjectType != GDI_OBJECT_TYPE_DONTCARE);
503
504 TypeIndex = GDI_OBJECT_GET_TYPE_INDEX(ObjectType);
505
506 newObject = GDIOBJ_AllocObj(TypeIndex);
507 if (!newObject)
508 {
509 DPRINT1("Not enough memory to allocate gdi object!\n");
510 return NULL;
511 }
512
513 UINT Index;
514 PGDI_TABLE_ENTRY Entry;
515 LONG TypeInfo;
516
517 CurrentProcessId = PsGetCurrentProcessId();
518 LockedProcessId = (HANDLE)((ULONG_PTR)CurrentProcessId | 0x1);
519
520 // RtlZeroMemory(newObject, GetObjectSize(TypeIndex));
521
522 /* On Windows the higher 16 bit of the type field don't contain the
523 full type from the handle, but the base type.
524 (type = BRSUH, PEN, EXTPEN, basetype = BRUSH) */
525 TypeInfo = (ObjectType & GDI_HANDLE_BASETYPE_MASK) | (ObjectType >> GDI_ENTRY_UPPER_SHIFT);
526
527 Index = InterlockedPopFreeEntry();
528 if (Index != 0)
529 {
530 HANDLE PrevProcId;
531
532 Entry = &GdiHandleTable->Entries[Index];
533
534 LockHandle:
535 PrevProcId = _InterlockedCompareExchangePointer((PVOID*)&Entry->ProcessId, LockedProcessId, 0);
536 if (PrevProcId == NULL)
537 {
538 PW32THREAD Thread = PsGetCurrentThreadWin32Thread();
539 HGDIOBJ Handle;
540
541 Entry->KernelData = newObject;
542
543 /* copy the reuse-counter */
544 TypeInfo |= Entry->Type & GDI_ENTRY_REUSE_MASK;
545
546 /* we found a free entry, no need to exchange this field atomically
547 since we're holding the lock */
548 Entry->Type = TypeInfo;
549
550 /* Create a handle */
551 Handle = (HGDIOBJ)((Index & 0xFFFF) | (TypeInfo << GDI_ENTRY_UPPER_SHIFT));
552
553 /* Initialize BaseObject fields */
554 newObject->hHmgr = Handle;
555 newObject->ulShareCount = 0;
556 newObject->cExclusiveLock = 1;
557 newObject->Tid = Thread;
558
559 /* unlock the entry */
560 (void)_InterlockedExchangePointer((PVOID*)&Entry->ProcessId, CurrentProcessId);
561
562 GDIDBG_CAPTUREALLOCATOR(Index);
563
564 if (W32Process != NULL)
565 {
566 _InterlockedIncrement(&W32Process->GDIObjects);
567 }
568
569 DPRINT("GDIOBJ_AllocObj: 0x%x ob: 0x%x\n", Handle, newObject);
570 return newObject;
571 }
572 else
573 {
574 #ifdef GDI_DEBUG
575 if (++Attempts > 20)
576 {
577 DPRINT1("[%d]Waiting on handle in index 0x%x\n", Attempts, Index);
578 }
579 #endif
580 /* damn, someone is trying to lock the object even though it doesn't
581 even exist anymore, wait a little and try again!
582 FIXME - we shouldn't loop forever! Give up after some time! */
583 DelayExecution();
584 /* try again */
585 goto LockHandle;
586 }
587 }
588
589 GDIOBJ_FreeObj(newObject, TypeIndex);
590
591 DPRINT1("Failed to insert gdi object into the handle table, no handles left!\n");
592 GDIDBG_DUMPHANDLETABLE();
593
594 return NULL;
595 }
596
597 /* Wrapper for compatibility with old calls, will be removed later */
598 HGDIOBJ INTERNAL_CALL
599 GDIOBJ_AllocObjDepricated(ULONG ObjectType)
600 {
601 POBJ pObject;
602 HGDIOBJ hObject;
603
604 pObject = GDIOBJ_AllocObjWithHandle(ObjectType);
605 hObject = pObject->hHmgr;
606 GDIOBJ_UnlockObjByPtr(pObject);
607
608 return hObject;
609 }
610
611
612 VOID INTERNAL_CALL
613 GDIOBJ_FreeObj(POBJ pObject, UCHAR BaseType)
614 {
615 /* Object must not have a handle! */
616 ASSERT(pObject->hHmgr == NULL);
617
618 if (ObjTypeInfo[BaseType].bUseLookaside)
619 {
620 PPAGED_LOOKASIDE_LIST LookasideList;
621
622 LookasideList = GdiHandleTable->LookasideLists + BaseType;
623 ExFreeToPagedLookasideList(LookasideList, pObject);
624 }
625 else
626 {
627 ExFreePool(pObject);
628 }
629 }
630
631 /*!
632 * Free memory allocated for the GDI object. For each object type this function calls the
633 * appropriate cleanup routine.
634 *
635 * \param hObj - handle of the object to be deleted.
636 *
637 * \return Returns TRUE if succesful.
638 * \return Returns FALSE if the cleanup routine returned FALSE or the object doesn't belong
639 * to the calling process.
640 */
641 BOOL INTERNAL_CALL
642 GDIOBJ_FreeObjByHandle(HGDIOBJ hObj, DWORD ExpectedType)
643 {
644 PGDI_TABLE_ENTRY Entry;
645 HANDLE ProcessId, LockedProcessId, PrevProcId;
646 ULONG HandleType, HandleUpper, TypeIndex;
647 BOOL Silent;
648 #ifdef GDI_DEBUG
649 ULONG Attempts = 0;
650 #endif
651
652 DPRINT("GDIOBJ_FreeObj: hObj: 0x%08x\n", hObj);
653
654 if (GDI_HANDLE_IS_STOCKOBJ(hObj))
655 {
656 DPRINT1("GDIOBJ_FreeObj() failed, can't delete stock object handle: 0x%x !!!\n", hObj);
657 GDIDBG_TRACECALLER();
658 return FALSE;
659 }
660
661 ProcessId = PsGetCurrentProcessId();
662 LockedProcessId = (HANDLE)((ULONG_PTR)ProcessId | 0x1);
663
664 Silent = (ExpectedType & GDI_OBJECT_TYPE_SILENT);
665 ExpectedType &= ~GDI_OBJECT_TYPE_SILENT;
666
667 HandleType = GDI_HANDLE_GET_TYPE(hObj);
668 HandleUpper = GDI_HANDLE_GET_UPPER(hObj);
669
670 /* Check if we have the requested type */
671 if ( (ExpectedType != GDI_OBJECT_TYPE_DONTCARE &&
672 HandleType != ExpectedType) ||
673 HandleType == 0 )
674 {
675 DPRINT1("Attempted to free object 0x%x of wrong type (Handle: 0x%x, expected: 0x%x)\n",
676 hObj, HandleType, ExpectedType);
677 GDIDBG_TRACECALLER();
678 return FALSE;
679 }
680
681 Entry = GDI_HANDLE_GET_ENTRY(GdiHandleTable, hObj);
682
683 LockHandle:
684 /* lock the object, we must not delete global objects, so don't exchange the locking
685 process ID to zero when attempting to lock a global object... */
686 PrevProcId = _InterlockedCompareExchangePointer((PVOID*)&Entry->ProcessId, LockedProcessId, ProcessId);
687 if (PrevProcId == ProcessId)
688 {
689 if ( (Entry->KernelData != NULL) &&
690 ((Entry->Type << GDI_ENTRY_UPPER_SHIFT) == HandleUpper) &&
691 ((Entry->Type & GDI_ENTRY_BASETYPE_MASK) == (HandleUpper & GDI_ENTRY_BASETYPE_MASK)) )
692 {
693 POBJ Object;
694
695 Object = Entry->KernelData;
696
697 if (Object->cExclusiveLock == 0)
698 {
699 BOOL Ret;
700 PW32PROCESS W32Process = PsGetCurrentProcessWin32Process();
701
702 /* Clear the basetype field so when unlocking the handle it gets finally deleted and increment reuse counter */
703 Entry->Type = (Entry->Type + GDI_ENTRY_REUSE_INC) & ~GDI_ENTRY_BASETYPE_MASK;
704
705 /* unlock the handle slot */
706 (void)_InterlockedExchangePointer((PVOID*)&Entry->ProcessId, NULL);
707
708 /* push this entry to the free list */
709 InterlockedPushFreeEntry(GDI_ENTRY_TO_INDEX(GdiHandleTable, Entry));
710
711 Object->hHmgr = NULL;
712
713 if (W32Process != NULL)
714 {
715 _InterlockedDecrement(&W32Process->GDIObjects);
716 }
717
718 /* call the cleanup routine. */
719 TypeIndex = GDI_OBJECT_GET_TYPE_INDEX(HandleType);
720 Ret = RunCleanupCallback(Object, TypeIndex);
721
722 /* Now it's time to free the memory */
723 GDIOBJ_FreeObj(Object, TypeIndex);
724
725 return Ret;
726 }
727 else
728 {
729 /*
730 * The object is currently locked, so freeing is forbidden!
731 */
732 DPRINT1("Object->cExclusiveLock = %d\n", Object->cExclusiveLock);
733 GDIDBG_TRACELOCKER(GDI_HANDLE_GET_INDEX(hObj));
734 ASSERT(FALSE);
735 }
736 }
737 else
738 {
739 LockErrorDebugOutput(hObj, Entry, "GDIOBJ_FreeObj");
740 (void)_InterlockedExchangePointer((PVOID*)&Entry->ProcessId, PrevProcId);
741 }
742 }
743 else if (PrevProcId == LockedProcessId)
744 {
745 #ifdef GDI_DEBUG
746 if (++Attempts > 20)
747 {
748 DPRINT1("[%d]Waiting on 0x%x\n", Attempts, hObj);
749 }
750 #endif
751 /* the object is currently locked, wait some time and try again.
752 FIXME - we shouldn't loop forever! Give up after some time! */
753 DelayExecution();
754 /* try again */
755 goto LockHandle;
756 }
757 else
758 {
759 if (!Silent)
760 {
761 if (((ULONG_PTR)PrevProcId & ~0x1) == 0)
762 {
763 DPRINT1("Attempted to free global gdi handle 0x%x, caller needs to get ownership first!!!\n", hObj);
764 DPRINT1("Type = 0x%lx, KernelData = 0x%p, ProcessId = 0x%p\n", Entry->Type, Entry->KernelData, Entry->ProcessId);
765 }
766 else
767 {
768 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);
769 }
770 GDIDBG_TRACECALLER();
771 GDIDBG_TRACEALLOCATOR(GDI_HANDLE_GET_INDEX(hObj));
772 }
773 }
774
775 return FALSE;
776 }
777
778 BOOL
779 FASTCALL
780 IsObjectDead(HGDIOBJ hObject)
781 {
782 INT Index = GDI_HANDLE_GET_INDEX(hObject);
783 PGDI_TABLE_ENTRY Entry = &GdiHandleTable->Entries[Index];
784 // We check to see if the objects are knocking on deaths door.
785 if ((Entry->Type & ~GDI_ENTRY_REUSE_MASK) != 0 && Entry->KernelData != NULL)
786 return FALSE;
787 else
788 {
789 DPRINT1("Object 0x%x currently being destroyed!!!\n",hObject);
790 return TRUE; // return true and move on.
791 }
792 }
793
794
795 /*!
796 * Delete GDI object
797 * \param hObject object handle
798 * \return if the function fails the returned value is FALSE.
799 */
800 BOOL
801 FASTCALL
802 NtGdiDeleteObject(HGDIOBJ hObject)
803 {
804 DPRINT("NtGdiDeleteObject handle 0x%08x\n", hObject);
805 if (!IsObjectDead(hObject))
806 {
807 return NULL != hObject
808 ? GDIOBJ_FreeObjByHandle(hObject, GDI_OBJECT_TYPE_DONTCARE) : FALSE;
809 }
810 else
811 {
812 DPRINT1("Attempt DeleteObject 0x%x currently being destroyed!!!\n",hObject);
813 return TRUE; // return true and move on.
814 }
815 }
816
817 /*!
818 * Internal function. Called when the process is destroyed to free the remaining GDI handles.
819 * \param Process - PID of the process that will be destroyed.
820 */
821 BOOL INTERNAL_CALL
822 GDI_CleanupForProcess(struct _EPROCESS *Process)
823 {
824 PGDI_TABLE_ENTRY Entry, End;
825 PEPROCESS CurrentProcess;
826 PW32PROCESS W32Process;
827 HANDLE ProcId;
828 ULONG Index = RESERVE_ENTRIES_COUNT;
829
830 DPRINT("Starting CleanupForProcess prochandle %x Pid %d\n", Process, Process->UniqueProcessId);
831 CurrentProcess = PsGetCurrentProcess();
832 if (CurrentProcess != Process)
833 {
834 KeAttachProcess(&Process->Pcb);
835 }
836 W32Process = (PW32PROCESS)Process->Win32Process;
837 ASSERT(W32Process);
838
839 if (W32Process->GDIObjects > 0)
840 {
841 /* FIXME - Instead of building the handle here and delete it using GDIOBJ_FreeObj
842 we should delete it directly here! */
843 ProcId = Process->UniqueProcessId;
844
845 End = &GdiHandleTable->Entries[GDI_HANDLE_COUNT];
846 for (Entry = &GdiHandleTable->Entries[RESERVE_ENTRIES_COUNT];
847 Entry != End;
848 Entry++, Index++)
849 {
850 /* ignore the lock bit */
851 if ( (HANDLE)((ULONG_PTR)Entry->ProcessId & ~0x1) == ProcId &&
852 (Entry->Type & ~GDI_ENTRY_REUSE_MASK) != 0 )
853 {
854 HGDIOBJ ObjectHandle;
855
856 /* Create the object handle for the entry, the lower(!) 16 bit of the
857 Type field includes the type of the object including the stock
858 object flag - but since stock objects don't have a process id we can
859 simply ignore this fact here. */
860 ObjectHandle = (HGDIOBJ)(Index | (Entry->Type << GDI_ENTRY_UPPER_SHIFT));
861
862 if (GDIOBJ_FreeObjByHandle(ObjectHandle, GDI_OBJECT_TYPE_DONTCARE) &&
863 W32Process->GDIObjects == 0)
864 {
865 /* there are no more gdi handles for this process, bail */
866 break;
867 }
868 }
869 }
870 }
871
872 if (CurrentProcess != Process)
873 {
874 KeDetachProcess();
875 }
876
877 DPRINT("Completed cleanup for process %d\n", Process->UniqueProcessId);
878
879 return TRUE;
880 }
881
882 /*!
883 * Return pointer to the object by handle.
884 *
885 * \param hObj Object handle
886 * \return Pointer to the object.
887 *
888 * \note Process can only get pointer to the objects it created or global objects.
889 *
890 * \todo Get rid of the ExpectedType parameter!
891 */
892 PGDIOBJ INTERNAL_CALL
893 GDIOBJ_LockObj(HGDIOBJ hObj, DWORD ExpectedType)
894 {
895 ULONG HandleIndex;
896 PGDI_TABLE_ENTRY Entry;
897 HANDLE ProcessId, HandleProcessId, LockedProcessId, PrevProcId;
898 POBJ Object = NULL;
899 ULONG HandleType, HandleUpper;
900
901 HandleIndex = GDI_HANDLE_GET_INDEX(hObj);
902 HandleType = GDI_HANDLE_GET_TYPE(hObj);
903 HandleUpper = GDI_HANDLE_GET_UPPER(hObj);
904
905 /* Check that the handle index is valid. */
906 if (HandleIndex >= GDI_HANDLE_COUNT)
907 return NULL;
908
909 Entry = &GdiHandleTable->Entries[HandleIndex];
910
911 /* Check if we have the requested type */
912 if ( (ExpectedType != GDI_OBJECT_TYPE_DONTCARE &&
913 HandleType != ExpectedType) ||
914 HandleType == 0 )
915 {
916 DPRINT1("Attempted to lock object 0x%x of wrong type (Handle: 0x%x, requested: 0x%x)\n",
917 hObj, HandleType, ExpectedType);
918 GDIDBG_TRACECALLER();
919 GDIDBG_TRACEALLOCATOR(GDI_HANDLE_GET_INDEX(hObj));
920 return NULL;
921 }
922
923 ProcessId = (HANDLE)((ULONG_PTR)PsGetCurrentProcessId() & ~1);
924 HandleProcessId = (HANDLE)((ULONG_PTR)Entry->ProcessId & ~1);
925
926 /* Check for invalid owner. */
927 if (ProcessId != HandleProcessId && HandleProcessId != NULL)
928 {
929 DPRINT1("Tried to lock object (0x%p) of wrong owner! ProcessId = %p, HandleProcessId = %p\n", hObj, ProcessId, HandleProcessId);
930 GDIDBG_TRACECALLER();
931 GDIDBG_TRACEALLOCATOR(GDI_HANDLE_GET_INDEX(hObj));
932 return NULL;
933 }
934
935 /*
936 * Prevent the thread from being terminated during the locking process.
937 * It would result in undesired effects and inconsistency of the global
938 * handle table.
939 */
940
941 KeEnterCriticalRegion();
942
943 /*
944 * Loop until we either successfully lock the handle entry & object or
945 * fail some of the check.
946 */
947
948 for (;;)
949 {
950 /* Lock the handle table entry. */
951 LockedProcessId = (HANDLE)((ULONG_PTR)HandleProcessId | 0x1);
952 PrevProcId = _InterlockedCompareExchangePointer((PVOID*)&Entry->ProcessId,
953 LockedProcessId,
954 HandleProcessId);
955
956 if (PrevProcId == HandleProcessId)
957 {
958 /*
959 * We're locking an object that belongs to our process or it's a
960 * global object if HandleProcessId is 0 here.
961 */
962
963 if ( (Entry->KernelData != NULL) &&
964 ((Entry->Type << GDI_ENTRY_UPPER_SHIFT) == HandleUpper) )
965 {
966 PW32THREAD Thread = PsGetCurrentThreadWin32Thread();
967 Object = Entry->KernelData;
968
969 if (Object->cExclusiveLock == 0)
970 {
971 Object->Tid = Thread;
972 Object->cExclusiveLock = 1;
973 GDIDBG_CAPTURELOCKER(GDI_HANDLE_GET_INDEX(hObj))
974 }
975 else
976 {
977 if (Object->Tid != Thread)
978 {
979 /* Unlock the handle table entry. */
980 (void)_InterlockedExchangePointer((PVOID*)&Entry->ProcessId, PrevProcId);
981
982 DelayExecution();
983 continue;
984 }
985 _InterlockedIncrement((PLONG)&Object->cExclusiveLock);
986 }
987 }
988 else
989 {
990 /*
991 * Debugging code. Report attempts to lock deleted handles and
992 * locking type mismatches.
993 */
994 LockErrorDebugOutput(hObj, Entry, "GDIOBJ_LockObj");
995 }
996
997 /* Unlock the handle table entry. */
998 (void)_InterlockedExchangePointer((PVOID*)&Entry->ProcessId, PrevProcId);
999
1000 break;
1001 }
1002 else
1003 {
1004 /*
1005 * The handle is currently locked, wait some time and try again.
1006 */
1007
1008 DelayExecution();
1009 continue;
1010 }
1011 }
1012
1013 KeLeaveCriticalRegion();
1014
1015 return Object;
1016 }
1017
1018
1019 /*!
1020 * Return pointer to the object by handle (and allow sharing of the handle
1021 * across threads).
1022 *
1023 * \param hObj Object handle
1024 * \return Pointer to the object.
1025 *
1026 * \note Process can only get pointer to the objects it created or global objects.
1027 *
1028 * \todo Get rid of the ExpectedType parameter!
1029 */
1030 PGDIOBJ INTERNAL_CALL
1031 GDIOBJ_ShareLockObj(HGDIOBJ hObj, DWORD ExpectedType)
1032 {
1033 ULONG HandleIndex;
1034 PGDI_TABLE_ENTRY Entry;
1035 HANDLE ProcessId, HandleProcessId, LockedProcessId, PrevProcId;
1036 POBJ Object = NULL;
1037 ULONG_PTR HandleType, HandleUpper;
1038
1039 HandleIndex = GDI_HANDLE_GET_INDEX(hObj);
1040 HandleType = GDI_HANDLE_GET_TYPE(hObj);
1041 HandleUpper = GDI_HANDLE_GET_UPPER(hObj);
1042
1043 /* Check that the handle index is valid. */
1044 if (HandleIndex >= GDI_HANDLE_COUNT)
1045 return NULL;
1046
1047 /* Check if we have the requested type */
1048 if ( (ExpectedType != GDI_OBJECT_TYPE_DONTCARE &&
1049 HandleType != ExpectedType) ||
1050 HandleType == 0 )
1051 {
1052 DPRINT1("Attempted to lock object 0x%x of wrong type (Handle: 0x%x, requested: 0x%x)\n",
1053 hObj, HandleType, ExpectedType);
1054 return NULL;
1055 }
1056
1057 Entry = &GdiHandleTable->Entries[HandleIndex];
1058
1059 ProcessId = (HANDLE)((ULONG_PTR)PsGetCurrentProcessId() & ~1);
1060 HandleProcessId = (HANDLE)((ULONG_PTR)Entry->ProcessId & ~1);
1061
1062 /* Check for invalid owner. */
1063 if (ProcessId != HandleProcessId && HandleProcessId != NULL)
1064 {
1065 return NULL;
1066 }
1067
1068 /*
1069 * Prevent the thread from being terminated during the locking process.
1070 * It would result in undesired effects and inconsistency of the global
1071 * handle table.
1072 */
1073
1074 KeEnterCriticalRegion();
1075
1076 /*
1077 * Loop until we either successfully lock the handle entry & object or
1078 * fail some of the check.
1079 */
1080
1081 for (;;)
1082 {
1083 /* Lock the handle table entry. */
1084 LockedProcessId = (HANDLE)((ULONG_PTR)HandleProcessId | 0x1);
1085 PrevProcId = _InterlockedCompareExchangePointer((PVOID*)&Entry->ProcessId,
1086 LockedProcessId,
1087 HandleProcessId);
1088
1089 if (PrevProcId == HandleProcessId)
1090 {
1091 /*
1092 * We're locking an object that belongs to our process or it's a
1093 * global object if HandleProcessId is 0 here.
1094 */
1095
1096 if ( (Entry->KernelData != NULL) &&
1097 (HandleUpper == (Entry->Type << GDI_ENTRY_UPPER_SHIFT)) )
1098 {
1099 Object = (POBJ)Entry->KernelData;
1100
1101 #ifdef GDI_DEBUG
1102 if (_InterlockedIncrement((PLONG)&Object->ulShareCount) == 1)
1103 {
1104 memset(GDIHandleLocker[HandleIndex], 0x00, GDI_STACK_LEVELS * sizeof(ULONG));
1105 RtlCaptureStackBackTrace(1, GDI_STACK_LEVELS, (PVOID*)GDIHandleLocker[HandleIndex], NULL);
1106 }
1107 #else
1108 _InterlockedIncrement((PLONG)&Object->ulShareCount);
1109 #endif
1110 }
1111 else
1112 {
1113 /*
1114 * Debugging code. Report attempts to lock deleted handles and
1115 * locking type mismatches.
1116 */
1117 LockErrorDebugOutput(hObj, Entry, "GDIOBJ_ShareLockObj");
1118 }
1119
1120 /* Unlock the handle table entry. */
1121 (void)_InterlockedExchangePointer((PVOID*)&Entry->ProcessId, PrevProcId);
1122
1123 break;
1124 }
1125 else
1126 {
1127 /*
1128 * The handle is currently locked, wait some time and try again.
1129 */
1130
1131 DelayExecution();
1132 continue;
1133 }
1134 }
1135
1136 KeLeaveCriticalRegion();
1137
1138 return Object;
1139 }
1140
1141
1142 /*!
1143 * Release GDI object. Every object locked by GDIOBJ_LockObj() must be unlocked. You should unlock the object
1144 * as soon as you don't need to have access to it's data.
1145
1146 * \param Object Object pointer (as returned by GDIOBJ_LockObj).
1147 */
1148 VOID INTERNAL_CALL
1149 GDIOBJ_UnlockObjByPtr(POBJ Object)
1150 {
1151 if (_InterlockedDecrement((PLONG)&Object->cExclusiveLock) < 0)
1152 {
1153 DPRINT1("Trying to unlock non-existant object\n");
1154 }
1155 }
1156
1157 VOID INTERNAL_CALL
1158 GDIOBJ_ShareUnlockObjByPtr(POBJ Object)
1159 {
1160 if (_InterlockedDecrement((PLONG)&Object->ulShareCount) < 0)
1161 {
1162 DPRINT1("Trying to unlock non-existant object\n");
1163 }
1164 }
1165
1166 BOOL INTERNAL_CALL
1167 GDIOBJ_OwnedByCurrentProcess(HGDIOBJ ObjectHandle)
1168 {
1169 PGDI_TABLE_ENTRY Entry;
1170 HANDLE ProcessId;
1171 BOOL Ret;
1172
1173 DPRINT("GDIOBJ_OwnedByCurrentProcess: ObjectHandle: 0x%08x\n", ObjectHandle);
1174
1175 if (!GDI_HANDLE_IS_STOCKOBJ(ObjectHandle))
1176 {
1177 ProcessId = PsGetCurrentProcessId();
1178
1179 Entry = GDI_HANDLE_GET_ENTRY(GdiHandleTable, ObjectHandle);
1180 Ret = Entry->KernelData != NULL &&
1181 (Entry->Type & ~GDI_ENTRY_REUSE_MASK) != 0 &&
1182 (HANDLE)((ULONG_PTR)Entry->ProcessId & ~0x1) == ProcessId;
1183
1184 return Ret;
1185 }
1186
1187 return FALSE;
1188 }
1189
1190 BOOL INTERNAL_CALL
1191 GDIOBJ_ConvertToStockObj(HGDIOBJ *phObj)
1192 {
1193 /*
1194 * FIXME !!!!! THIS FUNCTION NEEDS TO BE FIXED - IT IS NOT SAFE WHEN OTHER THREADS
1195 * MIGHT ATTEMPT TO LOCK THE OBJECT DURING THIS CALL!!!
1196 */
1197 PGDI_TABLE_ENTRY Entry;
1198 HANDLE ProcessId, LockedProcessId, PrevProcId;
1199 PW32THREAD Thread;
1200 HGDIOBJ hObj;
1201 #ifdef GDI_DEBUG
1202 ULONG Attempts = 0;
1203 #endif
1204
1205 ASSERT(phObj);
1206 hObj = *phObj;
1207
1208 DPRINT("GDIOBJ_ConvertToStockObj: hObj: 0x%08x\n", hObj);
1209
1210 Thread = PsGetCurrentThreadWin32Thread();
1211
1212 if (!GDI_HANDLE_IS_STOCKOBJ(hObj))
1213 {
1214 ProcessId = PsGetCurrentProcessId();
1215 LockedProcessId = (HANDLE)((ULONG_PTR)ProcessId | 0x1);
1216
1217 Entry = GDI_HANDLE_GET_ENTRY(GdiHandleTable, hObj);
1218
1219 LockHandle:
1220 /* lock the object, we must not convert stock objects, so don't check!!! */
1221 PrevProcId = _InterlockedCompareExchangePointer((PVOID*)&Entry->ProcessId, LockedProcessId, ProcessId);
1222 if (PrevProcId == ProcessId)
1223 {
1224 LONG NewType, PrevType, OldType;
1225
1226 /* we're locking an object that belongs to our process. First calculate
1227 the new object type including the stock object flag and then try to
1228 exchange it.*/
1229 /* On Windows the higher 16 bit of the type field don't contain the
1230 full type from the handle, but the base type.
1231 (type = BRSUH, PEN, EXTPEN, basetype = BRUSH) */
1232 OldType = ((ULONG)hObj & GDI_HANDLE_BASETYPE_MASK) | ((ULONG)hObj >> GDI_ENTRY_UPPER_SHIFT);
1233 /* We are currently not using bits 24..31 (flags) of the type field, but for compatibility
1234 we copy them as we can't get them from the handle */
1235 OldType |= Entry->Type & GDI_ENTRY_FLAGS_MASK;
1236
1237 /* As the object should be a stock object, set it's flag, but only in the lower 16 bits */
1238 NewType = OldType | GDI_ENTRY_STOCK_MASK;
1239
1240 /* Try to exchange the type field - but only if the old (previous type) matches! */
1241 PrevType = _InterlockedCompareExchange(&Entry->Type, NewType, OldType);
1242 if (PrevType == OldType && Entry->KernelData != NULL)
1243 {
1244 PW32THREAD PrevThread;
1245 POBJ Object;
1246
1247 /* We successfully set the stock object flag.
1248 KernelData should never be NULL here!!! */
1249 ASSERT(Entry->KernelData);
1250
1251 Object = Entry->KernelData;
1252
1253 PrevThread = Object->Tid;
1254 if (Object->cExclusiveLock == 0 || PrevThread == Thread)
1255 {
1256 /* dereference the process' object counter */
1257 if (PrevProcId != GDI_GLOBAL_PROCESS)
1258 {
1259 PEPROCESS OldProcess;
1260 PW32PROCESS W32Process;
1261 NTSTATUS Status;
1262
1263 /* FIXME */
1264 Status = PsLookupProcessByProcessId((HANDLE)((ULONG_PTR)PrevProcId & ~0x1), &OldProcess);
1265 if (NT_SUCCESS(Status))
1266 {
1267 W32Process = (PW32PROCESS)OldProcess->Win32Process;
1268 if (W32Process != NULL)
1269 {
1270 _InterlockedDecrement(&W32Process->GDIObjects);
1271 }
1272 ObDereferenceObject(OldProcess);
1273 }
1274 }
1275
1276 /* remove the process id lock and make it global */
1277 (void)_InterlockedExchangePointer((PVOID*)&Entry->ProcessId, GDI_GLOBAL_PROCESS);
1278
1279 hObj = (HGDIOBJ)((ULONG)(hObj) | GDI_HANDLE_STOCK_MASK);
1280 *phObj = hObj;
1281
1282 /* we're done, successfully converted the object */
1283 return TRUE;
1284 }
1285 else
1286 {
1287 #ifdef GDI_DEBUG
1288 if (++Attempts > 20)
1289 {
1290 DPRINT1("[%d]Locked by 0x%x (we're 0x%x)\n", Attempts, PrevThread, Thread);
1291 }
1292 #endif
1293 /* WTF?! The object is already locked by a different thread!
1294 Release the lock, wait a bit and try again!
1295 FIXME - we should give up after some time unless we want to wait forever! */
1296 (void)_InterlockedExchangePointer((PVOID*)&Entry->ProcessId, PrevProcId);
1297
1298 DelayExecution();
1299 goto LockHandle;
1300 }
1301 }
1302 else
1303 {
1304 DPRINT1("Attempted to convert object 0x%x that is deleted! Should never get here!!!\n", hObj);
1305 DPRINT1("OldType = 0x%x, Entry->Type = 0x%x, NewType = 0x%x, Entry->KernelData = 0x%x\n", OldType, Entry->Type, NewType, Entry->KernelData);
1306 }
1307 }
1308 else if (PrevProcId == LockedProcessId)
1309 {
1310 #ifdef GDI_DEBUG
1311 if (++Attempts > 20)
1312 {
1313 DPRINT1("[%d]Waiting on 0x%x\n", Attempts, hObj);
1314 }
1315 #endif
1316 /* the object is currently locked, wait some time and try again.
1317 FIXME - we shouldn't loop forever! Give up after some time! */
1318 DelayExecution();
1319 /* try again */
1320 goto LockHandle;
1321 }
1322 else
1323 {
1324 DPRINT1("Attempted to convert invalid handle: 0x%x\n", hObj);
1325 }
1326 }
1327
1328 return FALSE;
1329 }
1330
1331 void INTERNAL_CALL
1332 GDIOBJ_SetOwnership(HGDIOBJ ObjectHandle, PEPROCESS NewOwner)
1333 {
1334 PGDI_TABLE_ENTRY Entry;
1335 HANDLE ProcessId, LockedProcessId, PrevProcId;
1336 PW32THREAD Thread;
1337 #ifdef GDI_DEBUG
1338 ULONG Attempts = 0;
1339 #endif
1340
1341 DPRINT("GDIOBJ_SetOwnership: hObj: 0x%x, NewProcess: 0x%x\n", ObjectHandle, (NewOwner ? PsGetProcessId(NewOwner) : 0));
1342
1343 Thread = PsGetCurrentThreadWin32Thread();
1344
1345 if (!GDI_HANDLE_IS_STOCKOBJ(ObjectHandle))
1346 {
1347 ProcessId = PsGetCurrentProcessId();
1348 LockedProcessId = (HANDLE)((ULONG_PTR)ProcessId | 0x1);
1349
1350 Entry = GDI_HANDLE_GET_ENTRY(GdiHandleTable, ObjectHandle);
1351
1352 LockHandle:
1353 /* lock the object, we must not convert stock objects, so don't check!!! */
1354 PrevProcId = _InterlockedCompareExchangePointer((PVOID*)&Entry->ProcessId, ProcessId, LockedProcessId);
1355 if (PrevProcId == ProcessId)
1356 {
1357 PW32THREAD PrevThread;
1358
1359 if ((Entry->Type & ~GDI_ENTRY_REUSE_MASK) != 0 && Entry->KernelData != NULL)
1360 {
1361 POBJ Object = Entry->KernelData;
1362
1363 PrevThread = Object->Tid;
1364 if (Object->cExclusiveLock == 0 || PrevThread == Thread)
1365 {
1366 PEPROCESS OldProcess;
1367 PW32PROCESS W32Process;
1368 NTSTATUS Status;
1369
1370 /* dereference the process' object counter */
1371 /* FIXME */
1372 if ((ULONG_PTR)PrevProcId & ~0x1)
1373 {
1374 Status = PsLookupProcessByProcessId((HANDLE)((ULONG_PTR)PrevProcId & ~0x1), &OldProcess);
1375 if (NT_SUCCESS(Status))
1376 {
1377 W32Process = (PW32PROCESS)OldProcess->Win32Process;
1378 if (W32Process != NULL)
1379 {
1380 _InterlockedDecrement(&W32Process->GDIObjects);
1381 }
1382 ObDereferenceObject(OldProcess);
1383 }
1384 }
1385
1386 if (NewOwner != NULL)
1387 {
1388 ProcessId = PsGetProcessId(NewOwner);
1389
1390 /* Increase the new process' object counter */
1391 W32Process = (PW32PROCESS)NewOwner->Win32Process;
1392 if (W32Process != NULL)
1393 {
1394 _InterlockedIncrement(&W32Process->GDIObjects);
1395 }
1396 }
1397 else
1398 ProcessId = 0;
1399
1400 /* remove the process id lock and change it to the new process id */
1401 (void)_InterlockedExchangePointer((PVOID*)&Entry->ProcessId, ProcessId);
1402
1403 /* we're done! */
1404 return;
1405 }
1406 else
1407 {
1408 #ifdef GDI_DEBUG
1409 if (++Attempts > 20)
1410 {
1411 DPRINT1("[%d]Locked by 0x%x (we're 0x%x)\n", Attempts, PrevThread, Thread);
1412 }
1413 #endif
1414 /* WTF?! The object is already locked by a different thread!
1415 Release the lock, wait a bit and try again! DO reset the pid lock
1416 so we make sure we don't access invalid memory in case the object is
1417 being deleted in the meantime (because we don't have aquired a reference
1418 at this point).
1419 FIXME - we should give up after some time unless we want to wait forever! */
1420 (void)_InterlockedExchangePointer((PVOID*)&Entry->ProcessId, PrevProcId);
1421
1422 DelayExecution();
1423 goto LockHandle;
1424 }
1425 }
1426 else
1427 {
1428 DPRINT1("Attempted to change ownership of an object 0x%x currently being destroyed!!!\n", ObjectHandle);
1429 DPRINT1("Entry->Type = 0x%lx, Entry->KernelData = 0x%p\n", Entry->Type, Entry->KernelData);
1430 }
1431 }
1432 else if (PrevProcId == LockedProcessId)
1433 {
1434 #ifdef GDI_DEBUG
1435 if (++Attempts > 20)
1436 {
1437 DPRINT1("[%d]Waiting on 0x%x\n", Attempts, ObjectHandle);
1438 }
1439 #endif
1440 /* the object is currently locked, wait some time and try again.
1441 FIXME - we shouldn't loop forever! Give up after some time! */
1442 DelayExecution();
1443 /* try again */
1444 goto LockHandle;
1445 }
1446 else if (((ULONG_PTR)PrevProcId & ~0x1) == 0)
1447 {
1448 /* allow changing ownership of global objects */
1449 ProcessId = NULL;
1450 LockedProcessId = (HANDLE)((ULONG_PTR)ProcessId | 0x1);
1451 goto LockHandle;
1452 }
1453 else if ((HANDLE)((ULONG_PTR)PrevProcId & ~0x1) != PsGetCurrentProcessId())
1454 {
1455 DPRINT1("Attempted to change ownership of object 0x%x (pid: 0x%x) from pid 0x%x!!!\n", ObjectHandle, (ULONG_PTR)PrevProcId & ~0x1, PsGetCurrentProcessId());
1456 }
1457 else
1458 {
1459 DPRINT1("Attempted to change owner of invalid handle: 0x%x\n", ObjectHandle);
1460 }
1461 }
1462 }
1463
1464 void INTERNAL_CALL
1465 GDIOBJ_CopyOwnership(HGDIOBJ CopyFrom, HGDIOBJ CopyTo)
1466 {
1467 PGDI_TABLE_ENTRY FromEntry;
1468 PW32THREAD Thread;
1469 HANDLE FromProcessId, FromLockedProcessId, FromPrevProcId;
1470 #ifdef GDI_DEBUG
1471 ULONG Attempts = 0;
1472 #endif
1473
1474 DPRINT("GDIOBJ_CopyOwnership: from: 0x%x, to: 0x%x\n", CopyFrom, CopyTo);
1475
1476 Thread = PsGetCurrentThreadWin32Thread();
1477
1478 if (!GDI_HANDLE_IS_STOCKOBJ(CopyFrom) && !GDI_HANDLE_IS_STOCKOBJ(CopyTo))
1479 {
1480 FromEntry = GDI_HANDLE_GET_ENTRY(GdiHandleTable, CopyFrom);
1481
1482 FromProcessId = (HANDLE)((ULONG_PTR)FromEntry->ProcessId & ~0x1);
1483 FromLockedProcessId = (HANDLE)((ULONG_PTR)FromProcessId | 0x1);
1484
1485 LockHandleFrom:
1486 /* lock the object, we must not convert stock objects, so don't check!!! */
1487 FromPrevProcId = _InterlockedCompareExchangePointer((PVOID*)&FromEntry->ProcessId, FromProcessId, FromLockedProcessId);
1488 if (FromPrevProcId == FromProcessId)
1489 {
1490 PW32THREAD PrevThread;
1491 POBJ Object;
1492
1493 if ((FromEntry->Type & ~GDI_ENTRY_REUSE_MASK) != 0 && FromEntry->KernelData != NULL)
1494 {
1495 Object = FromEntry->KernelData;
1496
1497 /* save the pointer to the calling thread so we know it was this thread
1498 that locked the object */
1499 PrevThread = Object->Tid;
1500 if (Object->cExclusiveLock == 0 || PrevThread == Thread)
1501 {
1502 /* now let's change the ownership of the target object */
1503
1504 if (((ULONG_PTR)FromPrevProcId & ~0x1) != 0)
1505 {
1506 PEPROCESS ProcessTo;
1507 /* FIXME */
1508 if (NT_SUCCESS(PsLookupProcessByProcessId((HANDLE)((ULONG_PTR)FromPrevProcId & ~0x1), &ProcessTo)))
1509 {
1510 GDIOBJ_SetOwnership(CopyTo, ProcessTo);
1511 ObDereferenceObject(ProcessTo);
1512 }
1513 }
1514 else
1515 {
1516 /* mark the object as global */
1517 GDIOBJ_SetOwnership(CopyTo, NULL);
1518 }
1519
1520 (void)_InterlockedExchangePointer((PVOID*)&FromEntry->ProcessId, FromPrevProcId);
1521 }
1522 else
1523 {
1524 #ifdef GDI_DEBUG
1525 if (++Attempts > 20)
1526 {
1527 DPRINT1("[%d]Locked by 0x%x (we're 0x%x)\n", Attempts, PrevThread, Thread);
1528 }
1529 #endif
1530 /* WTF?! The object is already locked by a different thread!
1531 Release the lock, wait a bit and try again! DO reset the pid lock
1532 so we make sure we don't access invalid memory in case the object is
1533 being deleted in the meantime (because we don't have aquired a reference
1534 at this point).
1535 FIXME - we should give up after some time unless we want to wait forever! */
1536 (void)_InterlockedExchangePointer((PVOID*)&FromEntry->ProcessId, FromPrevProcId);
1537
1538 DelayExecution();
1539 goto LockHandleFrom;
1540 }
1541 }
1542 else
1543 {
1544 DPRINT1("Attempted to copy ownership from an object 0x%x currently being destroyed!!!\n", CopyFrom);
1545 }
1546 }
1547 else if (FromPrevProcId == FromLockedProcessId)
1548 {
1549 #ifdef GDI_DEBUG
1550 if (++Attempts > 20)
1551 {
1552 DPRINT1("[%d]Waiting on 0x%x\n", Attempts, CopyFrom);
1553 }
1554 #endif
1555 /* the object is currently locked, wait some time and try again.
1556 FIXME - we shouldn't loop forever! Give up after some time! */
1557 DelayExecution();
1558 /* try again */
1559 goto LockHandleFrom;
1560 }
1561 else if ((HANDLE)((ULONG_PTR)FromPrevProcId & ~0x1) != PsGetCurrentProcessId())
1562 {
1563 /* FIXME - should we really allow copying ownership from objects that we don't even own? */
1564 DPRINT1("WARNING! Changing copying ownership of object 0x%x (pid: 0x%x) to pid 0x%x!!!\n", CopyFrom, (ULONG_PTR)FromPrevProcId & ~0x1, PsGetCurrentProcessId());
1565 FromProcessId = (HANDLE)((ULONG_PTR)FromPrevProcId & ~0x1);
1566 FromLockedProcessId = (HANDLE)((ULONG_PTR)FromProcessId | 0x1);
1567 goto LockHandleFrom;
1568 }
1569 else
1570 {
1571 DPRINT1("Attempted to copy ownership from invalid handle: 0x%x\n", CopyFrom);
1572 }
1573 }
1574 }
1575
1576 PVOID INTERNAL_CALL
1577 GDI_MapHandleTable(PSECTION_OBJECT SectionObject, PEPROCESS Process)
1578 {
1579 PVOID MappedView = NULL;
1580 NTSTATUS Status;
1581 LARGE_INTEGER Offset;
1582 ULONG ViewSize = sizeof(GDI_HANDLE_TABLE);
1583
1584 Offset.QuadPart = 0;
1585
1586 ASSERT(SectionObject != NULL);
1587 ASSERT(Process != NULL);
1588
1589 Status = MmMapViewOfSection(SectionObject,
1590 Process,
1591 &MappedView,
1592 0,
1593 0,
1594 &Offset,
1595 &ViewSize,
1596 ViewUnmap,
1597 SEC_NO_CHANGE,
1598 PAGE_READONLY);
1599
1600 if (!NT_SUCCESS(Status))
1601 return NULL;
1602
1603 return MappedView;
1604 }
1605
1606 W32KAPI
1607 HANDLE
1608 APIENTRY
1609 NtGdiCreateClientObj(
1610 IN ULONG ulType
1611 )
1612 {
1613 // ATM we use DC object for KernelData. This is wrong.
1614 // The real type consists of BASEOBJECT and a pointer.
1615 // The UserData is set in user mode, so it is always NULL.
1616 // HANDLE should be HGDIOBJ
1617 //
1618 POBJ pObject;
1619 HANDLE handle;
1620
1621 /* Mask out everything that would change the type in a wrong manner */
1622 ulType &= (GDI_HANDLE_TYPE_MASK & ~GDI_HANDLE_BASETYPE_MASK);
1623
1624 /* Allocate a new object */
1625 pObject = GDIOBJ_AllocObjWithHandle(GDI_OBJECT_TYPE_CLIOBJ | ulType);
1626 if (!pObject)
1627 {
1628 return NULL;
1629 }
1630
1631 /* get the handle */
1632 handle = pObject->hHmgr;
1633
1634 /* Unlock it */
1635 GDIOBJ_UnlockObjByPtr(pObject);
1636
1637 return handle;
1638 }
1639
1640 W32KAPI
1641 BOOL
1642 APIENTRY
1643 NtGdiDeleteClientObj(
1644 IN HANDLE h
1645 )
1646 {
1647 /* We first need to get the real type from the handle */
1648 ULONG type = GDI_HANDLE_GET_TYPE(h);
1649
1650 /* Check if it's really a CLIENTOBJ */
1651 if ((type & GDI_HANDLE_BASETYPE_MASK) != GDILoObjType_LO_CLIENTOBJ_TYPE)
1652 {
1653 /* FIXME: SetLastError? */
1654 return FALSE;
1655 }
1656 return GDIOBJ_FreeObjByHandle(h, type);
1657 }
1658
1659 INT
1660 FASTCALL
1661 IntGdiGetObject(IN HANDLE Handle,
1662 IN INT cbCount,
1663 IN LPVOID lpBuffer)
1664 {
1665 PVOID pGdiObject;
1666 INT Result = 0;
1667 DWORD dwObjectType;
1668
1669 pGdiObject = GDIOBJ_LockObj(Handle, GDI_OBJECT_TYPE_DONTCARE);
1670 if (!pGdiObject)
1671 {
1672 SetLastWin32Error(ERROR_INVALID_HANDLE);
1673 return 0;
1674 }
1675
1676 dwObjectType = GDIOBJ_GetObjectType(Handle);
1677 switch (dwObjectType)
1678 {
1679 case GDI_OBJECT_TYPE_PEN:
1680 case GDI_OBJECT_TYPE_EXTPEN:
1681 Result = PEN_GetObject((PGDIBRUSHOBJ) pGdiObject, cbCount, (PLOGPEN) lpBuffer); // IntGdiCreatePenIndirect
1682 break;
1683
1684 case GDI_OBJECT_TYPE_BRUSH:
1685 Result = BRUSH_GetObject((PGDIBRUSHOBJ ) pGdiObject, cbCount, (LPLOGBRUSH)lpBuffer);
1686 break;
1687
1688 case GDI_OBJECT_TYPE_BITMAP:
1689 Result = BITMAP_GetObject((BITMAPOBJ *) pGdiObject, cbCount, lpBuffer);
1690 break;
1691 case GDI_OBJECT_TYPE_FONT:
1692 Result = FontGetObject((PTEXTOBJ) pGdiObject, cbCount, lpBuffer);
1693 #if 0
1694 // Fix the LOGFONT structure for the stock fonts
1695 if (FIRST_STOCK_HANDLE <= Handle && Handle <= LAST_STOCK_HANDLE)
1696 {
1697 FixStockFontSizeW(Handle, cbCount, lpBuffer);
1698 }
1699 #endif
1700 break;
1701
1702 case GDI_OBJECT_TYPE_PALETTE:
1703 Result = PALETTE_GetObject((PPALGDI) pGdiObject, cbCount, lpBuffer);
1704 break;
1705
1706 default:
1707 DPRINT1("GDI object type 0x%08x not implemented\n", dwObjectType);
1708 break;
1709 }
1710
1711 GDIOBJ_UnlockObjByPtr(pGdiObject);
1712
1713 return Result;
1714 }
1715
1716 INT
1717 NTAPI
1718 NtGdiExtGetObjectW(IN HANDLE hGdiObj,
1719 IN INT cbCount,
1720 OUT LPVOID lpBuffer)
1721 {
1722 INT iRetCount = 0;
1723 INT cbCopyCount;
1724 union
1725 {
1726 BITMAP bitmap;
1727 DIBSECTION dibsection;
1728 LOGPEN logpen;
1729 LOGBRUSH logbrush;
1730 LOGFONTW logfontw;
1731 EXTLOGFONTW extlogfontw;
1732 ENUMLOGFONTEXDVW enumlogfontexdvw;
1733 } Object;
1734
1735 // Normalize to the largest supported object size
1736 cbCount = min((UINT)cbCount, sizeof(Object));
1737
1738 // Now do the actual call
1739 iRetCount = IntGdiGetObject(hGdiObj, cbCount, lpBuffer ? &Object : NULL);
1740 cbCopyCount = min((UINT)cbCount, (UINT)iRetCount);
1741
1742 // Make sure we have a buffer and a copy size
1743 if ((cbCopyCount) && (lpBuffer))
1744 {
1745 // Enter SEH for buffer transfer
1746 _SEH_TRY
1747 {
1748 // Probe the buffer and copy it
1749 ProbeForWrite(lpBuffer, cbCopyCount, sizeof(WORD));
1750 RtlCopyMemory(lpBuffer, &Object, cbCopyCount);
1751 }
1752 _SEH_HANDLE
1753 {
1754 // Clear the return value.
1755 // Do *NOT* set last error here!
1756 iRetCount = 0;
1757 }
1758 _SEH_END;
1759 }
1760 // Return the count
1761 return iRetCount;
1762 }
1763
1764 /* EOF */