count all gdi handles and serialize removing gdi handles from the table.
[reactos.git] / reactos / subsys / win32k / objects / gdiobj.c
1 /*
2 * ReactOS W32 Subsystem
3 * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003 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.63 2004/03/14 12:16:50 weiden Exp $
23 *
24 */
25
26 #undef WIN32_LEAN_AND_MEAN
27 #define WIN32_NO_STATUS
28 #include <windows.h>
29 #include <ddk/ntddk.h>
30 #include <include/dce.h>
31 #include <include/object.h>
32 #include <win32k/gdiobj.h>
33 #include <win32k/brush.h>
34 #include <win32k/pen.h>
35 #include <win32k/text.h>
36 #include <win32k/dc.h>
37 #include <win32k/bitmaps.h>
38 #include <win32k/region.h>
39 #include <win32k/cursoricon.h>
40 #include <include/palette.h>
41 #include <include/intgdi.h>
42 #include <include/tags.h>
43 #define NDEBUG
44 #include <win32k/debug1.h>
45
46
47 /* enable/disable GDI object caching */
48 #define GDI_CACHE_OBJECTS 0
49 /* count all gdi objects */
50 #define GDI_COUNT_OBJECTS 1
51
52 /*! Size of the GDI handle table
53 * http://www.windevnet.com/documents/s=7290/wdj9902b/9902b.htm
54 * gdi handle table can hold 0x4000 handles
55 */
56 #define GDI_HANDLE_COUNT 0x4000
57
58 #define GDI_GLOBAL_PROCESS ((HANDLE) 0xffffffff)
59
60 #define GDI_HANDLE_INDEX_MASK (GDI_HANDLE_COUNT - 1)
61 #define GDI_HANDLE_TYPE_MASK 0x007f0000
62 #define GDI_HANDLE_STOCK_MASK 0x00800000
63
64 #define GDI_HANDLE_CREATE(i, t) ((HANDLE)(((i) & GDI_HANDLE_INDEX_MASK) | ((t) & GDI_HANDLE_TYPE_MASK)))
65 #define GDI_HANDLE_GET_INDEX(h) (((DWORD)(h)) & GDI_HANDLE_INDEX_MASK)
66 #define GDI_HANDLE_GET_TYPE(h) (((DWORD)(h)) & GDI_HANDLE_TYPE_MASK)
67 #define GDI_HANDLE_IS_TYPE(h, t) ((t) == (((DWORD)(h)) & GDI_HANDLE_TYPE_MASK))
68 #define GDI_HANDLE_IS_STOCKOBJ(h) (0 != (((DWORD)(h)) & GDI_HANDLE_STOCK_MASK))
69 #define GDI_HANDLE_SET_STOCKOBJ(h) ((h) = (HANDLE)(((DWORD)(h)) | GDI_HANDLE_STOCK_MASK))
70
71 #define GDI_TYPE_TO_MAGIC(t) ((WORD) ((t) >> 16))
72 #define GDI_MAGIC_TO_TYPE(m) ((DWORD)(m) << 16)
73
74 /* FIXME Ownership of GDI objects by processes not properly implemented yet */
75 #if 0
76 #define GDI_VALID_OBJECT(h, obj, t, f) \
77 (NULL != (obj) \
78 && (GDI_MAGIC_TO_TYPE((obj)->Magic) == (t) || GDI_OBJECT_TYPE_DONTCARE == (t)) \
79 && (GDI_HANDLE_GET_TYPE((h)) == GDI_MAGIC_TO_TYPE((obj)->Magic)) \
80 && (((obj)->hProcessId == PsGetCurrentProcessId()) \
81 || (GDI_GLOBAL_PROCESS == (obj)->hProcessId) \
82 || ((f) & GDIOBJFLAG_IGNOREPID)))
83 #else
84 #define GDI_VALID_OBJECT(h, obj, t, f) \
85 (NULL != (obj) \
86 && (GDI_MAGIC_TO_TYPE((obj)->Magic) == (t) || GDI_OBJECT_TYPE_DONTCARE == (t)) \
87 && (GDI_HANDLE_GET_TYPE((h)) == GDI_MAGIC_TO_TYPE((obj)->Magic)))
88 #endif
89
90 typedef struct _GDI_HANDLE_TABLE
91 {
92 WORD wTableSize;
93 WORD AllocationHint;
94 #if GDI_COUNT_OBJECTS
95 ULONG HandlesCount;
96 #endif
97 #if GDI_CACHE_OBJECTS
98 ULONG ObjHdrSize;
99 PGDIOBJHDR *CachedObjects;
100 #endif
101 PGDIOBJHDR Handles[1];
102 } GDI_HANDLE_TABLE, *PGDI_HANDLE_TABLE;
103
104 #if GDI_CACHE_OBJECTS
105
106 #define N_OBJ_TYPES 16
107
108 typedef struct
109 {
110 ULONG Type;
111 ULONG Size;
112 } GDI_OBJ_SIZE;
113
114 typedef struct _GDI_DONTCARE
115 {
116 PVOID Data;
117 } GDI_DONTCARE, *PGDI_DONTCARE;
118
119 const
120 GDI_OBJ_SIZE ObjSizes[N_OBJ_TYPES] =
121 {
122 {GDI_OBJECT_TYPE_DC, sizeof(DC)},
123 {GDI_OBJECT_TYPE_DCE, sizeof(DCE)},
124 {GDI_OBJECT_TYPE_PALETTE, sizeof(PALGDI)},
125 {GDI_OBJECT_TYPE_FONT, sizeof(TEXTOBJ)},
126 {GDI_OBJECT_TYPE_BRUSH, sizeof(BRUSHOBJ)},
127 {GDI_OBJECT_TYPE_PEN, sizeof(PENOBJ)},
128 {GDI_OBJECT_TYPE_REGION, sizeof(ROSRGNDATA)},
129 {GDI_OBJECT_TYPE_BITMAP, sizeof(BITMAPOBJ)},
130 /*
131 {GDI_OBJECT_TYPE_DIRECTDRAW, sizeof(DD_DIRECTDRAW)},
132 {GDI_OBJECT_TYPE_DD_SURFACE, sizeof(DD_SURFACE)},
133 */
134 {GDI_OBJECT_TYPE_DONTCARE, sizeof(GDI_DONTCARE)},
135 {GDI_OBJECT_TYPE_EXTPEN, 0},
136 {GDI_OBJECT_TYPE_METADC, 0},
137 {GDI_OBJECT_TYPE_METAFILE, 0},
138 {GDI_OBJECT_TYPE_ENHMETAFILE, 0},
139 {GDI_OBJECT_TYPE_ENHMETADC, 0},
140 {GDI_OBJECT_TYPE_MEMDC, 0},
141 {GDI_OBJECT_TYPE_EMF, 0}
142 };
143
144 ULONG FASTCALL
145 GDI_MaxGdiObjHeaderSize(VOID)
146 {
147 ULONG i, Size;
148 for(i = 0, Size = 0; i < N_OBJ_TYPES; i++)
149 {
150 Size = max(Size, ObjSizes[i].Size);
151 }
152 return Size;
153 }
154
155 #endif
156
157 /* GDI stock objects */
158
159 static LOGBRUSH WhiteBrush =
160 { BS_SOLID, RGB(255,255,255), 0 };
161
162 static LOGBRUSH LtGrayBrush =
163 /* FIXME : this should perhaps be BS_HATCHED, at least for 1 bitperpixel */
164 { BS_SOLID, RGB(192,192,192), 0 };
165
166 static LOGBRUSH GrayBrush =
167 /* FIXME : this should perhaps be BS_HATCHED, at least for 1 bitperpixel */
168 { BS_SOLID, RGB(128,128,128), 0 };
169
170 static LOGBRUSH DkGrayBrush =
171 /* This is BS_HATCHED, for 1 bitperpixel. This makes the spray work in pbrush */
172 /* NB_HATCH_STYLES is an index into HatchBrushes */
173 { BS_HATCHED, RGB(0,0,0), NB_HATCH_STYLES };
174
175 static LOGBRUSH BlackBrush =
176 { BS_SOLID, RGB(0,0,0), 0 };
177
178 static LOGBRUSH NullBrush =
179 { BS_NULL, 0, 0 };
180
181 static LOGPEN WhitePen =
182 { PS_SOLID, { 0, 0 }, RGB(255,255,255) };
183
184 static LOGPEN BlackPen =
185 { PS_SOLID, { 0, 0 }, RGB(0,0,0) };
186
187 static LOGPEN NullPen =
188 { PS_NULL, { 0, 0 }, 0 };
189
190 static LOGFONTW OEMFixedFont =
191 { 11, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, OEM_CHARSET,
192 0, 0, DEFAULT_QUALITY, FIXED_PITCH | FF_MODERN, L"Bitstream Vera Sans Mono" };
193
194 static LOGFONTW AnsiFixedFont =
195 { 11, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, ANSI_CHARSET,
196 0, 0, DEFAULT_QUALITY, FIXED_PITCH | FF_MODERN, L"Bitstream Vera Sans Mono" };
197
198 /*static LOGFONTW AnsiVarFont =
199 *{ 10, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, ANSI_CHARSET,
200 * 0, 0, DEFAULT_QUALITY, VARIABLE_PITCH | FF_SWISS, L"MS Sans Serif" }; */
201
202 static LOGFONTW SystemFont =
203 { 11, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, ANSI_CHARSET,
204 0, 0, DEFAULT_QUALITY, VARIABLE_PITCH | FF_SWISS, L"Bitstream Vera Sans" };
205
206 static LOGFONTW DeviceDefaultFont =
207 { 11, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, ANSI_CHARSET,
208 0, 0, DEFAULT_QUALITY, VARIABLE_PITCH | FF_SWISS, L"Bitstream Vera Sans" };
209
210 static LOGFONTW SystemFixedFont =
211 { 11, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, ANSI_CHARSET,
212 0, 0, DEFAULT_QUALITY, FIXED_PITCH | FF_MODERN, L"Bitstream Vera Sans Mono" };
213
214 /* FIXME: Is this correct? */
215 static LOGFONTW DefaultGuiFont =
216 { 11, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, ANSI_CHARSET,
217 0, 0, DEFAULT_QUALITY, VARIABLE_PITCH | FF_SWISS, L"Bitstream Vera Sans" };
218
219 #define NB_STOCK_OBJECTS (DEFAULT_GUI_FONT + 1)
220
221 static HGDIOBJ *StockObjects[NB_STOCK_OBJECTS];
222 static PGDI_HANDLE_TABLE HandleTable = 0;
223 static FAST_MUTEX HandleTableMutex;
224 static FAST_MUTEX RefCountHandling;
225
226 /*!
227 * Allocate GDI object table.
228 * \param Size - number of entries in the object table.
229 * Notes:: Must be called at IRQL < DISPATCH_LEVEL.
230 */
231 static PGDI_HANDLE_TABLE FASTCALL
232 GDIOBJ_iAllocHandleTable (WORD Size)
233 {
234 PGDI_HANDLE_TABLE handleTable;
235 ULONG MemSize;
236
237 #if GDI_CACHE_OBJECTS
238 MemSize = sizeof(GDI_HANDLE_TABLE) + sizeof(PGDIOBJ) * (Size << 1);
239 #else
240 MemSize = sizeof(GDI_HANDLE_TABLE) + sizeof(PGDIOBJ) * Size;
241 #endif
242
243 /* prevent APC delivery for the *FastMutexUnsafe calls */
244 const KIRQL PrevIrql = KfRaiseIrql(APC_LEVEL);
245 ExAcquireFastMutexUnsafe (&HandleTableMutex);
246 handleTable = ExAllocatePoolWithTag(PagedPool, MemSize, TAG_GDIHNDTBLE);
247 ASSERT( handleTable );
248 memset (handleTable, 0, MemSize);
249 #if GDI_COUNT_OBJECTS
250 handleTable->HandlesCount = 0;
251 #endif
252 #if GDI_CACHE_OBJECTS
253 handleTable->CachedObjects = &handleTable->Handles[Size];
254 handleTable->ObjHdrSize = sizeof(GDIOBJHDR) + GDI_MaxGdiObjHeaderSize();
255 #endif
256 handleTable->wTableSize = Size;
257 handleTable->AllocationHint = 1;
258 ExReleaseFastMutexUnsafe (&HandleTableMutex);
259 KfLowerIrql(PrevIrql);
260
261 return handleTable;
262 }
263
264 /*!
265 * Returns the entry into the handle table by index.
266 */
267 static PGDIOBJHDR FASTCALL
268 GDIOBJ_iGetObjectForIndex(WORD TableIndex)
269 {
270 if (0 == TableIndex || HandleTable->wTableSize < TableIndex)
271 {
272 DPRINT1("Invalid TableIndex %u\n", (unsigned) TableIndex);
273 return NULL;
274 }
275
276 return HandleTable->Handles[TableIndex];
277 }
278
279 /*!
280 * Finds next free entry in the GDI handle table.
281 * \return index into the table is successful, zero otherwise.
282 */
283 static WORD FASTCALL
284 GDIOBJ_iGetNextOpenHandleIndex (void)
285 {
286 WORD tableIndex;
287
288 for (tableIndex = HandleTable->AllocationHint;
289 tableIndex < HandleTable->wTableSize;
290 tableIndex++)
291 {
292 if (HandleTable->Handles[tableIndex] == NULL)
293 {
294 HandleTable->AllocationHint = tableIndex + 1;
295 return tableIndex;
296 }
297 }
298
299 for (tableIndex = 1;
300 tableIndex < HandleTable->AllocationHint;
301 tableIndex++)
302 {
303 if (HandleTable->Handles[tableIndex] == NULL)
304 {
305 HandleTable->AllocationHint = tableIndex + 1;
306 return tableIndex;
307 }
308 }
309
310 return 0;
311 }
312
313 /*!
314 * Allocate memory for GDI object and return handle to it.
315 *
316 * \param Size - size of the GDI object. This shouldn't to include the size of GDIOBJHDR.
317 * The actual amount of allocated memory is sizeof(GDIOBJHDR)+Size
318 * \param ObjectType - type of object \ref GDI object types
319 * \param CleanupProcPtr - Routine to be called on destruction of object
320 *
321 * \return Handle of the allocated object.
322 *
323 * \note Use GDIOBJ_Lock() to obtain pointer to the new object.
324 */
325 HGDIOBJ FASTCALL
326 GDIOBJ_AllocObj(WORD Size, DWORD ObjectType, GDICLEANUPPROC CleanupProc)
327 {
328 PW32PROCESS W32Process;
329 PGDIOBJHDR newObject;
330 WORD Index;
331 #if GDI_CACHE_OBJECTS
332 PGDIOBJHDR *CachedObject;
333 #endif
334
335 ExAcquireFastMutex(&HandleTableMutex);
336 Index = GDIOBJ_iGetNextOpenHandleIndex ();
337 if (0 == Index)
338 {
339 ExReleaseFastMutex(&HandleTableMutex);
340 DPRINT1("Out of GDI handles\n");
341 return NULL;
342 }
343
344 #if GDI_CACHE_OBJECTS
345 CachedObject = (PGDIOBJHDR*)(HandleTable->CachedObjects + Index);
346 if(!(newObject = *CachedObject))
347 {
348 /* allocate new gdi object */
349 newObject = ExAllocatePoolWithTag(PagedPool, HandleTable->ObjHdrSize, TAG_GDIOBJ);
350 if(!newObject)
351 {
352 ExReleaseFastMutex(&HandleTableMutex);
353 DPRINT1("GDIOBJ_AllocObj: failed\n");
354 return NULL;
355 }
356 RtlZeroMemory(newObject, HandleTable->ObjHdrSize);
357 *CachedObject = newObject;
358 }
359 /* Zero the memory when destroying the object */
360 if(ObjectType == GDI_OBJECT_TYPE_DONTCARE)
361 {
362 PVOID *Data;
363 PGDI_DONTCARE dc;
364
365 Data = ExAllocatePoolWithTag(PagedPool, sizeof(PVOID) + Size, TAG_GDIOBJ);
366 if(!Data)
367 {
368 ExReleaseFastMutex(&HandleTableMutex);
369 DPRINT1("GDIOBJ_AllocObj failed: %d bytes for GDI_OBJECT_TYPE_DONTCARE\n", Size + sizeof(PVOID));
370 return NULL;
371 }
372 dc = (PGDI_DONTCARE)((PCHAR)newObject + sizeof(GDIOBJHDR));
373 RtlZeroMemory((PVOID)(Data + 1), Size);
374 ((PGDI_DONTCARE)((PCHAR)newObject + sizeof(GDIOBJHDR)))->Data = Data;
375 *Data = newObject;
376 }
377 #else
378 DPRINT("GDIOBJ_AllocObj: handle: %d, size: %d, type: 0x%08x\n", Index, Size, ObjectType);
379 newObject = ExAllocatePoolWithTag(PagedPool, Size + sizeof (GDIOBJHDR), TAG_GDIOBJ);
380 if (newObject == NULL)
381 {
382 ExReleaseFastMutex(&HandleTableMutex);
383 DPRINT1("GDIOBJ_AllocObj: failed\n");
384 return NULL;
385 }
386 RtlZeroMemory (newObject, Size + sizeof(GDIOBJHDR));
387 #endif
388
389 newObject->wTableIndex = Index;
390
391 newObject->dwCount = 0;
392 newObject->hProcessId = PsGetCurrentProcessId ();
393 newObject->CleanupProc = CleanupProc;
394 newObject->Magic = GDI_TYPE_TO_MAGIC(ObjectType);
395 newObject->lockfile = NULL;
396 newObject->lockline = 0;
397 ExInitializeFastMutex(&newObject->Lock);
398 HandleTable->Handles[Index] = newObject;
399 #if GDI_COUNT_OBJECTS
400 HandleTable->HandlesCount++;
401 #endif
402 ExReleaseFastMutex(&HandleTableMutex);
403
404 W32Process = PsGetCurrentProcess()->Win32Process;
405 if(W32Process)
406 {
407 W32Process->GDIObjects++;
408 }
409
410 return GDI_HANDLE_CREATE(Index, ObjectType);
411 }
412
413 /*!
414 * Free memory allocated for the GDI object. For each object type this function calls the
415 * appropriate cleanup routine.
416 *
417 * \param hObj - handle of the object to be deleted.
418 * \param ObjectType - one of the \ref GDI object types
419 * or GDI_OBJECT_TYPE_DONTCARE.
420 * \param Flag - if set to GDIOBJFLAG_IGNOREPID then the routine doesn't check if the process that
421 * tries to delete the object is the same one that created it.
422 *
423 * \return Returns TRUE if succesful.
424 *
425 * \note You should only use GDIOBJFLAG_IGNOREPID if you are cleaning up after the process that terminated.
426 * \note This function deferres object deletion if it is still in use.
427 */
428 BOOL STDCALL
429 GDIOBJ_FreeObj(HGDIOBJ hObj, DWORD ObjectType, DWORD Flag)
430 {
431 PW32PROCESS W32Process;
432 PGDIOBJHDR objectHeader;
433 PGDIOBJ Obj;
434 BOOL bRet = TRUE;
435
436 objectHeader = GDIOBJ_iGetObjectForIndex(GDI_HANDLE_GET_INDEX(hObj));
437 DPRINT("GDIOBJ_FreeObj: hObj: 0x%08x, object: %x\n", hObj, objectHeader);
438
439 if (! GDI_VALID_OBJECT(hObj, objectHeader, ObjectType, Flag)
440 || GDI_GLOBAL_PROCESS == objectHeader->hProcessId)
441
442 {
443 DPRINT1("Can't delete hObj:0x%08x, type:0x%08x, flag:%d\n", hObj, ObjectType, Flag);
444 return FALSE;
445 }
446
447 DPRINT("FreeObj: locks: %x\n", objectHeader->dwCount );
448 if (!(Flag & GDIOBJFLAG_IGNORELOCK))
449 {
450 /* check that the reference count is zero. if not then set flag
451 * and delete object when releaseobj is called */
452 ExAcquireFastMutex(&RefCountHandling);
453 if ((objectHeader->dwCount & ~0x80000000) > 0 )
454 {
455 DPRINT("GDIOBJ_FreeObj: delayed object deletion: count %d\n", objectHeader->dwCount);
456 objectHeader->dwCount |= 0x80000000;
457 ExReleaseFastMutex(&RefCountHandling);
458 return TRUE;
459 }
460 ExReleaseFastMutex(&RefCountHandling);
461 }
462
463 /* allow object to delete internal data */
464 if (NULL != objectHeader->CleanupProc)
465 {
466 Obj = (PGDIOBJ)((PCHAR)objectHeader + sizeof(GDIOBJHDR));
467 bRet = (*(objectHeader->CleanupProc))(Obj);
468 }
469 #if GDI_CACHE_OBJECTS
470 if(GDI_MAGIC_TO_TYPE(objectHeader->Magic) == GDI_OBJECT_TYPE_DONTCARE)
471 {
472 PGDI_DONTCARE dc = (PGDI_DONTCARE)((PCHAR)objectHeader + sizeof(GDIOBJHDR));
473 if(dc->Data)
474 {
475 ExFreePool(dc->Data);
476 }
477 RtlZeroMemory(objectHeader, HandleTable->ObjHdrSize);
478 }
479 #else
480 ExFreePool(objectHeader);
481 #endif
482 ExAcquireFastMutexUnsafe (&HandleTableMutex);
483 HandleTable->Handles[GDI_HANDLE_GET_INDEX(hObj)] = NULL;
484 #if GDI_COUNT_OBJECTS
485 HandleTable->HandlesCount--;
486 #endif
487 ExReleaseFastMutexUnsafe (&HandleTableMutex);
488
489 W32Process = PsGetCurrentProcess()->Win32Process;
490 if(W32Process)
491 {
492 W32Process->GDIObjects--;
493 }
494
495 return bRet;
496 }
497
498 /*!
499 * Lock multiple objects. Use this function when you need to lock multiple objects and some of them may be
500 * duplicates. You should use this function to avoid trying to lock the same object twice!
501 *
502 * \param pList pointer to the list that contains handles to the objects. You should set hObj and ObjectType fields.
503 * \param nObj number of objects to lock
504 * \return for each entry in pList this function sets pObj field to point to the object.
505 *
506 * \note this function uses an O(n^2) algoritm because we shouldn't need to call it with more than 3 or 4 objects.
507 */
508 BOOL FASTCALL
509 GDIOBJ_LockMultipleObj(PGDIMULTILOCK pList, INT nObj)
510 {
511 INT i, j;
512 ASSERT( pList );
513 /* FIXME - check for "invalid" handles */
514 /* go through the list checking for duplicate objects */
515 for (i = 0; i < nObj; i++)
516 {
517 pList[i].pObj = NULL;
518 for (j = 0; j < i; j++)
519 {
520 if (pList[i].hObj == pList[j].hObj)
521 {
522 /* already locked, so just copy the pointer to the object */
523 pList[i].pObj = pList[j].pObj;
524 break;
525 }
526 }
527
528 if (NULL == pList[i].pObj)
529 {
530 /* object hasn't been locked, so lock it. */
531 if (NULL != pList[i].hObj)
532 {
533 pList[i].pObj = GDIOBJ_LockObj(pList[i].hObj, pList[i].ObjectType);
534 }
535 }
536 }
537
538 return TRUE;
539 }
540
541 /*!
542 * Unlock multiple objects. Use this function when you need to unlock multiple objects and some of them may be
543 * duplicates.
544 *
545 * \param pList pointer to the list that contains handles to the objects. You should set hObj and ObjectType fields.
546 * \param nObj number of objects to lock
547 *
548 * \note this function uses O(n^2) algoritm because we shouldn't need to call it with more than 3 or 4 objects.
549 */
550 BOOL FASTCALL
551 GDIOBJ_UnlockMultipleObj(PGDIMULTILOCK pList, INT nObj)
552 {
553 INT i, j;
554 ASSERT(pList);
555
556 /* go through the list checking for duplicate objects */
557 for (i = 0; i < nObj; i++)
558 {
559 if (NULL != pList[i].pObj)
560 {
561 for (j = i + 1; j < nObj; j++)
562 {
563 if ((pList[i].pObj == pList[j].pObj))
564 {
565 /* set the pointer to zero for all duplicates */
566 pList[j].pObj = NULL;
567 }
568 }
569 GDIOBJ_UnlockObj(pList[i].hObj, pList[i].ObjectType);
570 pList[i].pObj = NULL;
571 }
572 }
573
574 return TRUE;
575 }
576
577 /*!
578 * Get the type of the object.
579 * \param ObjectHandle - handle of the object.
580 * \return One of the \ref GDI object types
581 */
582 DWORD FASTCALL
583 GDIOBJ_GetObjectType(HGDIOBJ ObjectHandle)
584 {
585 PGDIOBJHDR ObjHdr;
586
587 ObjHdr = GDIOBJ_iGetObjectForIndex(GDI_HANDLE_GET_INDEX(ObjectHandle));
588 if (NULL == ObjHdr
589 || ! GDI_VALID_OBJECT(ObjectHandle, ObjHdr, GDI_MAGIC_TO_TYPE(ObjHdr->Magic), 0))
590 {
591 DPRINT1("Invalid ObjectHandle 0x%08x\n", ObjectHandle);
592 return 0;
593 }
594 DPRINT("GDIOBJ_GetObjectType for handle 0x%08x returns 0x%08x\n", ObjectHandle,
595 GDI_MAGIC_TO_TYPE(ObjHdr->Magic));
596
597 return GDI_MAGIC_TO_TYPE(ObjHdr->Magic);
598 }
599
600 /*!
601 * Initialization of the GDI object engine.
602 */
603 VOID FASTCALL
604 InitGdiObjectHandleTable (VOID)
605 {
606 DPRINT("InitGdiObjectHandleTable\n");
607 ExInitializeFastMutex (&HandleTableMutex);
608 ExInitializeFastMutex (&RefCountHandling);
609
610 HandleTable = GDIOBJ_iAllocHandleTable (GDI_HANDLE_COUNT);
611 DPRINT("HandleTable: %x\n", HandleTable );
612
613 InitEngHandleTable();
614 }
615
616 /*!
617 * Creates a bunch of stock objects: brushes, pens, fonts.
618 */
619 VOID FASTCALL
620 CreateStockObjects(void)
621 {
622 unsigned Object;
623
624 DPRINT("Beginning creation of stock objects\n");
625
626 /* Create GDI Stock Objects from the logical structures we've defined */
627
628 StockObjects[WHITE_BRUSH] = IntGdiCreateBrushIndirect(&WhiteBrush);
629 StockObjects[LTGRAY_BRUSH] = IntGdiCreateBrushIndirect(&LtGrayBrush);
630 StockObjects[GRAY_BRUSH] = IntGdiCreateBrushIndirect(&GrayBrush);
631 StockObjects[DKGRAY_BRUSH] = IntGdiCreateBrushIndirect(&DkGrayBrush);
632 StockObjects[BLACK_BRUSH] = IntGdiCreateBrushIndirect(&BlackBrush);
633 StockObjects[NULL_BRUSH] = IntGdiCreateBrushIndirect(&NullBrush);
634
635 StockObjects[WHITE_PEN] = IntGdiCreatePenIndirect(&WhitePen);
636 StockObjects[BLACK_PEN] = IntGdiCreatePenIndirect(&BlackPen);
637 StockObjects[NULL_PEN] = IntGdiCreatePenIndirect(&NullPen);
638
639 (void) TextIntCreateFontIndirect(&OEMFixedFont, (HFONT*)&StockObjects[OEM_FIXED_FONT]);
640 (void) TextIntCreateFontIndirect(&AnsiFixedFont, (HFONT*)&StockObjects[ANSI_FIXED_FONT]);
641 (void) TextIntCreateFontIndirect(&SystemFont, (HFONT*)&StockObjects[SYSTEM_FONT]);
642 (void) TextIntCreateFontIndirect(&DeviceDefaultFont, (HFONT*)&StockObjects[DEVICE_DEFAULT_FONT]);
643 (void) TextIntCreateFontIndirect(&SystemFixedFont, (HFONT*)&StockObjects[SYSTEM_FIXED_FONT]);
644 (void) TextIntCreateFontIndirect(&DefaultGuiFont, (HFONT*)&StockObjects[DEFAULT_GUI_FONT]);
645
646 StockObjects[DEFAULT_PALETTE] = (HGDIOBJ*)PALETTE_Init();
647
648 for (Object = 0; Object < NB_STOCK_OBJECTS; Object++)
649 {
650 if (NULL != StockObjects[Object])
651 {
652 GDIOBJ_SetOwnership(StockObjects[Object], NULL);
653 /* GDI_HANDLE_SET_STOCKOBJ(StockObjects[Object]);*/
654 }
655 }
656
657 DPRINT("Completed creation of stock objects\n");
658 }
659
660 /*!
661 * Return stock object.
662 * \param Object - stock object id.
663 * \return Handle to the object.
664 */
665 HGDIOBJ STDCALL
666 NtGdiGetStockObject(INT Object)
667 {
668 DPRINT("NtGdiGetStockObject index %d\n", Object);
669
670 return ((Object < 0) || (NB_STOCK_OBJECTS <= Object)) ? NULL : StockObjects[Object];
671 }
672
673 /*!
674 * Delete GDI object
675 * \param hObject object handle
676 * \return if the function fails the returned value is FALSE.
677 */
678 BOOL STDCALL
679 NtGdiDeleteObject(HGDIOBJ hObject)
680 {
681 DPRINT("NtGdiDeleteObject handle 0x%08x\n", hObject);
682
683 return NULL != hObject
684 ? GDIOBJ_FreeObj(hObject, GDI_OBJECT_TYPE_DONTCARE, GDIOBJFLAG_DEFAULT) : FALSE;
685 }
686
687 /*!
688 * Internal function. Called when the process is destroyed to free the remaining GDI handles.
689 * \param Process - PID of the process that will be destroyed.
690 */
691 BOOL FASTCALL
692 CleanupForProcess (struct _EPROCESS *Process, INT Pid)
693 {
694 DWORD i;
695 PGDIOBJHDR objectHeader;
696 PEPROCESS CurrentProcess;
697
698 DPRINT("Starting CleanupForProcess prochandle %x Pid %d\n", Process, Pid);
699 CurrentProcess = PsGetCurrentProcess();
700 if (CurrentProcess != Process)
701 {
702 KeAttachProcess(Process);
703 }
704
705 for(i = 1; i < HandleTable->wTableSize; i++)
706 {
707 objectHeader = GDIOBJ_iGetObjectForIndex(i);
708 if (NULL != objectHeader &&
709 (INT) objectHeader->hProcessId == Pid)
710 {
711 DPRINT("CleanupForProcess: %d, process: %d, locks: %d, magic: 0x%x", i, objectHeader->hProcessId, objectHeader->dwCount, objectHeader->Magic);
712 GDIOBJ_FreeObj(GDI_HANDLE_CREATE(i, GDI_MAGIC_TO_TYPE(objectHeader->Magic)),
713 GDI_MAGIC_TO_TYPE(objectHeader->Magic),
714 GDIOBJFLAG_IGNOREPID | GDIOBJFLAG_IGNORELOCK);
715 }
716 }
717
718 if (CurrentProcess != Process)
719 {
720 KeDetachProcess();
721 }
722
723 DPRINT("Completed cleanup for process %d\n", Pid);
724
725 return TRUE;
726 }
727
728 #define GDIOBJ_TRACKLOCKS
729
730 #ifdef GDIOBJ_LockObj
731 #undef GDIOBJ_LockObj
732 PGDIOBJ FASTCALL
733 GDIOBJ_LockObjDbg (const char* file, int line, HGDIOBJ hObj, DWORD ObjectType)
734 {
735 PGDIOBJHDR ObjHdr = GDIOBJ_iGetObjectForIndex(GDI_HANDLE_GET_INDEX(hObj));
736
737 DPRINT("(%s:%i) GDIOBJ_LockObjDbg(0x%08x,0x%08x)\n", file, line, hObj, ObjectType);
738 if (! GDI_VALID_OBJECT(hObj, ObjHdr, ObjectType, GDIOBJFLAG_DEFAULT))
739 {
740 int reason = 0;
741 if (NULL == ObjHdr)
742 {
743 reason = 1;
744 }
745 else if (GDI_MAGIC_TO_TYPE(ObjHdr->Magic) != ObjectType && ObjectType != GDI_OBJECT_TYPE_DONTCARE)
746 {
747 reason = 2;
748 }
749 else if (ObjHdr->hProcessId != GDI_GLOBAL_PROCESS
750 && ObjHdr->hProcessId != PsGetCurrentProcessId())
751 {
752 reason = 3;
753 }
754 else if (GDI_HANDLE_GET_TYPE(hObj) != ObjectType && ObjectType != GDI_OBJECT_TYPE_DONTCARE)
755 {
756 reason = 4;
757 }
758 DPRINT1("GDIOBJ_LockObj failed for 0x%08x, reqtype 0x%08x reason %d\n",
759 hObj, ObjectType, reason );
760 DPRINT1("\tcalled from: %s:%i\n", file, line );
761 return NULL;
762 }
763
764 #ifdef NDEBUG
765 ExAcquireFastMutex(&ObjHdr->Lock);
766 #else /* NDEBUG */
767 if (! ExTryToAcquireFastMutex(&ObjHdr->Lock))
768 {
769 DPRINT1("Caution! GDIOBJ_LockObj trying to lock object 0x%x second time\n", hObj);
770 DPRINT1(" called from: %s:%i\n", file, line);
771 if (NULL != ObjHdr->lockfile)
772 {
773 DPRINT1(" previously locked from: %s:%i\n", ObjHdr->lockfile, ObjHdr->lockline);
774 }
775 ExAcquireFastMutex(&ObjHdr->Lock);
776 DPRINT1(" Disregard previous message about object 0x%x, it's ok\n", hObj);
777 }
778 #endif /* NDEBUG */
779
780 ExAcquireFastMutex(&RefCountHandling);
781 ObjHdr->dwCount++;
782 ExReleaseFastMutex(&RefCountHandling);
783
784 if (NULL == ObjHdr->lockfile)
785 {
786 ObjHdr->lockfile = file;
787 ObjHdr->lockline = line;
788 }
789
790 #if GDI_CACHE_OBJECTS
791 if(GDI_MAGIC_TO_TYPE(ObjHdr->Magic) != GDI_OBJECT_TYPE_DONTCARE)
792 {
793 return (PGDIOBJ)((PCHAR)ObjHdr + sizeof(GDIOBJHDR));
794 }
795 return (PGDIOBJ)((PVOID)(((PGDI_DONTCARE)((PCHAR)ObjHdr + sizeof(GDIOBJHDR)))->Data) + 1);
796 #else
797 return (PGDIOBJ)((PCHAR)ObjHdr + sizeof(GDIOBJHDR));
798 #endif
799 }
800 #endif//GDIOBJ_LockObj
801
802 #ifdef GDIOBJ_UnlockObj
803 #undef GDIOBJ_UnlockObj
804 BOOL FASTCALL
805 GDIOBJ_UnlockObjDbg (const char* file, int line, HGDIOBJ hObj, DWORD ObjectType)
806 {
807 PGDIOBJHDR ObjHdr = GDIOBJ_iGetObjectForIndex(GDI_HANDLE_GET_INDEX(hObj));
808
809 if (! GDI_VALID_OBJECT(hObj, ObjHdr, ObjectType, GDIOBJFLAG_DEFAULT))
810 {
811 DPRINT1("GDIBOJ_UnlockObj failed for 0x%08x, reqtype 0x%08x\n",
812 hObj, ObjectType);
813 DPRINT1("\tcalled from: %s:%i\n", file, line);
814 return FALSE;
815 }
816 DPRINT("(%s:%i) GDIOBJ_UnlockObj(0x%08x,0x%08x)\n", file, line, hObj, ObjectType);
817 ObjHdr->lockfile = NULL;
818 ObjHdr->lockline = 0;
819
820 return GDIOBJ_UnlockObj(hObj, ObjectType);
821 }
822 #endif//GDIOBJ_LockObj
823
824 /*!
825 * Return pointer to the object by handle.
826 *
827 * \param hObj Object handle
828 * \param ObjectType one of the object types defined in \ref GDI object types
829 * \return Pointer to the object.
830 *
831 * \note Process can only get pointer to the objects it created or global objects.
832 *
833 * \todo Don't allow to lock the objects twice! Synchronization!
834 */
835 PGDIOBJ FASTCALL
836 GDIOBJ_LockObj(HGDIOBJ hObj, DWORD ObjectType)
837 {
838 PGDIOBJHDR ObjHdr = GDIOBJ_iGetObjectForIndex(GDI_HANDLE_GET_INDEX(hObj));
839
840 DPRINT("GDIOBJ_LockObj: hObj: 0x%08x, type: 0x%08x, objhdr: %x\n", hObj, ObjectType, ObjHdr);
841 if (! GDI_VALID_OBJECT(hObj, ObjHdr, ObjectType, GDIOBJFLAG_DEFAULT))
842 {
843 DPRINT1("GDIBOJ_LockObj failed for 0x%08x, type 0x%08x\n",
844 hObj, ObjectType);
845 return NULL;
846 }
847
848 ExAcquireFastMutex(&ObjHdr->Lock);
849
850 ExAcquireFastMutex(&RefCountHandling);
851 ObjHdr->dwCount++;
852 ExReleaseFastMutex(&RefCountHandling);
853 return (PGDIOBJ)((PCHAR)ObjHdr + sizeof(GDIOBJHDR));
854 }
855
856 /*!
857 * Release GDI object. Every object locked by GDIOBJ_LockObj() must be unlocked. You should unlock the object
858 * as soon as you don't need to have access to it's data.
859
860 * \param hObj Object handle
861 * \param ObjectType one of the object types defined in \ref GDI object types
862 *
863 * \note This function performs delayed cleanup. If the object is locked when GDI_FreeObj() is called
864 * then \em this function frees the object when reference count is zero.
865 *
866 * \todo Change synchronization algorithm.
867 */
868 #undef GDIOBJ_UnlockObj
869 BOOL FASTCALL
870 GDIOBJ_UnlockObj(HGDIOBJ hObj, DWORD ObjectType)
871 {
872 PGDIOBJHDR ObjHdr = GDIOBJ_iGetObjectForIndex(GDI_HANDLE_GET_INDEX(hObj));
873
874 DPRINT("GDIOBJ_UnlockObj: hObj: 0x%08x, type: 0x%08x, objhdr: %x\n", hObj, ObjectType, ObjHdr);
875 if (! GDI_VALID_OBJECT(hObj, ObjHdr, ObjectType, GDIOBJFLAG_DEFAULT))
876 {
877 DPRINT1( "GDIOBJ_UnLockObj: failed\n");
878 return FALSE;
879 }
880
881 ExReleaseFastMutex(&ObjHdr->Lock);
882
883 ExAcquireFastMutex(&RefCountHandling);
884 if (0 == (ObjHdr->dwCount & ~0x80000000))
885 {
886 ExReleaseFastMutex(&RefCountHandling);
887 DPRINT1( "GDIOBJ_UnLockObj: unlock object (0x%x) that is not locked\n", hObj );
888 return FALSE;
889 }
890
891 ObjHdr->dwCount--;
892
893 if (ObjHdr->dwCount == 0x80000000)
894 {
895 //delayed object release
896 ObjHdr->dwCount = 0;
897 ExReleaseFastMutex(&RefCountHandling);
898 DPRINT("GDIOBJ_UnlockObj: delayed delete\n");
899 return GDIOBJ_FreeObj(hObj, ObjectType, GDIOBJFLAG_DEFAULT);
900 }
901 ExReleaseFastMutex(&RefCountHandling);
902
903 return TRUE;
904 }
905
906 BOOL FASTCALL
907 GDIOBJ_OwnedByCurrentProcess(HGDIOBJ ObjectHandle)
908 {
909 PGDIOBJHDR ObjHdr = GDIOBJ_iGetObjectForIndex(GDI_HANDLE_GET_INDEX(ObjectHandle));
910
911 DPRINT("GDIOBJ_OwnedByCurrentProcess: ObjectHandle: 0x%08x\n", ObjectHandle);
912 ASSERT(GDI_VALID_OBJECT(ObjectHandle, ObjHdr, GDI_OBJECT_TYPE_DONTCARE, GDIOBJFLAG_IGNOREPID));
913
914 return ObjHdr->hProcessId == PsGetCurrentProcessId();
915 }
916
917 void FASTCALL
918 GDIOBJ_SetOwnership(HGDIOBJ ObjectHandle, PEPROCESS NewOwner)
919 {
920 PGDIOBJHDR ObjHdr = GDIOBJ_iGetObjectForIndex(GDI_HANDLE_GET_INDEX(ObjectHandle));
921 PEPROCESS OldProcess;
922 PW32PROCESS W32Process;
923 NTSTATUS Status;
924
925 DPRINT("GDIOBJ_OwnedByCurrentProcess: ObjectHandle: 0x%08x\n", ObjectHandle);
926 ASSERT(GDI_VALID_OBJECT(ObjectHandle, ObjHdr, GDI_OBJECT_TYPE_DONTCARE, GDIOBJFLAG_IGNOREPID));
927
928 if ((NULL == NewOwner && GDI_GLOBAL_PROCESS != ObjHdr->hProcessId)
929 || (NULL != NewOwner && ObjHdr->hProcessId != (HANDLE) NewOwner->UniqueProcessId))
930 {
931 Status = PsLookupProcessByProcessId((PVOID)ObjHdr->hProcessId, &OldProcess);
932 if (NT_SUCCESS(Status))
933 {
934 W32Process = OldProcess->Win32Process;
935 if (W32Process)
936 {
937 W32Process->GDIObjects--;
938 }
939 ObDereferenceObject(OldProcess);
940 }
941 }
942
943 if (NULL == NewOwner)
944 {
945 ObjHdr->hProcessId = GDI_GLOBAL_PROCESS;
946 }
947 else if (ObjHdr->hProcessId != (HANDLE) NewOwner->UniqueProcessId)
948 {
949 ObjHdr->hProcessId = (HANDLE) NewOwner->UniqueProcessId;
950 W32Process = NewOwner->Win32Process;
951 if (W32Process)
952 {
953 W32Process->GDIObjects++;
954 }
955 }
956 }
957
958 void FASTCALL
959 GDIOBJ_CopyOwnership(HGDIOBJ CopyFrom, HGDIOBJ CopyTo)
960 {
961 PGDIOBJHDR ObjHdrFrom = GDIOBJ_iGetObjectForIndex(GDI_HANDLE_GET_INDEX(CopyFrom));
962 PGDIOBJHDR ObjHdrTo = GDIOBJ_iGetObjectForIndex(GDI_HANDLE_GET_INDEX(CopyTo));
963 NTSTATUS Status;
964 PEPROCESS ProcessFrom;
965 PEPROCESS CurrentProcess;
966
967 ASSERT(NULL != ObjHdrFrom && NULL != ObjHdrTo);
968 if (NULL != ObjHdrFrom && NULL != ObjHdrTo
969 && ObjHdrTo->hProcessId != ObjHdrFrom->hProcessId)
970 {
971 if (ObjHdrFrom->hProcessId == GDI_GLOBAL_PROCESS)
972 {
973 GDIOBJ_SetOwnership(CopyTo, NULL);
974 }
975 else
976 {
977 /* Warning: ugly hack ahead
978 *
979 * During process cleanup, we can't call PsLookupProcessByProcessId
980 * for the current process, 'cause that function will try to
981 * reference the process, and since the process is closing down
982 * that will result in a bugcheck.
983 * So, instead, we call PsGetCurrentProcess, which doesn't reference
984 * the process. If the current process is indeed the one we're
985 * looking for, we use it, otherwise we can (safely) call
986 * PsLookupProcessByProcessId
987 */
988 CurrentProcess = PsGetCurrentProcess();
989 if (ObjHdrFrom->hProcessId == (HANDLE) CurrentProcess->UniqueProcessId)
990 {
991 GDIOBJ_SetOwnership(CopyTo, CurrentProcess);
992 }
993 else
994 {
995 Status = PsLookupProcessByProcessId((PVOID) ObjHdrFrom->hProcessId, &ProcessFrom);
996 if (NT_SUCCESS(Status))
997 {
998 GDIOBJ_SetOwnership(CopyTo, ProcessFrom);
999 ObDereferenceObject(ProcessFrom);
1000 }
1001 }
1002 }
1003 }
1004 }
1005
1006 /* EOF */