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