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