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