Synchronize with trunk r58457.
[reactos.git] / win32ss / user / ntuser / winsta.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS Win32k subsystem
4 * PURPOSE: Window stations
5 * FILE: subsystems/win32/win32k/ntuser/winsta.c
6 * PROGRAMER: Casper S. Hornstrup (chorns@users.sourceforge.net)
7 * TODO: The process window station is created on
8 * the first USER32/GDI32 call not related
9 * to window station/desktop handling
10 */
11
12 #include <win32k.h>
13 DBG_DEFAULT_CHANNEL(UserWinsta);
14
15 /* GLOBALS *******************************************************************/
16
17 /* Currently active window station */
18 PWINSTATION_OBJECT InputWindowStation = NULL;
19
20 /* Winlogon SAS window */
21 HWND hwndSAS = NULL;
22
23 /* Full path to WindowStations directory */
24 UNICODE_STRING gustrWindowStationsDir;
25
26 /* INITALIZATION FUNCTIONS ****************************************************/
27
28 INIT_FUNCTION
29 NTSTATUS
30 NTAPI
31 InitWindowStationImpl(VOID)
32 {
33 GENERIC_MAPPING IntWindowStationMapping = { WINSTA_READ,
34 WINSTA_WRITE,
35 WINSTA_EXECUTE,
36 WINSTA_ACCESS_ALL};
37
38 /* Set Winsta Object Attributes */
39 ExWindowStationObjectType->TypeInfo.DefaultNonPagedPoolCharge = sizeof(WINSTATION_OBJECT);
40 ExWindowStationObjectType->TypeInfo.GenericMapping = IntWindowStationMapping;
41 ExWindowStationObjectType->TypeInfo.ValidAccessMask = WINSTA_ACCESS_ALL;
42
43 return STATUS_SUCCESS;
44 }
45
46 NTSTATUS
47 NTAPI
48 UserCreateWinstaDirectory()
49 {
50 PPEB Peb;
51 NTSTATUS Status;
52 WCHAR wstrWindowStationsDir[MAX_PATH];
53 OBJECT_ATTRIBUTES ObjectAttributes;
54 HANDLE hWinstaDir;
55
56 /* Create the WindowStations directory and cache its path for later use */
57 Peb = NtCurrentPeb();
58 if(Peb->SessionId == 0)
59 {
60 if (!RtlCreateUnicodeString(&gustrWindowStationsDir, WINSTA_OBJ_DIR))
61 {
62 return STATUS_INSUFFICIENT_RESOURCES;
63 }
64 }
65 else
66 {
67 swprintf(wstrWindowStationsDir,
68 L"%ws\\%ld%ws",
69 SESSION_DIR,
70 Peb->SessionId,
71 WINSTA_OBJ_DIR);
72
73 if (!RtlCreateUnicodeString(&gustrWindowStationsDir, wstrWindowStationsDir))
74 {
75 return STATUS_INSUFFICIENT_RESOURCES;
76 }
77 }
78
79 InitializeObjectAttributes(&ObjectAttributes,
80 &gustrWindowStationsDir,
81 0,
82 NULL,
83 NULL);
84 Status = ZwCreateDirectoryObject(&hWinstaDir, 0, &ObjectAttributes);
85 if (!NT_SUCCESS(Status))
86 {
87 ERR("Could not create %wZ directory (Status 0x%X)\n", &gustrWindowStationsDir, Status);
88 return Status;
89 }
90
91 TRACE("Created directory %wZ for session %d\n", &gustrWindowStationsDir, Peb->SessionId);
92
93 return Status;
94 }
95
96 /* OBJECT CALLBACKS **********************************************************/
97
98 VOID APIENTRY
99 IntWinStaObjectDelete(PWIN32_DELETEMETHOD_PARAMETERS Parameters)
100 {
101 PWINSTATION_OBJECT WinSta = (PWINSTATION_OBJECT)Parameters->Object;
102
103 TRACE("Deleting window station (0x%X)\n", WinSta);
104
105 UserEmptyClipboardData(WinSta);
106
107 RtlDestroyAtomTable(WinSta->AtomTable);
108
109 RtlFreeUnicodeString(&WinSta->Name);
110 }
111
112 NTSTATUS
113 APIENTRY
114 IntWinStaObjectParse(PWIN32_PARSEMETHOD_PARAMETERS Parameters)
115 {
116 PUNICODE_STRING RemainingName = Parameters->RemainingName;
117
118 /* Assume we don't find anything */
119 *Parameters->Object = NULL;
120
121 /* Check for an empty name */
122 if (!RemainingName->Length)
123 {
124 /* Make sure this is a window station, can't parse a desktop now */
125 if (Parameters->ObjectType != ExWindowStationObjectType)
126 {
127 /* Fail */
128 return STATUS_OBJECT_TYPE_MISMATCH;
129 }
130
131 /* Reference the window station and return */
132 ObReferenceObject(Parameters->ParseObject);
133 *Parameters->Object = Parameters->ParseObject;
134 return STATUS_SUCCESS;
135 }
136
137 /* Check for leading slash */
138 if (RemainingName->Buffer[0] == OBJ_NAME_PATH_SEPARATOR)
139 {
140 /* Skip it */
141 RemainingName->Buffer++;
142 RemainingName->Length -= sizeof(WCHAR);
143 RemainingName->MaximumLength -= sizeof(WCHAR);
144 }
145
146 /* Check if there is still a slash */
147 if (wcschr(RemainingName->Buffer, OBJ_NAME_PATH_SEPARATOR))
148 {
149 /* In this case, fail */
150 return STATUS_OBJECT_PATH_INVALID;
151 }
152
153 /*
154 * Check if we are parsing a desktop.
155 */
156 if (Parameters->ObjectType == ExDesktopObjectType)
157 {
158 /* Then call the desktop parse routine */
159 return IntDesktopObjectParse(Parameters->ParseObject,
160 Parameters->ObjectType,
161 Parameters->AccessState,
162 Parameters->AccessMode,
163 Parameters->Attributes,
164 Parameters->CompleteName,
165 RemainingName,
166 Parameters->Context,
167 Parameters->SecurityQos,
168 Parameters->Object);
169 }
170
171 /* Should hopefully never get here */
172 return STATUS_OBJECT_TYPE_MISMATCH;
173 }
174
175 NTSTATUS
176 NTAPI
177 IntWinstaOkToClose(PWIN32_OKAYTOCLOSEMETHOD_PARAMETERS Parameters)
178 {
179 PPROCESSINFO ppi;
180
181 ppi = PsGetCurrentProcessWin32Process();
182
183 if(ppi && (Parameters->Handle == ppi->hwinsta))
184 {
185 return STATUS_ACCESS_DENIED;
186 }
187
188 return STATUS_SUCCESS;
189 }
190
191 /* PRIVATE FUNCTIONS **********************************************************/
192
193 /*
194 * IntValidateWindowStationHandle
195 *
196 * Validates the window station handle.
197 *
198 * Remarks
199 * If the function succeeds, the handle remains referenced. If the
200 * fucntion fails, last error is set.
201 */
202
203 NTSTATUS FASTCALL
204 IntValidateWindowStationHandle(
205 HWINSTA WindowStation,
206 KPROCESSOR_MODE AccessMode,
207 ACCESS_MASK DesiredAccess,
208 PWINSTATION_OBJECT *Object)
209 {
210 NTSTATUS Status;
211
212 if (WindowStation == NULL)
213 {
214 ERR("Invalid window station handle\n");
215 EngSetLastError(ERROR_INVALID_HANDLE);
216 return STATUS_INVALID_HANDLE;
217 }
218
219 Status = ObReferenceObjectByHandle(
220 WindowStation,
221 DesiredAccess,
222 ExWindowStationObjectType,
223 AccessMode,
224 (PVOID*)Object,
225 NULL);
226
227 if (!NT_SUCCESS(Status))
228 SetLastNtError(Status);
229
230 return Status;
231 }
232
233 BOOL FASTCALL
234 co_IntInitializeDesktopGraphics(VOID)
235 {
236 TEXTMETRICW tmw;
237 UNICODE_STRING DriverName = RTL_CONSTANT_STRING(L"DISPLAY");
238
239 ScreenDeviceContext = IntGdiCreateDC(&DriverName, NULL, NULL, NULL, FALSE);
240 if (NULL == ScreenDeviceContext)
241 {
242 IntDestroyPrimarySurface();
243 return FALSE;
244 }
245 GreSetDCOwner(ScreenDeviceContext, GDI_OBJ_HMGR_PUBLIC);
246
247 if (! IntCreatePrimarySurface())
248 {
249 return FALSE;
250 }
251
252 /* Setup the cursor */
253 co_IntLoadDefaultCursors();
254
255 hSystemBM = NtGdiCreateCompatibleDC(ScreenDeviceContext);
256
257 NtGdiSelectFont(hSystemBM, NtGdiGetStockObject(SYSTEM_FONT));
258 GreSetDCOwner(hSystemBM, GDI_OBJ_HMGR_PUBLIC);
259
260 // FIXME: Move these to a update routine.
261 gpsi->Planes = NtGdiGetDeviceCaps(ScreenDeviceContext, PLANES);
262 gpsi->BitsPixel = NtGdiGetDeviceCaps(ScreenDeviceContext, BITSPIXEL);
263 gpsi->BitCount = gpsi->Planes * gpsi->BitsPixel;
264 gpsi->dmLogPixels = NtGdiGetDeviceCaps(ScreenDeviceContext, LOGPIXELSY);
265 if (NtGdiGetDeviceCaps(ScreenDeviceContext, RASTERCAPS) & RC_PALETTE)
266 {
267 gpsi->PUSIFlags |= PUSIF_PALETTEDISPLAY;
268 }
269 else
270 gpsi->PUSIFlags &= ~PUSIF_PALETTEDISPLAY;
271 // Font is realized and this dc was previously set to internal DC_ATTR.
272 gpsi->cxSysFontChar = IntGetCharDimensions(hSystemBM, &tmw, (DWORD*)&gpsi->cySysFontChar);
273 gpsi->tmSysFont = tmw;
274
275 return TRUE;
276 }
277
278 VOID FASTCALL
279 IntEndDesktopGraphics(VOID)
280 {
281 if (NULL != ScreenDeviceContext)
282 { // No need to allocate a new dcattr.
283 GreSetDCOwner(ScreenDeviceContext, GDI_OBJ_HMGR_POWNED);
284 GreDeleteObject(ScreenDeviceContext);
285 ScreenDeviceContext = NULL;
286 }
287 IntHideDesktop(IntGetActiveDesktop());
288 IntDestroyPrimarySurface();
289 }
290
291 HDC FASTCALL
292 IntGetScreenDC(VOID)
293 {
294 return ScreenDeviceContext;
295 }
296
297 /* PUBLIC FUNCTIONS ***********************************************************/
298
299 /*
300 * NtUserCreateWindowStation
301 *
302 * Creates a new window station.
303 *
304 * Parameters
305 * lpszWindowStationName
306 * Pointer to a null-terminated string specifying the name of the
307 * window station to be created. Window station names are
308 * case-insensitive and cannot contain backslash characters (\).
309 * Only members of the Administrators group are allowed to specify a
310 * name.
311 *
312 * dwDesiredAccess
313 * Requested type of access
314 *
315 * lpSecurity
316 * Security descriptor
317 *
318 * Unknown3, Unknown4, Unknown5
319 * Unused
320 *
321 * Return Value
322 * If the function succeeds, the return value is a handle to the newly
323 * created window station. If the specified window station already
324 * exists, the function succeeds and returns a handle to the existing
325 * window station. If the function fails, the return value is NULL.
326 *
327 * Todo
328 * Correct the prototype to match the Windows one (with 7 parameters
329 * on Windows XP).
330 *
331 * Status
332 * @implemented
333 */
334
335 HWINSTA APIENTRY
336 NtUserCreateWindowStation(
337 POBJECT_ATTRIBUTES ObjectAttributes,
338 ACCESS_MASK dwDesiredAccess,
339 DWORD Unknown2,
340 DWORD Unknown3,
341 DWORD Unknown4,
342 DWORD Unknown5,
343 DWORD Unknown6)
344 {
345 UNICODE_STRING WindowStationName;
346 PWINSTATION_OBJECT WindowStationObject;
347 HWINSTA WindowStation;
348 NTSTATUS Status;
349
350 TRACE("NtUserCreateWindowStation called\n");
351
352 Status = ObOpenObjectByName(
353 ObjectAttributes,
354 ExWindowStationObjectType,
355 UserMode,
356 NULL,
357 dwDesiredAccess,
358 NULL,
359 (PVOID*)&WindowStation);
360
361 if (NT_SUCCESS(Status))
362 {
363 TRACE("NtUserCreateWindowStation opened window station %wZ\n", ObjectAttributes->ObjectName);
364 return (HWINSTA)WindowStation;
365 }
366
367 /*
368 * No existing window station found, try to create new one
369 */
370
371 /* Capture window station name */
372 _SEH2_TRY
373 {
374 ProbeForRead( ObjectAttributes, sizeof(OBJECT_ATTRIBUTES), 1);
375 Status = IntSafeCopyUnicodeStringTerminateNULL(&WindowStationName, ObjectAttributes->ObjectName);
376 }
377 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
378 {
379 Status =_SEH2_GetExceptionCode();
380 }
381 _SEH2_END
382
383 if (! NT_SUCCESS(Status))
384 {
385 ERR("Failed reading capturing window station name\n");
386 SetLastNtError(Status);
387 return NULL;
388 }
389
390 /* Create the window station object */
391 Status = ObCreateObject(
392 UserMode,
393 ExWindowStationObjectType,
394 ObjectAttributes,
395 UserMode,
396 NULL,
397 sizeof(WINSTATION_OBJECT),
398 0,
399 0,
400 (PVOID*)&WindowStationObject);
401
402 if (!NT_SUCCESS(Status))
403 {
404 ERR("ObCreateObject failed for window station %wZ\n", &WindowStationName);
405 ExFreePoolWithTag(WindowStationName.Buffer, TAG_STRING);
406 SetLastNtError(STATUS_INSUFFICIENT_RESOURCES);
407 return 0;
408 }
409
410 Status = ObInsertObject(
411 (PVOID)WindowStationObject,
412 NULL,
413 dwDesiredAccess,
414 0,
415 NULL,
416 (PVOID*)&WindowStation);
417
418 if (!NT_SUCCESS(Status))
419 {
420 ERR("ObInsertObject failed for window station %wZ\n", &WindowStationName);
421 ExFreePoolWithTag(WindowStationName.Buffer, TAG_STRING);
422 SetLastNtError(STATUS_INSUFFICIENT_RESOURCES);
423 ObDereferenceObject(WindowStationObject);
424 return 0;
425 }
426
427 /* Initialize the window station */
428 RtlZeroMemory(WindowStationObject, sizeof(WINSTATION_OBJECT));
429
430 KeInitializeSpinLock(&WindowStationObject->Lock);
431 InitializeListHead(&WindowStationObject->DesktopListHead);
432 Status = RtlCreateAtomTable(37, &WindowStationObject->AtomTable);
433 WindowStationObject->SystemMenuTemplate = (HANDLE)0;
434 WindowStationObject->Name = WindowStationName;
435
436 if (InputWindowStation == NULL)
437 {
438 TRACE("Initializeing input window station\n");
439 InputWindowStation = WindowStationObject;
440
441 InitCursorImpl();
442 }
443
444 TRACE("NtUserCreateWindowStation created object 0x%x with name %wZ handle 0x%x\n",
445 WindowStation, &WindowStationObject->Name, WindowStation);
446 return WindowStation;
447 }
448
449 /*
450 * NtUserOpenWindowStation
451 *
452 * Opens an existing window station.
453 *
454 * Parameters
455 * lpszWindowStationName
456 * Name of the existing window station.
457 *
458 * dwDesiredAccess
459 * Requested type of access.
460 *
461 * Return Value
462 * If the function succeeds, the return value is the handle to the
463 * specified window station. If the function fails, the return value
464 * is NULL.
465 *
466 * Remarks
467 * The returned handle can be closed with NtUserCloseWindowStation.
468 *
469 * Status
470 * @implemented
471 */
472
473 HWINSTA APIENTRY
474 NtUserOpenWindowStation(
475 POBJECT_ATTRIBUTES ObjectAttributes,
476 ACCESS_MASK dwDesiredAccess)
477 {
478 HWINSTA hwinsta;
479 NTSTATUS Status;
480
481 Status = ObOpenObjectByName(
482 ObjectAttributes,
483 ExWindowStationObjectType,
484 UserMode,
485 NULL,
486 dwDesiredAccess,
487 NULL,
488 (PVOID*)&hwinsta);
489
490 if (!NT_SUCCESS(Status))
491 {
492 ERR("NtUserOpenWindowStation failed\n");
493 SetLastNtError(Status);
494 return 0;
495 }
496
497 TRACE("Opened window station %wZ with handle 0x%x\n", ObjectAttributes->ObjectName, hwinsta);
498
499 return hwinsta;
500 }
501
502 /*
503 * NtUserCloseWindowStation
504 *
505 * Closes a window station handle.
506 *
507 * Parameters
508 * hWinSta
509 * Handle to the window station.
510 *
511 * Return Value
512 * Status
513 *
514 * Remarks
515 * The window station handle can be created with NtUserCreateWindowStation
516 * or NtUserOpenWindowStation. Attemps to close a handle to the window
517 * station assigned to the calling process will fail.
518 *
519 * Status
520 * @implemented
521 */
522
523 BOOL
524 APIENTRY
525 NtUserCloseWindowStation(
526 HWINSTA hWinSta)
527 {
528 PWINSTATION_OBJECT Object;
529 NTSTATUS Status;
530
531 TRACE("NtUserCloseWindowStation called (0x%x)\n", hWinSta);
532
533 if (hWinSta == UserGetProcessWindowStation())
534 {
535 ERR("Attempted to close process window station\n");
536 return FALSE;
537 }
538
539 Status = IntValidateWindowStationHandle(
540 hWinSta,
541 KernelMode,
542 0,
543 &Object);
544
545 if (!NT_SUCCESS(Status))
546 {
547 ERR("Validation of window station handle (0x%x) failed\n", hWinSta);
548 return FALSE;
549 }
550
551 ObDereferenceObject(Object);
552
553 TRACE("Closing window station handle (0x%x)\n", hWinSta);
554
555 Status = ObCloseHandle(hWinSta, UserMode);
556 if (!NT_SUCCESS(Status))
557 {
558 SetLastNtError(Status);
559 return FALSE;
560 }
561
562 return TRUE;
563 }
564
565 /*
566 * NtUserGetObjectInformation
567 *
568 * The NtUserGetObjectInformation function retrieves information about a
569 * window station or desktop object.
570 *
571 * Parameters
572 * hObj
573 * Handle to the window station or desktop object for which to
574 * return information. This can be a handle of type HDESK or HWINSTA
575 * (for example, a handle returned by NtUserCreateWindowStation,
576 * NtUserOpenWindowStation, NtUserCreateDesktop, or NtUserOpenDesktop).
577 *
578 * nIndex
579 * Specifies the object information to be retrieved.
580 *
581 * pvInfo
582 * Pointer to a buffer to receive the object information.
583 *
584 * nLength
585 * Specifies the size, in bytes, of the buffer pointed to by the
586 * pvInfo parameter.
587 *
588 * lpnLengthNeeded
589 * Pointer to a variable receiving the number of bytes required to
590 * store the requested information. If this variable's value is
591 * greater than the value of the nLength parameter when the function
592 * returns, the function returns FALSE, and none of the information
593 * is copied to the pvInfo buffer. If the value of the variable pointed
594 * to by lpnLengthNeeded is less than or equal to the value of nLength,
595 * the entire information block is copied.
596 *
597 * Return Value
598 * If the function succeeds, the return value is nonzero. If the function
599 * fails, the return value is zero.
600 *
601 * Status
602 * @unimplemented
603 */
604
605 BOOL APIENTRY
606 NtUserGetObjectInformation(
607 HANDLE hObject,
608 DWORD nIndex,
609 PVOID pvInformation,
610 DWORD nLength,
611 PDWORD nLengthNeeded)
612 {
613 PWINSTATION_OBJECT WinStaObject = NULL;
614 PDESKTOP DesktopObject = NULL;
615 NTSTATUS Status;
616 PVOID pvData = NULL;
617 DWORD nDataSize = 0;
618
619 /* try windowstation */
620 TRACE("Trying to open window station 0x%x\n", hObject);
621 Status = ObReferenceObjectByHandle(
622 hObject,
623 0,
624 ExWindowStationObjectType,
625 UserMode,
626 (PVOID*)&WinStaObject,
627 NULL);
628
629 if (Status == STATUS_OBJECT_TYPE_MISMATCH)
630 {
631 /* try desktop */
632 TRACE("Trying to open desktop 0x%x\n", hObject);
633 Status = IntValidateDesktopHandle(
634 hObject,
635 UserMode,
636 0,
637 &DesktopObject);
638 }
639
640 if (!NT_SUCCESS(Status))
641 {
642 ERR("Failed: 0x%x\n", Status);
643 SetLastNtError(Status);
644 return FALSE;
645 }
646
647 TRACE("WinSta or Desktop opened!!\n");
648
649 /* get data */
650 switch (nIndex)
651 {
652 case UOI_FLAGS:
653 Status = STATUS_NOT_IMPLEMENTED;
654 ERR("UOI_FLAGS unimplemented!\n");
655 break;
656
657 case UOI_NAME:
658 if (WinStaObject != NULL)
659 {
660 pvData = WinStaObject->Name.Buffer;
661 nDataSize = WinStaObject->Name.Length + sizeof(WCHAR);
662 Status = STATUS_SUCCESS;
663 }
664 else if (DesktopObject != NULL)
665 {
666 pvData = DesktopObject->pDeskInfo->szDesktopName;
667 nDataSize = (wcslen(DesktopObject->pDeskInfo->szDesktopName) + 1) * sizeof(WCHAR);
668 Status = STATUS_SUCCESS;
669 }
670 else
671 Status = STATUS_INVALID_PARAMETER;
672 break;
673
674 case UOI_TYPE:
675 if (WinStaObject != NULL)
676 {
677 pvData = L"WindowStation";
678 nDataSize = sizeof(L"WindowStation");
679 Status = STATUS_SUCCESS;
680 }
681 else if (DesktopObject != NULL)
682 {
683 pvData = L"Desktop";
684 nDataSize = sizeof(L"Desktop");
685 Status = STATUS_SUCCESS;
686 }
687 else
688 Status = STATUS_INVALID_PARAMETER;
689 break;
690
691 case UOI_USER_SID:
692 Status = STATUS_NOT_IMPLEMENTED;
693 ERR("UOI_USER_SID unimplemented!\n");
694 break;
695
696 default:
697 Status = STATUS_INVALID_PARAMETER;
698 break;
699 }
700
701 /* try to copy data to caller */
702 if (Status == STATUS_SUCCESS)
703 {
704 TRACE("Trying to copy data to caller (len = %d, len needed = %d)\n", nLength, nDataSize);
705 *nLengthNeeded = nDataSize;
706 if (nLength >= nDataSize)
707 Status = MmCopyToCaller(pvInformation, pvData, nDataSize);
708 else
709 Status = STATUS_BUFFER_TOO_SMALL;
710 }
711
712 /* release objects */
713 if (WinStaObject != NULL)
714 ObDereferenceObject(WinStaObject);
715 if (DesktopObject != NULL)
716 ObDereferenceObject(DesktopObject);
717
718 if (!NT_SUCCESS(Status))
719 {
720 SetLastNtError(Status);
721 return FALSE;
722 }
723
724 return TRUE;
725 }
726
727 /*
728 * NtUserSetObjectInformation
729 *
730 * The NtUserSetObjectInformation function sets information about a
731 * window station or desktop object.
732 *
733 * Parameters
734 * hObj
735 * Handle to the window station or desktop object for which to set
736 * object information. This value can be a handle of type HDESK or
737 * HWINSTA.
738 *
739 * nIndex
740 * Specifies the object information to be set.
741 *
742 * pvInfo
743 * Pointer to a buffer containing the object information.
744 *
745 * nLength
746 * Specifies the size, in bytes, of the information contained in the
747 * buffer pointed to by pvInfo.
748 *
749 * Return Value
750 * If the function succeeds, the return value is nonzero. If the function
751 * fails the return value is zero.
752 *
753 * Status
754 * @unimplemented
755 */
756
757 BOOL
758 APIENTRY
759 NtUserSetObjectInformation(
760 HANDLE hObject,
761 DWORD nIndex,
762 PVOID pvInformation,
763 DWORD nLength)
764 {
765 /* FIXME: ZwQueryObject */
766 /* FIXME: ZwSetInformationObject */
767 SetLastNtError(STATUS_UNSUCCESSFUL);
768 return FALSE;
769 }
770
771
772
773
774 HWINSTA FASTCALL
775 UserGetProcessWindowStation(VOID)
776 {
777 PPROCESSINFO ppi = PsGetCurrentProcessWin32Process();
778
779 return ppi->hwinsta;
780 }
781
782
783 /*
784 * NtUserGetProcessWindowStation
785 *
786 * Returns a handle to the current process window station.
787 *
788 * Return Value
789 * If the function succeeds, the return value is handle to the window
790 * station assigned to the current process. If the function fails, the
791 * return value is NULL.
792 *
793 * Status
794 * @implemented
795 */
796
797 HWINSTA APIENTRY
798 NtUserGetProcessWindowStation(VOID)
799 {
800 return UserGetProcessWindowStation();
801 }
802
803 BOOL FASTCALL
804 UserSetProcessWindowStation(HWINSTA hWindowStation)
805 {
806 PPROCESSINFO ppi;
807 NTSTATUS Status;
808 HWINSTA hwinstaOld;
809 PWINSTATION_OBJECT NewWinSta = NULL, OldWinSta;
810
811 ppi = PsGetCurrentProcessWin32Process();
812
813 /* Reference the new window station */
814 if(hWindowStation !=NULL)
815 {
816 Status = IntValidateWindowStationHandle( hWindowStation,
817 KernelMode,
818 0,
819 &NewWinSta);
820 if (!NT_SUCCESS(Status))
821 {
822 TRACE("Validation of window station handle (0x%X) failed\n",
823 hWindowStation);
824 SetLastNtError(Status);
825 return FALSE;
826 }
827 }
828
829 OldWinSta = ppi->prpwinsta;
830 hwinstaOld = PsGetProcessWin32WindowStation(ppi->peProcess);
831
832 /* Dereference the previous window station */
833 if(OldWinSta != NULL)
834 {
835 ObDereferenceObject(OldWinSta);
836 }
837
838 /* Check if we have a stale handle (it should happen for console apps) */
839 if(hwinstaOld != ppi->hwinsta)
840 {
841 ObCloseHandle(hwinstaOld, UserMode);
842 }
843
844 /*
845 * FIXME: Don't allow changing the window station if there are threads that are attached to desktops and own GUI objects.
846 */
847
848 PsSetProcessWindowStation(ppi->peProcess, hWindowStation);
849
850 ppi->prpwinsta = NewWinSta;
851 ppi->hwinsta = hWindowStation;
852
853 return TRUE;
854 }
855
856 /*
857 * NtUserSetProcessWindowStation
858 *
859 * Assigns a window station to the current process.
860 *
861 * Parameters
862 * hWinSta
863 * Handle to the window station.
864 *
865 * Return Value
866 * Status
867 *
868 * Status
869 * @implemented
870 */
871
872 BOOL APIENTRY
873 NtUserSetProcessWindowStation(HWINSTA hWindowStation)
874 {
875 BOOL ret;
876
877 UserEnterExclusive();
878
879 ret = UserSetProcessWindowStation(hWindowStation);
880
881 UserLeave();
882
883 return ret;
884 }
885
886 /*
887 * NtUserLockWindowStation
888 *
889 * Locks switching desktops. Only the logon application is allowed to call this function.
890 *
891 * Status
892 * @implemented
893 */
894
895 BOOL APIENTRY
896 NtUserLockWindowStation(HWINSTA hWindowStation)
897 {
898 PWINSTATION_OBJECT Object;
899 NTSTATUS Status;
900
901 TRACE("About to set process window station with handle (0x%X)\n",
902 hWindowStation);
903
904 if(PsGetCurrentProcessWin32Process() != LogonProcess)
905 {
906 ERR("Unauthorized process attempted to lock the window station!\n");
907 EngSetLastError(ERROR_ACCESS_DENIED);
908 return FALSE;
909 }
910
911 Status = IntValidateWindowStationHandle(
912 hWindowStation,
913 KernelMode,
914 0,
915 &Object);
916 if (!NT_SUCCESS(Status))
917 {
918 TRACE("Validation of window station handle (0x%X) failed\n",
919 hWindowStation);
920 SetLastNtError(Status);
921 return FALSE;
922 }
923
924 Object->Flags |= WSS_LOCKED;
925
926 ObDereferenceObject(Object);
927 return TRUE;
928 }
929
930 /*
931 * NtUserUnlockWindowStation
932 *
933 * Unlocks switching desktops. Only the logon application is allowed to call this function.
934 *
935 * Status
936 * @implemented
937 */
938
939 BOOL APIENTRY
940 NtUserUnlockWindowStation(HWINSTA hWindowStation)
941 {
942 PWINSTATION_OBJECT Object;
943 NTSTATUS Status;
944 BOOL Ret;
945
946 TRACE("About to set process window station with handle (0x%X)\n",
947 hWindowStation);
948
949 if(PsGetCurrentProcessWin32Process() != LogonProcess)
950 {
951 ERR("Unauthorized process attempted to unlock the window station!\n");
952 EngSetLastError(ERROR_ACCESS_DENIED);
953 return FALSE;
954 }
955
956 Status = IntValidateWindowStationHandle(
957 hWindowStation,
958 KernelMode,
959 0,
960 &Object);
961 if (!NT_SUCCESS(Status))
962 {
963 TRACE("Validation of window station handle (0x%X) failed\n",
964 hWindowStation);
965 SetLastNtError(Status);
966 return FALSE;
967 }
968
969 Ret = (Object->Flags & WSS_LOCKED) == WSS_LOCKED;
970 Object->Flags &= ~WSS_LOCKED;
971
972 ObDereferenceObject(Object);
973 return Ret;
974 }
975
976 static NTSTATUS FASTCALL
977 BuildWindowStationNameList(
978 ULONG dwSize,
979 PVOID lpBuffer,
980 PULONG pRequiredSize)
981 {
982 OBJECT_ATTRIBUTES ObjectAttributes;
983 NTSTATUS Status;
984 HANDLE DirectoryHandle;
985 char InitialBuffer[256], *Buffer;
986 ULONG Context, ReturnLength, BufferSize;
987 DWORD EntryCount;
988 POBJECT_DIRECTORY_INFORMATION DirEntry;
989 WCHAR NullWchar;
990
991 /*
992 * Try to open the directory.
993 */
994 InitializeObjectAttributes(
995 &ObjectAttributes,
996 &gustrWindowStationsDir,
997 OBJ_CASE_INSENSITIVE,
998 NULL,
999 NULL);
1000
1001 Status = ZwOpenDirectoryObject(
1002 &DirectoryHandle,
1003 DIRECTORY_QUERY,
1004 &ObjectAttributes);
1005
1006 if (!NT_SUCCESS(Status))
1007 {
1008 return Status;
1009 }
1010
1011 /* First try to query the directory using a fixed-size buffer */
1012 Context = 0;
1013 Buffer = NULL;
1014 Status = ZwQueryDirectoryObject(DirectoryHandle, InitialBuffer, sizeof(InitialBuffer),
1015 FALSE, TRUE, &Context, &ReturnLength);
1016 if (NT_SUCCESS(Status))
1017 {
1018 if (STATUS_NO_MORE_ENTRIES == ZwQueryDirectoryObject(DirectoryHandle, NULL, 0, FALSE,
1019 FALSE, &Context, NULL))
1020 {
1021 /* Our fixed-size buffer is large enough */
1022 Buffer = InitialBuffer;
1023 }
1024 }
1025
1026 if (NULL == Buffer)
1027 {
1028 /* Need a larger buffer, check how large exactly */
1029 Status = ZwQueryDirectoryObject(DirectoryHandle, NULL, 0, FALSE, TRUE, &Context,
1030 &ReturnLength);
1031 if (STATUS_BUFFER_TOO_SMALL == Status)
1032 {
1033 ObDereferenceObject(DirectoryHandle);
1034 return STATUS_NO_MEMORY;
1035 }
1036
1037 BufferSize = ReturnLength;
1038 Buffer = ExAllocatePoolWithTag(PagedPool, BufferSize, TAG_WINSTA);
1039 if (NULL == Buffer)
1040 {
1041 ObDereferenceObject(DirectoryHandle);
1042 return STATUS_NO_MEMORY;
1043 }
1044
1045 /* We should have a sufficiently large buffer now */
1046 Context = 0;
1047 Status = ZwQueryDirectoryObject(DirectoryHandle, Buffer, BufferSize,
1048 FALSE, TRUE, &Context, &ReturnLength);
1049 if (! NT_SUCCESS(Status) ||
1050 STATUS_NO_MORE_ENTRIES != ZwQueryDirectoryObject(DirectoryHandle, NULL, 0, FALSE,
1051 FALSE, &Context, NULL))
1052 {
1053 /* Something went wrong, maybe someone added a directory entry? Just give up. */
1054 ExFreePoolWithTag(Buffer, TAG_WINSTA);
1055 ObDereferenceObject(DirectoryHandle);
1056 return NT_SUCCESS(Status) ? STATUS_INTERNAL_ERROR : Status;
1057 }
1058 }
1059
1060 ZwClose(DirectoryHandle);
1061
1062 /*
1063 * Count the required size of buffer.
1064 */
1065 ReturnLength = sizeof(DWORD);
1066 EntryCount = 0;
1067 for (DirEntry = (POBJECT_DIRECTORY_INFORMATION) Buffer; 0 != DirEntry->Name.Length;
1068 DirEntry++)
1069 {
1070 ReturnLength += DirEntry->Name.Length + sizeof(WCHAR);
1071 EntryCount++;
1072 }
1073 TRACE("Required size: %d Entry count: %d\n", ReturnLength, EntryCount);
1074 if (NULL != pRequiredSize)
1075 {
1076 Status = MmCopyToCaller(pRequiredSize, &ReturnLength, sizeof(ULONG));
1077 if (! NT_SUCCESS(Status))
1078 {
1079 if (Buffer != InitialBuffer)
1080 {
1081 ExFreePoolWithTag(Buffer, TAG_WINSTA);
1082 }
1083 return STATUS_BUFFER_TOO_SMALL;
1084 }
1085 }
1086
1087 /*
1088 * Check if the supplied buffer is large enough.
1089 */
1090 if (dwSize < ReturnLength)
1091 {
1092 if (Buffer != InitialBuffer)
1093 {
1094 ExFreePoolWithTag(Buffer, TAG_WINSTA);
1095 }
1096 return STATUS_BUFFER_TOO_SMALL;
1097 }
1098
1099 /*
1100 * Generate the resulting buffer contents.
1101 */
1102 Status = MmCopyToCaller(lpBuffer, &EntryCount, sizeof(DWORD));
1103 if (! NT_SUCCESS(Status))
1104 {
1105 if (Buffer != InitialBuffer)
1106 {
1107 ExFreePoolWithTag(Buffer, TAG_WINSTA);
1108 }
1109 return Status;
1110 }
1111 lpBuffer = (PVOID) ((PCHAR) lpBuffer + sizeof(DWORD));
1112
1113 NullWchar = L'\0';
1114 for (DirEntry = (POBJECT_DIRECTORY_INFORMATION) Buffer; 0 != DirEntry->Name.Length;
1115 DirEntry++)
1116 {
1117 Status = MmCopyToCaller(lpBuffer, DirEntry->Name.Buffer, DirEntry->Name.Length);
1118 if (! NT_SUCCESS(Status))
1119 {
1120 if (Buffer != InitialBuffer)
1121 {
1122 ExFreePoolWithTag(Buffer, TAG_WINSTA);
1123 }
1124 return Status;
1125 }
1126 lpBuffer = (PVOID) ((PCHAR) lpBuffer + DirEntry->Name.Length);
1127 Status = MmCopyToCaller(lpBuffer, &NullWchar, sizeof(WCHAR));
1128 if (! NT_SUCCESS(Status))
1129 {
1130 if (Buffer != InitialBuffer)
1131 {
1132 ExFreePoolWithTag(Buffer, TAG_WINSTA);
1133 }
1134 return Status;
1135 }
1136 lpBuffer = (PVOID) ((PCHAR) lpBuffer + sizeof(WCHAR));
1137 }
1138
1139 /*
1140 * Clean up
1141 */
1142 if (Buffer != InitialBuffer)
1143 {
1144 ExFreePoolWithTag(Buffer, TAG_WINSTA);
1145 }
1146
1147 return STATUS_SUCCESS;
1148 }
1149
1150 static NTSTATUS FASTCALL
1151 BuildDesktopNameList(
1152 HWINSTA hWindowStation,
1153 ULONG dwSize,
1154 PVOID lpBuffer,
1155 PULONG pRequiredSize)
1156 {
1157 NTSTATUS Status;
1158 PWINSTATION_OBJECT WindowStation;
1159 KIRQL OldLevel;
1160 PLIST_ENTRY DesktopEntry;
1161 PDESKTOP DesktopObject;
1162 DWORD EntryCount;
1163 ULONG ReturnLength;
1164 WCHAR NullWchar;
1165 PUNICODE_STRING DesktopName;
1166
1167 Status = IntValidateWindowStationHandle(hWindowStation,
1168 KernelMode,
1169 0,
1170 &WindowStation);
1171 if (! NT_SUCCESS(Status))
1172 {
1173 return Status;
1174 }
1175
1176 KeAcquireSpinLock(&WindowStation->Lock, &OldLevel);
1177
1178 /*
1179 * Count the required size of buffer.
1180 */
1181 ReturnLength = sizeof(DWORD);
1182 EntryCount = 0;
1183 for (DesktopEntry = WindowStation->DesktopListHead.Flink;
1184 DesktopEntry != &WindowStation->DesktopListHead;
1185 DesktopEntry = DesktopEntry->Flink)
1186 {
1187 DesktopObject = CONTAINING_RECORD(DesktopEntry, DESKTOP, ListEntry);
1188 DesktopName = GET_DESKTOP_NAME(DesktopObject);
1189 if (DesktopName) ReturnLength += DesktopName->Length + sizeof(WCHAR);
1190 EntryCount++;
1191 }
1192 TRACE("Required size: %d Entry count: %d\n", ReturnLength, EntryCount);
1193 if (NULL != pRequiredSize)
1194 {
1195 Status = MmCopyToCaller(pRequiredSize, &ReturnLength, sizeof(ULONG));
1196 if (! NT_SUCCESS(Status))
1197 {
1198 KeReleaseSpinLock(&WindowStation->Lock, OldLevel);
1199 ObDereferenceObject(WindowStation);
1200 return STATUS_BUFFER_TOO_SMALL;
1201 }
1202 }
1203
1204 /*
1205 * Check if the supplied buffer is large enough.
1206 */
1207 if (dwSize < ReturnLength)
1208 {
1209 KeReleaseSpinLock(&WindowStation->Lock, OldLevel);
1210 ObDereferenceObject(WindowStation);
1211 return STATUS_BUFFER_TOO_SMALL;
1212 }
1213
1214 /*
1215 * Generate the resulting buffer contents.
1216 */
1217 Status = MmCopyToCaller(lpBuffer, &EntryCount, sizeof(DWORD));
1218 if (! NT_SUCCESS(Status))
1219 {
1220 KeReleaseSpinLock(&WindowStation->Lock, OldLevel);
1221 ObDereferenceObject(WindowStation);
1222 return Status;
1223 }
1224 lpBuffer = (PVOID) ((PCHAR) lpBuffer + sizeof(DWORD));
1225
1226 NullWchar = L'\0';
1227 for (DesktopEntry = WindowStation->DesktopListHead.Flink;
1228 DesktopEntry != &WindowStation->DesktopListHead;
1229 DesktopEntry = DesktopEntry->Flink)
1230 {
1231 DesktopObject = CONTAINING_RECORD(DesktopEntry, DESKTOP, ListEntry);
1232 _PRAGMA_WARNING_SUPPRESS(__WARNING_DEREF_NULL_PTR)
1233 DesktopName = GET_DESKTOP_NAME(DesktopObject);/// @todo Don't mess around with the object headers!
1234 if (!DesktopName) continue;
1235
1236 Status = MmCopyToCaller(lpBuffer, DesktopName->Buffer, DesktopName->Length);
1237 if (! NT_SUCCESS(Status))
1238 {
1239 KeReleaseSpinLock(&WindowStation->Lock, OldLevel);
1240 ObDereferenceObject(WindowStation);
1241 return Status;
1242 }
1243 lpBuffer = (PVOID) ((PCHAR)lpBuffer + DesktopName->Length);
1244 Status = MmCopyToCaller(lpBuffer, &NullWchar, sizeof(WCHAR));
1245 if (! NT_SUCCESS(Status))
1246 {
1247 KeReleaseSpinLock(&WindowStation->Lock, OldLevel);
1248 ObDereferenceObject(WindowStation);
1249 return Status;
1250 }
1251 lpBuffer = (PVOID) ((PCHAR) lpBuffer + sizeof(WCHAR));
1252 }
1253
1254 /*
1255 * Clean up
1256 */
1257 KeReleaseSpinLock(&WindowStation->Lock, OldLevel);
1258 ObDereferenceObject(WindowStation);
1259
1260 return STATUS_SUCCESS;
1261 }
1262
1263 /*
1264 * NtUserBuildNameList
1265 *
1266 * Function used for enumeration of desktops or window stations.
1267 *
1268 * Parameters
1269 * hWinSta
1270 * For enumeration of window stations this parameter must be set to
1271 * zero. Otherwise it's handle for window station.
1272 *
1273 * dwSize
1274 * Size of buffer passed by caller.
1275 *
1276 * lpBuffer
1277 * Buffer passed by caller. If the function succedes, the buffer is
1278 * filled with window station/desktop count (in first DWORD) and
1279 * NULL-terminated window station/desktop names.
1280 *
1281 * pRequiredSize
1282 * If the function suceedes, this is the number of bytes copied.
1283 * Otherwise it's size of buffer needed for function to succeed.
1284 *
1285 * Status
1286 * @implemented
1287 */
1288
1289 NTSTATUS APIENTRY
1290 NtUserBuildNameList(
1291 HWINSTA hWindowStation,
1292 ULONG dwSize,
1293 PVOID lpBuffer,
1294 PULONG pRequiredSize)
1295 {
1296 /* The WindowStation name list and desktop name list are build in completely
1297 different ways. Call the appropriate function */
1298 return NULL == hWindowStation ? BuildWindowStationNameList(dwSize, lpBuffer, pRequiredSize) :
1299 BuildDesktopNameList(hWindowStation, dwSize, lpBuffer, pRequiredSize);
1300 }
1301
1302 /*
1303 * @implemented
1304 */
1305 BOOL APIENTRY
1306 NtUserSetLogonNotifyWindow(HWND hWnd)
1307 {
1308 if(LogonProcess != PsGetCurrentProcessWin32Process())
1309 {
1310 return FALSE;
1311 }
1312
1313 if(!IntIsWindow(hWnd))
1314 {
1315 return FALSE;
1316 }
1317
1318 hwndSAS = hWnd;
1319
1320 return TRUE;
1321 }
1322
1323 BOOL
1324 APIENTRY
1325 NtUserLockWorkStation(VOID)
1326 {
1327 BOOL ret;
1328 PTHREADINFO pti = PsGetCurrentThreadWin32Thread();
1329
1330 UserEnterExclusive();
1331
1332 if (pti->rpdesk == IntGetActiveDesktop())
1333 {
1334 ret = UserPostMessage(hwndSAS, WM_LOGONNOTIFY, LN_LOCK_WORKSTATION, 0);
1335 }
1336 else
1337 {
1338 ret = FALSE;
1339 }
1340
1341 UserLeave();
1342
1343 return ret;
1344 }
1345
1346 /* EOF */