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