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