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