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