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