HACK: hard-coded gdi handle quotas, excepting code paths I haven't found yet, reactos...
[reactos.git] / reactos / subsys / win32k / objects / gdiobj.c
1 /*
2 * ReactOS W32 Subsystem
3 * Copyright (C) 1998 - 2004 ReactOS Team
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 */
19 /*
20 * GDIOBJ.C - GDI object manipulation routines
21 *
22 * $Id: gdiobj.c,v 1.80 2004/12/19 00:03:56 royce Exp $
23 */
24 #include <w32k.h>
25
26 #define NDEBUG
27 #include <debug.h>
28
29 #ifdef __USE_W32API
30 /* F*(&#$ header mess!!!! */
31 HANDLE
32 STDCALL PsGetProcessId(
33 PEPROCESS Process
34 );
35 #endif /* __USE_W32API */
36
37
38
39
40 #define GDI_ENTRY_TO_INDEX(ht, e) \
41 (((ULONG_PTR)(e) - (ULONG_PTR)&((ht)->Entries[0])) / sizeof(GDI_TABLE_ENTRY))
42 #define GDI_HANDLE_GET_ENTRY(HandleTable, h) \
43 (&(HandleTable)->Entries[GDI_HANDLE_GET_INDEX((h))])
44
45 #define GDIBdyToHdr(body) \
46 ((PGDIOBJHDR)(body) - 1)
47 #define GDIHdrToBdy(hdr) \
48 (PGDIOBJ)((PGDIOBJHDR)(hdr) + 1)
49
50 /* apparently the first 10 entries are never used in windows as they are empty */
51 #define RESERVE_ENTRIES_COUNT 10
52
53 typedef struct _GDI_HANDLE_TABLE
54 {
55 PPAGED_LOOKASIDE_LIST LookasideLists;
56
57 SLIST_HEADER FreeEntriesHead;
58 SLIST_ENTRY FreeEntries[((GDI_HANDLE_COUNT * sizeof(GDI_TABLE_ENTRY)) << 3) /
59 (sizeof(SLIST_ENTRY) << 3)];
60
61 GDI_TABLE_ENTRY Entries[GDI_HANDLE_COUNT];
62 } GDI_HANDLE_TABLE, *PGDI_HANDLE_TABLE;
63
64 typedef struct
65 {
66 ULONG Type;
67 ULONG Size;
68 GDICLEANUPPROC CleanupProc;
69 } GDI_OBJ_INFO, *PGDI_OBJ_INFO;
70
71 /*
72 * Dummy GDI Cleanup Callback
73 */
74 BOOL INTERNAL_CALL
75 GDI_CleanupDummy(PVOID ObjectBody)
76 {
77 return TRUE;
78 }
79
80 /* Testing shows that regions are the most used GDIObj type,
81 so put that one first for performance */
82 const
83 GDI_OBJ_INFO ObjInfo[] =
84 {
85 /* Type */ /* Size */ /* CleanupProc */
86 {GDI_OBJECT_TYPE_REGION, sizeof(ROSRGNDATA), RGNDATA_Cleanup},
87 {GDI_OBJECT_TYPE_BITMAP, sizeof(BITMAPOBJ), BITMAP_Cleanup},
88 {GDI_OBJECT_TYPE_DC, sizeof(DC), DC_Cleanup},
89 {GDI_OBJECT_TYPE_PALETTE, sizeof(PALGDI), PALETTE_Cleanup},
90 {GDI_OBJECT_TYPE_BRUSH, sizeof(GDIBRUSHOBJ), BRUSH_Cleanup},
91 {GDI_OBJECT_TYPE_PEN, sizeof(GDIBRUSHOBJ), GDI_CleanupDummy},
92 {GDI_OBJECT_TYPE_FONT, sizeof(TEXTOBJ), GDI_CleanupDummy},
93 {GDI_OBJECT_TYPE_DCE, sizeof(DCE), DCE_Cleanup},
94 /*{GDI_OBJECT_TYPE_DIRECTDRAW, sizeof(DD_DIRECTDRAW), DD_Cleanup},
95 {GDI_OBJECT_TYPE_DD_SURFACE, sizeof(DD_SURFACE), DDSURF_Cleanup},*/
96 {GDI_OBJECT_TYPE_EXTPEN, 0, GDI_CleanupDummy},
97 {GDI_OBJECT_TYPE_METADC, 0, GDI_CleanupDummy},
98 {GDI_OBJECT_TYPE_METAFILE, 0, GDI_CleanupDummy},
99 {GDI_OBJECT_TYPE_ENHMETAFILE, 0, GDI_CleanupDummy},
100 {GDI_OBJECT_TYPE_ENHMETADC, 0, GDI_CleanupDummy},
101 {GDI_OBJECT_TYPE_MEMDC, 0, GDI_CleanupDummy},
102 {GDI_OBJECT_TYPE_EMF, 0, GDI_CleanupDummy}
103 };
104
105 #define OBJTYPE_COUNT (sizeof(ObjInfo) / sizeof(ObjInfo[0]))
106
107 static PGDI_HANDLE_TABLE HandleTable = NULL;
108 static LARGE_INTEGER ShortDelay;
109
110 #define DelayExecution() \
111 DPRINT("%s:%i: Delay\n", __FILE__, __LINE__); \
112 KeDelayExecutionThread(KernelMode, FALSE, &ShortDelay)
113
114 /*!
115 * Allocate GDI object table.
116 * \param Size - number of entries in the object table.
117 */
118 static PGDI_HANDLE_TABLE INTERNAL_CALL
119 GDIOBJ_iAllocHandleTable(VOID)
120 {
121 PGDI_HANDLE_TABLE handleTable;
122 UINT ObjType;
123 UINT i;
124 PGDI_TABLE_ENTRY Entry;
125
126 handleTable = ExAllocatePoolWithTag(NonPagedPool, sizeof(GDI_HANDLE_TABLE), TAG_GDIHNDTBLE);
127 ASSERT( handleTable );
128 RtlZeroMemory(handleTable, sizeof(GDI_HANDLE_TABLE));
129
130 /*
131 * initialize the free entry cache
132 */
133 InitializeSListHead(&handleTable->FreeEntriesHead);
134 Entry = &HandleTable->Entries[RESERVE_ENTRIES_COUNT];
135 for(i = GDI_HANDLE_COUNT - 1; i >= RESERVE_ENTRIES_COUNT; i--)
136 {
137 InterlockedPushEntrySList(&handleTable->FreeEntriesHead, &handleTable->FreeEntries[i]);
138 }
139
140 handleTable->LookasideLists = ExAllocatePoolWithTag(NonPagedPool,
141 OBJTYPE_COUNT * sizeof(PAGED_LOOKASIDE_LIST),
142 TAG_GDIHNDTBLE);
143 if(handleTable->LookasideLists == NULL)
144 {
145 ExFreePool(handleTable);
146 return NULL;
147 }
148
149 for(ObjType = 0; ObjType < OBJTYPE_COUNT; ObjType++)
150 {
151 ExInitializePagedLookasideList(handleTable->LookasideLists + ObjType, NULL, NULL, 0,
152 ObjInfo[ObjType].Size + sizeof(GDIOBJHDR), TAG_GDIOBJ, 0);
153 }
154
155 ShortDelay.QuadPart = -5000LL; /* FIXME - 0.5 ms? */
156
157 return handleTable;
158 }
159
160 static inline PPAGED_LOOKASIDE_LIST
161 FindLookasideList(DWORD ObjectType)
162 {
163 int Index;
164
165 for (Index = 0; Index < OBJTYPE_COUNT; Index++)
166 {
167 if (ObjInfo[Index].Type == ObjectType)
168 {
169 return HandleTable->LookasideLists + Index;
170 }
171 }
172
173 DPRINT1("Can't find lookaside list for object type 0x%08x\n", ObjectType);
174
175 return NULL;
176 }
177
178 static inline BOOL
179 RunCleanupCallback(PGDIOBJ pObj, DWORD ObjectType)
180 {
181 int Index;
182
183 for (Index = 0; Index < OBJTYPE_COUNT; Index++)
184 {
185 if (ObjInfo[Index].Type == ObjectType)
186 {
187 return ((GDICLEANUPPROC)ObjInfo[Index].CleanupProc)(pObj);
188 }
189 }
190
191 DPRINT1("Can't find cleanup callback for object type 0x%08x\n", ObjectType);
192 return TRUE;
193 }
194
195 static inline ULONG
196 GetObjectSize(DWORD ObjectType)
197 {
198 int Index;
199
200 for (Index = 0; Index < OBJTYPE_COUNT; Index++)
201 {
202 if (ObjInfo[Index].Type == ObjectType)
203 {
204 return ObjInfo[Index].Size;
205 }
206 }
207
208 DPRINT1("Can't find size for object type 0x%08x\n", ObjectType);
209 return 0;
210 }
211
212 #ifdef DBG
213
214 static int leak_reported = 0;
215 #define GDI_STACK_LEVELS 4
216 static ULONG GDIHandleAllocator[GDI_STACK_LEVELS][GDI_HANDLE_COUNT];
217 struct DbgOpenGDIHandle
218 {
219 ULONG loc;
220 int count;
221 };
222 #define H 1024
223 static struct DbgOpenGDIHandle h[H];
224
225 void IntDumpHandleTable ( int which )
226 {
227 int i, n = 0, j;
228
229 /* step through GDI handle table and find out who our culprit is... */
230 for ( i = RESERVE_ENTRIES_COUNT; i < GDI_HANDLE_COUNT; i++ )
231 {
232 for ( j = 0; j < n; j++ )
233 {
234 if ( GDIHandleAllocator[which][i] == h[j].loc )
235 break;
236 }
237 if ( j < H )
238 {
239 if ( j == n )
240 {
241 h[j].loc = GDIHandleAllocator[which][i];
242 h[j].count = 1;
243 n = n + 1;
244 }
245 else
246 h[j].count++;
247 }
248 }
249 /* bubble sort time! weeeeee!! */
250 for ( i = 0; i < n-1; i++ )
251 {
252 if ( h[i].count < h[i+1].count )
253 {
254 struct DbgOpenGDIHandle t;
255 t.loc = h[i+1].loc;
256 t.count = h[i+1].count;
257 h[i+1].loc = h[i].loc;
258 h[i+1].count = h[i].count;
259 j = i;
260 while ( j > 0 && h[j-1].count < t.count )
261 j--;
262 h[j] = t;
263 }
264 }
265 /* print the first 30 offenders... */
266 DbgPrint ( "Worst GDI Handle leak offenders - stack trace level %i (out of %i unique locations):\n", which, n );
267 for ( i = 0; i < 30 && i < n; i++ )
268 {
269 DbgPrint ( "\t" );
270 if ( !KeRosPrintAddress ( (PVOID)h[i].loc ) )
271 DbgPrint ( "<%X>", h[i].loc );
272 DbgPrint ( " (%i allocations)\n", h[i].count );
273 }
274 }
275 #endif /* DBG */
276
277 /*!
278 * Allocate memory for GDI object and return handle to it.
279 *
280 * \param ObjectType - type of object \ref GDI object types
281 *
282 * \return Handle of the allocated object.
283 *
284 * \note Use GDIOBJ_Lock() to obtain pointer to the new object.
285 * \todo return the object pointer and lock it by default.
286 */
287 HGDIOBJ INTERNAL_CALL
288 #ifdef GDI_DEBUG
289 GDIOBJ_AllocObjDbg(const char* file, int line, ULONG ObjectType)
290 #else /* !GDI_DEBUG */
291 GDIOBJ_AllocObj(ULONG ObjectType)
292 #endif /* GDI_DEBUG */
293 {
294 PW32PROCESS W32Process;
295 PGDIOBJHDR newObject;
296 PPAGED_LOOKASIDE_LIST LookasideList;
297 LONG CurrentProcessId, LockedProcessId;
298 #ifdef GDI_DEBUG
299 ULONG Attempts = 0;
300 #endif
301
302 W32Process = PsGetWin32Process();
303 /* HACK HACK HACK: simplest-possible quota implementation - don't allow a process
304 to take too many GDI objects, itself. */
305 if ( W32Process && W32Process->GDIObjects >= 0x2710 )
306 return NULL;
307
308 ASSERT(ObjectType != GDI_OBJECT_TYPE_DONTCARE);
309
310 LookasideList = FindLookasideList(ObjectType);
311 if(LookasideList != NULL)
312 {
313 newObject = ExAllocateFromPagedLookasideList(LookasideList);
314 if(newObject != NULL)
315 {
316 PSLIST_ENTRY FreeEntry;
317 PGDI_TABLE_ENTRY Entry;
318 PGDIOBJ ObjectBody;
319 LONG TypeInfo;
320
321 /* shift the process id to the left so we can use the first bit to lock
322 the object.
323 FIXME - don't shift once ROS' PIDs match with nt! */
324 CurrentProcessId = (LONG)PsGetCurrentProcessId() << 1;
325 LockedProcessId = CurrentProcessId | 0x1;
326
327 newObject->LockingThread = NULL;
328 newObject->Locks = 0;
329
330 #ifdef GDI_DEBUG
331 newObject->createdfile = file;
332 newObject->createdline = line;
333 newObject->lockfile = NULL;
334 newObject->lockline = 0;
335 #endif
336
337 ObjectBody = GDIHdrToBdy(newObject);
338
339 RtlZeroMemory(ObjectBody, GetObjectSize(ObjectType));
340
341 TypeInfo = (ObjectType & 0xFFFF0000) | (ObjectType >> 16);
342
343 FreeEntry = InterlockedPopEntrySList(&HandleTable->FreeEntriesHead);
344 if(FreeEntry != NULL)
345 {
346 LONG PrevProcId;
347 UINT Index;
348 HGDIOBJ Handle;
349
350 /* calculate the entry from the address of the entry in the free slot array */
351 Index = ((ULONG_PTR)FreeEntry - (ULONG_PTR)&HandleTable->FreeEntries[0]) /
352 sizeof(HandleTable->FreeEntries[0]);
353 Entry = &HandleTable->Entries[Index];
354 Handle = (HGDIOBJ)((Index & 0xFFFF) | (ObjectType & 0xFFFF0000));
355
356 LockHandle:
357 PrevProcId = InterlockedCompareExchange(&Entry->ProcessId, LockedProcessId, 0);
358 if(PrevProcId == 0)
359 {
360 ASSERT(Entry->KernelData == NULL);
361
362 Entry->KernelData = ObjectBody;
363
364 /* we found a free entry, no need to exchange this field atomically
365 since we're holding the lock */
366 Entry->Type = TypeInfo;
367
368 /* unlock the entry */
369 InterlockedExchange(&Entry->ProcessId, CurrentProcessId);
370
371 #ifdef DBG
372 {
373 PULONG Frame;
374 int which;
375 #if defined __GNUC__
376 __asm__("mov %%ebp, %%ebx" : "=b" (Frame) : );
377 #elif defined(_MSC_VER)
378 __asm mov [Frame], ebp
379 #endif
380 Frame = (PULONG)Frame[0]; /* step out of AllocObj() */
381 for ( which = 0; which < GDI_STACK_LEVELS && Frame[1] != 0 && Frame[1] != 0xDEADBEEF; which++ )
382 {
383 GDIHandleAllocator[which][Index] = Frame[1]; /* step out of AllocObj() */
384 Frame = ((PULONG)Frame[0]);
385 }
386 for ( ; which < GDI_STACK_LEVELS; which++ )
387 GDIHandleAllocator[which][Index] = 0xDEADBEEF;
388 }
389 #endif /* DBG */
390
391 if(W32Process != NULL)
392 {
393 InterlockedIncrement(&W32Process->GDIObjects);
394 }
395
396 DPRINT("GDIOBJ_AllocObj: 0x%x ob: 0x%x\n", Handle, ObjectBody);
397 return Handle;
398 }
399 else
400 {
401 #ifdef GDI_DEBUG
402 if(++Attempts > 20)
403 {
404 DPRINT1("[%d]Waiting on 0x%x\n", Attempts, Handle);
405 }
406 #endif
407 /* damn, someone is trying to lock the object even though it doesn't
408 eve nexist anymore, wait a little and try again!
409 FIXME - we shouldn't loop forever! Give up after some time! */
410 DelayExecution();
411 /* try again */
412 goto LockHandle;
413 }
414 }
415
416 ExFreeToPagedLookasideList(LookasideList, newObject);
417 DPRINT1("Failed to insert gdi object into the handle table, no handles left!\n");
418 #ifdef DBG
419 if ( !leak_reported )
420 {
421 DPRINT1("reporting gdi handle abusers:\n");
422 int which;
423 for ( which = 0; which < GDI_STACK_LEVELS; which++ )
424 IntDumpHandleTable(which);
425 leak_reported = 1;
426 }
427 else
428 {
429 DPRINT1("gdi handle abusers already reported!\n");
430 }
431 #endif /* DBG */
432 }
433 else
434 {
435 DPRINT1("Not enough memory to allocate gdi object!\n");
436 }
437 }
438 else
439 {
440 DPRINT1("Failed to find lookaside list for object type 0x%x\n", ObjectType);
441 }
442 return NULL;
443 }
444
445 /*!
446 * Free memory allocated for the GDI object. For each object type this function calls the
447 * appropriate cleanup routine.
448 *
449 * \param hObj - handle of the object to be deleted.
450 *
451 * \return Returns TRUE if succesful.
452 * \return Returns FALSE if the cleanup routine returned FALSE or the object doesn't belong
453 * to the calling process.
454 */
455 BOOL INTERNAL_CALL
456 #ifdef GDI_DEBUG
457 GDIOBJ_FreeObjDbg(const char* file, int line, HGDIOBJ hObj, DWORD ObjectType)
458 #else /* !GDI_DEBUG */
459 GDIOBJ_FreeObj(HGDIOBJ hObj, DWORD ObjectType)
460 #endif /* GDI_DEBUG */
461 {
462 PGDI_TABLE_ENTRY Entry;
463 PPAGED_LOOKASIDE_LIST LookasideList;
464 LONG ProcessId, LockedProcessId, PrevProcId, ExpectedType;
465 #ifdef GDI_DEBUG
466 ULONG Attempts = 0;
467 #endif
468
469 DPRINT("GDIOBJ_FreeObj: hObj: 0x%08x\n", hObj);
470
471 if(GDI_HANDLE_IS_STOCKOBJ(hObj))
472 {
473 DPRINT1("GDIOBJ_FreeObj() failed, can't delete stock object handle: 0x%x !!!\n", hObj);
474 #ifdef GDI_DEBUG
475 DPRINT1("-> called from %s:%i\n", file, line);
476 #endif
477 return FALSE;
478 }
479
480 /* shift the process id to the left so we can use the first bit to lock the object.
481 FIXME - don't shift once ROS' PIDs match with nt! */
482 ProcessId = (LONG)PsGetCurrentProcessId() << 1;
483 LockedProcessId = ProcessId | 0x1;
484
485 ExpectedType = ((ObjectType != GDI_OBJECT_TYPE_DONTCARE) ? ObjectType : 0);
486
487 Entry = GDI_HANDLE_GET_ENTRY(HandleTable, hObj);
488
489 LockHandle:
490 /* lock the object, we must not delete global objects, so don't exchange the locking
491 process ID to zero when attempting to lock a global object... */
492 PrevProcId = InterlockedCompareExchange(&Entry->ProcessId, LockedProcessId, ProcessId);
493 if(PrevProcId == ProcessId)
494 {
495 if(Entry->Type != 0 && Entry->KernelData != NULL && (ExpectedType == 0 || ((Entry->Type << 16) == ExpectedType)))
496 {
497 PGDIOBJHDR GdiHdr;
498
499 GdiHdr = GDIBdyToHdr(Entry->KernelData);
500
501 if(GdiHdr->LockingThread == NULL)
502 {
503 BOOL Ret;
504 PW32PROCESS W32Process = PsGetWin32Process();
505 ULONG Type = Entry->Type << 16;
506
507 /* Clear the type field so when unlocking the handle it gets finally deleted */
508 Entry->Type = 0;
509 Entry->KernelData = NULL;
510
511 /* unlock the handle slot */
512 InterlockedExchange(&Entry->ProcessId, 0);
513
514 /* push this entry to the free list */
515 InterlockedPushEntrySList(&HandleTable->FreeEntriesHead,
516 &HandleTable->FreeEntries[GDI_ENTRY_TO_INDEX(HandleTable, Entry)]);
517
518 if(W32Process != NULL)
519 {
520 InterlockedDecrement(&W32Process->GDIObjects);
521 }
522
523 /* call the cleanup routine. */
524 Ret = RunCleanupCallback(GDIHdrToBdy(GdiHdr), Type);
525
526 /* Now it's time to free the memory */
527 LookasideList = FindLookasideList(Type);
528 if(LookasideList != NULL)
529 {
530 ExFreeToPagedLookasideList(LookasideList, GdiHdr);
531 }
532
533 return Ret;
534 }
535 else
536 {
537 /* the object is currently locked. just clear the type field so when the
538 object gets unlocked it will be finally deleted from the table. */
539 Entry->Type = 0;
540
541 /* unlock the handle slot */
542 InterlockedExchange(&Entry->ProcessId, 0);
543
544 /* report a successful deletion as the object is actually removed from the table */
545 return TRUE;
546 }
547 }
548 else
549 {
550 if(Entry->Type != 0)
551 {
552 DPRINT1("Attempted to delete object 0x%x, type mismatch (0x%x : 0x%x)\n", hObj, ObjectType, ExpectedType);
553 }
554 else
555 {
556 DPRINT1("Attempted to delete object 0x%x which was already deleted!\n", hObj);
557 }
558 InterlockedExchange(&Entry->ProcessId, PrevProcId);
559 }
560 }
561 else if(PrevProcId == LockedProcessId)
562 {
563 #ifdef GDI_DEBUG
564 if(++Attempts > 20)
565 {
566 DPRINT1("[%d]Waiting on 0x%x\n", Attempts, hObj);
567 }
568 #endif
569 /* the object is currently locked, wait some time and try again.
570 FIXME - we shouldn't loop forever! Give up after some time! */
571 DelayExecution();
572 /* try again */
573 goto LockHandle;
574 }
575 else
576 {
577 if((PrevProcId >> 1) == 0)
578 {
579 DPRINT1("Attempted to free global gdi handle 0x%x, caller needs to get ownership first!!!", hObj);
580 }
581 else
582 {
583 DPRINT1("Attempted to free foreign handle: 0x%x Owner: 0x%x from Caller: 0x%x\n", hObj, PrevProcId >> 1, ProcessId >> 1);
584 }
585 #ifdef GDI_DEBUG
586 DPRINT1("-> called from %s:%i\n", file, line);
587 #endif
588 }
589
590 return FALSE;
591 }
592
593 /*!
594 * Lock multiple objects. Use this function when you need to lock multiple objects and some of them may be
595 * duplicates. You should use this function to avoid trying to lock the same object twice!
596 *
597 * \param pList pointer to the list that contains handles to the objects. You should set hObj and ObjectType fields.
598 * \param nObj number of objects to lock
599 * \return for each entry in pList this function sets pObj field to point to the object.
600 *
601 * \note this function uses an O(n^2) algoritm because we shouldn't need to call it with more than 3 or 4 objects.
602 */
603 BOOL INTERNAL_CALL
604 GDIOBJ_LockMultipleObj(PGDIMULTILOCK pList, INT nObj)
605 {
606 INT i, j;
607 ASSERT( pList );
608 /* FIXME - check for "invalid" handles */
609 /* go through the list checking for duplicate objects */
610 for (i = 0; i < nObj; i++)
611 {
612 pList[i].pObj = NULL;
613 for (j = 0; j < i; j++)
614 {
615 if (pList[i].hObj == pList[j].hObj)
616 {
617 /* already locked, so just copy the pointer to the object */
618 pList[i].pObj = pList[j].pObj;
619 break;
620 }
621 }
622
623 if (NULL == pList[i].pObj)
624 {
625 /* object hasn't been locked, so lock it. */
626 if (NULL != pList[i].hObj)
627 {
628 pList[i].pObj = GDIOBJ_LockObj(pList[i].hObj, pList[i].ObjectType);
629 }
630 }
631 }
632
633 return TRUE;
634 }
635
636 /*!
637 * Unlock multiple objects. Use this function when you need to unlock multiple objects and some of them may be
638 * duplicates.
639 *
640 * \param pList pointer to the list that contains handles to the objects. You should set hObj and ObjectType fields.
641 * \param nObj number of objects to lock
642 *
643 * \note this function uses O(n^2) algoritm because we shouldn't need to call it with more than 3 or 4 objects.
644 */
645 BOOL INTERNAL_CALL
646 GDIOBJ_UnlockMultipleObj(PGDIMULTILOCK pList, INT nObj)
647 {
648 INT i, j;
649 ASSERT(pList);
650
651 /* go through the list checking for duplicate objects */
652 for (i = 0; i < nObj; i++)
653 {
654 if (NULL != pList[i].pObj)
655 {
656 for (j = i + 1; j < nObj; j++)
657 {
658 if ((pList[i].pObj == pList[j].pObj))
659 {
660 /* set the pointer to zero for all duplicates */
661 pList[j].pObj = NULL;
662 }
663 }
664 GDIOBJ_UnlockObj(pList[i].hObj);
665 pList[i].pObj = NULL;
666 }
667 }
668
669 return TRUE;
670 }
671
672 /*!
673 * Initialization of the GDI object engine.
674 */
675 VOID INTERNAL_CALL
676 InitGdiObjectHandleTable (VOID)
677 {
678 DPRINT("InitGdiObjectHandleTable\n");
679
680 HandleTable = GDIOBJ_iAllocHandleTable();
681 DPRINT("HandleTable: %x\n", HandleTable);
682 }
683
684 /*!
685 * Delete GDI object
686 * \param hObject object handle
687 * \return if the function fails the returned value is FALSE.
688 */
689 BOOL STDCALL
690 NtGdiDeleteObject(HGDIOBJ hObject)
691 {
692 DPRINT("NtGdiDeleteObject handle 0x%08x\n", hObject);
693
694 return NULL != hObject
695 ? GDIOBJ_FreeObj(hObject, GDI_OBJECT_TYPE_DONTCARE) : FALSE;
696 }
697
698 /*!
699 * Internal function. Called when the process is destroyed to free the remaining GDI handles.
700 * \param Process - PID of the process that will be destroyed.
701 */
702 BOOL INTERNAL_CALL
703 GDI_CleanupForProcess (struct _EPROCESS *Process)
704 {
705 PGDI_TABLE_ENTRY Entry, End;
706 PEPROCESS CurrentProcess;
707 PW32PROCESS W32Process;
708 LONG ProcId;
709 ULONG Index = RESERVE_ENTRIES_COUNT;
710
711 DPRINT("Starting CleanupForProcess prochandle %x Pid %d\n", Process, Pid);
712 CurrentProcess = PsGetCurrentProcess();
713 if (CurrentProcess != Process)
714 {
715 KeAttachProcess(Process);
716 }
717 W32Process = Process->Win32Process;
718 ASSERT(W32Process);
719
720 if(W32Process->GDIObjects > 0)
721 {
722 /* FIXME - Instead of building the handle here and delete it using GDIOBJ_FreeObj
723 we should delete it directly here! */
724 ProcId = ((LONG)Process->UniqueProcessId << 1);
725
726 End = &HandleTable->Entries[GDI_HANDLE_COUNT];
727 for(Entry = &HandleTable->Entries[RESERVE_ENTRIES_COUNT];
728 Entry < End;
729 Entry++, Index++)
730 {
731 /* ignore the lock bit */
732 if((Entry->ProcessId & ~0x1) == ProcId && Entry->Type != 0)
733 {
734 HGDIOBJ ObjectHandle;
735
736 /* Create the object handle for the entry, the upper 16 bit of the
737 Type field includes the type of the object including the stock
738 object flag - but since stock objects don't have a process id we can
739 simply ignore this fact here. */
740 ObjectHandle = (HGDIOBJ)(Index | (Entry->Type & 0xFFFF0000));
741
742 if(GDIOBJ_FreeObj(ObjectHandle, GDI_OBJECT_TYPE_DONTCARE) &&
743 W32Process->GDIObjects == 0)
744 {
745 /* there are no more gdi handles for this process, bail */
746 break;
747 }
748 }
749 }
750 }
751
752 if (CurrentProcess != Process)
753 {
754 KeDetachProcess();
755 }
756
757 DPRINT("Completed cleanup for process %d\n", Pid);
758
759 return TRUE;
760 }
761
762 /*!
763 * Return pointer to the object by handle.
764 *
765 * \param hObj Object handle
766 * \return Pointer to the object.
767 *
768 * \note Process can only get pointer to the objects it created or global objects.
769 *
770 * \todo Get rid of the ObjectType parameter!
771 */
772 PGDIOBJ INTERNAL_CALL
773 #ifdef GDI_DEBUG
774 GDIOBJ_LockObjDbg (const char* file, int line, HGDIOBJ hObj, DWORD ObjectType)
775 #else /* !GDI_DEBUG */
776 GDIOBJ_LockObj (HGDIOBJ hObj, DWORD ObjectType)
777 #endif /* GDI_DEBUG */
778 {
779 PGDI_TABLE_ENTRY Entry;
780 PETHREAD Thread;
781 LONG ProcessId, LockedProcessId, PrevProcId, ExpectedType;
782 #ifdef GDI_DEBUG
783 ULONG Attempts = 0;
784 #endif
785
786 DPRINT("GDIOBJ_LockObj: hObj: 0x%08x\n", hObj);
787
788 Thread = PsGetCurrentThread();
789
790 /* shift the process id to the left so we can use the first bit to lock the object.
791 FIXME - don't shift once ROS' PIDs match with nt! */
792 ProcessId = (LONG)PsGetCurrentProcessId() << 1;
793 LockedProcessId = ProcessId | 0x1;
794
795 ExpectedType = ((ObjectType != GDI_OBJECT_TYPE_DONTCARE) ? ObjectType : 0);
796
797 Entry = GDI_HANDLE_GET_ENTRY(HandleTable, hObj);
798
799 LockHandle:
800 /* lock the object, we must not delete stock objects, so don't check!!! */
801 PrevProcId = InterlockedCompareExchange(&Entry->ProcessId, LockedProcessId, ProcessId);
802 if(PrevProcId == ProcessId)
803 {
804 LONG EntryType = Entry->Type << 16;
805
806 /* we're locking an object that belongs to our process or it's a global
807 object if ProcessId == 0 here. ProcessId can only be 0 here if it previously
808 failed to lock the object and it turned out to be a global object. */
809 if(EntryType != 0 && Entry->KernelData != NULL && (ExpectedType == 0 || (EntryType == ExpectedType)))
810 {
811 PETHREAD PrevThread;
812 PGDIOBJHDR GdiHdr;
813
814 GdiHdr = GDIBdyToHdr(Entry->KernelData);
815
816 /* save the pointer to the calling thread so we know it was this thread
817 that locked the object. There's no need to do this atomically as we're
818 holding the lock of the handle slot, but this way it's easier ;) */
819 PrevThread = InterlockedCompareExchangePointer(&GdiHdr->LockingThread, Thread, NULL);
820
821 if(PrevThread == NULL || PrevThread == Thread)
822 {
823 if(++GdiHdr->Locks == 1)
824 {
825 #ifdef GDI_DEBUG
826 GdiHdr->lockfile = file;
827 GdiHdr->lockline = line;
828 #endif
829 }
830
831 InterlockedExchange(&Entry->ProcessId, PrevProcId);
832
833 /* we're done, return the object body */
834 return GDIHdrToBdy(GdiHdr);
835 }
836 else
837 {
838 InterlockedExchange(&Entry->ProcessId, PrevProcId);
839
840 #ifdef GDI_DEBUG
841 if(++Attempts > 20)
842 {
843 DPRINT1("[%d]Waiting at %s:%i as 0x%x on 0x%x\n", Attempts, file, line, Thread, PrevThread);
844 }
845 #endif
846
847 DelayExecution();
848 goto LockHandle;
849 }
850 }
851 else
852 {
853 InterlockedExchange(&Entry->ProcessId, PrevProcId);
854
855 if(EntryType == 0)
856 {
857 DPRINT1("Attempted to lock object 0x%x that is deleted!\n", hObj);
858 KeRosDumpStackFrames ( NULL, 20 );
859 }
860 else
861 {
862 DPRINT1("Attempted to lock object 0x%x, type mismatch (0x%x : 0x%x)\n", hObj, EntryType, ExpectedType);
863 KeRosDumpStackFrames ( NULL, 20 );
864 }
865 #ifdef GDI_DEBUG
866 DPRINT1("-> called from %s:%i\n", file, line);
867 #endif
868 }
869 }
870 else if(PrevProcId == LockedProcessId)
871 {
872 #ifdef GDI_DEBUG
873 if(++Attempts > 20)
874 {
875 DPRINT1("[%d]Waiting from %s:%i on 0x%x\n", Attempts, file, line, hObj);
876 }
877 #endif
878 /* the handle is currently locked, wait some time and try again.
879 FIXME - we shouldn't loop forever! Give up after some time! */
880 DelayExecution();
881 /* try again */
882 goto LockHandle;
883 }
884 else if((PrevProcId & ~0x1) == 0)
885 {
886 /* we're trying to lock a global object, change the ProcessId to 0 and try again */
887 ProcessId = 0x0;
888 LockedProcessId = ProcessId |0x1;
889
890 goto LockHandle;
891 }
892 else
893 {
894 DPRINT1("Attempted to lock foreign handle: 0x%x, Owner: 0x%x locked: 0x%x Caller: 0x%x, stockobj: 0x%x\n", hObj, PrevProcId >> 1, PrevProcId & 0x1, PsGetCurrentProcessId(), GDI_HANDLE_IS_STOCKOBJ(hObj));
895 KeRosDumpStackFrames ( NULL, 20 );
896 #ifdef GDI_DEBUG
897 DPRINT1("-> called from %s:%i\n", file, line);
898 #endif
899 }
900
901 return NULL;
902 }
903
904
905 /*!
906 * Release GDI object. Every object locked by GDIOBJ_LockObj() must be unlocked. You should unlock the object
907 * as soon as you don't need to have access to it's data.
908
909 * \param hObj Object handle
910 *
911 * \note This function performs delayed cleanup. If the object is locked when GDI_FreeObj() is called
912 * then \em this function frees the object when reference count is zero.
913 */
914 BOOL INTERNAL_CALL
915 #ifdef GDI_DEBUG
916 GDIOBJ_UnlockObjDbg (const char* file, int line, HGDIOBJ hObj)
917 #else /* !GDI_DEBUG */
918 GDIOBJ_UnlockObj (HGDIOBJ hObj)
919 #endif /* GDI_DEBUG */
920 {
921 PGDI_TABLE_ENTRY Entry;
922 PETHREAD Thread;
923 LONG ProcessId, LockedProcessId, PrevProcId;
924 #ifdef GDI_DEBUG
925 ULONG Attempts = 0;
926 #endif
927
928 DPRINT("GDIOBJ_UnlockObj: hObj: 0x%08x\n", hObj);
929 Thread = PsGetCurrentThread();
930
931 /* shift the process id to the left so we can use the first bit to lock the object.
932 FIXME - don't shift once ROS' PIDs match with nt! */
933 ProcessId = (LONG)PsGetCurrentProcessId() << 1;
934 LockedProcessId = ProcessId | 0x1;
935
936 Entry = GDI_HANDLE_GET_ENTRY(HandleTable, hObj);
937
938 LockHandle:
939 /* lock the handle, we must not delete stock objects, so don't check!!! */
940 PrevProcId = InterlockedCompareExchange(&Entry->ProcessId, LockedProcessId, ProcessId);
941 if(PrevProcId == ProcessId)
942 {
943 /* we're unlocking an object that belongs to our process or it's a global
944 object if ProcessId == 0 here. ProcessId can only be 0 here if it previously
945 failed to lock the object and it turned out to be a global object. */
946 if(Entry->KernelData != NULL)
947 {
948 PETHREAD PrevThread;
949 PGDIOBJHDR GdiHdr;
950
951 GdiHdr = GDIBdyToHdr(Entry->KernelData);
952
953 PrevThread = GdiHdr->LockingThread;
954 if(PrevThread == Thread)
955 {
956 BOOL Ret;
957
958 if(--GdiHdr->Locks == 0)
959 {
960 GdiHdr->LockingThread = NULL;
961
962 #ifdef GDI_DEBUG
963 GdiHdr->lockfile = NULL;
964 GdiHdr->lockline = 0;
965 #endif
966 }
967
968 if(Entry->Type == 0 && GdiHdr->Locks == 0)
969 {
970 PPAGED_LOOKASIDE_LIST LookasideList;
971 PW32PROCESS W32Process = PsGetWin32Process();
972 DWORD Type = GDI_HANDLE_GET_TYPE(hObj);
973
974 ASSERT(ProcessId != 0); /* must not delete a global handle!!!! */
975
976 /* we should delete the handle */
977 Entry->KernelData = NULL;
978 InterlockedExchange(&Entry->ProcessId, 0);
979
980 InterlockedPushEntrySList(&HandleTable->FreeEntriesHead,
981 &HandleTable->FreeEntries[GDI_ENTRY_TO_INDEX(HandleTable, Entry)]);
982
983 if(W32Process != NULL)
984 {
985 InterlockedDecrement(&W32Process->GDIObjects);
986 }
987
988 /* call the cleanup routine. */
989 Ret = RunCleanupCallback(GDIHdrToBdy(GdiHdr), Type);
990
991 /* Now it's time to free the memory */
992 LookasideList = FindLookasideList(Type);
993 if(LookasideList != NULL)
994 {
995 ExFreeToPagedLookasideList(LookasideList, GdiHdr);
996 }
997 }
998 else
999 {
1000 /* remove the handle slot lock */
1001 InterlockedExchange(&Entry->ProcessId, PrevProcId);
1002 Ret = TRUE;
1003 }
1004
1005 /* we're done*/
1006 return Ret;
1007 }
1008 #ifdef GDI_DEBUG
1009 else if(PrevThread != NULL)
1010 {
1011 DPRINT1("Attempted to unlock object 0x%x, previously locked by other thread (0x%x) from %s:%i (called from %s:%i)\n",
1012 hObj, PrevThread, GdiHdr->lockfile, GdiHdr->lockline, file, line);
1013 InterlockedExchange(&Entry->ProcessId, PrevProcId);
1014 }
1015 #endif
1016 else
1017 {
1018 #ifdef GDI_DEBUG
1019 if(++Attempts > 20)
1020 {
1021 DPRINT1("[%d]Waiting at %s:%i as 0x%x on 0x%x\n", Attempts, file, line, Thread, PrevThread);
1022 }
1023 #endif
1024 /* FIXME - we should give up after some time unless we want to wait forever! */
1025 InterlockedExchange(&Entry->ProcessId, PrevProcId);
1026
1027 DelayExecution();
1028 goto LockHandle;
1029 }
1030 }
1031 else
1032 {
1033 InterlockedExchange(&Entry->ProcessId, PrevProcId);
1034 DPRINT1("Attempted to unlock object 0x%x that is deleted!\n", hObj);
1035 }
1036 }
1037 else if(PrevProcId == LockedProcessId)
1038 {
1039 #ifdef GDI_DEBUG
1040 if(++Attempts > 20)
1041 {
1042 DPRINT1("[%d]Waiting from %s:%i on 0x%x\n", Attempts, file, line, hObj);
1043 }
1044 #endif
1045 /* the handle is currently locked, wait some time and try again.
1046 FIXME - we shouldn't loop forever! Give up after some time! */
1047 DelayExecution();
1048 /* try again */
1049 goto LockHandle;
1050 }
1051 else if((PrevProcId & ~0x1) == 0)
1052 {
1053 /* we're trying to unlock a global object, change the ProcessId to 0 and try again */
1054 ProcessId = 0x0;
1055 LockedProcessId = ProcessId |0x1;
1056
1057 goto LockHandle;
1058 }
1059 else
1060 {
1061 DPRINT1("Attempted to unlock foreign handle: 0x%x, Owner: 0x%x locked: 0x%x Caller: 0x%x, stockobj: 0x%x\n", hObj, PrevProcId >> 1, PrevProcId & 0x1, PsGetCurrentProcessId(), GDI_HANDLE_IS_STOCKOBJ(hObj));
1062 }
1063
1064 return FALSE;
1065 }
1066
1067 BOOL INTERNAL_CALL
1068 GDIOBJ_OwnedByCurrentProcess(HGDIOBJ ObjectHandle)
1069 {
1070 PGDI_TABLE_ENTRY Entry;
1071 LONG ProcessId;
1072 BOOL Ret;
1073
1074 DPRINT("GDIOBJ_OwnedByCurrentProcess: ObjectHandle: 0x%08x\n", ObjectHandle);
1075
1076 if(!GDI_HANDLE_IS_STOCKOBJ(ObjectHandle))
1077 {
1078 ProcessId = (LONG)PsGetCurrentProcessId() << 1;
1079
1080 Entry = GDI_HANDLE_GET_ENTRY(HandleTable, ObjectHandle);
1081 Ret = Entry->KernelData != NULL &&
1082 Entry->Type != 0 &&
1083 (Entry->ProcessId & ~0x1) == ProcessId;
1084
1085 return Ret;
1086 }
1087
1088 return FALSE;
1089 }
1090
1091 BOOL INTERNAL_CALL
1092 GDIOBJ_ConvertToStockObj(HGDIOBJ *hObj)
1093 {
1094 /*
1095 * FIXME !!!!! THIS FUNCTION NEEDS TO BE FIXED - IT IS NOT SAFE WHEN OTHER THREADS
1096 * MIGHT ATTEMPT TO LOCK THE OBJECT DURING THIS CALL!!!
1097 */
1098 PGDI_TABLE_ENTRY Entry;
1099 LONG ProcessId, LockedProcessId, PrevProcId;
1100 PETHREAD Thread;
1101 #ifdef GDI_DEBUG
1102 ULONG Attempts = 0;
1103 #endif
1104
1105 ASSERT(hObj);
1106
1107 DPRINT("GDIOBJ_ConvertToStockObj: hObj: 0x%08x\n", *hObj);
1108
1109 Thread = PsGetCurrentThread();
1110
1111 if(!GDI_HANDLE_IS_STOCKOBJ(*hObj))
1112 {
1113 /* shift the process id to the left so we can use the first bit to lock the object.
1114 FIXME - don't shift once ROS' PIDs match with nt! */
1115 ProcessId = (LONG)PsGetCurrentProcessId() << 1;
1116 LockedProcessId = ProcessId | 0x1;
1117
1118 Entry = GDI_HANDLE_GET_ENTRY(HandleTable, *hObj);
1119
1120 LockHandle:
1121 /* lock the object, we must not convert stock objects, so don't check!!! */
1122 PrevProcId = InterlockedCompareExchange(&Entry->ProcessId, LockedProcessId, ProcessId);
1123 if(PrevProcId == ProcessId)
1124 {
1125 LONG NewType, PrevType, OldType;
1126
1127 /* we're locking an object that belongs to our process. First calculate
1128 the new object type including the stock object flag and then try to
1129 exchange it.*/
1130 NewType = GDI_HANDLE_GET_TYPE(*hObj);
1131 NewType |= NewType >> 16;
1132 /* This is the type that the object should have right now, save it */
1133 OldType = NewType;
1134 /* As the object should be a stock object, set it's flag, but only in the upper 16 bits */
1135 NewType |= GDI_HANDLE_STOCK_MASK;
1136
1137 /* Try to exchange the type field - but only if the old (previous type) matches! */
1138 PrevType = InterlockedCompareExchange(&Entry->Type, NewType, OldType);
1139 if(PrevType == OldType && Entry->KernelData != NULL)
1140 {
1141 PETHREAD PrevThread;
1142 PGDIOBJHDR GdiHdr;
1143
1144 /* We successfully set the stock object flag.
1145 KernelData should never be NULL here!!! */
1146 ASSERT(Entry->KernelData);
1147
1148 GdiHdr = GDIBdyToHdr(Entry->KernelData);
1149
1150 PrevThread = GdiHdr->LockingThread;
1151 if(PrevThread == NULL || PrevThread == Thread)
1152 {
1153 /* dereference the process' object counter */
1154 if(PrevProcId != GDI_GLOBAL_PROCESS)
1155 {
1156 PEPROCESS OldProcess;
1157 PW32PROCESS W32Process;
1158 NTSTATUS Status;
1159
1160 /* FIXME */
1161 Status = PsLookupProcessByProcessId((PVOID)(PrevProcId >> 1), &OldProcess);
1162 if(NT_SUCCESS(Status))
1163 {
1164 W32Process = OldProcess->Win32Process;
1165 if(W32Process != NULL)
1166 {
1167 InterlockedDecrement(&W32Process->GDIObjects);
1168 }
1169 ObDereferenceObject(OldProcess);
1170 }
1171 }
1172
1173 /* remove the process id lock and make it global */
1174 InterlockedExchange(&Entry->ProcessId, GDI_GLOBAL_PROCESS);
1175
1176 *hObj = (HGDIOBJ)((ULONG)(*hObj) | GDI_HANDLE_STOCK_MASK);
1177
1178 /* we're done, successfully converted the object */
1179 return TRUE;
1180 }
1181 else
1182 {
1183 #ifdef GDI_DEBUG
1184 if(++Attempts > 20)
1185 {
1186 if(GdiHdr->lockfile != NULL)
1187 {
1188 DPRINT1("[%d]Locked %s:%i by 0x%x (we're 0x%x)\n", Attempts, GdiHdr->lockfile, GdiHdr->lockline, PrevThread, Thread);
1189 }
1190 }
1191 #endif
1192 /* WTF?! The object is already locked by a different thread!
1193 Release the lock, wait a bit and try again!
1194 FIXME - we should give up after some time unless we want to wait forever! */
1195 InterlockedExchange(&Entry->ProcessId, PrevProcId);
1196
1197 DelayExecution();
1198 goto LockHandle;
1199 }
1200 }
1201 else
1202 {
1203 DPRINT1("Attempted to convert object 0x%x that is deleted! Should never get here!!!\n", hObj);
1204 }
1205 }
1206 else if(PrevProcId == LockedProcessId)
1207 {
1208 #ifdef GDI_DEBUG
1209 if(++Attempts > 20)
1210 {
1211 DPRINT1("[%d]Waiting on 0x%x\n", Attempts, hObj);
1212 }
1213 #endif
1214 /* the object is currently locked, wait some time and try again.
1215 FIXME - we shouldn't loop forever! Give up after some time! */
1216 DelayExecution();
1217 /* try again */
1218 goto LockHandle;
1219 }
1220 else
1221 {
1222 DPRINT1("Attempted to convert invalid handle: 0x%x\n", hObj);
1223 }
1224 }
1225
1226 return FALSE;
1227 }
1228
1229 void INTERNAL_CALL
1230 GDIOBJ_SetOwnership(HGDIOBJ ObjectHandle, PEPROCESS NewOwner)
1231 {
1232 PGDI_TABLE_ENTRY Entry;
1233 LONG ProcessId, LockedProcessId, PrevProcId;
1234 PETHREAD Thread;
1235 #ifdef GDI_DEBUG
1236 ULONG Attempts = 0;
1237 #endif
1238
1239 DPRINT("GDIOBJ_SetOwnership: hObj: 0x%x, NewProcess: 0x%x\n", ObjectHandle, (NewOwner ? PsGetProcessId(NewOwner) : 0));
1240
1241 Thread = PsGetCurrentThread();
1242
1243 if(!GDI_HANDLE_IS_STOCKOBJ(ObjectHandle))
1244 {
1245 /* shift the process id to the left so we can use the first bit to lock the object.
1246 FIXME - don't shift once ROS' PIDs match with nt! */
1247 ProcessId = (LONG)PsGetCurrentProcessId() << 1;
1248 LockedProcessId = ProcessId | 0x1;
1249
1250 Entry = GDI_HANDLE_GET_ENTRY(HandleTable, ObjectHandle);
1251
1252 LockHandle:
1253 /* lock the object, we must not convert stock objects, so don't check!!! */
1254 PrevProcId = InterlockedCompareExchange(&Entry->ProcessId, ProcessId, LockedProcessId);
1255 if(PrevProcId == ProcessId)
1256 {
1257 PETHREAD PrevThread;
1258
1259 if(Entry->Type != 0 && Entry->KernelData != NULL)
1260 {
1261 PGDIOBJHDR GdiHdr = GDIBdyToHdr(Entry->KernelData);
1262
1263 PrevThread = GdiHdr->LockingThread;
1264 if(PrevThread == NULL || PrevThread == Thread)
1265 {
1266 PEPROCESS OldProcess;
1267 PW32PROCESS W32Process;
1268 NTSTATUS Status;
1269
1270 /* dereference the process' object counter */
1271 /* FIXME */
1272 Status = PsLookupProcessByProcessId((PVOID)(PrevProcId >> 1), &OldProcess);
1273 if(NT_SUCCESS(Status))
1274 {
1275 W32Process = OldProcess->Win32Process;
1276 if(W32Process != NULL)
1277 {
1278 InterlockedDecrement(&W32Process->GDIObjects);
1279 }
1280 ObDereferenceObject(OldProcess);
1281 }
1282
1283 if(NewOwner != NULL)
1284 {
1285 /* FIXME */
1286 ProcessId = (LONG)PsGetProcessId(NewOwner) << 1;
1287
1288 /* Increase the new process' object counter */
1289 W32Process = NewOwner->Win32Process;
1290 if(W32Process != NULL)
1291 {
1292 InterlockedIncrement(&W32Process->GDIObjects);
1293 }
1294 }
1295 else
1296 ProcessId = 0;
1297
1298 /* remove the process id lock and change it to the new process id */
1299 InterlockedExchange(&Entry->ProcessId, ProcessId);
1300
1301 /* we're done! */
1302 return;
1303 }
1304 else
1305 {
1306 #ifdef GDI_DEBUG
1307 if(++Attempts > 20)
1308 {
1309 if(GdiHdr->lockfile != NULL)
1310 {
1311 DPRINT1("[%d]Locked from %s:%i by 0x%x (we're 0x%x)\n", Attempts, GdiHdr->lockfile, GdiHdr->lockline, PrevThread, Thread);
1312 }
1313 }
1314 #endif
1315 /* WTF?! The object is already locked by a different thread!
1316 Release the lock, wait a bit and try again! DO reset the pid lock
1317 so we make sure we don't access invalid memory in case the object is
1318 being deleted in the meantime (because we don't have aquired a reference
1319 at this point).
1320 FIXME - we should give up after some time unless we want to wait forever! */
1321 InterlockedExchange(&Entry->ProcessId, PrevProcId);
1322
1323 DelayExecution();
1324 goto LockHandle;
1325 }
1326 }
1327 else
1328 {
1329 DPRINT1("Attempted to change ownership of an object 0x%x currently being destroyed!!!\n", ObjectHandle);
1330 }
1331 }
1332 else if(PrevProcId == LockedProcessId)
1333 {
1334 #ifdef GDI_DEBUG
1335 if(++Attempts > 20)
1336 {
1337 DPRINT1("[%d]Waiting on 0x%x\n", Attempts, ObjectHandle);
1338 }
1339 #endif
1340 /* the object is currently locked, wait some time and try again.
1341 FIXME - we shouldn't loop forever! Give up after some time! */
1342 DelayExecution();
1343 /* try again */
1344 goto LockHandle;
1345 }
1346 else if((PrevProcId >> 1) == 0)
1347 {
1348 /* allow changing ownership of global objects */
1349 ProcessId = 0;
1350 LockedProcessId = ProcessId | 0x1;
1351 goto LockHandle;
1352 }
1353 else if((PrevProcId >> 1) != (LONG)PsGetCurrentProcessId())
1354 {
1355 DPRINT1("Attempted to change ownership of object 0x%x (pid: 0x%x) from pid 0x%x!!!\n", ObjectHandle, PrevProcId >> 1, PsGetCurrentProcessId());
1356 }
1357 else
1358 {
1359 DPRINT1("Attempted to change owner of invalid handle: 0x%x\n", ObjectHandle);
1360 }
1361 }
1362 }
1363
1364 void INTERNAL_CALL
1365 GDIOBJ_CopyOwnership(HGDIOBJ CopyFrom, HGDIOBJ CopyTo)
1366 {
1367 PGDI_TABLE_ENTRY FromEntry;
1368 PETHREAD Thread;
1369 LONG FromProcessId, FromLockedProcessId, FromPrevProcId;
1370 #ifdef GDI_DEBUG
1371 ULONG Attempts = 0;
1372 #endif
1373
1374 DPRINT("GDIOBJ_CopyOwnership: from: 0x%x, to: 0x%x\n", CopyFrom, CopyTo);
1375
1376 Thread = PsGetCurrentThread();
1377
1378 if(!GDI_HANDLE_IS_STOCKOBJ(CopyFrom) && !GDI_HANDLE_IS_STOCKOBJ(CopyTo))
1379 {
1380 FromEntry = GDI_HANDLE_GET_ENTRY(HandleTable, CopyFrom);
1381
1382 FromProcessId = FromEntry->ProcessId & ~0x1;
1383 FromLockedProcessId = FromProcessId | 0x1;
1384
1385 LockHandleFrom:
1386 /* lock the object, we must not convert stock objects, so don't check!!! */
1387 FromPrevProcId = InterlockedCompareExchange(&FromEntry->ProcessId, FromProcessId, FromLockedProcessId);
1388 if(FromPrevProcId == FromProcessId)
1389 {
1390 PETHREAD PrevThread;
1391 PGDIOBJHDR GdiHdr;
1392
1393 if(FromEntry->Type != 0 && FromEntry->KernelData != NULL)
1394 {
1395 GdiHdr = GDIBdyToHdr(FromEntry->KernelData);
1396
1397 /* save the pointer to the calling thread so we know it was this thread
1398 that locked the object */
1399 PrevThread = GdiHdr->LockingThread;
1400 if(PrevThread == NULL || PrevThread == Thread)
1401 {
1402 /* now let's change the ownership of the target object */
1403
1404 if((FromPrevProcId & ~0x1) != 0)
1405 {
1406 PEPROCESS ProcessTo;
1407 /* FIXME */
1408 if(NT_SUCCESS(PsLookupProcessByProcessId((PVOID)(FromPrevProcId >> 1), &ProcessTo)))
1409 {
1410 GDIOBJ_SetOwnership(CopyTo, ProcessTo);
1411 ObDereferenceObject(ProcessTo);
1412 }
1413 }
1414 else
1415 {
1416 /* mark the object as global */
1417 GDIOBJ_SetOwnership(CopyTo, NULL);
1418 }
1419
1420 InterlockedExchange(&FromEntry->ProcessId, FromPrevProcId);
1421 }
1422 else
1423 {
1424 #ifdef GDI_DEBUG
1425 if(++Attempts > 20)
1426 {
1427 if(GdiHdr->lockfile != NULL)
1428 {
1429 DPRINT1("[%d]Locked from %s:%i by 0x%x (we're 0x%x)\n", Attempts, GdiHdr->lockfile, GdiHdr->lockline, PrevThread, Thread);
1430 }
1431 }
1432 #endif
1433 /* WTF?! The object is already locked by a different thread!
1434 Release the lock, wait a bit and try again! DO reset the pid lock
1435 so we make sure we don't access invalid memory in case the object is
1436 being deleted in the meantime (because we don't have aquired a reference
1437 at this point).
1438 FIXME - we should give up after some time unless we want to wait forever! */
1439 InterlockedExchange(&FromEntry->ProcessId, FromPrevProcId);
1440
1441 DelayExecution();
1442 goto LockHandleFrom;
1443 }
1444 }
1445 else
1446 {
1447 DPRINT1("Attempted to copy ownership from an object 0x%x currently being destroyed!!!\n", CopyFrom);
1448 }
1449 }
1450 else if(FromPrevProcId == FromLockedProcessId)
1451 {
1452 #ifdef GDI_DEBUG
1453 if(++Attempts > 20)
1454 {
1455 DPRINT1("[%d]Waiting on 0x%x\n", Attempts, CopyFrom);
1456 }
1457 #endif
1458 /* the object is currently locked, wait some time and try again.
1459 FIXME - we shouldn't loop forever! Give up after some time! */
1460 DelayExecution();
1461 /* try again */
1462 goto LockHandleFrom;
1463 }
1464 else if((FromPrevProcId >> 1) != (LONG)PsGetCurrentProcessId())
1465 {
1466 /* FIXME - should we really allow copying ownership from objects that we don't even own? */
1467 DPRINT1("WARNING! Changing copying ownership of object 0x%x (pid: 0x%x) to pid 0x%x!!!\n", CopyFrom, FromPrevProcId >> 1, PsGetCurrentProcessId());
1468 FromProcessId = FromPrevProcId & ~0x1;
1469 FromLockedProcessId = FromProcessId | 0x1;
1470 goto LockHandleFrom;
1471 }
1472 else
1473 {
1474 DPRINT1("Attempted to copy ownership from invalid handle: 0x%x\n", CopyFrom);
1475 }
1476 }
1477 }
1478
1479 PVOID INTERNAL_CALL
1480 GDI_MapHandleTable(HANDLE hProcess)
1481 {
1482 DPRINT("%s:%i: %s(): FIXME - Map handle table into the process memory space!\n",
1483 __FILE__, __LINE__, __FUNCTION__);
1484 /* FIXME - Map the entire gdi handle table read-only to userland into the
1485 scope of hProcess and return the pointer */
1486 return NULL;
1487 }
1488
1489 /* EOF */