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