2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS kernel
4 * PURPOSE: User handle manager
5 * FILE: win32ss/user/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 _Success_(return!=NULL
)
17 static PVOID
AllocThreadObject(
21 _Out_ PVOID
* HandleOwner
)
25 UNREFERENCED_PARAMETER(pDesk
);
27 ASSERT(Size
> sizeof(*ObjHead
));
30 ObjHead
= UserHeapAlloc(Size
);
34 RtlZeroMemory(ObjHead
, Size
);
37 IntReferenceThreadInfo(pti
);
39 /* It's a thread object, but it still count as one for the process */
40 pti
->ppi
->UserHandleCount
++;
45 static void FreeThreadObject(
48 PTHROBJHEAD ObjHead
= (PTHROBJHEAD
)Object
;
49 PTHREADINFO pti
= ObjHead
->pti
;
51 UserHeapFree(ObjHead
);
53 pti
->ppi
->UserHandleCount
--;
54 IntDereferenceThreadInfo(pti
);
57 _Success_(return!=NULL
)
58 static PVOID
AllocDeskThreadObject(
62 _Out_ PVOID
* HandleOwner
)
66 ASSERT(Size
> sizeof(*ObjHead
));
72 ObjHead
= DesktopHeapAlloc(pDesk
, Size
);
76 RtlZeroMemory(ObjHead
, Size
);
78 ObjHead
->pSelf
= ObjHead
;
79 ObjHead
->rpdesk
= pDesk
;
81 IntReferenceThreadInfo(pti
);
83 /* It's a thread object, but it still count as one for the process */
84 pti
->ppi
->UserHandleCount
++;
89 static void FreeDeskThreadObject(
92 PTHRDESKHEAD ObjHead
= (PTHRDESKHEAD
)Object
;
93 PDESKTOP pDesk
= ObjHead
->rpdesk
;
94 PTHREADINFO pti
= ObjHead
->pti
;
96 DesktopHeapFree(pDesk
, Object
);
98 pti
->ppi
->UserHandleCount
--;
99 IntDereferenceThreadInfo(pti
);
102 _Success_(return!=NULL
)
103 static PVOID
AllocDeskProcObject(
105 _In_ PTHREADINFO pti
,
107 _Out_ PVOID
* HandleOwner
)
109 PPROCDESKHEAD ObjHead
;
112 ASSERT(Size
> sizeof(*ObjHead
));
113 ASSERT(pDesk
!= NULL
);
116 ObjHead
= DesktopHeapAlloc(pDesk
, Size
);
120 RtlZeroMemory(ObjHead
, Size
);
124 ObjHead
->pSelf
= ObjHead
;
125 ObjHead
->rpdesk
= pDesk
;
126 ObjHead
->hTaskWow
= (DWORD_PTR
)ppi
;
127 ppi
->UserHandleCount
++;
128 IntReferenceProcessInfo(ppi
);
134 static void FreeDeskProcObject(
137 PPROCDESKHEAD ObjHead
= (PPROCDESKHEAD
)Object
;
138 PDESKTOP pDesk
= ObjHead
->rpdesk
;
139 PPROCESSINFO ppi
= (PPROCESSINFO
)ObjHead
->hTaskWow
;
141 ppi
->UserHandleCount
--;
142 IntDereferenceProcessInfo(ppi
);
144 DesktopHeapFree(pDesk
, Object
);
147 _Success_(return!=NULL
)
148 static PVOID
AllocProcMarkObject(
150 _In_ PTHREADINFO pti
,
152 _Out_ PVOID
* HandleOwner
)
154 PPROCMARKHEAD ObjHead
;
155 PPROCESSINFO ppi
= pti
->ppi
;
157 UNREFERENCED_PARAMETER(pDesk
);
159 ASSERT(Size
> sizeof(*ObjHead
));
161 ObjHead
= UserHeapAlloc(Size
);
165 RtlZeroMemory(ObjHead
, Size
);
168 IntReferenceProcessInfo(ppi
);
170 ppi
->UserHandleCount
++;
175 void FreeProcMarkObject(
178 PPROCESSINFO ppi
= ((PPROCMARKHEAD
)Object
)->ppi
;
180 UserHeapFree(Object
);
182 ppi
->UserHandleCount
--;
183 IntDereferenceProcessInfo(ppi
);
186 _Success_(return!=NULL
)
187 static PVOID
AllocSysObject(
189 _In_ PTHREADINFO pti
,
191 _Out_ PVOID
* ObjectOwner
)
195 UNREFERENCED_PARAMETER(pDesk
);
196 UNREFERENCED_PARAMETER(pti
);
198 ASSERT(Size
> sizeof(HEAD
));
200 Object
= UserHeapAlloc(Size
);
206 RtlZeroMemory(Object
, Size
);
210 _Success_(return!=NULL
)
211 static PVOID
AllocSysObjectCB(
213 _In_ PTHREADINFO pti
,
215 _Out_ PVOID
* ObjectOwner
)
219 UNREFERENCED_PARAMETER(pDesk
);
220 UNREFERENCED_PARAMETER(pti
);
221 ASSERT(Size
> sizeof(HEAD
));
223 /* Allocate the clipboard data */
224 // FIXME: This allocation should be done on the current session pool;
225 // however ReactOS' MM doesn't support session pool yet.
226 Object
= ExAllocatePoolZero(/* SESSION_POOL_MASK | */ PagedPool
, Size
, USERTAG_CLIPBOARD
);
229 ERR("ExAllocatePoolZero failed. No object created.\n");
237 static void FreeSysObject(
240 UserHeapFree(Object
);
243 static void FreeSysObjectCB(
246 ExFreePoolWithTag(Object
, USERTAG_CLIPBOARD
);
251 PVOID (*ObjectAlloc
)(PDESKTOP
, PTHREADINFO
, SIZE_T
, PVOID
*);
252 BOOLEAN (*ObjectDestroy
)(PVOID
);
253 void (*ObjectFree
)(PVOID
);
254 } ObjectCallbacks
[TYPE_CTYPES
] =
256 { NULL
, NULL
, NULL
}, /* TYPE_FREE */
257 { AllocDeskThreadObject
, co_UserDestroyWindow
, FreeDeskThreadObject
}, /* TYPE_WINDOW */
258 { AllocDeskProcObject
, UserDestroyMenuObject
, FreeDeskProcObject
}, /* TYPE_MENU */
259 { AllocProcMarkObject
, IntDestroyCurIconObject
, FreeCurIconObject
}, /* TYPE_CURSOR */
260 { AllocSysObject
, /*UserSetWindowPosCleanup*/NULL
, FreeSysObject
}, /* TYPE_SETWINDOWPOS */
261 { AllocDeskThreadObject
, IntRemoveHook
, FreeDeskThreadObject
}, /* TYPE_HOOK */
262 { AllocSysObjectCB
, /*UserClipDataCleanup*/NULL
,FreeSysObjectCB
}, /* TYPE_CLIPDATA */
263 { AllocDeskProcObject
, DestroyCallProc
, FreeDeskProcObject
}, /* TYPE_CALLPROC */
264 { AllocProcMarkObject
, UserDestroyAccelTable
, FreeProcMarkObject
}, /* TYPE_ACCELTABLE */
265 { NULL
, NULL
, NULL
}, /* TYPE_DDEACCESS */
266 { NULL
, NULL
, NULL
}, /* TYPE_DDECONV */
267 { NULL
, NULL
, NULL
}, /* TYPE_DDEXACT */
268 { AllocSysObject
, /*UserMonitorCleanup*/NULL
, FreeSysObject
}, /* TYPE_MONITOR */
269 { AllocSysObject
, /*UserKbdLayoutCleanup*/NULL
,FreeSysObject
}, /* TYPE_KBDLAYOUT */
270 { AllocSysObject
, /*UserKbdFileCleanup*/NULL
, FreeSysObject
}, /* TYPE_KBDFILE */
271 { AllocThreadObject
, IntRemoveEvent
, FreeThreadObject
}, /* TYPE_WINEVENTHOOK */
272 { AllocSysObject
, /*UserTimerCleanup*/NULL
, FreeSysObject
}, /* TYPE_TIMER */
273 { AllocInputContextObject
, UserDestroyInputContext
, UserFreeInputContext
}, /* TYPE_INPUTCONTEXT */
274 { NULL
, NULL
, NULL
}, /* TYPE_HIDDATA */
275 { NULL
, NULL
, NULL
}, /* TYPE_DEVICEINFO */
276 { NULL
, NULL
, NULL
}, /* TYPE_TOUCHINPUTINFO */
277 { NULL
, NULL
, NULL
}, /* TYPE_GESTUREINFOOBJ */
282 void DbgUserDumpHandleTable(VOID
)
284 int HandleCounts
[TYPE_CTYPES
];
285 PPROCESSINFO ppiList
;
287 PWCHAR TypeNames
[] = {L
"Free",L
"Window",L
"Menu", L
"CursorIcon", L
"SMWP", L
"Hook", L
"ClipBoardData", L
"CallProc",
288 L
"Accel", L
"DDEaccess", L
"DDEconv", L
"DDExact", L
"Monitor", L
"KBDlayout", L
"KBDfile",
289 L
"Event", L
"Timer", L
"InputContext", L
"HidData", L
"DeviceInfo", L
"TouchInput",L
"GestureInfo"};
291 ERR("Total handles count: %lu\n", gpsi
->cHandleEntries
);
293 memset(HandleCounts
, 0, sizeof(HandleCounts
));
295 /* First of all count the number of handles per type */
299 ERR("Process %s (%p) handles count: %d\n\t", ppiList
->peProcess
->ImageFileName
, ppiList
->peProcess
->UniqueProcessId
, ppiList
->UserHandleCount
);
301 for (i
= 1 ;i
< TYPE_CTYPES
; i
++)
303 HandleCounts
[i
] += ppiList
->DbgHandleCount
[i
];
305 DbgPrint("%S: %lu, ", TypeNames
[i
], ppiList
->DbgHandleCount
[i
]);
311 ppiList
= ppiList
->ppiNext
;
314 /* Print total type counts */
315 ERR("Total handles of the running processes: \n\t");
316 for (i
= 1 ;i
< TYPE_CTYPES
; i
++)
318 DbgPrint("%S: %d, ", TypeNames
[i
], HandleCounts
[i
]);
324 /* Now count the handle counts that are allocated from the handle table */
325 memset(HandleCounts
, 0, sizeof(HandleCounts
));
326 for (i
= 0; i
< gHandleTable
->nb_handles
; i
++)
327 HandleCounts
[gHandleTable
->handles
[i
].type
]++;
329 ERR("Total handles count allocated: \n\t");
330 for (i
= 1 ;i
< TYPE_CTYPES
; i
++)
332 DbgPrint("%S: %d, ", TypeNames
[i
], HandleCounts
[i
]);
341 PUSER_HANDLE_ENTRY
handle_to_entry(PUSER_HANDLE_TABLE ht
, HANDLE handle
)
343 unsigned short generation
;
344 int index
= (LOWORD(handle
) - FIRST_USER_HANDLE
) >> 1;
345 if (index
< 0 || index
>= ht
->nb_handles
)
347 if (!ht
->handles
[index
].type
)
349 generation
= HIWORD(handle
);
350 if (generation
== ht
->handles
[index
].generation
|| !generation
|| generation
== 0xffff)
351 return &ht
->handles
[index
];
355 __inline
static HANDLE
entry_to_handle(PUSER_HANDLE_TABLE ht
, PUSER_HANDLE_ENTRY ptr
)
357 int index
= ptr
- ht
->handles
;
358 return (HANDLE
)((((INT_PTR
)index
<< 1) + FIRST_USER_HANDLE
) + (ptr
->generation
<< 16));
361 __inline
static PUSER_HANDLE_ENTRY
alloc_user_entry(PUSER_HANDLE_TABLE ht
)
363 PUSER_HANDLE_ENTRY entry
;
364 TRACE("handles used %lu\n", gpsi
->cHandleEntries
);
368 entry
= ht
->freelist
;
369 ht
->freelist
= entry
->ptr
;
371 gpsi
->cHandleEntries
++;
375 if (ht
->nb_handles
>= ht
->allocated_handles
) /* Need to grow the array */
377 ERR("Out of user handles! Used -> %lu, NM_Handle -> %d\n", gpsi
->cHandleEntries
, ht
->nb_handles
);
380 DbgUserDumpHandleTable();
385 PUSER_HANDLE_ENTRY new_handles
;
386 /* Grow array by 50% (but at minimum 32 entries) */
387 int growth
= max( 32, ht
->allocated_handles
/ 2 );
388 int new_size
= min( ht
->allocated_handles
+ growth
, (LAST_USER_HANDLE
-FIRST_USER_HANDLE
+1) >> 1 );
389 if (new_size
<= ht
->allocated_handles
)
391 if (!(new_handles
= UserHeapReAlloc( ht
->handles
, new_size
* sizeof(*ht
->handles
) )))
393 ht
->handles
= new_handles
;
394 ht
->allocated_handles
= new_size
;
398 entry
= &ht
->handles
[ht
->nb_handles
++];
400 entry
->generation
= 1;
402 gpsi
->cHandleEntries
++;
407 VOID
UserInitHandleTable(PUSER_HANDLE_TABLE ht
, PVOID mem
, ULONG bytes
)
413 ht
->allocated_handles
= bytes
/ sizeof(USER_HANDLE_ENTRY
);
417 __inline
static void *free_user_entry(PUSER_HANDLE_TABLE ht
, PUSER_HANDLE_ENTRY entry
)
428 case TYPE_WINEVENTHOOK
:
429 ppi
= ((PTHREADINFO
)entry
->pi
)->ppi
;
434 case TYPE_ACCELTABLE
:
441 ppi
->DbgHandleCount
[entry
->type
]--;
446 entry
->ptr
= ht
->freelist
;
450 ht
->freelist
= entry
;
452 gpsi
->cHandleEntries
--;
457 /* allocate a user handle for a given object */
458 HANDLE
UserAllocHandle(
459 _Inout_ PUSER_HANDLE_TABLE ht
,
461 _In_ HANDLE_TYPE type
,
462 _In_ PVOID HandleOwner
)
464 PUSER_HANDLE_ENTRY entry
= alloc_user_entry(ht
);
470 entry
->pi
= HandleOwner
;
471 if (++entry
->generation
>= 0xffff)
472 entry
->generation
= 1;
474 /* We have created a handle, which is a reference! */
475 UserReferenceObject(object
);
477 return entry_to_handle(ht
, entry
);
480 /* return a pointer to a user object from its handle without setting an error */
481 PVOID
UserGetObjectNoErr(PUSER_HANDLE_TABLE ht
, HANDLE handle
, HANDLE_TYPE type
)
483 PUSER_HANDLE_ENTRY entry
;
487 if (!(entry
= handle_to_entry(ht
, handle
)) || entry
->type
!= type
)
494 /* return a pointer to a user object from its handle */
495 PVOID
UserGetObject(PUSER_HANDLE_TABLE ht
, HANDLE handle
, HANDLE_TYPE type
)
497 PUSER_HANDLE_ENTRY entry
;
501 if (!(entry
= handle_to_entry(ht
, handle
)) || entry
->type
!= type
)
503 EngSetLastError(ERROR_INVALID_HANDLE
);
510 /* Get the full handle (32bit) for a possibly truncated (16bit) handle */
511 HANDLE
get_user_full_handle(PUSER_HANDLE_TABLE ht
, HANDLE handle
)
513 PUSER_HANDLE_ENTRY entry
;
515 if ((ULONG_PTR
)handle
>> 16)
517 if (!(entry
= handle_to_entry(ht
, handle
)))
519 return entry_to_handle( ht
, entry
);
523 /* Same as get_user_object plus set the handle to the full 32-bit value */
524 void *get_user_object_handle(PUSER_HANDLE_TABLE ht
, HANDLE
* handle
, HANDLE_TYPE type
)
526 PUSER_HANDLE_ENTRY entry
;
528 if (!(entry
= handle_to_entry(ht
, *handle
)) || entry
->type
!= type
)
530 *handle
= entry_to_handle( ht
, entry
);
536 BOOL FASTCALL
UserCreateHandleTable(VOID
)
539 INT HandleCount
= 1024 * 4;
541 // FIXME: Don't alloc all at once! Must be mapped into umode also...
542 mem
= UserHeapAlloc(sizeof(USER_HANDLE_ENTRY
) * HandleCount
);
545 ERR("Failed creating handle table\n");
549 gHandleTable
= UserHeapAlloc(sizeof(USER_HANDLE_TABLE
));
550 if (gHandleTable
== NULL
)
553 ERR("Failed creating handle table\n");
557 // FIXME: Make auto growable
558 UserInitHandleTable(gHandleTable
, mem
, sizeof(USER_HANDLE_ENTRY
) * HandleCount
);
568 UserCreateObject( PUSER_HANDLE_TABLE ht
,
579 /* Some sanity checks. Other checks will be made in the allocator */
580 ASSERT(type
< TYPE_CTYPES
);
581 ASSERT(type
!= TYPE_FREE
);
584 /* Allocate the object */
585 ASSERT(ObjectCallbacks
[type
].ObjectAlloc
!= NULL
);
586 Object
= ObjectCallbacks
[type
].ObjectAlloc(pDesktop
, pti
, size
, &ObjectOwner
);
589 ERR("User object allocation failed. Out of memory!\n");
593 hi
= UserAllocHandle(ht
, Object
, type
, ObjectOwner
);
596 ERR("Out of user handles!\n");
597 ObjectCallbacks
[type
].ObjectFree(Object
);
603 pti
->ppi
->DbgHandleCount
[type
]++;
606 /* Give this object its identity. */
607 ((PHEAD
)Object
)->h
= hi
;
609 /* The caller will get a locked object.
610 * Note: with the reference from the handle, that makes two */
611 UserReferenceObject(Object
);
618 // Win: HMMarkObjectDestroy
621 UserMarkObjectDestroy(PVOID Object
)
623 PUSER_HANDLE_ENTRY entry
;
624 PHEAD ObjHead
= Object
;
626 entry
= handle_to_entry(gHandleTable
, ObjHead
->h
);
628 ASSERT(entry
!= NULL
);
630 entry
->flags
|= HANDLEENTRY_DESTROY
;
632 if (ObjHead
->cLockObj
> 1)
634 entry
->flags
&= ~HANDLEENTRY_INDESTROY
;
635 TRACE("Count %d\n",ObjHead
->cLockObj
);
644 UserDereferenceObject(PVOID Object
)
646 PHEAD ObjHead
= Object
;
648 ASSERT(ObjHead
->cLockObj
>= 1);
649 ASSERT(ObjHead
->cLockObj
< 0x10000);
651 if (--ObjHead
->cLockObj
== 0)
653 PUSER_HANDLE_ENTRY entry
;
656 entry
= handle_to_entry(gHandleTable
, ObjHead
->h
);
658 ASSERT(entry
!= NULL
);
659 /* The entry should be marked as in deletion */
660 ASSERT(entry
->flags
& HANDLEENTRY_INDESTROY
);
663 ASSERT(type
!= TYPE_FREE
);
664 ASSERT(type
< TYPE_CTYPES
);
666 /* We can now get rid of everything */
667 free_user_entry(gHandleTable
, entry
);
670 /* Call the object destructor */
671 ASSERT(ObjectCallbacks
[type
].ObjectCleanup
!= NULL
);
672 ObjectCallbacks
[type
].ObjectCleanup(Object
);
676 ASSERT(ObjectCallbacks
[type
].ObjectFree
!= NULL
);
677 ObjectCallbacks
[type
].ObjectFree(Object
);
686 UserFreeHandle(PUSER_HANDLE_TABLE ht
, HANDLE handle
)
688 PUSER_HANDLE_ENTRY entry
;
690 if (!(entry
= handle_to_entry( ht
, handle
)))
692 SetLastNtError( STATUS_INVALID_HANDLE
);
696 entry
->flags
= HANDLEENTRY_INDESTROY
;
698 return UserDereferenceObject(entry
->ptr
);
703 UserObjectInDestroy(HANDLE h
)
705 PUSER_HANDLE_ENTRY entry
;
707 if (!(entry
= handle_to_entry( gHandleTable
, h
)))
709 SetLastNtError( STATUS_INVALID_HANDLE
);
712 return (entry
->flags
& HANDLEENTRY_INDESTROY
);
717 UserDeleteObject(HANDLE h
, HANDLE_TYPE type
)
719 PVOID body
= UserGetObject(gHandleTable
, h
, type
);
721 if (!body
) return FALSE
;
723 ASSERT( ((PHEAD
)body
)->cLockObj
>= 1);
724 ASSERT( ((PHEAD
)body
)->cLockObj
< 0x10000);
726 return UserFreeHandle(gHandleTable
, h
);
731 UserReferenceObject(PVOID obj
)
734 ASSERT(ObjHead
->cLockObj
< 0x10000);
741 UserReferenceObjectByHandle(HANDLE handle
, HANDLE_TYPE type
)
745 object
= UserGetObject(gHandleTable
, handle
, type
);
748 UserReferenceObject(object
);
754 UserDestroyObjectsForOwner(PUSER_HANDLE_TABLE Table
, PVOID Owner
)
757 PUSER_HANDLE_ENTRY Entry
;
760 /* Sweep the whole handle table */
761 for (i
= 0; i
< Table
->allocated_handles
; i
++)
763 Entry
= &Table
->handles
[i
];
765 if (Entry
->pi
!= Owner
)
768 /* Do not destroy if it's already been done */
769 if (Entry
->flags
& HANDLEENTRY_INDESTROY
)
772 /* Call destructor */
773 if (!ObjectCallbacks
[Entry
->type
].ObjectDestroy(Entry
->ptr
))
775 ERR("Failed destructing object %p, type %u.\n", Entry
->ptr
, Entry
->type
);
776 /* Don't return immediately, we must continue destroying the other objects */
792 NtUserValidateHandleSecure(
797 PUSER_HANDLE_ENTRY entry
;
800 UserEnterExclusive();
802 if (!(entry
= handle_to_entry(gHandleTable
, handle
)))
804 EngSetLastError(ERROR_INVALID_HANDLE
);
805 goto Exit
; // Return FALSE
811 case TYPE_INPUTCONTEXT
:
812 ppi
= ((PTHREADINFO
)entry
->pi
)->ppi
;
815 case TYPE_ACCELTABLE
:
819 case TYPE_SETWINDOWPOS
:
828 goto Exit
; // Return FALSE
830 // Same process job returns TRUE.
831 if (gptiCurrent
->ppi
->pW32Job
== ppi
->pW32Job
) Ret
= TRUE
;
838 // Win: HMAssignmentLock
839 PVOID FASTCALL
UserAssignmentLock(PVOID
*ppvObj
, PVOID pvNew
)
841 PVOID pvOld
= *ppvObj
;
844 if (pvOld
&& pvOld
== pvNew
)
848 UserReferenceObject(pvNew
);
852 if (UserDereferenceObject(pvOld
))
859 // Win: HMAssignmentUnlock
860 PVOID FASTCALL
UserAssignmentUnlock(PVOID
*ppvObj
)
862 PVOID pvOld
= *ppvObj
;
867 if (UserDereferenceObject(pvOld
))