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