c88df71617fe844f029db653f57bd2dd9f596982
[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.78 2004/12/17 15:12:37 navaraf 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 /*!
213 * Allocate memory for GDI object and return handle to it.
214 *
215 * \param ObjectType - type of object \ref GDI object types
216 *
217 * \return Handle of the allocated object.
218 *
219 * \note Use GDIOBJ_Lock() to obtain pointer to the new object.
220 * \todo return the object pointer and lock it by default.
221 */
222 HGDIOBJ INTERNAL_CALL
223 #ifdef GDI_DEBUG
224 GDIOBJ_AllocObjDbg(const char* file, int line, ULONG ObjectType)
225 #else /* !GDI_DEBUG */
226 GDIOBJ_AllocObj(ULONG ObjectType)
227 #endif /* GDI_DEBUG */
228 {
229 PW32PROCESS W32Process;
230 PGDIOBJHDR newObject;
231 PPAGED_LOOKASIDE_LIST LookasideList;
232 LONG CurrentProcessId, LockedProcessId;
233 #ifdef GDI_DEBUG
234 ULONG Attempts = 0;
235 #endif
236
237 ASSERT(ObjectType != GDI_OBJECT_TYPE_DONTCARE);
238
239 LookasideList = FindLookasideList(ObjectType);
240 if(LookasideList != NULL)
241 {
242 newObject = ExAllocateFromPagedLookasideList(LookasideList);
243 if(newObject != NULL)
244 {
245 PSLIST_ENTRY FreeEntry;
246 PGDI_TABLE_ENTRY Entry;
247 PGDIOBJ ObjectBody;
248 LONG TypeInfo;
249
250 /* shift the process id to the left so we can use the first bit to lock
251 the object.
252 FIXME - don't shift once ROS' PIDs match with nt! */
253 CurrentProcessId = (LONG)PsGetCurrentProcessId() << 1;
254 LockedProcessId = CurrentProcessId | 0x1;
255 W32Process = PsGetWin32Process();
256
257 newObject->LockingThread = NULL;
258 newObject->Locks = 0;
259
260 #ifdef GDI_DEBUG
261 newObject->createdfile = file;
262 newObject->createdline = line;
263 newObject->lockfile = NULL;
264 newObject->lockline = 0;
265 #endif
266
267 ObjectBody = GDIHdrToBdy(newObject);
268
269 RtlZeroMemory(ObjectBody, GetObjectSize(ObjectType));
270
271 TypeInfo = (ObjectType & 0xFFFF0000) | (ObjectType >> 16);
272
273 FreeEntry = InterlockedPopEntrySList(&HandleTable->FreeEntriesHead);
274 if(FreeEntry != NULL)
275 {
276 LONG PrevProcId;
277 UINT Index;
278 HGDIOBJ Handle;
279
280 /* calculate the entry from the address of the entry in the free slot array */
281 Index = ((ULONG_PTR)FreeEntry - (ULONG_PTR)&HandleTable->FreeEntries[0]) /
282 sizeof(HandleTable->FreeEntries[0]);
283 Entry = &HandleTable->Entries[Index];
284 Handle = (HGDIOBJ)((Index & 0xFFFF) | (ObjectType & 0xFFFF0000));
285
286 LockHandle:
287 PrevProcId = InterlockedCompareExchange(&Entry->ProcessId, LockedProcessId, 0);
288 if(PrevProcId == 0)
289 {
290 ASSERT(Entry->KernelData == NULL);
291
292 Entry->KernelData = ObjectBody;
293
294 /* we found a free entry, no need to exchange this field atomically
295 since we're holding the lock */
296 Entry->Type = TypeInfo;
297
298 /* unlock the entry */
299 InterlockedExchange(&Entry->ProcessId, CurrentProcessId);
300
301 if(W32Process != NULL)
302 {
303 InterlockedIncrement(&W32Process->GDIObjects);
304 }
305
306 DPRINT("GDIOBJ_AllocObj: 0x%x ob: 0x%x\n", Handle, ObjectBody);
307 return Handle;
308 }
309 else
310 {
311 #ifdef GDI_DEBUG
312 if(++Attempts > 20)
313 {
314 DPRINT1("[%d]Waiting on 0x%x\n", Attempts, Handle);
315 }
316 #endif
317 /* damn, someone is trying to lock the object even though it doesn't
318 eve nexist anymore, wait a little and try again!
319 FIXME - we shouldn't loop forever! Give up after some time! */
320 DelayExecution();
321 /* try again */
322 goto LockHandle;
323 }
324 }
325
326 ExFreeToPagedLookasideList(LookasideList, newObject);
327 DPRINT1("Failed to insert gdi object into the handle table, no handles left!\n");
328 }
329 else
330 {
331 DPRINT1("Not enough memory to allocate gdi object!\n");
332 }
333 }
334 else
335 {
336 DPRINT1("Failed to find lookaside list for object type 0x%x\n", ObjectType);
337 }
338 return NULL;
339 }
340
341 /*!
342 * Free memory allocated for the GDI object. For each object type this function calls the
343 * appropriate cleanup routine.
344 *
345 * \param hObj - handle of the object to be deleted.
346 *
347 * \return Returns TRUE if succesful.
348 * \return Returns FALSE if the cleanup routine returned FALSE or the object doesn't belong
349 * to the calling process.
350 */
351 BOOL INTERNAL_CALL
352 #ifdef GDI_DEBUG
353 GDIOBJ_FreeObjDbg(const char* file, int line, HGDIOBJ hObj, DWORD ObjectType)
354 #else /* !GDI_DEBUG */
355 GDIOBJ_FreeObj(HGDIOBJ hObj, DWORD ObjectType)
356 #endif /* GDI_DEBUG */
357 {
358 PGDI_TABLE_ENTRY Entry;
359 PPAGED_LOOKASIDE_LIST LookasideList;
360 LONG ProcessId, LockedProcessId, PrevProcId, ExpectedType;
361 #ifdef GDI_DEBUG
362 ULONG Attempts = 0;
363 #endif
364
365 DPRINT("GDIOBJ_FreeObj: hObj: 0x%08x\n", hObj);
366
367 if(GDI_HANDLE_IS_STOCKOBJ(hObj))
368 {
369 DPRINT1("GDIOBJ_FreeObj() failed, can't delete stock object handle: 0x%x !!!\n", hObj);
370 #ifdef GDI_DEBUG
371 DPRINT1("-> called from %s:%i\n", file, line);
372 #endif
373 return FALSE;
374 }
375
376 /* shift the process id to the left so we can use the first bit to lock the object.
377 FIXME - don't shift once ROS' PIDs match with nt! */
378 ProcessId = (LONG)PsGetCurrentProcessId() << 1;
379 LockedProcessId = ProcessId | 0x1;
380
381 ExpectedType = ((ObjectType != GDI_OBJECT_TYPE_DONTCARE) ? ObjectType : 0);
382
383 Entry = GDI_HANDLE_GET_ENTRY(HandleTable, hObj);
384
385 LockHandle:
386 /* lock the object, we must not delete global objects, so don't exchange the locking
387 process ID to zero when attempting to lock a global object... */
388 PrevProcId = InterlockedCompareExchange(&Entry->ProcessId, LockedProcessId, ProcessId);
389 if(PrevProcId == ProcessId)
390 {
391 if(Entry->Type != 0 && Entry->KernelData != NULL && (ExpectedType == 0 || ((Entry->Type << 16) == ExpectedType)))
392 {
393 PGDIOBJHDR GdiHdr;
394
395 GdiHdr = GDIBdyToHdr(Entry->KernelData);
396
397 if(GdiHdr->LockingThread == NULL)
398 {
399 BOOL Ret;
400 PW32PROCESS W32Process = PsGetWin32Process();
401 ULONG Type = Entry->Type << 16;
402
403 /* Clear the type field so when unlocking the handle it gets finally deleted */
404 Entry->Type = 0;
405 Entry->KernelData = NULL;
406
407 /* unlock the handle slot */
408 InterlockedExchange(&Entry->ProcessId, 0);
409
410 /* push this entry to the free list */
411 InterlockedPushEntrySList(&HandleTable->FreeEntriesHead,
412 &HandleTable->FreeEntries[GDI_ENTRY_TO_INDEX(HandleTable, Entry)]);
413
414 if(W32Process != NULL)
415 {
416 InterlockedDecrement(&W32Process->GDIObjects);
417 }
418
419 /* call the cleanup routine. */
420 Ret = RunCleanupCallback(GDIHdrToBdy(GdiHdr), Type);
421
422 /* Now it's time to free the memory */
423 LookasideList = FindLookasideList(Type);
424 if(LookasideList != NULL)
425 {
426 ExFreeToPagedLookasideList(LookasideList, GdiHdr);
427 }
428
429 return Ret;
430 }
431 else
432 {
433 /* the object is currently locked. just clear the type field so when the
434 object gets unlocked it will be finally deleted from the table. */
435 Entry->Type = 0;
436
437 /* unlock the handle slot */
438 InterlockedExchange(&Entry->ProcessId, 0);
439
440 /* report a successful deletion as the object is actually removed from the table */
441 return TRUE;
442 }
443 }
444 else
445 {
446 if(Entry->Type != 0)
447 {
448 DPRINT1("Attempted to delete object 0x%x, type mismatch (0x%x : 0x%x)\n", hObj, ObjectType, ExpectedType);
449 }
450 else
451 {
452 DPRINT1("Attempted to delete object 0x%x which was already deleted!\n", hObj);
453 }
454 InterlockedExchange(&Entry->ProcessId, PrevProcId);
455 }
456 }
457 else if(PrevProcId == LockedProcessId)
458 {
459 #ifdef GDI_DEBUG
460 if(++Attempts > 20)
461 {
462 DPRINT1("[%d]Waiting on 0x%x\n", Attempts, hObj);
463 }
464 #endif
465 /* the object is currently locked, wait some time and try again.
466 FIXME - we shouldn't loop forever! Give up after some time! */
467 DelayExecution();
468 /* try again */
469 goto LockHandle;
470 }
471 else
472 {
473 if((PrevProcId >> 1) == 0)
474 {
475 DPRINT1("Attempted to free global gdi handle 0x%x, caller needs to get ownership first!!!", hObj);
476 }
477 else
478 {
479 DPRINT1("Attempted to free foreign handle: 0x%x Owner: 0x%x from Caller: 0x%x\n", hObj, PrevProcId >> 1, ProcessId >> 1);
480 }
481 #ifdef GDI_DEBUG
482 DPRINT1("-> called from %s:%i\n", file, line);
483 #endif
484 }
485
486 return FALSE;
487 }
488
489 /*!
490 * Lock multiple objects. Use this function when you need to lock multiple objects and some of them may be
491 * duplicates. You should use this function to avoid trying to lock the same object twice!
492 *
493 * \param pList pointer to the list that contains handles to the objects. You should set hObj and ObjectType fields.
494 * \param nObj number of objects to lock
495 * \return for each entry in pList this function sets pObj field to point to the object.
496 *
497 * \note this function uses an O(n^2) algoritm because we shouldn't need to call it with more than 3 or 4 objects.
498 */
499 BOOL INTERNAL_CALL
500 GDIOBJ_LockMultipleObj(PGDIMULTILOCK pList, INT nObj)
501 {
502 INT i, j;
503 ASSERT( pList );
504 /* FIXME - check for "invalid" handles */
505 /* go through the list checking for duplicate objects */
506 for (i = 0; i < nObj; i++)
507 {
508 pList[i].pObj = NULL;
509 for (j = 0; j < i; j++)
510 {
511 if (pList[i].hObj == pList[j].hObj)
512 {
513 /* already locked, so just copy the pointer to the object */
514 pList[i].pObj = pList[j].pObj;
515 break;
516 }
517 }
518
519 if (NULL == pList[i].pObj)
520 {
521 /* object hasn't been locked, so lock it. */
522 if (NULL != pList[i].hObj)
523 {
524 pList[i].pObj = GDIOBJ_LockObj(pList[i].hObj, pList[i].ObjectType);
525 }
526 }
527 }
528
529 return TRUE;
530 }
531
532 /*!
533 * Unlock multiple objects. Use this function when you need to unlock multiple objects and some of them may be
534 * duplicates.
535 *
536 * \param pList pointer to the list that contains handles to the objects. You should set hObj and ObjectType fields.
537 * \param nObj number of objects to lock
538 *
539 * \note this function uses O(n^2) algoritm because we shouldn't need to call it with more than 3 or 4 objects.
540 */
541 BOOL INTERNAL_CALL
542 GDIOBJ_UnlockMultipleObj(PGDIMULTILOCK pList, INT nObj)
543 {
544 INT i, j;
545 ASSERT(pList);
546
547 /* go through the list checking for duplicate objects */
548 for (i = 0; i < nObj; i++)
549 {
550 if (NULL != pList[i].pObj)
551 {
552 for (j = i + 1; j < nObj; j++)
553 {
554 if ((pList[i].pObj == pList[j].pObj))
555 {
556 /* set the pointer to zero for all duplicates */
557 pList[j].pObj = NULL;
558 }
559 }
560 GDIOBJ_UnlockObj(pList[i].hObj);
561 pList[i].pObj = NULL;
562 }
563 }
564
565 return TRUE;
566 }
567
568 /*!
569 * Initialization of the GDI object engine.
570 */
571 VOID INTERNAL_CALL
572 InitGdiObjectHandleTable (VOID)
573 {
574 DPRINT("InitGdiObjectHandleTable\n");
575
576 HandleTable = GDIOBJ_iAllocHandleTable();
577 DPRINT("HandleTable: %x\n", HandleTable);
578 }
579
580 /*!
581 * Delete GDI object
582 * \param hObject object handle
583 * \return if the function fails the returned value is FALSE.
584 */
585 BOOL STDCALL
586 NtGdiDeleteObject(HGDIOBJ hObject)
587 {
588 DPRINT("NtGdiDeleteObject handle 0x%08x\n", hObject);
589
590 return NULL != hObject
591 ? GDIOBJ_FreeObj(hObject, GDI_OBJECT_TYPE_DONTCARE) : FALSE;
592 }
593
594 /*!
595 * Internal function. Called when the process is destroyed to free the remaining GDI handles.
596 * \param Process - PID of the process that will be destroyed.
597 */
598 BOOL INTERNAL_CALL
599 GDI_CleanupForProcess (struct _EPROCESS *Process)
600 {
601 PGDI_TABLE_ENTRY Entry, End;
602 PEPROCESS CurrentProcess;
603 PW32PROCESS W32Process;
604 LONG ProcId;
605 ULONG Index = RESERVE_ENTRIES_COUNT;
606
607 DPRINT("Starting CleanupForProcess prochandle %x Pid %d\n", Process, Pid);
608 CurrentProcess = PsGetCurrentProcess();
609 if (CurrentProcess != Process)
610 {
611 KeAttachProcess(Process);
612 }
613 W32Process = Process->Win32Process;
614 ASSERT(W32Process);
615
616 if(W32Process->GDIObjects > 0)
617 {
618 /* FIXME - Instead of building the handle here and delete it using GDIOBJ_FreeObj
619 we should delete it directly here! */
620 ProcId = ((LONG)Process->UniqueProcessId << 1);
621
622 End = &HandleTable->Entries[GDI_HANDLE_COUNT];
623 for(Entry = &HandleTable->Entries[RESERVE_ENTRIES_COUNT];
624 Entry < End;
625 Entry++, Index++)
626 {
627 /* ignore the lock bit */
628 if((Entry->ProcessId & ~0x1) == ProcId && Entry->Type != 0)
629 {
630 HGDIOBJ ObjectHandle;
631
632 /* Create the object handle for the entry, the upper 16 bit of the
633 Type field includes the type of the object including the stock
634 object flag - but since stock objects don't have a process id we can
635 simply ignore this fact here. */
636 ObjectHandle = (HGDIOBJ)(Index | (Entry->Type & 0xFFFF0000));
637
638 if(GDIOBJ_FreeObj(ObjectHandle, GDI_OBJECT_TYPE_DONTCARE) &&
639 W32Process->GDIObjects == 0)
640 {
641 /* there are no more gdi handles for this process, bail */
642 break;
643 }
644 }
645 }
646 }
647
648 if (CurrentProcess != Process)
649 {
650 KeDetachProcess();
651 }
652
653 DPRINT("Completed cleanup for process %d\n", Pid);
654
655 return TRUE;
656 }
657
658 /*!
659 * Return pointer to the object by handle.
660 *
661 * \param hObj Object handle
662 * \return Pointer to the object.
663 *
664 * \note Process can only get pointer to the objects it created or global objects.
665 *
666 * \todo Get rid of the ObjectType parameter!
667 */
668 PGDIOBJ INTERNAL_CALL
669 #ifdef GDI_DEBUG
670 GDIOBJ_LockObjDbg (const char* file, int line, HGDIOBJ hObj, DWORD ObjectType)
671 #else /* !GDI_DEBUG */
672 GDIOBJ_LockObj (HGDIOBJ hObj, DWORD ObjectType)
673 #endif /* GDI_DEBUG */
674 {
675 PGDI_TABLE_ENTRY Entry;
676 PETHREAD Thread;
677 LONG ProcessId, LockedProcessId, PrevProcId, ExpectedType;
678 #ifdef GDI_DEBUG
679 ULONG Attempts = 0;
680 #endif
681
682 DPRINT("GDIOBJ_LockObj: hObj: 0x%08x\n", hObj);
683
684 Thread = PsGetCurrentThread();
685
686 /* shift the process id to the left so we can use the first bit to lock the object.
687 FIXME - don't shift once ROS' PIDs match with nt! */
688 ProcessId = (LONG)PsGetCurrentProcessId() << 1;
689 LockedProcessId = ProcessId | 0x1;
690
691 ExpectedType = ((ObjectType != GDI_OBJECT_TYPE_DONTCARE) ? ObjectType : 0);
692
693 Entry = GDI_HANDLE_GET_ENTRY(HandleTable, hObj);
694
695 LockHandle:
696 /* lock the object, we must not delete stock objects, so don't check!!! */
697 PrevProcId = InterlockedCompareExchange(&Entry->ProcessId, LockedProcessId, ProcessId);
698 if(PrevProcId == ProcessId)
699 {
700 LONG EntryType = Entry->Type << 16;
701
702 /* we're locking an object that belongs to our process or it's a global
703 object if ProcessId == 0 here. ProcessId can only be 0 here if it previously
704 failed to lock the object and it turned out to be a global object. */
705 if(EntryType != 0 && Entry->KernelData != NULL && (ExpectedType == 0 || (EntryType == ExpectedType)))
706 {
707 PETHREAD PrevThread;
708 PGDIOBJHDR GdiHdr;
709
710 GdiHdr = GDIBdyToHdr(Entry->KernelData);
711
712 /* save the pointer to the calling thread so we know it was this thread
713 that locked the object. There's no need to do this atomically as we're
714 holding the lock of the handle slot, but this way it's easier ;) */
715 PrevThread = InterlockedCompareExchangePointer(&GdiHdr->LockingThread, Thread, NULL);
716
717 if(PrevThread == NULL || PrevThread == Thread)
718 {
719 if(++GdiHdr->Locks == 1)
720 {
721 #ifdef GDI_DEBUG
722 GdiHdr->lockfile = file;
723 GdiHdr->lockline = line;
724 #endif
725 }
726
727 InterlockedExchange(&Entry->ProcessId, PrevProcId);
728
729 /* we're done, return the object body */
730 return GDIHdrToBdy(GdiHdr);
731 }
732 else
733 {
734 InterlockedExchange(&Entry->ProcessId, PrevProcId);
735
736 #ifdef GDI_DEBUG
737 if(++Attempts > 20)
738 {
739 DPRINT1("[%d]Waiting at %s:%i as 0x%x on 0x%x\n", Attempts, file, line, Thread, PrevThread);
740 }
741 #endif
742
743 DelayExecution();
744 goto LockHandle;
745 }
746 }
747 else
748 {
749 InterlockedExchange(&Entry->ProcessId, PrevProcId);
750
751 if(EntryType == 0)
752 {
753 DPRINT1("Attempted to lock object 0x%x that is deleted!\n", hObj);
754 }
755 else
756 {
757 DPRINT1("Attempted to lock object 0x%x, type mismatch (0x%x : 0x%x)\n", hObj, EntryType, ExpectedType);
758 }
759 #ifdef GDI_DEBUG
760 DPRINT1("-> called from %s:%i\n", file, line);
761 #endif
762 }
763 }
764 else if(PrevProcId == LockedProcessId)
765 {
766 #ifdef GDI_DEBUG
767 if(++Attempts > 20)
768 {
769 DPRINT1("[%d]Waiting from %s:%i on 0x%x\n", Attempts, file, line, hObj);
770 }
771 #endif
772 /* the handle is currently locked, wait some time and try again.
773 FIXME - we shouldn't loop forever! Give up after some time! */
774 DelayExecution();
775 /* try again */
776 goto LockHandle;
777 }
778 else if((PrevProcId & ~0x1) == 0)
779 {
780 /* we're trying to lock a global object, change the ProcessId to 0 and try again */
781 ProcessId = 0x0;
782 LockedProcessId = ProcessId |0x1;
783
784 goto LockHandle;
785 }
786 else
787 {
788 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));
789 #ifdef GDI_DEBUG
790 DPRINT1("-> called from %s:%i\n", file, line);
791 #endif
792 }
793
794 return NULL;
795 }
796
797
798 /*!
799 * Release GDI object. Every object locked by GDIOBJ_LockObj() must be unlocked. You should unlock the object
800 * as soon as you don't need to have access to it's data.
801
802 * \param hObj Object handle
803 *
804 * \note This function performs delayed cleanup. If the object is locked when GDI_FreeObj() is called
805 * then \em this function frees the object when reference count is zero.
806 */
807 BOOL INTERNAL_CALL
808 #ifdef GDI_DEBUG
809 GDIOBJ_UnlockObjDbg (const char* file, int line, HGDIOBJ hObj)
810 #else /* !GDI_DEBUG */
811 GDIOBJ_UnlockObj (HGDIOBJ hObj)
812 #endif /* GDI_DEBUG */
813 {
814 PGDI_TABLE_ENTRY Entry;
815 PETHREAD Thread;
816 LONG ProcessId, LockedProcessId, PrevProcId;
817 #ifdef GDI_DEBUG
818 ULONG Attempts = 0;
819 #endif
820
821 DPRINT("GDIOBJ_UnlockObj: hObj: 0x%08x\n", hObj);
822 Thread = PsGetCurrentThread();
823
824 /* shift the process id to the left so we can use the first bit to lock the object.
825 FIXME - don't shift once ROS' PIDs match with nt! */
826 ProcessId = (LONG)PsGetCurrentProcessId() << 1;
827 LockedProcessId = ProcessId | 0x1;
828
829 Entry = GDI_HANDLE_GET_ENTRY(HandleTable, hObj);
830
831 LockHandle:
832 /* lock the handle, we must not delete stock objects, so don't check!!! */
833 PrevProcId = InterlockedCompareExchange(&Entry->ProcessId, LockedProcessId, ProcessId);
834 if(PrevProcId == ProcessId)
835 {
836 /* we're unlocking an object that belongs to our process or it's a global
837 object if ProcessId == 0 here. ProcessId can only be 0 here if it previously
838 failed to lock the object and it turned out to be a global object. */
839 if(Entry->KernelData != NULL)
840 {
841 PETHREAD PrevThread;
842 PGDIOBJHDR GdiHdr;
843
844 GdiHdr = GDIBdyToHdr(Entry->KernelData);
845
846 PrevThread = GdiHdr->LockingThread;
847 if(PrevThread == Thread)
848 {
849 BOOL Ret;
850
851 if(--GdiHdr->Locks == 0)
852 {
853 GdiHdr->LockingThread = NULL;
854
855 #ifdef GDI_DEBUG
856 GdiHdr->lockfile = NULL;
857 GdiHdr->lockline = 0;
858 #endif
859 }
860
861 if(Entry->Type == 0 && GdiHdr->Locks == 0)
862 {
863 PPAGED_LOOKASIDE_LIST LookasideList;
864 PW32PROCESS W32Process = PsGetWin32Process();
865 DWORD Type = GDI_HANDLE_GET_TYPE(hObj);
866
867 ASSERT(ProcessId != 0); /* must not delete a global handle!!!! */
868
869 /* we should delete the handle */
870 Entry->KernelData = NULL;
871 InterlockedExchange(&Entry->ProcessId, 0);
872
873 InterlockedPushEntrySList(&HandleTable->FreeEntriesHead,
874 &HandleTable->FreeEntries[GDI_ENTRY_TO_INDEX(HandleTable, Entry)]);
875
876 if(W32Process != NULL)
877 {
878 InterlockedDecrement(&W32Process->GDIObjects);
879 }
880
881 /* call the cleanup routine. */
882 Ret = RunCleanupCallback(GDIHdrToBdy(GdiHdr), Type);
883
884 /* Now it's time to free the memory */
885 LookasideList = FindLookasideList(Type);
886 if(LookasideList != NULL)
887 {
888 ExFreeToPagedLookasideList(LookasideList, GdiHdr);
889 }
890 }
891 else
892 {
893 /* remove the handle slot lock */
894 InterlockedExchange(&Entry->ProcessId, PrevProcId);
895 Ret = TRUE;
896 }
897
898 /* we're done*/
899 return Ret;
900 }
901 #ifdef GDI_DEBUG
902 else if(PrevThread != NULL)
903 {
904 DPRINT1("Attempted to unlock object 0x%x, previously locked by other thread (0x%x) from %s:%i (called from %s:%i)\n",
905 hObj, PrevThread, GdiHdr->lockfile, GdiHdr->lockline, file, line);
906 InterlockedExchange(&Entry->ProcessId, PrevProcId);
907 }
908 #endif
909 else
910 {
911 #ifdef GDI_DEBUG
912 if(++Attempts > 20)
913 {
914 DPRINT1("[%d]Waiting at %s:%i as 0x%x on 0x%x\n", Attempts, file, line, Thread, PrevThread);
915 }
916 #endif
917 /* FIXME - we should give up after some time unless we want to wait forever! */
918 InterlockedExchange(&Entry->ProcessId, PrevProcId);
919
920 DelayExecution();
921 goto LockHandle;
922 }
923 }
924 else
925 {
926 InterlockedExchange(&Entry->ProcessId, PrevProcId);
927 DPRINT1("Attempted to unlock object 0x%x that is deleted!\n", hObj);
928 }
929 }
930 else if(PrevProcId == LockedProcessId)
931 {
932 #ifdef GDI_DEBUG
933 if(++Attempts > 20)
934 {
935 DPRINT1("[%d]Waiting from %s:%i on 0x%x\n", Attempts, file, line, hObj);
936 }
937 #endif
938 /* the handle is currently locked, wait some time and try again.
939 FIXME - we shouldn't loop forever! Give up after some time! */
940 DelayExecution();
941 /* try again */
942 goto LockHandle;
943 }
944 else if((PrevProcId & ~0x1) == 0)
945 {
946 /* we're trying to unlock a global object, change the ProcessId to 0 and try again */
947 ProcessId = 0x0;
948 LockedProcessId = ProcessId |0x1;
949
950 goto LockHandle;
951 }
952 else
953 {
954 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));
955 }
956
957 return FALSE;
958 }
959
960 BOOL INTERNAL_CALL
961 GDIOBJ_OwnedByCurrentProcess(HGDIOBJ ObjectHandle)
962 {
963 PGDI_TABLE_ENTRY Entry;
964 LONG ProcessId;
965 BOOL Ret;
966
967 DPRINT("GDIOBJ_OwnedByCurrentProcess: ObjectHandle: 0x%08x\n", ObjectHandle);
968
969 if(!GDI_HANDLE_IS_STOCKOBJ(ObjectHandle))
970 {
971 ProcessId = (LONG)PsGetCurrentProcessId() << 1;
972
973 Entry = GDI_HANDLE_GET_ENTRY(HandleTable, ObjectHandle);
974 Ret = Entry->KernelData != NULL &&
975 Entry->Type != 0 &&
976 (Entry->ProcessId & ~0x1) == ProcessId;
977
978 return Ret;
979 }
980
981 return FALSE;
982 }
983
984 BOOL INTERNAL_CALL
985 GDIOBJ_ConvertToStockObj(HGDIOBJ *hObj)
986 {
987 /*
988 * FIXME !!!!! THIS FUNCTION NEEDS TO BE FIXED - IT IS NOT SAFE WHEN OTHER THREADS
989 * MIGHT ATTEMPT TO LOCK THE OBJECT DURING THIS CALL!!!
990 */
991 PGDI_TABLE_ENTRY Entry;
992 LONG ProcessId, LockedProcessId, PrevProcId;
993 PETHREAD Thread;
994 #ifdef GDI_DEBUG
995 ULONG Attempts = 0;
996 #endif
997
998 ASSERT(hObj);
999
1000 DPRINT("GDIOBJ_ConvertToStockObj: hObj: 0x%08x\n", *hObj);
1001
1002 Thread = PsGetCurrentThread();
1003
1004 if(!GDI_HANDLE_IS_STOCKOBJ(*hObj))
1005 {
1006 /* shift the process id to the left so we can use the first bit to lock the object.
1007 FIXME - don't shift once ROS' PIDs match with nt! */
1008 ProcessId = (LONG)PsGetCurrentProcessId() << 1;
1009 LockedProcessId = ProcessId | 0x1;
1010
1011 Entry = GDI_HANDLE_GET_ENTRY(HandleTable, *hObj);
1012
1013 LockHandle:
1014 /* lock the object, we must not convert stock objects, so don't check!!! */
1015 PrevProcId = InterlockedCompareExchange(&Entry->ProcessId, LockedProcessId, ProcessId);
1016 if(PrevProcId == ProcessId)
1017 {
1018 LONG NewType, PrevType, OldType;
1019
1020 /* we're locking an object that belongs to our process. First calculate
1021 the new object type including the stock object flag and then try to
1022 exchange it.*/
1023 NewType = GDI_HANDLE_GET_TYPE(*hObj);
1024 NewType |= NewType >> 16;
1025 /* This is the type that the object should have right now, save it */
1026 OldType = NewType;
1027 /* As the object should be a stock object, set it's flag, but only in the upper 16 bits */
1028 NewType |= GDI_HANDLE_STOCK_MASK;
1029
1030 /* Try to exchange the type field - but only if the old (previous type) matches! */
1031 PrevType = InterlockedCompareExchange(&Entry->Type, NewType, OldType);
1032 if(PrevType == OldType && Entry->KernelData != NULL)
1033 {
1034 PETHREAD PrevThread;
1035 PGDIOBJHDR GdiHdr;
1036
1037 /* We successfully set the stock object flag.
1038 KernelData should never be NULL here!!! */
1039 ASSERT(Entry->KernelData);
1040
1041 GdiHdr = GDIBdyToHdr(Entry->KernelData);
1042
1043 PrevThread = GdiHdr->LockingThread;
1044 if(PrevThread == NULL || PrevThread == Thread)
1045 {
1046 /* dereference the process' object counter */
1047 if(PrevProcId != GDI_GLOBAL_PROCESS)
1048 {
1049 PEPROCESS OldProcess;
1050 PW32PROCESS W32Process;
1051 NTSTATUS Status;
1052
1053 /* FIXME */
1054 Status = PsLookupProcessByProcessId((PVOID)(PrevProcId >> 1), &OldProcess);
1055 if(NT_SUCCESS(Status))
1056 {
1057 W32Process = OldProcess->Win32Process;
1058 if(W32Process != NULL)
1059 {
1060 InterlockedDecrement(&W32Process->GDIObjects);
1061 }
1062 ObDereferenceObject(OldProcess);
1063 }
1064 }
1065
1066 /* remove the process id lock and make it global */
1067 InterlockedExchange(&Entry->ProcessId, GDI_GLOBAL_PROCESS);
1068
1069 *hObj = (HGDIOBJ)((ULONG)(*hObj) | GDI_HANDLE_STOCK_MASK);
1070
1071 /* we're done, successfully converted the object */
1072 return TRUE;
1073 }
1074 else
1075 {
1076 #ifdef GDI_DEBUG
1077 if(++Attempts > 20)
1078 {
1079 if(GdiHdr->lockfile != NULL)
1080 {
1081 DPRINT1("[%d]Locked %s:%i by 0x%x (we're 0x%x)\n", Attempts, GdiHdr->lockfile, GdiHdr->lockline, PrevThread, Thread);
1082 }
1083 }
1084 #endif
1085 /* WTF?! The object is already locked by a different thread!
1086 Release the lock, wait a bit and try again!
1087 FIXME - we should give up after some time unless we want to wait forever! */
1088 InterlockedExchange(&Entry->ProcessId, PrevProcId);
1089
1090 DelayExecution();
1091 goto LockHandle;
1092 }
1093 }
1094 else
1095 {
1096 DPRINT1("Attempted to convert object 0x%x that is deleted! Should never get here!!!\n", hObj);
1097 }
1098 }
1099 else if(PrevProcId == LockedProcessId)
1100 {
1101 #ifdef GDI_DEBUG
1102 if(++Attempts > 20)
1103 {
1104 DPRINT1("[%d]Waiting on 0x%x\n", Attempts, hObj);
1105 }
1106 #endif
1107 /* the object is currently locked, wait some time and try again.
1108 FIXME - we shouldn't loop forever! Give up after some time! */
1109 DelayExecution();
1110 /* try again */
1111 goto LockHandle;
1112 }
1113 else
1114 {
1115 DPRINT1("Attempted to convert invalid handle: 0x%x\n", hObj);
1116 }
1117 }
1118
1119 return FALSE;
1120 }
1121
1122 void INTERNAL_CALL
1123 GDIOBJ_SetOwnership(HGDIOBJ ObjectHandle, PEPROCESS NewOwner)
1124 {
1125 PGDI_TABLE_ENTRY Entry;
1126 LONG ProcessId, LockedProcessId, PrevProcId;
1127 PETHREAD Thread;
1128 #ifdef GDI_DEBUG
1129 ULONG Attempts = 0;
1130 #endif
1131
1132 DPRINT("GDIOBJ_SetOwnership: hObj: 0x%x, NewProcess: 0x%x\n", ObjectHandle, (NewOwner ? PsGetProcessId(NewOwner) : 0));
1133
1134 Thread = PsGetCurrentThread();
1135
1136 if(!GDI_HANDLE_IS_STOCKOBJ(ObjectHandle))
1137 {
1138 /* shift the process id to the left so we can use the first bit to lock the object.
1139 FIXME - don't shift once ROS' PIDs match with nt! */
1140 ProcessId = (LONG)PsGetCurrentProcessId() << 1;
1141 LockedProcessId = ProcessId | 0x1;
1142
1143 Entry = GDI_HANDLE_GET_ENTRY(HandleTable, ObjectHandle);
1144
1145 LockHandle:
1146 /* lock the object, we must not convert stock objects, so don't check!!! */
1147 PrevProcId = InterlockedCompareExchange(&Entry->ProcessId, ProcessId, LockedProcessId);
1148 if(PrevProcId == ProcessId)
1149 {
1150 PETHREAD PrevThread;
1151
1152 if(Entry->Type != 0 && Entry->KernelData != NULL)
1153 {
1154 PGDIOBJHDR GdiHdr = GDIBdyToHdr(Entry->KernelData);
1155
1156 PrevThread = GdiHdr->LockingThread;
1157 if(PrevThread == NULL || PrevThread == Thread)
1158 {
1159 PEPROCESS OldProcess;
1160 PW32PROCESS W32Process;
1161 NTSTATUS Status;
1162
1163 /* dereference the process' object counter */
1164 /* FIXME */
1165 Status = PsLookupProcessByProcessId((PVOID)(PrevProcId >> 1), &OldProcess);
1166 if(NT_SUCCESS(Status))
1167 {
1168 W32Process = OldProcess->Win32Process;
1169 if(W32Process != NULL)
1170 {
1171 InterlockedDecrement(&W32Process->GDIObjects);
1172 }
1173 ObDereferenceObject(OldProcess);
1174 }
1175
1176 if(NewOwner != NULL)
1177 {
1178 /* FIXME */
1179 ProcessId = (LONG)PsGetProcessId(NewOwner) << 1;
1180
1181 /* Increase the new process' object counter */
1182 W32Process = NewOwner->Win32Process;
1183 if(W32Process != NULL)
1184 {
1185 InterlockedIncrement(&W32Process->GDIObjects);
1186 }
1187 }
1188 else
1189 ProcessId = 0;
1190
1191 /* remove the process id lock and change it to the new process id */
1192 InterlockedExchange(&Entry->ProcessId, ProcessId);
1193
1194 /* we're done! */
1195 return;
1196 }
1197 else
1198 {
1199 #ifdef GDI_DEBUG
1200 if(++Attempts > 20)
1201 {
1202 if(GdiHdr->lockfile != NULL)
1203 {
1204 DPRINT1("[%d]Locked from %s:%i by 0x%x (we're 0x%x)\n", Attempts, GdiHdr->lockfile, GdiHdr->lockline, PrevThread, Thread);
1205 }
1206 }
1207 #endif
1208 /* WTF?! The object is already locked by a different thread!
1209 Release the lock, wait a bit and try again! DO reset the pid lock
1210 so we make sure we don't access invalid memory in case the object is
1211 being deleted in the meantime (because we don't have aquired a reference
1212 at this point).
1213 FIXME - we should give up after some time unless we want to wait forever! */
1214 InterlockedExchange(&Entry->ProcessId, PrevProcId);
1215
1216 DelayExecution();
1217 goto LockHandle;
1218 }
1219 }
1220 else
1221 {
1222 DPRINT1("Attempted to change ownership of an object 0x%x currently being destroyed!!!\n", ObjectHandle);
1223 }
1224 }
1225 else if(PrevProcId == LockedProcessId)
1226 {
1227 #ifdef GDI_DEBUG
1228 if(++Attempts > 20)
1229 {
1230 DPRINT1("[%d]Waiting on 0x%x\n", Attempts, ObjectHandle);
1231 }
1232 #endif
1233 /* the object is currently locked, wait some time and try again.
1234 FIXME - we shouldn't loop forever! Give up after some time! */
1235 DelayExecution();
1236 /* try again */
1237 goto LockHandle;
1238 }
1239 else if((PrevProcId >> 1) == 0)
1240 {
1241 /* allow changing ownership of global objects */
1242 ProcessId = 0;
1243 LockedProcessId = ProcessId | 0x1;
1244 goto LockHandle;
1245 }
1246 else if((PrevProcId >> 1) != (LONG)PsGetCurrentProcessId())
1247 {
1248 DPRINT1("Attempted to change ownership of object 0x%x (pid: 0x%x) from pid 0x%x!!!\n", ObjectHandle, PrevProcId >> 1, PsGetCurrentProcessId());
1249 }
1250 else
1251 {
1252 DPRINT1("Attempted to change owner of invalid handle: 0x%x\n", ObjectHandle);
1253 }
1254 }
1255 }
1256
1257 void INTERNAL_CALL
1258 GDIOBJ_CopyOwnership(HGDIOBJ CopyFrom, HGDIOBJ CopyTo)
1259 {
1260 PGDI_TABLE_ENTRY FromEntry;
1261 PETHREAD Thread;
1262 LONG FromProcessId, FromLockedProcessId, FromPrevProcId;
1263 #ifdef GDI_DEBUG
1264 ULONG Attempts = 0;
1265 #endif
1266
1267 DPRINT("GDIOBJ_CopyOwnership: from: 0x%x, to: 0x%x\n", CopyFrom, CopyTo);
1268
1269 Thread = PsGetCurrentThread();
1270
1271 if(!GDI_HANDLE_IS_STOCKOBJ(CopyFrom) && !GDI_HANDLE_IS_STOCKOBJ(CopyTo))
1272 {
1273 FromEntry = GDI_HANDLE_GET_ENTRY(HandleTable, CopyFrom);
1274
1275 FromProcessId = FromEntry->ProcessId & ~0x1;
1276 FromLockedProcessId = FromProcessId | 0x1;
1277
1278 LockHandleFrom:
1279 /* lock the object, we must not convert stock objects, so don't check!!! */
1280 FromPrevProcId = InterlockedCompareExchange(&FromEntry->ProcessId, FromProcessId, FromLockedProcessId);
1281 if(FromPrevProcId == FromProcessId)
1282 {
1283 PETHREAD PrevThread;
1284 PGDIOBJHDR GdiHdr;
1285
1286 if(FromEntry->Type != 0 && FromEntry->KernelData != NULL)
1287 {
1288 GdiHdr = GDIBdyToHdr(FromEntry->KernelData);
1289
1290 /* save the pointer to the calling thread so we know it was this thread
1291 that locked the object */
1292 PrevThread = GdiHdr->LockingThread;
1293 if(PrevThread == NULL || PrevThread == Thread)
1294 {
1295 /* now let's change the ownership of the target object */
1296
1297 if((FromPrevProcId & ~0x1) != 0)
1298 {
1299 PEPROCESS ProcessTo;
1300 /* FIXME */
1301 if(NT_SUCCESS(PsLookupProcessByProcessId((PVOID)(FromPrevProcId >> 1), &ProcessTo)))
1302 {
1303 GDIOBJ_SetOwnership(CopyTo, ProcessTo);
1304 ObDereferenceObject(ProcessTo);
1305 }
1306 }
1307 else
1308 {
1309 /* mark the object as global */
1310 GDIOBJ_SetOwnership(CopyTo, NULL);
1311 }
1312
1313 InterlockedExchange(&FromEntry->ProcessId, FromPrevProcId);
1314 }
1315 else
1316 {
1317 #ifdef GDI_DEBUG
1318 if(++Attempts > 20)
1319 {
1320 if(GdiHdr->lockfile != NULL)
1321 {
1322 DPRINT1("[%d]Locked from %s:%i by 0x%x (we're 0x%x)\n", Attempts, GdiHdr->lockfile, GdiHdr->lockline, PrevThread, Thread);
1323 }
1324 }
1325 #endif
1326 /* WTF?! The object is already locked by a different thread!
1327 Release the lock, wait a bit and try again! DO reset the pid lock
1328 so we make sure we don't access invalid memory in case the object is
1329 being deleted in the meantime (because we don't have aquired a reference
1330 at this point).
1331 FIXME - we should give up after some time unless we want to wait forever! */
1332 InterlockedExchange(&FromEntry->ProcessId, FromPrevProcId);
1333
1334 DelayExecution();
1335 goto LockHandleFrom;
1336 }
1337 }
1338 else
1339 {
1340 DPRINT1("Attempted to copy ownership from an object 0x%x currently being destroyed!!!\n", CopyFrom);
1341 }
1342 }
1343 else if(FromPrevProcId == FromLockedProcessId)
1344 {
1345 #ifdef GDI_DEBUG
1346 if(++Attempts > 20)
1347 {
1348 DPRINT1("[%d]Waiting on 0x%x\n", Attempts, CopyFrom);
1349 }
1350 #endif
1351 /* the object is currently locked, wait some time and try again.
1352 FIXME - we shouldn't loop forever! Give up after some time! */
1353 DelayExecution();
1354 /* try again */
1355 goto LockHandleFrom;
1356 }
1357 else if((FromPrevProcId >> 1) != (LONG)PsGetCurrentProcessId())
1358 {
1359 /* FIXME - should we really allow copying ownership from objects that we don't even own? */
1360 DPRINT1("WARNING! Changing copying ownership of object 0x%x (pid: 0x%x) to pid 0x%x!!!\n", CopyFrom, FromPrevProcId >> 1, PsGetCurrentProcessId());
1361 FromProcessId = FromPrevProcId & ~0x1;
1362 FromLockedProcessId = FromProcessId | 0x1;
1363 goto LockHandleFrom;
1364 }
1365 else
1366 {
1367 DPRINT1("Attempted to copy ownership from invalid handle: 0x%x\n", CopyFrom);
1368 }
1369 }
1370 }
1371
1372 PVOID INTERNAL_CALL
1373 GDI_MapHandleTable(HANDLE hProcess)
1374 {
1375 DPRINT("%s:%i: %s(): FIXME - Map handle table into the process memory space!\n",
1376 __FILE__, __LINE__, __FUNCTION__);
1377 /* FIXME - Map the entire gdi handle table read-only to userland into the
1378 scope of hProcess and return the pointer */
1379 return NULL;
1380 }
1381
1382 /* EOF */