[CMAKE]
[reactos.git] / 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 static 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 * Process Environment Cached GDI Handles
746 *
747 * What types of GDI handle objects that are cached in the GDI handle buffer?
748 * Brushes set to BS_SOLID, Pens with widths of zero or set to PS_SOLID, and
749 * Regions that are set to NULLREGION or SIMPLEREGION.
750 */
751 BOOL
752 FASTCALL
753 bPEBCacheHandle(HGDIOBJ Handle, int oType, PVOID pAttr)
754 {
755 PGDIHANDLECACHE GdiHandleCache;
756 HGDIOBJ *hPtr;
757 HANDLE Lock;
758 int Number, Offset, MaxNum = CACHE_PEN_ENTRIES;
759 BOOL Ret = FALSE;
760
761 GdiHandleCache = (PGDIHANDLECACHE)NtCurrentTeb()->ProcessEnvironmentBlock->GdiHandleBuffer;
762
763 switch (oType)
764 {
765 case hctBrushHandle:
766 Offset = 0;
767 MaxNum = CACHE_BRUSH_ENTRIES;
768 break;
769
770 case hctPenHandle:
771 Offset = CACHE_BRUSH_ENTRIES;
772 break;
773
774 case hctRegionHandle:
775 Offset = CACHE_BRUSH_ENTRIES+CACHE_PEN_ENTRIES;
776 break;
777
778 default:
779 return FALSE;
780 }
781
782 Lock = InterlockedCompareExchangePointer( (PVOID*)&GdiHandleCache->ulLock,
783 NtCurrentTeb(),
784 NULL );
785 if (Lock) return FALSE;
786
787 _SEH2_TRY
788 {
789 Number = GdiHandleCache->ulNumHandles[oType];
790
791 hPtr = GdiHandleCache->Handle + Offset;
792
793 if ( pAttr )
794 {
795 if ( Number < MaxNum )
796 { // This object is cached and waiting for it's resurrection by the users.
797 ((PRGN_ATTR)pAttr)->AttrFlags |= ATTR_CACHED;
798 hPtr[Number] = Handle;
799 GdiHandleCache->ulNumHandles[oType]++;
800 DPRINT("Put Handle Count %d PEB 0x%x\n", GdiHandleCache->ulNumHandles[oType], NtCurrentTeb()->ProcessEnvironmentBlock);
801 Ret = TRUE;
802 }
803 }
804 }
805 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
806 {
807 Ret = FALSE;
808 }
809 _SEH2_END;
810
811 (void)InterlockedExchangePointer((PVOID*)&GdiHandleCache->ulLock, Lock);
812 return Ret;
813 }
814
815 /*!
816 * Delete GDI object
817 * \param hObject object handle
818 * \return if the function fails the returned value is FALSE.
819 */
820 BOOL
821 FASTCALL
822 GreDeleteObject(HGDIOBJ hObject)
823 {
824 INT Index;
825 PGDI_TABLE_ENTRY Entry;
826 DWORD dwObjectType;
827 INT ihct;
828 PVOID pAttr = NULL;
829
830 DPRINT("NtGdiDeleteObject handle 0x%08x\n", hObject);
831 if (!IsObjectDead(hObject))
832 {
833 dwObjectType = GDIOBJ_GetObjectType(hObject);
834
835 Index = GDI_HANDLE_GET_INDEX(hObject);
836 Entry = &GdiHandleTable->Entries[Index];
837 pAttr = Entry->UserData;
838
839 switch (dwObjectType)
840 {
841 case GDI_OBJECT_TYPE_BRUSH:
842 ihct = hctBrushHandle;
843 break;
844
845 case GDI_OBJECT_TYPE_PEN:
846 ihct = hctPenHandle;
847 break;
848
849 case GDI_OBJECT_TYPE_REGION:
850 ihct = hctRegionHandle;
851 break;
852 }
853
854 switch (dwObjectType)
855 {
856 // case GDI_OBJECT_TYPE_BRUSH:
857 // case GDI_OBJECT_TYPE_PEN:
858 case GDI_OBJECT_TYPE_REGION:
859 /* If pAttr NULL, the probability is high for System GDI handle object. */
860 if ( pAttr &&
861 bPEBCacheHandle(hObject, ihct, pAttr) )
862 { /* User space handle only! */
863 return TRUE;
864 }
865 if (pAttr)
866 {
867 FreeObjectAttr(pAttr);
868 Entry->UserData = NULL;
869 }
870 break;
871
872 case GDI_OBJECT_TYPE_DC:
873 // DC_FreeDcAttr(hObject);
874 break;
875 }
876
877 return NULL != hObject
878 ? GDIOBJ_FreeObjByHandle(hObject, dwObjectType) : FALSE;
879 }
880 else
881 {
882 DPRINT1("Attempt DeleteObject 0x%x currently being destroyed!!!\n",hObject);
883 return TRUE; // return true and move on.
884 }
885 }
886
887 VOID
888 FASTCALL
889 IntDeleteHandlesForProcess(struct _EPROCESS *Process, ULONG ObjectType)
890 {
891 PGDI_TABLE_ENTRY Entry, End;
892 ULONG Index = RESERVE_ENTRIES_COUNT;
893 HANDLE ProcId;
894 PPROCESSINFO W32Process;
895
896 W32Process = (PPROCESSINFO)Process->Win32Process;
897 ASSERT(W32Process);
898
899 if (W32Process->GDIHandleCount > 0)
900 {
901 ProcId = Process->UniqueProcessId;
902
903 /* FIXME - Instead of building the handle here and delete it using GDIOBJ_FreeObj
904 we should delete it directly here! */
905
906 End = &GdiHandleTable->Entries[GDI_HANDLE_COUNT];
907 for (Entry = &GdiHandleTable->Entries[RESERVE_ENTRIES_COUNT];
908 Entry != End;
909 Entry++, Index++)
910 {
911 /* ignore the lock bit */
912 if ( (HANDLE)((ULONG_PTR)Entry->ProcessId & ~0x1) == ProcId)
913 {
914 if ( (Entry->Type & GDI_ENTRY_BASETYPE_MASK) == ObjectType ||
915 ObjectType == GDI_OBJECT_TYPE_DONTCARE)
916 {
917 HGDIOBJ ObjectHandle;
918
919 /* Create the object handle for the entry, the lower(!) 16 bit of the
920 Type field includes the type of the object including the stock
921 object flag - but since stock objects don't have a process id we can
922 simply ignore this fact here. */
923 ObjectHandle = (HGDIOBJ)(Index | (Entry->Type << GDI_ENTRY_UPPER_SHIFT));
924
925 if (!GDIOBJ_FreeObjByHandle(ObjectHandle, GDI_OBJECT_TYPE_DONTCARE))
926 {
927 DPRINT1("Failed to delete object %p!\n", ObjectHandle);
928 }
929
930 if (W32Process->GDIHandleCount == 0)
931 {
932 /* there are no more gdi handles for this process, bail */
933 break;
934 }
935 }
936 }
937 }
938 }
939 }
940
941
942 /*!
943 * Internal function. Called when the process is destroyed to free the remaining GDI handles.
944 * \param Process - PID of the process that will be destroyed.
945 */
946 BOOL INTERNAL_CALL
947 GDI_CleanupForProcess(struct _EPROCESS *Process)
948 {
949 PEPROCESS CurrentProcess;
950 PPROCESSINFO W32Process;
951
952 DPRINT("Starting CleanupForProcess prochandle %x Pid %d\n", Process, Process->UniqueProcessId);
953 CurrentProcess = PsGetCurrentProcess();
954 if (CurrentProcess != Process)
955 {
956 KeAttachProcess(&Process->Pcb);
957 }
958
959 W32Process = (PPROCESSINFO)CurrentProcess->Win32Process;
960
961 /* Delete objects. Begin with types that are not referenced by other types */
962 IntDeleteHandlesForProcess(Process, GDILoObjType_LO_DC_TYPE);
963 IntDeleteHandlesForProcess(Process, GDILoObjType_LO_BRUSH_TYPE);
964 IntDeleteHandlesForProcess(Process, GDILoObjType_LO_BITMAP_TYPE);
965
966 /* Finally finish with what's left */
967 IntDeleteHandlesForProcess(Process, GDI_OBJECT_TYPE_DONTCARE);
968
969 if (CurrentProcess != Process)
970 {
971 KeDetachProcess();
972 }
973
974 #ifdef GDI_DEBUG
975 GdiDbgHTIntegrityCheck();
976 #endif
977
978 DPRINT("Completed cleanup for process %d\n", Process->UniqueProcessId);
979 if (W32Process->GDIHandleCount > 0)
980 {
981 DPRINT1("Leaking %d handles!\n", W32Process->GDIHandleCount);
982 }
983
984 return TRUE;
985 }
986
987 /*!
988 * Return pointer to the object by handle.
989 *
990 * \param hObj Object handle
991 * \return Pointer to the object.
992 *
993 * \note Process can only get pointer to the objects it created or global objects.
994 *
995 * \todo Get rid of the ExpectedType parameter!
996 */
997 PGDIOBJ INTERNAL_CALL
998 GDIOBJ_LockObj(HGDIOBJ hObj, DWORD ExpectedType)
999 {
1000 ULONG HandleIndex;
1001 PGDI_TABLE_ENTRY Entry;
1002 HANDLE ProcessId, HandleProcessId, LockedProcessId, PrevProcId;
1003 POBJ Object = NULL;
1004 ULONG HandleType, HandleUpper;
1005
1006 /* Check for dummy call */
1007 if(hObj == NULL)
1008 return NULL ;
1009
1010 GDIDBG_INITLOOPTRACE();
1011
1012 HandleIndex = GDI_HANDLE_GET_INDEX(hObj);
1013 HandleType = GDI_HANDLE_GET_TYPE(hObj);
1014 HandleUpper = GDI_HANDLE_GET_UPPER(hObj);
1015
1016 /* Check that the handle index is valid. */
1017 if (HandleIndex >= GDI_HANDLE_COUNT )
1018 return NULL;
1019
1020 Entry = &GdiHandleTable->Entries[HandleIndex];
1021
1022 /* Check if we have the requested type */
1023 if ( (ExpectedType != GDI_OBJECT_TYPE_DONTCARE &&
1024 HandleType != ExpectedType) ||
1025 HandleType == 0 )
1026 {
1027 DPRINT("Attempted to lock object 0x%x of wrong type (Handle: 0x%x, requested: 0x%x)\n",
1028 hObj, HandleType, ExpectedType);
1029 GDIDBG_TRACECALLER();
1030 GDIDBG_TRACEALLOCATOR(hObj);
1031 GDIDBG_TRACEDELETER(hObj);
1032 return NULL;
1033 }
1034
1035 ProcessId = (HANDLE)((ULONG_PTR)PsGetCurrentProcessId() & ~1);
1036
1037 /*
1038 * Prevent the thread from being terminated during the locking process.
1039 * It would result in undesired effects and inconsistency of the global
1040 * handle table.
1041 */
1042
1043 KeEnterCriticalRegion();
1044
1045 /*
1046 * Loop until we either successfully lock the handle entry & object or
1047 * fail some of the check.
1048 */
1049
1050 for (;;)
1051 {
1052 HandleProcessId = (HANDLE)((ULONG_PTR)Entry->ProcessId & ~1);
1053
1054 /* Check for invalid owner. */
1055 if (ProcessId != HandleProcessId && HandleProcessId != NULL)
1056 {
1057 DPRINT1("Tried to lock object (0x%p) of wrong owner! ProcessId = %p, HandleProcessId = %p\n", hObj, ProcessId, HandleProcessId);
1058 GDIDBG_TRACECALLER();
1059 GDIDBG_TRACEALLOCATOR(hObj);
1060 break;
1061 }
1062
1063 /* Lock the handle table entry. */
1064 LockedProcessId = (HANDLE)((ULONG_PTR)HandleProcessId | 0x1);
1065 PrevProcId = InterlockedCompareExchangePointer((PVOID*)&Entry->ProcessId,
1066 LockedProcessId,
1067 HandleProcessId);
1068
1069 if (PrevProcId == HandleProcessId)
1070 {
1071 /*
1072 * We're locking an object that belongs to our process or it's a
1073 * global object if HandleProcessId is 0 here.
1074 */
1075
1076 if ( (Entry->KernelData != NULL) &&
1077 ((Entry->Type << GDI_ENTRY_UPPER_SHIFT) == HandleUpper) )
1078 {
1079 PTHREADINFO Thread = (PTHREADINFO)PsGetCurrentThreadWin32Thread();
1080 Object = Entry->KernelData;
1081
1082 if (Object->cExclusiveLock == 0)
1083 {
1084 Object->Tid = Thread;
1085 Object->cExclusiveLock = 1;
1086 GDIDBG_CAPTURELOCKER(GDI_HANDLE_GET_INDEX(hObj))
1087 #if DBG
1088 if (Thread) Thread->cExclusiveLocks++;
1089 #endif
1090 }
1091 else
1092 {
1093 if (Object->Tid != Thread)
1094 {
1095 /* Unlock the handle table entry. */
1096 (void)InterlockedExchangePointer((PVOID*)&Entry->ProcessId, PrevProcId);
1097
1098 DelayExecution();
1099 continue;
1100 }
1101 InterlockedIncrement((PLONG)&Object->cExclusiveLock);
1102 #if DBG
1103 if (Thread) Thread->cExclusiveLocks++;
1104 #endif
1105 }
1106 }
1107 else
1108 {
1109 /*
1110 * Debugging code. Report attempts to lock deleted handles and
1111 * locking type mismatches.
1112 */
1113 LockErrorDebugOutput(hObj, Entry, "GDIOBJ_LockObj");
1114 }
1115
1116 /* Unlock the handle table entry. */
1117 (void)InterlockedExchangePointer((PVOID*)&Entry->ProcessId, PrevProcId);
1118
1119 break;
1120 }
1121 else
1122 {
1123 /*
1124 * The handle is currently locked, wait some time and try again.
1125 */
1126 GDIDBG_TRACELOOP(hObj, PrevProcId, NULL);
1127
1128 DelayExecution();
1129 continue;
1130 }
1131 }
1132
1133 KeLeaveCriticalRegion();
1134
1135 return Object;
1136 }
1137
1138
1139 /*!
1140 * Return pointer to the object by handle (and allow sharing of the handle
1141 * across threads).
1142 *
1143 * \param hObj Object handle
1144 * \return Pointer to the object.
1145 *
1146 * \note Process can only get pointer to the objects it created or global objects.
1147 *
1148 * \todo Get rid of the ExpectedType parameter!
1149 */
1150 PGDIOBJ INTERNAL_CALL
1151 GDIOBJ_ShareLockObj(HGDIOBJ hObj, DWORD ExpectedType)
1152 {
1153 ULONG HandleIndex;
1154 PGDI_TABLE_ENTRY Entry;
1155 HANDLE ProcessId, HandleProcessId, LockedProcessId, PrevProcId;
1156 POBJ Object = NULL;
1157 ULONG_PTR HandleType, HandleUpper;
1158
1159 /* Check for dummy call */
1160 if(hObj == NULL)
1161 return NULL ;
1162
1163 HandleIndex = GDI_HANDLE_GET_INDEX(hObj);
1164 HandleType = GDI_HANDLE_GET_TYPE(hObj);
1165 HandleUpper = GDI_HANDLE_GET_UPPER(hObj);
1166
1167 /* Check that the handle index is valid. */
1168 if (HandleIndex >= GDI_HANDLE_COUNT)
1169 return NULL;
1170
1171 /* Check if we have the requested type */
1172 if ( (ExpectedType != GDI_OBJECT_TYPE_DONTCARE &&
1173 HandleType != ExpectedType) ||
1174 HandleType == 0 )
1175 {
1176 DPRINT1("Attempted to lock object 0x%x of wrong type (Handle: 0x%x, requested: 0x%x)\n",
1177 hObj, HandleType, ExpectedType);
1178 return NULL;
1179 }
1180
1181 Entry = &GdiHandleTable->Entries[HandleIndex];
1182
1183 ProcessId = (HANDLE)((ULONG_PTR)PsGetCurrentProcessId() & ~1);
1184 HandleProcessId = (HANDLE)((ULONG_PTR)Entry->ProcessId & ~1);
1185
1186 /* Check for invalid owner. */
1187 if (ProcessId != HandleProcessId && HandleProcessId != NULL)
1188 {
1189 return NULL;
1190 }
1191
1192 /*
1193 * Prevent the thread from being terminated during the locking process.
1194 * It would result in undesired effects and inconsistency of the global
1195 * handle table.
1196 */
1197
1198 KeEnterCriticalRegion();
1199
1200 /*
1201 * Loop until we either successfully lock the handle entry & object or
1202 * fail some of the check.
1203 */
1204
1205 for (;;)
1206 {
1207 /* Lock the handle table entry. */
1208 LockedProcessId = (HANDLE)((ULONG_PTR)HandleProcessId | 0x1);
1209 PrevProcId = InterlockedCompareExchangePointer((PVOID*)&Entry->ProcessId,
1210 LockedProcessId,
1211 HandleProcessId);
1212
1213 if (PrevProcId == HandleProcessId)
1214 {
1215 /*
1216 * We're locking an object that belongs to our process or it's a
1217 * global object if HandleProcessId is 0 here.
1218 */
1219
1220 if ( (Entry->KernelData != NULL) &&
1221 (HandleUpper == (Entry->Type << GDI_ENTRY_UPPER_SHIFT)) )
1222 {
1223 Object = (POBJ)Entry->KernelData;
1224
1225 GDIDBG_CAPTURESHARELOCKER(HandleIndex);
1226 #ifdef GDI_DEBUG3
1227 if (InterlockedIncrement((PLONG)&Object->ulShareCount) == 1)
1228 {
1229 memset(GDIHandleLocker[HandleIndex], 0x00, GDI_STACK_LEVELS * sizeof(ULONG));
1230 RtlCaptureStackBackTrace(1, GDI_STACK_LEVELS, (PVOID*)GDIHandleShareLocker[HandleIndex], NULL);
1231 }
1232 #else
1233 InterlockedIncrement((PLONG)&Object->ulShareCount);
1234 #endif
1235 }
1236 else
1237 {
1238 /*
1239 * Debugging code. Report attempts to lock deleted handles and
1240 * locking type mismatches.
1241 */
1242 LockErrorDebugOutput(hObj, Entry, "GDIOBJ_ShareLockObj");
1243 }
1244
1245 /* Unlock the handle table entry. */
1246 (void)InterlockedExchangePointer((PVOID*)&Entry->ProcessId, PrevProcId);
1247
1248 break;
1249 }
1250 else
1251 {
1252 /*
1253 * The handle is currently locked, wait some time and try again.
1254 */
1255
1256 DelayExecution();
1257 continue;
1258 }
1259 }
1260
1261 KeLeaveCriticalRegion();
1262
1263 return Object;
1264 }
1265
1266 BOOL INTERNAL_CALL
1267 GDIOBJ_OwnedByCurrentProcess(HGDIOBJ ObjectHandle)
1268 {
1269 PGDI_TABLE_ENTRY Entry;
1270 HANDLE ProcessId;
1271 BOOL Ret;
1272
1273 DPRINT("GDIOBJ_OwnedByCurrentProcess: ObjectHandle: 0x%08x\n", ObjectHandle);
1274
1275 if (!GDI_HANDLE_IS_STOCKOBJ(ObjectHandle))
1276 {
1277 ProcessId = PsGetCurrentProcessId();
1278
1279 Entry = GDI_HANDLE_GET_ENTRY(GdiHandleTable, ObjectHandle);
1280 Ret = Entry->KernelData != NULL &&
1281 (Entry->Type & GDI_ENTRY_BASETYPE_MASK) != 0 &&
1282 (HANDLE)((ULONG_PTR)Entry->ProcessId & ~0x1) == ProcessId;
1283
1284 return Ret;
1285 }
1286
1287 return FALSE;
1288 }
1289
1290 BOOL INTERNAL_CALL
1291 GDIOBJ_ConvertToStockObj(HGDIOBJ *phObj)
1292 {
1293 /*
1294 * FIXME !!!!! THIS FUNCTION NEEDS TO BE FIXED - IT IS NOT SAFE WHEN OTHER THREADS
1295 * MIGHT ATTEMPT TO LOCK THE OBJECT DURING THIS CALL!!!
1296 */
1297 PGDI_TABLE_ENTRY Entry;
1298 HANDLE ProcessId, LockedProcessId, PrevProcId;
1299 PTHREADINFO Thread;
1300 HGDIOBJ hObj;
1301
1302 GDIDBG_INITLOOPTRACE();
1303
1304 ASSERT(phObj);
1305 hObj = *phObj;
1306
1307 DPRINT("GDIOBJ_ConvertToStockObj: hObj: 0x%08x\n", hObj);
1308
1309 Thread = (PTHREADINFO)PsGetCurrentThreadWin32Thread();
1310
1311 if (!GDI_HANDLE_IS_STOCKOBJ(hObj))
1312 {
1313 ProcessId = PsGetCurrentProcessId();
1314 LockedProcessId = (HANDLE)((ULONG_PTR)ProcessId | 0x1);
1315
1316 Entry = GDI_HANDLE_GET_ENTRY(GdiHandleTable, hObj);
1317
1318 LockHandle:
1319 /* lock the object, we must not convert stock objects, so don't check!!! */
1320 PrevProcId = InterlockedCompareExchangePointer((PVOID*)&Entry->ProcessId, LockedProcessId, ProcessId);
1321 if (PrevProcId == ProcessId)
1322 {
1323 LONG NewType, PrevType, OldType;
1324
1325 /* we're locking an object that belongs to our process. First calculate
1326 the new object type including the stock object flag and then try to
1327 exchange it.*/
1328 /* On Windows the higher 16 bit of the type field don't contain the
1329 full type from the handle, but the base type.
1330 (type = BRSUH, PEN, EXTPEN, basetype = BRUSH) */
1331 OldType = ((ULONG)hObj & GDI_HANDLE_BASETYPE_MASK) | ((ULONG)hObj >> GDI_ENTRY_UPPER_SHIFT);
1332 /* We are currently not using bits 24..31 (flags) of the type field, but for compatibility
1333 we copy them as we can't get them from the handle */
1334 OldType |= Entry->Type & GDI_ENTRY_FLAGS_MASK;
1335
1336 /* As the object should be a stock object, set it's flag, but only in the lower 16 bits */
1337 NewType = OldType | GDI_ENTRY_STOCK_MASK;
1338
1339 /* Try to exchange the type field - but only if the old (previous type) matches! */
1340 PrevType = InterlockedCompareExchange(&Entry->Type, NewType, OldType);
1341 if (PrevType == OldType && Entry->KernelData != NULL)
1342 {
1343 PTHREADINFO PrevThread;
1344 POBJ Object;
1345
1346 /* We successfully set the stock object flag.
1347 KernelData should never be NULL here!!! */
1348 ASSERT(Entry->KernelData);
1349
1350 Object = Entry->KernelData;
1351
1352 PrevThread = Object->Tid;
1353 if (Object->cExclusiveLock == 0 || PrevThread == Thread)
1354 {
1355 /* dereference the process' object counter */
1356 if (PrevProcId != GDI_GLOBAL_PROCESS)
1357 {
1358 PEPROCESS OldProcess;
1359 PPROCESSINFO W32Process;
1360 NTSTATUS Status;
1361
1362 /* FIXME */
1363 Status = PsLookupProcessByProcessId((HANDLE)((ULONG_PTR)PrevProcId & ~0x1), &OldProcess);
1364 if (NT_SUCCESS(Status))
1365 {
1366 W32Process = (PPROCESSINFO)OldProcess->Win32Process;
1367 if (W32Process != NULL)
1368 {
1369 InterlockedDecrement(&W32Process->GDIHandleCount);
1370 }
1371 ObDereferenceObject(OldProcess);
1372 }
1373 }
1374
1375 hObj = (HGDIOBJ)((ULONG)(hObj) | GDI_HANDLE_STOCK_MASK);
1376 *phObj = hObj;
1377 Object->hHmgr = hObj;
1378
1379 /* remove the process id lock and make it global */
1380 (void)InterlockedExchangePointer((PVOID*)&Entry->ProcessId, GDI_GLOBAL_PROCESS);
1381
1382 /* we're done, successfully converted the object */
1383 return TRUE;
1384 }
1385 else
1386 {
1387 GDIDBG_TRACELOOP(hObj, PrevThread, Thread);
1388
1389 /* WTF?! The object is already locked by a different thread!
1390 Release the lock, wait a bit and try again!
1391 FIXME - we should give up after some time unless we want to wait forever! */
1392 (void)InterlockedExchangePointer((PVOID*)&Entry->ProcessId, PrevProcId);
1393
1394 DelayExecution();
1395 goto LockHandle;
1396 }
1397 }
1398 else
1399 {
1400 DPRINT1("Attempted to convert object 0x%x that is deleted! Should never get here!!!\n", hObj);
1401 DPRINT1("OldType = 0x%x, Entry->Type = 0x%x, NewType = 0x%x, Entry->KernelData = 0x%x\n", OldType, Entry->Type, NewType, Entry->KernelData);
1402 }
1403 }
1404 else if (PrevProcId == LockedProcessId)
1405 {
1406 GDIDBG_TRACELOOP(hObj, PrevProcId, ProcessId);
1407
1408 /* the object is currently locked, wait some time and try again.
1409 FIXME - we shouldn't loop forever! Give up after some time! */
1410 DelayExecution();
1411 /* try again */
1412 goto LockHandle;
1413 }
1414 else
1415 {
1416 DPRINT1("Attempted to convert invalid handle: 0x%x\n", hObj);
1417 }
1418 }
1419
1420 return FALSE;
1421 }
1422
1423 BOOL INTERNAL_CALL
1424 GDIOBJ_SetOwnership(HGDIOBJ ObjectHandle, PEPROCESS NewOwner)
1425 {
1426 PGDI_TABLE_ENTRY Entry;
1427 HANDLE ProcessId, LockedProcessId, PrevProcId;
1428 PTHREADINFO Thread;
1429 BOOL Ret = TRUE;
1430
1431 GDIDBG_INITLOOPTRACE();
1432
1433 DPRINT("GDIOBJ_SetOwnership: hObj: 0x%x, NewProcess: 0x%x\n", ObjectHandle, (NewOwner ? PsGetProcessId(NewOwner) : 0));
1434
1435 Thread = (PTHREADINFO)PsGetCurrentThreadWin32Thread();
1436
1437 if (!GDI_HANDLE_IS_STOCKOBJ(ObjectHandle))
1438 {
1439 ProcessId = PsGetCurrentProcessId();
1440 LockedProcessId = (HANDLE)((ULONG_PTR)ProcessId | 0x1);
1441
1442 Entry = GDI_HANDLE_GET_ENTRY(GdiHandleTable, ObjectHandle);
1443
1444 LockHandle:
1445 /* lock the object, we must not convert stock objects, so don't check!!! */
1446 PrevProcId = InterlockedCompareExchangePointer((PVOID*)&Entry->ProcessId, ProcessId, LockedProcessId);
1447 if (PrevProcId == ProcessId)
1448 {
1449 PTHREADINFO PrevThread;
1450
1451 if ((Entry->Type & GDI_ENTRY_BASETYPE_MASK) != 0)
1452 {
1453 POBJ Object = Entry->KernelData;
1454
1455 PrevThread = Object->Tid;
1456 if (Object->cExclusiveLock == 0 || PrevThread == Thread)
1457 {
1458 PEPROCESS OldProcess;
1459 PPROCESSINFO W32Process;
1460 NTSTATUS Status;
1461
1462 if (NewOwner != NULL)
1463 {
1464 ProcessId = PsGetProcessId(NewOwner);
1465 }
1466 else
1467 ProcessId = 0;
1468
1469 if((ULONG_PTR)ProcessId == ((ULONG_PTR)PrevProcId & ~0x1))
1470 {
1471 DPRINT("Setting same process than previous one, nothing to do\n");
1472 goto done;
1473 }
1474
1475 /* dereference the process' object counter */
1476 /* FIXME */
1477 if ((ULONG_PTR)PrevProcId & ~0x1)
1478 {
1479 Status = PsLookupProcessByProcessId((HANDLE)((ULONG_PTR)PrevProcId & ~0x1), &OldProcess);
1480 if (NT_SUCCESS(Status))
1481 {
1482 W32Process = (PPROCESSINFO)OldProcess->Win32Process;
1483 if (W32Process != NULL)
1484 {
1485 InterlockedDecrement(&W32Process->GDIHandleCount);
1486 }
1487 ObDereferenceObject(OldProcess);
1488 }
1489 }
1490
1491 if (NewOwner != NULL)
1492 {
1493 /* Increase the new process' object counter */
1494 W32Process = (PPROCESSINFO)NewOwner->Win32Process;
1495 if (W32Process != NULL)
1496 {
1497 InterlockedIncrement(&W32Process->GDIHandleCount);
1498 }
1499 }
1500
1501 done:
1502 /* remove the process id lock and change it to the new process id */
1503 (void)InterlockedExchangePointer((PVOID*)&Entry->ProcessId, ProcessId);
1504
1505 /* we're done! */
1506 return Ret;
1507 }
1508 else
1509 {
1510 GDIDBG_TRACELOOP(ObjectHandle, PrevThread, Thread);
1511
1512 /* WTF?! The object is already locked by a different thread!
1513 Release the lock, wait a bit and try again! DO reset the pid lock
1514 so we make sure we don't access invalid memory in case the object is
1515 being deleted in the meantime (because we don't have aquired a reference
1516 at this point).
1517 FIXME - we should give up after some time unless we want to wait forever! */
1518 (void)InterlockedExchangePointer((PVOID*)&Entry->ProcessId, PrevProcId);
1519
1520 DelayExecution();
1521 goto LockHandle;
1522 }
1523 }
1524 else
1525 {
1526 DPRINT1("Attempted to change ownership of an object 0x%x currently being destroyed!!!\n", ObjectHandle);
1527 DPRINT1("Entry->Type = 0x%lx, Entry->KernelData = 0x%p\n", Entry->Type, Entry->KernelData);
1528 Ret = FALSE;
1529 }
1530 }
1531 else if (PrevProcId == LockedProcessId)
1532 {
1533 GDIDBG_TRACELOOP(ObjectHandle, PrevProcId, ProcessId);
1534
1535 /* the object is currently locked, wait some time and try again.
1536 FIXME - we shouldn't loop forever! Give up after some time! */
1537 DelayExecution();
1538 /* try again */
1539 goto LockHandle;
1540 }
1541 else if (((ULONG_PTR)PrevProcId & ~0x1) == 0)
1542 {
1543 /* allow changing ownership of global objects */
1544 ProcessId = NULL;
1545 LockedProcessId = (HANDLE)((ULONG_PTR)ProcessId | 0x1);
1546 goto LockHandle;
1547 }
1548 else if ((HANDLE)((ULONG_PTR)PrevProcId & ~0x1) != PsGetCurrentProcessId())
1549 {
1550 DPRINT1("Attempted to change ownership of object 0x%x (pid: 0x%x) from pid 0x%x!!!\n", ObjectHandle, (ULONG_PTR)PrevProcId & ~0x1, PsGetCurrentProcessId());
1551 Ret = FALSE;
1552 }
1553 else
1554 {
1555 DPRINT1("Attempted to change owner of invalid handle: 0x%x\n", ObjectHandle);
1556 Ret = FALSE;
1557 }
1558 }
1559 return Ret;
1560 }
1561
1562 BOOL INTERNAL_CALL
1563 GDIOBJ_CopyOwnership(HGDIOBJ CopyFrom, HGDIOBJ CopyTo)
1564 {
1565 PGDI_TABLE_ENTRY FromEntry;
1566 PTHREADINFO Thread;
1567 HANDLE FromProcessId, FromLockedProcessId, FromPrevProcId;
1568 BOOL Ret = TRUE;
1569
1570 GDIDBG_INITLOOPTRACE();
1571
1572 DPRINT("GDIOBJ_CopyOwnership: from: 0x%x, to: 0x%x\n", CopyFrom, CopyTo);
1573
1574 Thread = (PTHREADINFO)PsGetCurrentThreadWin32Thread();
1575
1576 if (!GDI_HANDLE_IS_STOCKOBJ(CopyFrom) && !GDI_HANDLE_IS_STOCKOBJ(CopyTo))
1577 {
1578 FromEntry = GDI_HANDLE_GET_ENTRY(GdiHandleTable, CopyFrom);
1579
1580 FromProcessId = (HANDLE)((ULONG_PTR)FromEntry->ProcessId & ~0x1);
1581 FromLockedProcessId = (HANDLE)((ULONG_PTR)FromProcessId | 0x1);
1582
1583 LockHandleFrom:
1584 /* lock the object, we must not convert stock objects, so don't check!!! */
1585 FromPrevProcId = InterlockedCompareExchangePointer((PVOID*)&FromEntry->ProcessId, FromProcessId, FromLockedProcessId);
1586 if (FromPrevProcId == FromProcessId)
1587 {
1588 PTHREADINFO PrevThread;
1589 POBJ Object;
1590
1591 if ((FromEntry->Type & GDI_ENTRY_BASETYPE_MASK) != 0)
1592 {
1593 Object = FromEntry->KernelData;
1594
1595 /* save the pointer to the calling thread so we know it was this thread
1596 that locked the object */
1597 PrevThread = Object->Tid;
1598 if (Object->cExclusiveLock == 0 || PrevThread == Thread)
1599 {
1600 /* now let's change the ownership of the target object */
1601
1602 if (((ULONG_PTR)FromPrevProcId & ~0x1) != 0)
1603 {
1604 PEPROCESS ProcessTo;
1605 /* FIXME */
1606 if (NT_SUCCESS(PsLookupProcessByProcessId((HANDLE)((ULONG_PTR)FromPrevProcId & ~0x1), &ProcessTo)))
1607 {
1608 GDIOBJ_SetOwnership(CopyTo, ProcessTo);
1609 ObDereferenceObject(ProcessTo);
1610 }
1611 }
1612 else
1613 {
1614 /* mark the object as global */
1615 GDIOBJ_SetOwnership(CopyTo, NULL);
1616 }
1617
1618 (void)InterlockedExchangePointer((PVOID*)&FromEntry->ProcessId, FromPrevProcId);
1619 }
1620 else
1621 {
1622 GDIDBG_TRACELOOP(CopyFrom, PrevThread, Thread);
1623
1624 /* WTF?! The object is already locked by a different thread!
1625 Release the lock, wait a bit and try again! DO reset the pid lock
1626 so we make sure we don't access invalid memory in case the object is
1627 being deleted in the meantime (because we don't have aquired a reference
1628 at this point).
1629 FIXME - we should give up after some time unless we want to wait forever! */
1630 (void)InterlockedExchangePointer((PVOID*)&FromEntry->ProcessId, FromPrevProcId);
1631
1632 DelayExecution();
1633 goto LockHandleFrom;
1634 }
1635 }
1636 else
1637 {
1638 DPRINT1("Attempted to copy ownership from an object 0x%x currently being destroyed!!!\n", CopyFrom);
1639 Ret = FALSE;
1640 }
1641 }
1642 else if (FromPrevProcId == FromLockedProcessId)
1643 {
1644 GDIDBG_TRACELOOP(CopyFrom, FromPrevProcId, FromProcessId);
1645
1646 /* the object is currently locked, wait some time and try again.
1647 FIXME - we shouldn't loop forever! Give up after some time! */
1648 DelayExecution();
1649 /* try again */
1650 goto LockHandleFrom;
1651 }
1652 else if ((HANDLE)((ULONG_PTR)FromPrevProcId & ~0x1) != PsGetCurrentProcessId())
1653 {
1654 /* FIXME - should we really allow copying ownership from objects that we don't even own? */
1655 DPRINT1("WARNING! Changing copying ownership of object 0x%x (pid: 0x%x) to pid 0x%x!!!\n", CopyFrom, (ULONG_PTR)FromPrevProcId & ~0x1, PsGetCurrentProcessId());
1656 FromProcessId = (HANDLE)((ULONG_PTR)FromPrevProcId & ~0x1);
1657 FromLockedProcessId = (HANDLE)((ULONG_PTR)FromProcessId | 0x1);
1658 goto LockHandleFrom;
1659 }
1660 else
1661 {
1662 DPRINT1("Attempted to copy ownership from invalid handle: 0x%x\n", CopyFrom);
1663 Ret = FALSE;
1664 }
1665 }
1666 return Ret;
1667 }
1668
1669 PVOID INTERNAL_CALL
1670 GDI_MapHandleTable(PEPROCESS Process)
1671 {
1672 PVOID MappedView = NULL;
1673 NTSTATUS Status;
1674 LARGE_INTEGER Offset;
1675 ULONG ViewSize = sizeof(GDI_HANDLE_TABLE);
1676
1677 Offset.QuadPart = 0;
1678
1679 ASSERT(GdiTableSection != NULL);
1680 ASSERT(Process != NULL);
1681
1682 Status = MmMapViewOfSection(GdiTableSection,
1683 Process,
1684 &MappedView,
1685 0,
1686 0,
1687 &Offset,
1688 &ViewSize,
1689 ViewUnmap,
1690 SEC_NO_CHANGE,
1691 PAGE_READONLY);
1692
1693 if (!NT_SUCCESS(Status))
1694 return NULL;
1695
1696 return MappedView;
1697 }
1698
1699 /* Locks 2 or 3 objects at a time */
1700 VOID
1701 INTERNAL_CALL
1702 GDIOBJ_LockMultipleObjs(ULONG ulCount,
1703 IN HGDIOBJ* ahObj,
1704 OUT PGDIOBJ* apObj)
1705 {
1706 UINT auiIndices[3] = {0,1,2};
1707 UINT i, tmp ;
1708 BOOL bUnsorted = TRUE;
1709
1710 /* First is greatest */
1711 while(bUnsorted)
1712 {
1713 bUnsorted = FALSE;
1714 for(i=1; i<ulCount; i++)
1715 {
1716 if((ULONG_PTR)ahObj[auiIndices[i-1]] < (ULONG_PTR)ahObj[auiIndices[i]])
1717 {
1718 tmp = auiIndices[i-1];
1719 auiIndices[i-1] = auiIndices[i];
1720 auiIndices[i] = tmp;
1721 bUnsorted = TRUE;
1722 }
1723 }
1724 }
1725
1726 for(i=0;i<ulCount;i++)
1727 apObj[auiIndices[i]] = GDIOBJ_LockObj(ahObj[auiIndices[i]], GDI_OBJECT_TYPE_DONTCARE);
1728 }
1729
1730
1731 /** PUBLIC FUNCTIONS **********************************************************/
1732
1733 INT
1734 FASTCALL
1735 GreGetObjectOwner(HGDIOBJ Handle, GDIOBJTYPE ObjType)
1736 {
1737 INT Ret = GDI_OBJ_HMGR_RESTRICTED;
1738
1739 if ( GDI_HANDLE_GET_INDEX(Handle) < GDI_HANDLE_COUNT )
1740 {
1741 PGDI_TABLE_ENTRY pEntry = &GdiHandleTable->Entries[GDI_HANDLE_GET_INDEX(Handle)];
1742
1743 if (pEntry->ObjectType == ObjType)
1744 {
1745 if (pEntry->FullUnique == (GDI_HANDLE_GET_UPPER(Handle) >> GDI_ENTRY_UPPER_SHIFT))
1746 Ret = pEntry->ProcessId & ~1;
1747 }
1748 }
1749 return Ret;
1750 }
1751
1752 W32KAPI
1753 HANDLE
1754 APIENTRY
1755 NtGdiCreateClientObj(
1756 IN ULONG ulType
1757 )
1758 {
1759 POBJ pObject;
1760 HANDLE handle;
1761
1762 /* Mask out everything that would change the type in a wrong manner */
1763 ulType &= (GDI_HANDLE_TYPE_MASK & ~GDI_HANDLE_BASETYPE_MASK);
1764
1765 /* Allocate a new object */
1766 pObject = GDIOBJ_AllocObjWithHandle(GDI_OBJECT_TYPE_CLIOBJ | ulType);
1767 if (!pObject)
1768 {
1769 return NULL;
1770 }
1771
1772 /* get the handle */
1773 handle = pObject->hHmgr;
1774
1775 /* Unlock it */
1776 GDIOBJ_UnlockObjByPtr(pObject);
1777
1778 return handle;
1779 }
1780
1781 W32KAPI
1782 BOOL
1783 APIENTRY
1784 NtGdiDeleteClientObj(
1785 IN HANDLE h
1786 )
1787 {
1788 /* We first need to get the real type from the handle */
1789 ULONG type = GDI_HANDLE_GET_TYPE(h);
1790
1791 /* Check if it's really a CLIENTOBJ */
1792 if ((type & GDI_HANDLE_BASETYPE_MASK) != GDILoObjType_LO_CLIENTOBJ_TYPE)
1793 {
1794 /* FIXME: SetLastError? */
1795 return FALSE;
1796 }
1797 return GDIOBJ_FreeObjByHandle(h, type);
1798 }
1799
1800 INT
1801 FASTCALL
1802 IntGdiGetObject(IN HANDLE Handle,
1803 IN INT cbCount,
1804 IN LPVOID lpBuffer)
1805 {
1806 PVOID pGdiObject;
1807 INT Result = 0;
1808 DWORD dwObjectType;
1809
1810 pGdiObject = GDIOBJ_LockObj(Handle, GDI_OBJECT_TYPE_DONTCARE);
1811 if (!pGdiObject)
1812 {
1813 EngSetLastError(ERROR_INVALID_HANDLE);
1814 return 0;
1815 }
1816
1817 dwObjectType = GDIOBJ_GetObjectType(Handle);
1818 switch (dwObjectType)
1819 {
1820 case GDI_OBJECT_TYPE_PEN:
1821 case GDI_OBJECT_TYPE_EXTPEN:
1822 Result = PEN_GetObject((PBRUSH) pGdiObject, cbCount, (PLOGPEN) lpBuffer); // IntGdiCreatePenIndirect
1823 break;
1824
1825 case GDI_OBJECT_TYPE_BRUSH:
1826 Result = BRUSH_GetObject((PBRUSH ) pGdiObject, cbCount, (LPLOGBRUSH)lpBuffer);
1827 break;
1828
1829 case GDI_OBJECT_TYPE_BITMAP:
1830 Result = BITMAP_GetObject((SURFACE *) pGdiObject, cbCount, lpBuffer);
1831 break;
1832 case GDI_OBJECT_TYPE_FONT:
1833 Result = FontGetObject((PTEXTOBJ) pGdiObject, cbCount, lpBuffer);
1834 #if 0
1835 // Fix the LOGFONT structure for the stock fonts
1836 if (FIRST_STOCK_HANDLE <= Handle && Handle <= LAST_STOCK_HANDLE)
1837 {
1838 FixStockFontSizeW(Handle, cbCount, lpBuffer);
1839 }
1840 #endif
1841 break;
1842
1843 case GDI_OBJECT_TYPE_PALETTE:
1844 Result = PALETTE_GetObject((PPALETTE) pGdiObject, cbCount, lpBuffer);
1845 break;
1846
1847 default:
1848 DPRINT1("GDI object type 0x%08x not implemented\n", dwObjectType);
1849 break;
1850 }
1851
1852 GDIOBJ_UnlockObjByPtr(pGdiObject);
1853
1854 return Result;
1855 }
1856
1857
1858
1859 W32KAPI
1860 INT
1861 APIENTRY
1862 NtGdiExtGetObjectW(IN HANDLE hGdiObj,
1863 IN INT cbCount,
1864 OUT LPVOID lpBuffer)
1865 {
1866 INT iRetCount = 0;
1867 INT cbCopyCount;
1868 union
1869 {
1870 BITMAP bitmap;
1871 DIBSECTION dibsection;
1872 LOGPEN logpen;
1873 LOGBRUSH logbrush;
1874 LOGFONTW logfontw;
1875 EXTLOGFONTW extlogfontw;
1876 ENUMLOGFONTEXDVW enumlogfontexdvw;
1877 } Object;
1878
1879 // Normalize to the largest supported object size
1880 cbCount = min((UINT)cbCount, sizeof(Object));
1881
1882 // Now do the actual call
1883 iRetCount = IntGdiGetObject(hGdiObj, cbCount, lpBuffer ? &Object : NULL);
1884 cbCopyCount = min((UINT)cbCount, (UINT)iRetCount);
1885
1886 // Make sure we have a buffer and a copy size
1887 if ((cbCopyCount) && (lpBuffer))
1888 {
1889 // Enter SEH for buffer transfer
1890 _SEH2_TRY
1891 {
1892 // Probe the buffer and copy it
1893 ProbeForWrite(lpBuffer, cbCopyCount, sizeof(WORD));
1894 RtlCopyMemory(lpBuffer, &Object, cbCopyCount);
1895 }
1896 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1897 {
1898 // Clear the return value.
1899 // Do *NOT* set last error here!
1900 iRetCount = 0;
1901 }
1902 _SEH2_END;
1903 }
1904 // Return the count
1905 return iRetCount;
1906 }
1907
1908 /* EOF */