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