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