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