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