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 static void FreeSysObject(
213 UserHeapFree(Object
);
218 PVOID (*ObjectAlloc
)(PDESKTOP
, PTHREADINFO
, SIZE_T
, PVOID
*);
219 BOOLEAN (*ObjectDestroy
)(PVOID
);
220 void (*ObjectFree
)(PVOID
);
221 } ObjectCallbacks
[TYPE_CTYPES
] =
223 { NULL
, NULL
, NULL
}, /* TYPE_FREE */
224 { AllocDeskThreadObject
, co_UserDestroyWindow
, FreeDeskThreadObject
}, /* TYPE_WINDOW */
225 { AllocDeskProcObject
, UserDestroyMenuObject
, FreeDeskProcObject
}, /* TYPE_MENU */
226 { AllocProcMarkObject
, IntDestroyCurIconObject
, FreeCurIconObject
}, /* TYPE_CURSOR */
227 { AllocSysObject
, /*UserSetWindowPosCleanup*/NULL
, FreeSysObject
}, /* TYPE_SETWINDOWPOS */
228 { AllocDeskThreadObject
, IntRemoveHook
, FreeDeskThreadObject
}, /* TYPE_HOOK */
229 { AllocSysObject
, /*UserClipDataCleanup*/NULL
,FreeSysObject
}, /* TYPE_CLIPDATA */
230 { AllocDeskProcObject
, DestroyCallProc
, FreeDeskProcObject
}, /* TYPE_CALLPROC */
231 { AllocProcMarkObject
, UserDestroyAccelTable
, FreeProcMarkObject
}, /* TYPE_ACCELTABLE */
232 { NULL
, NULL
, NULL
}, /* TYPE_DDEACCESS */
233 { NULL
, NULL
, NULL
}, /* TYPE_DDECONV */
234 { NULL
, NULL
, NULL
}, /* TYPE_DDEXACT */
235 { AllocSysObject
, /*UserMonitorCleanup*/NULL
, FreeSysObject
}, /* TYPE_MONITOR */
236 { AllocSysObject
, /*UserKbdLayoutCleanup*/NULL
,FreeSysObject
}, /* TYPE_KBDLAYOUT */
237 { AllocSysObject
, /*UserKbdFileCleanup*/NULL
, FreeSysObject
}, /* TYPE_KBDFILE */
238 { AllocThreadObject
, IntRemoveEvent
, FreeThreadObject
}, /* TYPE_WINEVENTHOOK */
239 { AllocSysObject
, /*UserTimerCleanup*/NULL
, FreeSysObject
}, /* TYPE_TIMER */
240 { NULL
, NULL
, NULL
}, /* TYPE_INPUTCONTEXT */
241 { NULL
, NULL
, NULL
}, /* TYPE_HIDDATA */
242 { NULL
, NULL
, NULL
}, /* TYPE_DEVICEINFO */
243 { NULL
, NULL
, NULL
}, /* TYPE_TOUCHINPUTINFO */
244 { NULL
, NULL
, NULL
}, /* TYPE_GESTUREINFOOBJ */
249 void DbgUserDumpHandleTable(VOID
)
251 int HandleCounts
[TYPE_CTYPES
];
252 PPROCESSINFO ppiList
;
254 PWCHAR TypeNames
[] = {L
"Free",L
"Window",L
"Menu", L
"CursorIcon", L
"SMWP", L
"Hook", L
"ClipBoardData", L
"CallProc",
255 L
"Accel", L
"DDEaccess", L
"DDEconv", L
"DDExact", L
"Monitor", L
"KBDlayout", L
"KBDfile",
256 L
"Event", L
"Timer", L
"InputContext", L
"HidData", L
"DeviceInfo", L
"TouchInput",L
"GestureInfo"};
258 ERR("Total handles count: %lu\n", gpsi
->cHandleEntries
);
260 memset(HandleCounts
, 0, sizeof(HandleCounts
));
262 /* First of all count the number of handles per type */
266 ERR("Process %s (%p) handles count: %d\n\t", ppiList
->peProcess
->ImageFileName
, ppiList
->peProcess
->UniqueProcessId
, ppiList
->UserHandleCount
);
268 for (i
= 1 ;i
< TYPE_CTYPES
; i
++)
270 HandleCounts
[i
] += ppiList
->DbgHandleCount
[i
];
272 DbgPrint("%S: %lu, ", TypeNames
[i
], ppiList
->DbgHandleCount
[i
]);
278 ppiList
= ppiList
->ppiNext
;
281 /* Print total type counts */
282 ERR("Total handles of the running processes: \n\t");
283 for (i
= 1 ;i
< TYPE_CTYPES
; i
++)
285 DbgPrint("%S: %d, ", TypeNames
[i
], HandleCounts
[i
]);
291 /* Now count the handle counts that are allocated from the handle table */
292 memset(HandleCounts
, 0, sizeof(HandleCounts
));
293 for (i
= 0; i
< gHandleTable
->nb_handles
; i
++)
294 HandleCounts
[gHandleTable
->handles
[i
].type
]++;
296 ERR("Total handles count allocated: \n\t");
297 for (i
= 1 ;i
< TYPE_CTYPES
; i
++)
299 DbgPrint("%S: %d, ", TypeNames
[i
], HandleCounts
[i
]);
308 PUSER_HANDLE_ENTRY
handle_to_entry(PUSER_HANDLE_TABLE ht
, HANDLE handle
)
310 unsigned short generation
;
311 int index
= (((unsigned int)handle
& 0xffff) - FIRST_USER_HANDLE
) >> 1;
312 if (index
< 0 || index
>= ht
->nb_handles
)
314 if (!ht
->handles
[index
].type
)
316 generation
= (unsigned int)handle
>> 16;
317 if (generation
== ht
->handles
[index
].generation
|| !generation
|| generation
== 0xffff)
318 return &ht
->handles
[index
];
322 __inline
static HANDLE
entry_to_handle(PUSER_HANDLE_TABLE ht
, PUSER_HANDLE_ENTRY ptr
)
324 int index
= ptr
- ht
->handles
;
325 return (HANDLE
)(((index
<< 1) + FIRST_USER_HANDLE
) + (ptr
->generation
<< 16));
328 __inline
static PUSER_HANDLE_ENTRY
alloc_user_entry(PUSER_HANDLE_TABLE ht
)
330 PUSER_HANDLE_ENTRY entry
;
331 TRACE("handles used %lu\n", gpsi
->cHandleEntries
);
335 entry
= ht
->freelist
;
336 ht
->freelist
= entry
->ptr
;
338 gpsi
->cHandleEntries
++;
342 if (ht
->nb_handles
>= ht
->allocated_handles
) /* Need to grow the array */
344 ERR("Out of user handles! Used -> %lu, NM_Handle -> %d\n", gpsi
->cHandleEntries
, ht
->nb_handles
);
347 DbgUserDumpHandleTable();
352 PUSER_HANDLE_ENTRY new_handles
;
353 /* Grow array by 50% (but at minimum 32 entries) */
354 int growth
= max( 32, ht
->allocated_handles
/ 2 );
355 int new_size
= min( ht
->allocated_handles
+ growth
, (LAST_USER_HANDLE
-FIRST_USER_HANDLE
+1) >> 1 );
356 if (new_size
<= ht
->allocated_handles
)
358 if (!(new_handles
= UserHeapReAlloc( ht
->handles
, new_size
* sizeof(*ht
->handles
) )))
360 ht
->handles
= new_handles
;
361 ht
->allocated_handles
= new_size
;
365 entry
= &ht
->handles
[ht
->nb_handles
++];
367 entry
->generation
= 1;
369 gpsi
->cHandleEntries
++;
374 VOID
UserInitHandleTable(PUSER_HANDLE_TABLE ht
, PVOID mem
, ULONG bytes
)
380 ht
->allocated_handles
= bytes
/ sizeof(USER_HANDLE_ENTRY
);
384 __inline
static void *free_user_entry(PUSER_HANDLE_TABLE ht
, PUSER_HANDLE_ENTRY entry
)
395 case TYPE_WINEVENTHOOK
:
396 ppi
= ((PTHREADINFO
)entry
->pi
)->ppi
;
401 case TYPE_ACCELTABLE
:
408 ppi
->DbgHandleCount
[entry
->type
]--;
413 entry
->ptr
= ht
->freelist
;
417 ht
->freelist
= entry
;
419 gpsi
->cHandleEntries
--;
424 /* allocate a user handle for a given object */
425 HANDLE
UserAllocHandle(
426 _Inout_ PUSER_HANDLE_TABLE ht
,
428 _In_ HANDLE_TYPE type
,
429 _In_ PVOID HandleOwner
)
431 PUSER_HANDLE_ENTRY entry
= alloc_user_entry(ht
);
437 entry
->pi
= HandleOwner
;
438 if (++entry
->generation
>= 0xffff)
439 entry
->generation
= 1;
441 /* We have created a handle, which is a reference! */
442 UserReferenceObject(object
);
444 return entry_to_handle(ht
, entry
);
447 /* return a pointer to a user object from its handle without setting an error */
448 PVOID
UserGetObjectNoErr(PUSER_HANDLE_TABLE ht
, HANDLE handle
, HANDLE_TYPE type
)
450 PUSER_HANDLE_ENTRY entry
;
454 if (!(entry
= handle_to_entry(ht
, handle
)) || entry
->type
!= type
)
461 /* return a pointer to a user object from its handle */
462 PVOID
UserGetObject(PUSER_HANDLE_TABLE ht
, HANDLE handle
, HANDLE_TYPE type
)
464 PUSER_HANDLE_ENTRY entry
;
468 if (!(entry
= handle_to_entry(ht
, handle
)) || entry
->type
!= type
)
470 EngSetLastError(ERROR_INVALID_HANDLE
);
477 /* Get the full handle (32bit) for a possibly truncated (16bit) handle */
478 HANDLE
get_user_full_handle(PUSER_HANDLE_TABLE ht
, HANDLE handle
)
480 PUSER_HANDLE_ENTRY entry
;
482 if ((unsigned int)handle
>> 16)
484 if (!(entry
= handle_to_entry(ht
, handle
)))
486 return entry_to_handle( ht
, entry
);
490 /* Same as get_user_object plus set the handle to the full 32-bit value */
491 void *get_user_object_handle(PUSER_HANDLE_TABLE ht
, HANDLE
* handle
, HANDLE_TYPE type
)
493 PUSER_HANDLE_ENTRY entry
;
495 if (!(entry
= handle_to_entry(ht
, *handle
)) || entry
->type
!= type
)
497 *handle
= entry_to_handle( ht
, entry
);
503 BOOL FASTCALL
UserCreateHandleTable(VOID
)
506 INT HandleCount
= 1024 * 4;
508 // FIXME: Don't alloc all at once! Must be mapped into umode also...
509 mem
= UserHeapAlloc(sizeof(USER_HANDLE_ENTRY
) * HandleCount
);
512 ERR("Failed creating handle table\n");
516 gHandleTable
= UserHeapAlloc(sizeof(USER_HANDLE_TABLE
));
517 if (gHandleTable
== NULL
)
520 ERR("Failed creating handle table\n");
524 // FIXME: Make auto growable
525 UserInitHandleTable(gHandleTable
, mem
, sizeof(USER_HANDLE_ENTRY
) * HandleCount
);
535 UserCreateObject( PUSER_HANDLE_TABLE ht
,
546 /* Some sanity checks. Other checks will be made in the allocator */
547 ASSERT(type
< TYPE_CTYPES
);
548 ASSERT(type
!= TYPE_FREE
);
551 /* Allocate the object */
552 ASSERT(ObjectCallbacks
[type
].ObjectAlloc
!= NULL
);
553 Object
= ObjectCallbacks
[type
].ObjectAlloc(pDesktop
, pti
, size
, &ObjectOwner
);
556 ERR("User object allocation failed. Out of memory!\n");
560 hi
= UserAllocHandle(ht
, Object
, type
, ObjectOwner
);
563 ERR("Out of user handles!\n");
564 ObjectCallbacks
[type
].ObjectFree(Object
);
570 pti
->ppi
->DbgHandleCount
[type
]++;
573 /* Give this object its identity. */
574 ((PHEAD
)Object
)->h
= hi
;
576 /* The caller will get a locked object.
577 * Note: with the reference from the handle, that makes two */
578 UserReferenceObject(Object
);
587 UserMarkObjectDestroy(PVOID Object
)
589 PUSER_HANDLE_ENTRY entry
;
590 PHEAD ObjHead
= Object
;
592 entry
= handle_to_entry(gHandleTable
, ObjHead
->h
);
594 ASSERT(entry
!= NULL
);
596 entry
->flags
|= HANDLEENTRY_DESTROY
;
598 if (ObjHead
->cLockObj
> 1)
600 entry
->flags
&= ~HANDLEENTRY_INDESTROY
;
601 TRACE("Count %d\n",ObjHead
->cLockObj
);
610 UserDereferenceObject(PVOID Object
)
612 PHEAD ObjHead
= Object
;
614 ASSERT(ObjHead
->cLockObj
>= 1);
615 ASSERT(ObjHead
->cLockObj
< 0x10000);
617 if (--ObjHead
->cLockObj
== 0)
619 PUSER_HANDLE_ENTRY entry
;
622 entry
= handle_to_entry(gHandleTable
, ObjHead
->h
);
624 ASSERT(entry
!= NULL
);
625 /* The entry should be marked as in deletion */
626 ASSERT(entry
->flags
& HANDLEENTRY_INDESTROY
);
629 ASSERT(type
!= TYPE_FREE
);
630 ASSERT(type
< TYPE_CTYPES
);
632 /* We can now get rid of everything */
633 free_user_entry(gHandleTable
, entry
);
636 /* Call the object destructor */
637 ASSERT(ObjectCallbacks
[type
].ObjectCleanup
!= NULL
);
638 ObjectCallbacks
[type
].ObjectCleanup(Object
);
642 ASSERT(ObjectCallbacks
[type
].ObjectFree
!= NULL
);
643 ObjectCallbacks
[type
].ObjectFree(Object
);
652 UserFreeHandle(PUSER_HANDLE_TABLE ht
, HANDLE handle
)
654 PUSER_HANDLE_ENTRY entry
;
656 if (!(entry
= handle_to_entry( ht
, handle
)))
658 SetLastNtError( STATUS_INVALID_HANDLE
);
662 entry
->flags
= HANDLEENTRY_INDESTROY
;
664 return UserDereferenceObject(entry
->ptr
);
669 UserObjectInDestroy(HANDLE h
)
671 PUSER_HANDLE_ENTRY entry
;
673 if (!(entry
= handle_to_entry( gHandleTable
, h
)))
675 SetLastNtError( STATUS_INVALID_HANDLE
);
678 return (entry
->flags
& HANDLEENTRY_INDESTROY
);
683 UserDeleteObject(HANDLE h
, HANDLE_TYPE type
)
685 PVOID body
= UserGetObject(gHandleTable
, h
, type
);
687 if (!body
) return FALSE
;
689 ASSERT( ((PHEAD
)body
)->cLockObj
>= 1);
690 ASSERT( ((PHEAD
)body
)->cLockObj
< 0x10000);
692 return UserFreeHandle(gHandleTable
, h
);
697 UserReferenceObject(PVOID obj
)
700 ASSERT(ObjHead
->cLockObj
< 0x10000);
707 UserReferenceObjectByHandle(HANDLE handle
, HANDLE_TYPE type
)
711 object
= UserGetObject(gHandleTable
, handle
, type
);
714 UserReferenceObject(object
);
720 UserDestroyObjectsForOwner(PUSER_HANDLE_TABLE Table
, PVOID Owner
)
723 PUSER_HANDLE_ENTRY Entry
;
726 /* Sweep the whole handle table */
727 for (i
= 0; i
< Table
->allocated_handles
; i
++)
729 Entry
= &Table
->handles
[i
];
731 if (Entry
->pi
!= Owner
)
734 /* Do not destroy if it's already been done */
735 if (Entry
->flags
& HANDLEENTRY_INDESTROY
)
738 /* Call destructor */
739 if (!ObjectCallbacks
[Entry
->type
].ObjectDestroy(Entry
->ptr
))
741 ERR("Failed destructing object %p, type %u.\n", Entry
->ptr
, Entry
->type
);
742 /* Don't return immediately, we must continue destroying the other objects */
758 NtUserValidateHandleSecure(
763 PUSER_HANDLE_ENTRY entry
;
765 DECLARE_RETURN(BOOL
);
766 UserEnterExclusive();
768 if (!(entry
= handle_to_entry(gHandleTable
, handle
)))
770 EngSetLastError(ERROR_INVALID_HANDLE
);
777 case TYPE_INPUTCONTEXT
:
778 ppi
= ((PTHREADINFO
)entry
->pi
)->ppi
;
781 case TYPE_ACCELTABLE
:
785 case TYPE_SETWINDOWPOS
:
793 if (!ppi
) RETURN( FALSE
);
795 // Same process job returns TRUE.
796 if (gptiCurrent
->ppi
->pW32Job
== ppi
->pW32Job
) RETURN( TRUE
);