[CONSRV]
[reactos.git] / reactos / win32ss / user / ntuser / object.c
1 /*
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
7 */
8
9 #include <win32k.h>
10 DBG_DEFAULT_CHANNEL(UserObj);
11
12 //int usedHandles=0;
13 PUSER_HANDLE_TABLE gHandleTable = NULL;
14
15 #if DBG
16
17 void DbgUserDumpHandleTable()
18 {
19 int HandleCounts[TYPE_CTYPES];
20 PPROCESSINFO ppiList;
21 int i;
22 PWCHAR TypeNames[] = {L"Free",L"Window",L"Menu", L"CursorIcon", L"SMWP", L"Hook", L"ClipBoardData", L"CallProc",
23 L"Accel", L"DDEaccess", L"DDEconv", L"DDExact", L"Monitor", L"KBDlayout", L"KBDfile",
24 L"Event", L"Timer", L"InputContext", L"HidData", L"DeviceInfo", L"TouchInput",L"GestureInfo"};
25
26 ERR("Total handles count: %lu\n", gpsi->cHandleEntries);
27
28 memset(HandleCounts, 0, sizeof(HandleCounts));
29
30 /* First of all count the number of handles per tpe */
31 ppiList = gppiList;
32 while (ppiList)
33 {
34 ERR("Process %s (%p) handles count: %d\n\t", ppiList->peProcess->ImageFileName, ppiList->peProcess->UniqueProcessId, ppiList->UserHandleCount);
35
36 for (i = 1 ;i < TYPE_CTYPES; i++)
37 {
38 HandleCounts[i] += ppiList->DbgHandleCount[i];
39
40 DbgPrint("%S: %lu, ", TypeNames[i], ppiList->DbgHandleCount[i]);
41 if (i % 6 == 0)
42 DbgPrint("\n\t");
43 }
44 DbgPrint("\n");
45
46 ppiList = ppiList->ppiNext;
47 }
48
49 /* Print total type counts */
50 ERR("Total handles of the running processes: \n\t");
51 for (i = 1 ;i < TYPE_CTYPES; i++)
52 {
53 DbgPrint("%S: %d, ", TypeNames[i], HandleCounts[i]);
54 if (i % 6 == 0)
55 DbgPrint("\n\t");
56 }
57 DbgPrint("\n");
58
59 /* Now count the handle counts that are allocated from the handle table */
60 memset(HandleCounts, 0, sizeof(HandleCounts));
61 for (i = 0; i < gHandleTable->nb_handles; i++)
62 HandleCounts[gHandleTable->handles[i].type]++;
63
64 ERR("Total handles count allocated: \n\t");
65 for (i = 1 ;i < TYPE_CTYPES; i++)
66 {
67 DbgPrint("%S: %d, ", TypeNames[i], HandleCounts[i]);
68 if (i % 6 == 0)
69 DbgPrint("\n\t");
70 }
71 DbgPrint("\n");
72 }
73
74 #endif
75
76 PUSER_HANDLE_ENTRY handle_to_entry(PUSER_HANDLE_TABLE ht, HANDLE handle )
77 {
78 unsigned short generation;
79 int index = (((unsigned int)handle & 0xffff) - FIRST_USER_HANDLE) >> 1;
80 if (index < 0 || index >= ht->nb_handles)
81 return NULL;
82 if (!ht->handles[index].type)
83 return NULL;
84 generation = (unsigned int)handle >> 16;
85 if (generation == ht->handles[index].generation || !generation || generation == 0xffff)
86 return &ht->handles[index];
87 return NULL;
88 }
89
90 __inline static HANDLE entry_to_handle(PUSER_HANDLE_TABLE ht, PUSER_HANDLE_ENTRY ptr )
91 {
92 int index = ptr - ht->handles;
93 return (HANDLE)(((index << 1) + FIRST_USER_HANDLE) + (ptr->generation << 16));
94 }
95
96 __inline static PUSER_HANDLE_ENTRY alloc_user_entry(PUSER_HANDLE_TABLE ht)
97 {
98 PUSER_HANDLE_ENTRY entry;
99 PPROCESSINFO ppi = PsGetCurrentProcessWin32Process();
100 TRACE("handles used %lu\n", gpsi->cHandleEntries);
101
102 if (ht->freelist)
103 {
104 entry = ht->freelist;
105 ht->freelist = entry->ptr;
106
107 gpsi->cHandleEntries++;
108 ppi->UserHandleCount++;
109 return entry;
110 }
111
112 if (ht->nb_handles >= ht->allocated_handles) /* Need to grow the array */
113 {
114 ERR("Out of user handles! Used -> %lu, NM_Handle -> %d\n", gpsi->cHandleEntries, ht->nb_handles);
115
116 #if DBG
117 DbgUserDumpHandleTable();
118 #endif
119
120 return NULL;
121 #if 0
122 PUSER_HANDLE_ENTRY new_handles;
123 /* Grow array by 50% (but at minimum 32 entries) */
124 int growth = max( 32, ht->allocated_handles / 2 );
125 int new_size = min( ht->allocated_handles + growth, (LAST_USER_HANDLE-FIRST_USER_HANDLE+1) >> 1 );
126 if (new_size <= ht->allocated_handles)
127 return NULL;
128 if (!(new_handles = UserHeapReAlloc( ht->handles, new_size * sizeof(*ht->handles) )))
129 return NULL;
130 ht->handles = new_handles;
131 ht->allocated_handles = new_size;
132 #endif
133 }
134
135 entry = &ht->handles[ht->nb_handles++];
136
137 entry->generation = 1;
138
139 gpsi->cHandleEntries++;
140 ppi->UserHandleCount++;
141
142 return entry;
143 }
144
145 VOID UserInitHandleTable(PUSER_HANDLE_TABLE ht, PVOID mem, ULONG bytes)
146 {
147 ht->freelist = NULL;
148 ht->handles = mem;
149
150 ht->nb_handles = 0;
151 ht->allocated_handles = bytes / sizeof(USER_HANDLE_ENTRY);
152 }
153
154 __inline static void *free_user_entry(PUSER_HANDLE_TABLE ht, PUSER_HANDLE_ENTRY entry)
155 {
156 PPROCESSINFO ppi = PsGetCurrentProcessWin32Process();
157 void *ret;
158
159 #if DBG
160 ppi->DbgHandleCount[entry->type]--;
161 #endif
162
163 ret = entry->ptr;
164 entry->ptr = ht->freelist;
165 entry->type = 0;
166 entry->flags = 0;
167 entry->pi = NULL;
168 ht->freelist = entry;
169
170 gpsi->cHandleEntries--;
171 ppi->UserHandleCount--;
172
173 return ret;
174 }
175
176 static __inline PVOID
177 UserHandleOwnerByType(HANDLE_TYPE type)
178 {
179 PVOID pi;
180
181 switch (type)
182 {
183 case TYPE_WINDOW:
184 case TYPE_INPUTCONTEXT:
185 pi = GetW32ThreadInfo();
186 break;
187
188 case TYPE_MENU:
189 case TYPE_CURSOR:
190 case TYPE_HOOK:
191 case TYPE_CALLPROC:
192 case TYPE_ACCELTABLE:
193 case TYPE_SETWINDOWPOS:
194 pi = GetW32ProcessInfo();
195 break;
196
197 case TYPE_MONITOR:
198 pi = NULL; /* System */
199 break;
200
201 default:
202 pi = NULL;
203 break;
204 }
205
206 return pi;
207 }
208
209 /* allocate a user handle for a given object */
210 HANDLE UserAllocHandle(PUSER_HANDLE_TABLE ht, PVOID object, HANDLE_TYPE type )
211 {
212 PUSER_HANDLE_ENTRY entry = alloc_user_entry(ht);
213 if (!entry)
214 return 0;
215 entry->ptr = object;
216 entry->type = type;
217 entry->flags = 0;
218 entry->pi = UserHandleOwnerByType(type);
219 if (++entry->generation >= 0xffff)
220 entry->generation = 1;
221
222 /* We have created a handle, which is a reference! */
223 UserReferenceObject(object);
224
225 return entry_to_handle(ht, entry );
226 }
227
228 /* return a pointer to a user object from its handle without setting an error */
229 PVOID UserGetObjectNoErr(PUSER_HANDLE_TABLE ht, HANDLE handle, HANDLE_TYPE type )
230 {
231 PUSER_HANDLE_ENTRY entry;
232
233 ASSERT(ht);
234
235 if (!(entry = handle_to_entry(ht, handle )) || entry->type != type)
236 {
237 return NULL;
238 }
239 return entry->ptr;
240 }
241
242 /* return a pointer to a user object from its handle */
243 PVOID UserGetObject(PUSER_HANDLE_TABLE ht, HANDLE handle, HANDLE_TYPE type )
244 {
245 PUSER_HANDLE_ENTRY entry;
246
247 ASSERT(ht);
248
249 if (!(entry = handle_to_entry(ht, handle )) || entry->type != type)
250 {
251 EngSetLastError(ERROR_INVALID_HANDLE);
252 return NULL;
253 }
254 return entry->ptr;
255 }
256
257
258 /* Get the full handle (32bit) for a possibly truncated (16bit) handle */
259 HANDLE get_user_full_handle(PUSER_HANDLE_TABLE ht, HANDLE handle )
260 {
261 PUSER_HANDLE_ENTRY entry;
262
263 if ((unsigned int)handle >> 16)
264 return handle;
265 if (!(entry = handle_to_entry(ht, handle )))
266 return handle;
267 return entry_to_handle( ht, entry );
268 }
269
270
271 /* Same as get_user_object plus set the handle to the full 32-bit value */
272 void *get_user_object_handle(PUSER_HANDLE_TABLE ht, HANDLE* handle, HANDLE_TYPE type )
273 {
274 PUSER_HANDLE_ENTRY entry;
275
276 if (!(entry = handle_to_entry(ht, *handle )) || entry->type != type)
277 return NULL;
278 *handle = entry_to_handle( ht, entry );
279 return entry->ptr;
280 }
281
282
283
284 BOOL FASTCALL UserCreateHandleTable(VOID)
285 {
286 PVOID mem;
287 INT HandleCount = 1024 * 4;
288
289 // FIXME: Don't alloc all at once! Must be mapped into umode also...
290 mem = UserHeapAlloc(sizeof(USER_HANDLE_ENTRY) * HandleCount);
291 if (!mem)
292 {
293 ERR("Failed creating handle table\n");
294 return FALSE;
295 }
296
297 gHandleTable = UserHeapAlloc(sizeof(USER_HANDLE_TABLE));
298 if (gHandleTable == NULL)
299 {
300 UserHeapFree(mem);
301 ERR("Failed creating handle table\n");
302 return FALSE;
303 }
304
305 // FIXME: Make auto growable
306 UserInitHandleTable(gHandleTable, mem, sizeof(USER_HANDLE_ENTRY) * HandleCount);
307
308 return TRUE;
309 }
310
311 //
312 // New
313 //
314 PVOID
315 FASTCALL
316 UserCreateObject( PUSER_HANDLE_TABLE ht,
317 PDESKTOP pDesktop,
318 PTHREADINFO pti,
319 HANDLE* h,
320 HANDLE_TYPE type,
321 ULONG size)
322 {
323 HANDLE hi;
324 PVOID Object;
325 PPROCESSINFO ppi;
326 BOOL dt;
327 PDESKTOP rpdesk = pDesktop;
328
329 /* We could get the desktop for the new object from the pti however this is
330 * not always the case for example when creating a new desktop window for
331 * the desktop thread*/
332
333 if (!pti) pti = GetW32ThreadInfo();
334 if (!pDesktop) rpdesk = pti->rpdesk;
335 ppi = pti->ppi;
336
337 switch (type)
338 {
339 case TYPE_WINDOW:
340 // case TYPE_MENU:
341 case TYPE_HOOK:
342 case TYPE_CALLPROC:
343 case TYPE_INPUTCONTEXT:
344 Object = DesktopHeapAlloc(rpdesk, size);
345 dt = TRUE;
346 break;
347
348 default:
349 Object = UserHeapAlloc(size);
350 dt = FALSE;
351 break;
352 }
353
354 if (!Object)
355 return NULL;
356
357
358 hi = UserAllocHandle(ht, Object, type );
359 if (!hi)
360 {
361 if (dt)
362 DesktopHeapFree(rpdesk, Object);
363 else
364 UserHeapFree(Object);
365 return NULL;
366 }
367
368 #if DBG
369 ppi->DbgHandleCount[type]++;
370 #endif
371
372 RtlZeroMemory(Object, size);
373
374 switch (type)
375 {
376 case TYPE_WINDOW:
377 case TYPE_HOOK:
378 case TYPE_INPUTCONTEXT:
379 ((PTHRDESKHEAD)Object)->rpdesk = rpdesk;
380 ((PTHRDESKHEAD)Object)->pSelf = Object;
381 case TYPE_WINEVENTHOOK:
382 ((PTHROBJHEAD)Object)->pti = pti;
383 break;
384
385 case TYPE_MENU:
386 case TYPE_CALLPROC:
387 ((PPROCDESKHEAD)Object)->rpdesk = rpdesk;
388 ((PPROCDESKHEAD)Object)->pSelf = Object;
389 break;
390
391 case TYPE_CURSOR:
392 ((PPROCMARKHEAD)Object)->ppi = ppi;
393 break;
394
395 default:
396 break;
397 }
398 /* Now set default headers. */
399 ((PHEAD)Object)->h = hi;
400 ((PHEAD)Object)->cLockObj = 2; // We need this, because we create 2 refs: handle and pointer!
401
402 if (h)
403 *h = hi;
404 return Object;
405 }
406
407
408 BOOL
409 FASTCALL
410 UserDereferenceObject(PVOID object)
411 {
412 PUSER_HANDLE_ENTRY entry;
413 HANDLE_TYPE type;
414
415 ASSERT(((PHEAD)object)->cLockObj >= 1);
416
417 if ((INT)--((PHEAD)object)->cLockObj <= 0)
418 {
419 entry = handle_to_entry(gHandleTable, ((PHEAD)object)->h );
420
421 if (!entry)
422 {
423 ERR("Warning! Dereference Object without ENTRY! Obj -> %p\n", object);
424 return FALSE;
425 }
426 TRACE("Warning! Dereference to zero! Obj -> %p\n", object);
427
428 ((PHEAD)object)->cLockObj = 0;
429
430 if (!(entry->flags & HANDLEENTRY_INDESTROY))
431 return TRUE;
432
433 type = entry->type;
434 free_user_entry(gHandleTable, entry );
435
436 switch (type)
437 {
438 case TYPE_WINDOW:
439 // case TYPE_MENU:
440 case TYPE_HOOK:
441 case TYPE_CALLPROC:
442 case TYPE_INPUTCONTEXT:
443 return DesktopHeapFree(((PTHRDESKHEAD)object)->rpdesk, object);
444
445 default:
446 return UserHeapFree(object);
447 }
448 }
449 return FALSE;
450 }
451
452 BOOL
453 FASTCALL
454 UserFreeHandle(PUSER_HANDLE_TABLE ht, HANDLE handle )
455 {
456 PUSER_HANDLE_ENTRY entry;
457
458 if (!(entry = handle_to_entry( ht, handle )))
459 {
460 SetLastNtError( STATUS_INVALID_HANDLE );
461 return FALSE;
462 }
463
464 entry->flags = HANDLEENTRY_INDESTROY;
465
466 return UserDereferenceObject(entry->ptr);
467 }
468
469 BOOL
470 FASTCALL
471 UserObjectInDestroy(HANDLE h)
472 {
473 PUSER_HANDLE_ENTRY entry;
474
475 if (!(entry = handle_to_entry( gHandleTable, h )))
476 {
477 SetLastNtError( STATUS_INVALID_HANDLE );
478 return TRUE;
479 }
480 return (entry->flags & HANDLEENTRY_INDESTROY);
481 }
482
483 BOOL
484 FASTCALL
485 UserDeleteObject(HANDLE h, HANDLE_TYPE type )
486 {
487 PVOID body = UserGetObject(gHandleTable, h, type);
488
489 if (!body) return FALSE;
490
491 ASSERT( ((PHEAD)body)->cLockObj >= 1);
492
493 return UserFreeHandle(gHandleTable, h);
494 }
495
496 VOID
497 FASTCALL
498 UserReferenceObject(PVOID obj)
499 {
500 ASSERT(((PHEAD)obj)->cLockObj >= 0);
501
502 ((PHEAD)obj)->cLockObj++;
503 }
504
505 PVOID
506 FASTCALL
507 UserReferenceObjectByHandle(HANDLE handle, HANDLE_TYPE type)
508 {
509 PVOID object;
510
511 object = UserGetObject(gHandleTable, handle, type);
512 if (object)
513 {
514 UserReferenceObject(object);
515 }
516 return object;
517 }
518
519 VOID
520 FASTCALL
521 UserSetObjectOwner(PVOID obj, HANDLE_TYPE type, PVOID owner)
522 {
523 PUSER_HANDLE_ENTRY entry = handle_to_entry(gHandleTable, ((PHEAD)obj)->h );
524 PPROCESSINFO ppi, oldppi;
525
526 /* This must be called with a valid object */
527 ASSERT(entry);
528
529 /* For now, only supported for CursorIcon object */
530 switch(type)
531 {
532 case TYPE_CURSOR:
533 ppi = (PPROCESSINFO)owner;
534 entry->pi = ppi;
535 oldppi = ((PPROCMARKHEAD)obj)->ppi;
536 ((PPROCMARKHEAD)obj)->ppi = ppi;
537 break;
538 default:
539 ASSERT(FALSE);
540 return;
541 }
542
543 oldppi->UserHandleCount--;
544 ppi->UserHandleCount++;
545 #if DBG
546 oldppi->DbgHandleCount[type]--;
547 ppi->DbgHandleCount[type]++;
548 #endif
549 }
550
551 /*
552 * NtUserValidateHandleSecure
553 *
554 * Status
555 * @implemented
556 */
557
558 BOOL
559 APIENTRY
560 NtUserValidateHandleSecure(
561 HANDLE handle,
562 BOOL Restricted)
563 {
564 if(!Restricted)
565 {
566 UINT uType;
567 {
568 PUSER_HANDLE_ENTRY entry;
569 if (!(entry = handle_to_entry(gHandleTable, handle )))
570 {
571 EngSetLastError(ERROR_INVALID_HANDLE);
572 return FALSE;
573 }
574 uType = entry->type;
575 }
576 switch (uType)
577 {
578 case TYPE_WINDOW:
579 {
580 if (UserGetWindowObject((HWND) handle)) return TRUE;
581 return FALSE;
582 }
583 case TYPE_MENU:
584 {
585 if (UserGetMenuObject((HMENU) handle)) return TRUE;
586 return FALSE;
587 }
588 case TYPE_ACCELTABLE:
589 {
590 if (UserGetAccelObject((HACCEL) handle)) return TRUE;
591 return FALSE;
592 }
593 case TYPE_CURSOR:
594 {
595 if (UserGetCurIconObject((HCURSOR) handle)) return TRUE;
596 return FALSE;
597 }
598 case TYPE_HOOK:
599 {
600 if (IntGetHookObject((HHOOK) handle)) return TRUE;
601 return FALSE;
602 }
603 case TYPE_MONITOR:
604 {
605 if (UserGetMonitorObject((HMONITOR) handle)) return TRUE;
606 return FALSE;
607 }
608 case TYPE_CALLPROC:
609 {
610 WNDPROC_INFO Proc;
611 return UserGetCallProcInfo( handle, &Proc );
612 }
613 default:
614 EngSetLastError(ERROR_INVALID_HANDLE);
615 }
616 }
617 else
618 { /* Is handle entry restricted? */
619 STUB
620 }
621 return FALSE;
622 }