2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS kernel
4 * PURPOSE: User handle manager
5 * FILE: subsystems/win32/win32k/ntuser/object.c
6 * PROGRAMER: Copyright (C) 2001 Alexandre Julliard
10 DBG_DEFAULT_CHANNEL(UserObj
);
13 PUSER_HANDLE_TABLE gHandleTable
= NULL
;
15 /* Forward declarations */
16 static PVOID
AllocThreadObject(
20 _Out_ PVOID
* HandleOwner
)
24 UNREFERENCED_PARAMETER(pDesk
);
26 ASSERT(Size
> sizeof(*ObjHead
));
29 ObjHead
= UserHeapAlloc(Size
);
33 RtlZeroMemory(ObjHead
, Size
);
36 IntReferenceThreadInfo(pti
);
38 /* It's a thread object, but it still count as one for the process */
39 pti
->ppi
->UserHandleCount
++;
44 static void FreeThreadObject(
47 PTHROBJHEAD ObjHead
= (PTHROBJHEAD
)Object
;
48 PTHREADINFO pti
= ObjHead
->pti
;
50 UserHeapFree(ObjHead
);
52 pti
->ppi
->UserHandleCount
--;
53 IntDereferenceThreadInfo(pti
);
56 static PVOID
AllocDeskThreadObject(
60 _Out_ PVOID
* HandleOwner
)
64 ASSERT(Size
> sizeof(*ObjHead
));
70 ObjHead
= DesktopHeapAlloc(pDesk
, Size
);
74 RtlZeroMemory(ObjHead
, Size
);
76 ObjHead
->pSelf
= ObjHead
;
77 ObjHead
->rpdesk
= pDesk
;
79 IntReferenceThreadInfo(pti
);
81 /* It's a thread object, but it still count as one for the process */
82 pti
->ppi
->UserHandleCount
++;
87 static void FreeDeskThreadObject(
90 PTHRDESKHEAD ObjHead
= (PTHRDESKHEAD
)Object
;
91 PDESKTOP pDesk
= ObjHead
->rpdesk
;
92 PTHREADINFO pti
= ObjHead
->pti
;
94 DesktopHeapFree(pDesk
, Object
);
96 pti
->ppi
->UserHandleCount
--;
97 IntDereferenceThreadInfo(pti
);
100 static PVOID
AllocDeskProcObject(
102 _In_ PTHREADINFO pti
,
104 _Out_ PVOID
* HandleOwner
)
106 PPROCDESKHEAD ObjHead
;
109 ASSERT(Size
> sizeof(*ObjHead
));
110 ASSERT(pDesk
!= NULL
);
113 ObjHead
= DesktopHeapAlloc(pDesk
, Size
);
117 RtlZeroMemory(ObjHead
, Size
);
121 ObjHead
->pSelf
= ObjHead
;
122 ObjHead
->rpdesk
= pDesk
;
123 ObjHead
->hTaskWow
= (DWORD_PTR
)ppi
;
124 ppi
->UserHandleCount
++;
125 IntReferenceProcessInfo(ppi
);
131 static void FreeDeskProcObject(
134 PPROCDESKHEAD ObjHead
= (PPROCDESKHEAD
)Object
;
135 PDESKTOP pDesk
= ObjHead
->rpdesk
;
136 PPROCESSINFO ppi
= (PPROCESSINFO
)ObjHead
->hTaskWow
;
138 ppi
->UserHandleCount
--;
139 IntDereferenceProcessInfo(ppi
);
141 DesktopHeapFree(pDesk
, Object
);
144 static PVOID
AllocProcMarkObject(
146 _In_ PTHREADINFO pti
,
148 _Out_ PVOID
* HandleOwner
)
150 PPROCMARKHEAD ObjHead
;
151 PPROCESSINFO ppi
= pti
->ppi
;
153 UNREFERENCED_PARAMETER(pDesk
);
155 ASSERT(Size
> sizeof(*ObjHead
));
157 ObjHead
= UserHeapAlloc(Size
);
161 RtlZeroMemory(ObjHead
, Size
);
164 IntReferenceProcessInfo(ppi
);
166 ppi
->UserHandleCount
++;
171 static void FreeProcMarkObject(
174 PPROCESSINFO ppi
= ((PPROCMARKHEAD
)Object
)->ppi
;
176 UserHeapFree(Object
);
178 ppi
->UserHandleCount
--;
179 IntDereferenceProcessInfo(ppi
);
182 static PVOID
AllocSysObject(
184 _In_ PTHREADINFO pti
,
186 _Out_ PVOID
* ObjectOwner
)
190 UNREFERENCED_PARAMETER(pDesk
);
191 UNREFERENCED_PARAMETER(pti
);
193 ASSERT(Size
> sizeof(HEAD
));
195 Object
= UserHeapAlloc(Size
);
201 RtlZeroMemory(Object
, Size
);
205 static void FreeSysObject(
208 UserHeapFree(Object
);
213 PVOID (*ObjectAlloc
)(PDESKTOP
, PTHREADINFO
, SIZE_T
, PVOID
*);
214 BOOLEAN (*ObjectDestroy
)(PVOID
);
215 void (*ObjectFree
)(PVOID
);
216 } ObjectCallbacks
[TYPE_CTYPES
] =
218 { NULL
, NULL
, NULL
}, /* TYPE_FREE */
219 { AllocDeskThreadObject
, co_UserDestroyWindow
, FreeDeskThreadObject
}, /* TYPE_WINDOW */
220 { AllocDeskProcObject
, UserDestroyMenuObject
, FreeDeskProcObject
}, /* TYPE_MENU */
221 { AllocProcMarkObject
, /*UserCursorCleanup*/NULL
, FreeProcMarkObject
}, /* TYPE_CURSOR */
222 { AllocSysObject
, /*UserSetWindowPosCleanup*/NULL
, FreeSysObject
}, /* TYPE_SETWINDOWPOS */
223 { AllocDeskThreadObject
, IntRemoveHook
, FreeDeskThreadObject
}, /* TYPE_HOOK */
224 { AllocSysObject
, /*UserClipDataCleanup*/NULL
,FreeSysObject
}, /* TYPE_CLIPDATA */
225 { AllocDeskProcObject
, DestroyCallProc
, FreeDeskProcObject
}, /* TYPE_CALLPROC */
226 { AllocProcMarkObject
, UserDestroyAccelTable
, FreeProcMarkObject
}, /* TYPE_ACCELTABLE */
227 { NULL
, NULL
, NULL
}, /* TYPE_DDEACCESS */
228 { NULL
, NULL
, NULL
}, /* TYPE_DDECONV */
229 { NULL
, NULL
, NULL
}, /* TYPE_DDEXACT */
230 { AllocSysObject
, /*UserMonitorCleanup*/NULL
, FreeSysObject
}, /* TYPE_MONITOR */
231 { AllocSysObject
, /*UserKbdLayoutCleanup*/NULL
,FreeSysObject
}, /* TYPE_KBDLAYOUT */
232 { AllocSysObject
, /*UserKbdFileCleanup*/NULL
, FreeSysObject
}, /* TYPE_KBDFILE */
233 { AllocThreadObject
, IntRemoveEvent
, FreeThreadObject
}, /* TYPE_WINEVENTHOOK */
234 { AllocSysObject
, /*UserTimerCleanup*/NULL
, FreeSysObject
}, /* TYPE_TIMER */
235 { NULL
, NULL
, NULL
}, /* TYPE_INPUTCONTEXT */
236 { NULL
, NULL
, NULL
}, /* TYPE_HIDDATA */
237 { NULL
, NULL
, NULL
}, /* TYPE_DEVICEINFO */
238 { NULL
, NULL
, NULL
}, /* TYPE_TOUCHINPUTINFO */
239 { NULL
, NULL
, NULL
}, /* TYPE_GESTUREINFOOBJ */
244 void DbgUserDumpHandleTable()
246 int HandleCounts
[TYPE_CTYPES
];
247 PPROCESSINFO ppiList
;
249 PWCHAR TypeNames
[] = {L
"Free",L
"Window",L
"Menu", L
"CursorIcon", L
"SMWP", L
"Hook", L
"ClipBoardData", L
"CallProc",
250 L
"Accel", L
"DDEaccess", L
"DDEconv", L
"DDExact", L
"Monitor", L
"KBDlayout", L
"KBDfile",
251 L
"Event", L
"Timer", L
"InputContext", L
"HidData", L
"DeviceInfo", L
"TouchInput",L
"GestureInfo"};
253 ERR("Total handles count: %lu\n", gpsi
->cHandleEntries
);
255 memset(HandleCounts
, 0, sizeof(HandleCounts
));
257 /* First of all count the number of handles per type */
261 ERR("Process %s (%p) handles count: %d\n\t", ppiList
->peProcess
->ImageFileName
, ppiList
->peProcess
->UniqueProcessId
, ppiList
->UserHandleCount
);
263 for (i
= 1 ;i
< TYPE_CTYPES
; i
++)
265 HandleCounts
[i
] += ppiList
->DbgHandleCount
[i
];
267 DbgPrint("%S: %lu, ", TypeNames
[i
], ppiList
->DbgHandleCount
[i
]);
273 ppiList
= ppiList
->ppiNext
;
276 /* Print total type counts */
277 ERR("Total handles of the running processes: \n\t");
278 for (i
= 1 ;i
< TYPE_CTYPES
; i
++)
280 DbgPrint("%S: %d, ", TypeNames
[i
], HandleCounts
[i
]);
286 /* Now count the handle counts that are allocated from the handle table */
287 memset(HandleCounts
, 0, sizeof(HandleCounts
));
288 for (i
= 0; i
< gHandleTable
->nb_handles
; i
++)
289 HandleCounts
[gHandleTable
->handles
[i
].type
]++;
291 ERR("Total handles count allocated: \n\t");
292 for (i
= 1 ;i
< TYPE_CTYPES
; i
++)
294 DbgPrint("%S: %d, ", TypeNames
[i
], HandleCounts
[i
]);
303 PUSER_HANDLE_ENTRY
handle_to_entry(PUSER_HANDLE_TABLE ht
, HANDLE handle
)
305 unsigned short generation
;
306 int index
= (((unsigned int)handle
& 0xffff) - FIRST_USER_HANDLE
) >> 1;
307 if (index
< 0 || index
>= ht
->nb_handles
)
309 if (!ht
->handles
[index
].type
)
311 generation
= (unsigned int)handle
>> 16;
312 if (generation
== ht
->handles
[index
].generation
|| !generation
|| generation
== 0xffff)
313 return &ht
->handles
[index
];
317 __inline
static HANDLE
entry_to_handle(PUSER_HANDLE_TABLE ht
, PUSER_HANDLE_ENTRY ptr
)
319 int index
= ptr
- ht
->handles
;
320 return (HANDLE
)(((index
<< 1) + FIRST_USER_HANDLE
) + (ptr
->generation
<< 16));
323 __inline
static PUSER_HANDLE_ENTRY
alloc_user_entry(PUSER_HANDLE_TABLE ht
)
325 PUSER_HANDLE_ENTRY entry
;
326 TRACE("handles used %lu\n", gpsi
->cHandleEntries
);
330 entry
= ht
->freelist
;
331 ht
->freelist
= entry
->ptr
;
333 gpsi
->cHandleEntries
++;
337 if (ht
->nb_handles
>= ht
->allocated_handles
) /* Need to grow the array */
339 ERR("Out of user handles! Used -> %lu, NM_Handle -> %d\n", gpsi
->cHandleEntries
, ht
->nb_handles
);
342 DbgUserDumpHandleTable();
347 PUSER_HANDLE_ENTRY new_handles
;
348 /* Grow array by 50% (but at minimum 32 entries) */
349 int growth
= max( 32, ht
->allocated_handles
/ 2 );
350 int new_size
= min( ht
->allocated_handles
+ growth
, (LAST_USER_HANDLE
-FIRST_USER_HANDLE
+1) >> 1 );
351 if (new_size
<= ht
->allocated_handles
)
353 if (!(new_handles
= UserHeapReAlloc( ht
->handles
, new_size
* sizeof(*ht
->handles
) )))
355 ht
->handles
= new_handles
;
356 ht
->allocated_handles
= new_size
;
360 entry
= &ht
->handles
[ht
->nb_handles
++];
362 entry
->generation
= 1;
364 gpsi
->cHandleEntries
++;
369 VOID
UserInitHandleTable(PUSER_HANDLE_TABLE ht
, PVOID mem
, ULONG bytes
)
375 ht
->allocated_handles
= bytes
/ sizeof(USER_HANDLE_ENTRY
);
379 __inline
static void *free_user_entry(PUSER_HANDLE_TABLE ht
, PUSER_HANDLE_ENTRY entry
)
390 case TYPE_WINEVENTHOOK
:
391 ppi
= ((PTHREADINFO
)entry
->pi
)->ppi
;
396 case TYPE_ACCELTABLE
:
403 ppi
->DbgHandleCount
[entry
->type
]--;
408 entry
->ptr
= ht
->freelist
;
412 ht
->freelist
= entry
;
414 gpsi
->cHandleEntries
--;
419 /* allocate a user handle for a given object */
420 HANDLE
UserAllocHandle(
421 _Inout_ PUSER_HANDLE_TABLE ht
,
423 _In_ HANDLE_TYPE type
,
424 _In_ PVOID HandleOwner
)
426 PUSER_HANDLE_ENTRY entry
= alloc_user_entry(ht
);
432 entry
->pi
= HandleOwner
;
433 if (++entry
->generation
>= 0xffff)
434 entry
->generation
= 1;
436 /* We have created a handle, which is a reference! */
437 UserReferenceObject(object
);
439 return entry_to_handle(ht
, entry
);
442 /* return a pointer to a user object from its handle without setting an error */
443 PVOID
UserGetObjectNoErr(PUSER_HANDLE_TABLE ht
, HANDLE handle
, HANDLE_TYPE type
)
445 PUSER_HANDLE_ENTRY entry
;
449 if (!(entry
= handle_to_entry(ht
, handle
)) || entry
->type
!= type
)
456 /* return a pointer to a user object from its handle */
457 PVOID
UserGetObject(PUSER_HANDLE_TABLE ht
, HANDLE handle
, HANDLE_TYPE type
)
459 PUSER_HANDLE_ENTRY entry
;
463 if (!(entry
= handle_to_entry(ht
, handle
)) || entry
->type
!= type
)
465 EngSetLastError(ERROR_INVALID_HANDLE
);
472 /* Get the full handle (32bit) for a possibly truncated (16bit) handle */
473 HANDLE
get_user_full_handle(PUSER_HANDLE_TABLE ht
, HANDLE handle
)
475 PUSER_HANDLE_ENTRY entry
;
477 if ((unsigned int)handle
>> 16)
479 if (!(entry
= handle_to_entry(ht
, handle
)))
481 return entry_to_handle( ht
, entry
);
485 /* Same as get_user_object plus set the handle to the full 32-bit value */
486 void *get_user_object_handle(PUSER_HANDLE_TABLE ht
, HANDLE
* handle
, HANDLE_TYPE type
)
488 PUSER_HANDLE_ENTRY entry
;
490 if (!(entry
= handle_to_entry(ht
, *handle
)) || entry
->type
!= type
)
492 *handle
= entry_to_handle( ht
, entry
);
498 BOOL FASTCALL
UserCreateHandleTable(VOID
)
501 INT HandleCount
= 1024 * 4;
503 // FIXME: Don't alloc all at once! Must be mapped into umode also...
504 mem
= UserHeapAlloc(sizeof(USER_HANDLE_ENTRY
) * HandleCount
);
507 ERR("Failed creating handle table\n");
511 gHandleTable
= UserHeapAlloc(sizeof(USER_HANDLE_TABLE
));
512 if (gHandleTable
== NULL
)
515 ERR("Failed creating handle table\n");
519 // FIXME: Make auto growable
520 UserInitHandleTable(gHandleTable
, mem
, sizeof(USER_HANDLE_ENTRY
) * HandleCount
);
530 UserCreateObject( PUSER_HANDLE_TABLE ht
,
541 /* Some sanity checks. Other checks will be made in the allocator */
542 ASSERT(type
< TYPE_CTYPES
);
543 ASSERT(type
!= TYPE_FREE
);
546 /* Allocate the object */
547 ASSERT(ObjectCallbacks
[type
].ObjectAlloc
!= NULL
);
548 Object
= ObjectCallbacks
[type
].ObjectAlloc(pDesktop
, pti
, size
, &ObjectOwner
);
551 ERR("User object allocation failed. Out of memory!\n");
555 hi
= UserAllocHandle(ht
, Object
, type
, ObjectOwner
);
558 ERR("Out of user handles!\n");
559 ObjectCallbacks
[type
].ObjectFree(Object
);
565 pti
->ppi
->DbgHandleCount
[type
]++;
568 /* Give this object its identity. */
569 ((PHEAD
)Object
)->h
= hi
;
571 /* The caller will get a locked object.
572 * Note: with the reference from the handle, that makes two */
573 UserReferenceObject(Object
);
583 UserDereferenceObject(PVOID Object
)
585 PHEAD ObjHead
= (PHEAD
)Object
;
587 ASSERT(ObjHead
->cLockObj
>= 1);
589 if (--ObjHead
->cLockObj
== 0)
591 PUSER_HANDLE_ENTRY entry
;
594 entry
= handle_to_entry(gHandleTable
, ObjHead
->h
);
596 ASSERT(entry
!= NULL
);
597 /* The entry should be marked as in deletion */
598 ASSERT(entry
->flags
& HANDLEENTRY_INDESTROY
);
601 ASSERT(type
!= TYPE_FREE
);
602 ASSERT(type
< TYPE_CTYPES
);
604 /* We can now get rid of everything */
605 free_user_entry(gHandleTable
, entry
);
608 /* Call the object destructor */
609 ASSERT(ObjectCallbacks
[type
].ObjectCleanup
!= NULL
);
610 ObjectCallbacks
[type
].ObjectCleanup(Object
);
614 ASSERT(ObjectCallbacks
[type
].ObjectFree
!= NULL
);
615 ObjectCallbacks
[type
].ObjectFree(Object
);
624 UserFreeHandle(PUSER_HANDLE_TABLE ht
, HANDLE handle
)
626 PUSER_HANDLE_ENTRY entry
;
628 if (!(entry
= handle_to_entry( ht
, handle
)))
630 SetLastNtError( STATUS_INVALID_HANDLE
);
634 entry
->flags
= HANDLEENTRY_INDESTROY
;
636 return UserDereferenceObject(entry
->ptr
);
641 UserObjectInDestroy(HANDLE h
)
643 PUSER_HANDLE_ENTRY entry
;
645 if (!(entry
= handle_to_entry( gHandleTable
, h
)))
647 SetLastNtError( STATUS_INVALID_HANDLE
);
650 return (entry
->flags
& HANDLEENTRY_INDESTROY
);
655 UserDeleteObject(HANDLE h
, HANDLE_TYPE type
)
657 PVOID body
= UserGetObject(gHandleTable
, h
, type
);
659 if (!body
) return FALSE
;
661 ASSERT( ((PHEAD
)body
)->cLockObj
>= 1);
663 return UserFreeHandle(gHandleTable
, h
);
668 UserReferenceObject(PVOID obj
)
670 ASSERT(((PHEAD
)obj
)->cLockObj
>= 0);
672 ((PHEAD
)obj
)->cLockObj
++;
677 UserReferenceObjectByHandle(HANDLE handle
, HANDLE_TYPE type
)
681 object
= UserGetObject(gHandleTable
, handle
, type
);
684 UserReferenceObject(object
);
691 UserSetObjectOwner(PVOID obj
, HANDLE_TYPE type
, PVOID owner
)
693 PUSER_HANDLE_ENTRY entry
= handle_to_entry(gHandleTable
, ((PHEAD
)obj
)->h
);
694 PPROCESSINFO ppi
, oldppi
;
696 /* This must be called with a valid object */
699 /* For now, only supported for CursorIcon object */
703 ppi
= (PPROCESSINFO
)owner
;
705 oldppi
= ((PPROCMARKHEAD
)obj
)->ppi
;
706 ((PPROCMARKHEAD
)obj
)->ppi
= ppi
;
713 oldppi
->UserHandleCount
--;
714 IntDereferenceProcessInfo(oldppi
);
715 ppi
->UserHandleCount
++;
716 IntReferenceProcessInfo(ppi
);
718 oldppi
->DbgHandleCount
[type
]--;
719 ppi
->DbgHandleCount
[type
]++;
724 UserDestroyObjectsForOwner(PUSER_HANDLE_TABLE Table
, PVOID Owner
)
727 PUSER_HANDLE_ENTRY Entry
;
730 /* Sweep the whole handle table */
731 for (i
= 0; i
< Table
->allocated_handles
; i
++)
733 Entry
= &Table
->handles
[i
];
735 if (Entry
->pi
!= Owner
)
738 /* Do not destroy if it's already been done */
739 if (Entry
->flags
& HANDLEENTRY_INDESTROY
)
742 /* Spcial case for cursors until cursoricon_new is there */
743 if (Entry
->type
== TYPE_CURSOR
)
745 UserReferenceObject(Entry
->ptr
);
746 if (!IntDestroyCurIconObject(Entry
->ptr
, Owner
))
753 /* Call destructor */
754 if (!ObjectCallbacks
[Entry
->type
].ObjectDestroy(Entry
->ptr
))
756 ERR("Failed destructing object %p, type %u.\n", Entry
->ptr
, Entry
->type
);
757 /* Don't return immediately, we must continue destroying the other objects */
766 * NtUserValidateHandleSecure W2k3 has one argument.
774 NtUserValidateHandleSecure(
780 PUSER_HANDLE_ENTRY entry
;
782 DECLARE_RETURN(BOOL
);
783 UserEnterExclusive();
785 if (!(entry
= handle_to_entry(gHandleTable
, handle
)))
787 EngSetLastError(ERROR_INVALID_HANDLE
);
794 case TYPE_INPUTCONTEXT
:
795 ppi
= ((PTHREADINFO
)entry
->pi
)->ppi
;
798 case TYPE_ACCELTABLE
:
802 case TYPE_SETWINDOWPOS
:
810 if (!ppi
) RETURN( FALSE
);
812 // Same process job returns TRUE.
813 if (gptiCurrent
->ppi
->pW32Job
== ppi
->pW32Job
) RETURN( TRUE
);