a62fa78f06663be727c9dafd028b4602874d75e1
[reactos.git] / reactos / subsys / win32k / ntuser / winsta.c
1 /*
2 * ReactOS W32 Subsystem
3 * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003 ReactOS Team
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 *
19 * $Id$
20 *
21 * COPYRIGHT: See COPYING in the top level directory
22 * PROJECT: ReactOS kernel
23 * PURPOSE: Window stations
24 * FILE: subsys/win32k/ntuser/winsta.c
25 * PROGRAMER: Casper S. Hornstrup (chorns@users.sourceforge.net)
26 * REVISION HISTORY:
27 * 06-06-2001 CSH Created
28 * NOTES: Exported functions set the Win32 last error value
29 * on errors. The value can be retrieved with the Win32
30 * function GetLastError().
31 * TODO: The process window station is created on
32 * the first USER32/GDI32 call not related
33 * to window station/desktop handling
34 */
35
36 /* INCLUDES ******************************************************************/
37
38 #include <w32k.h>
39
40 #define NDEBUG
41 #include <debug.h>
42
43 /* GLOBALS *******************************************************************/
44
45 /* Currently active window station */
46 PWINSTATION_OBJECT InputWindowStation = NULL;
47
48 /* INITALIZATION FUNCTIONS ****************************************************/
49
50 static GENERIC_MAPPING IntWindowStationMapping =
51 {
52 STANDARD_RIGHTS_READ | WINSTA_ENUMDESKTOPS | WINSTA_ENUMERATE | WINSTA_READATTRIBUTES | WINSTA_READSCREEN,
53 STANDARD_RIGHTS_WRITE | WINSTA_ACCESSCLIPBOARD | WINSTA_CREATEDESKTOP | WINSTA_WRITEATTRIBUTES,
54 STANDARD_RIGHTS_EXECUTE | WINSTA_ACCESSGLOBALATOMS | WINSTA_EXITWINDOWS,
55 STANDARD_RIGHTS_REQUIRED | WINSTA_ACCESSCLIPBOARD | WINSTA_ACCESSGLOBALATOMS | WINSTA_CREATEDESKTOP |
56 WINSTA_ENUMDESKTOPS | WINSTA_ENUMERATE | WINSTA_EXITWINDOWS |
57 WINSTA_READATTRIBUTES | WINSTA_READSCREEN | WINSTA_WRITEATTRIBUTES
58 };
59
60 NTSTATUS FASTCALL
61 InitWindowStationImpl(VOID)
62 {
63 OBJECT_ATTRIBUTES ObjectAttributes;
64 HANDLE WindowStationsDirectory;
65 UNICODE_STRING UnicodeString;
66 NTSTATUS Status;
67
68 /*
69 * Create the '\Windows\WindowStations' directory
70 */
71
72 RtlInitUnicodeString(&UnicodeString, WINSTA_ROOT_NAME);
73 InitializeObjectAttributes(&ObjectAttributes, &UnicodeString,
74 0, NULL, NULL);
75 Status = ZwCreateDirectoryObject(&WindowStationsDirectory, 0,
76 &ObjectAttributes);
77 if (!NT_SUCCESS(Status))
78 {
79 DPRINT("Could not create \\Windows\\WindowStations directory "
80 "(Status 0x%X)\n", Status);
81 return Status;
82 }
83
84 /* Set Winsta Object Attributes */
85 ExWindowStationObjectType->TypeInfo.DefaultNonPagedPoolCharge = sizeof(WINSTATION_OBJECT);
86 ExWindowStationObjectType->TypeInfo.GenericMapping = IntWindowStationMapping;
87
88 return STATUS_SUCCESS;
89 }
90
91 NTSTATUS FASTCALL
92 CleanupWindowStationImpl(VOID)
93 {
94 return STATUS_SUCCESS;
95 }
96
97 /* OBJECT CALLBACKS **********************************************************/
98
99 NTSTATUS
100 STDCALL
101 IntWinStaObjectOpen(OB_OPEN_REASON Reason,
102 PVOID ObjectBody,
103 PEPROCESS Process,
104 ULONG HandleCount,
105 ACCESS_MASK GrantedAccess)
106 {
107 PWINSTATION_OBJECT WinSta = (PWINSTATION_OBJECT)ObjectBody;
108 NTSTATUS Status;
109
110 if (Reason == ObCreateHandle)
111 {
112 DPRINT("Creating window station (0x%X)\n", WinSta);
113
114 KeInitializeSpinLock(&WinSta->Lock);
115
116 InitializeListHead(&WinSta->DesktopListHead);
117
118 WinSta->AtomTable = NULL;
119
120 Status = RtlCreateAtomTable(37, &WinSta->AtomTable);
121
122 WinSta->SystemMenuTemplate = (HANDLE)0;
123
124 DPRINT("Window station successfully created.\n");
125 }
126
127 return STATUS_SUCCESS;
128 }
129
130 VOID STDCALL
131 IntWinStaObjectDelete(PVOID DeletedObject)
132 {
133 PWINSTATION_OBJECT WinSta = (PWINSTATION_OBJECT)DeletedObject;
134
135 DPRINT("Deleting window station (0x%X)\n", WinSta);
136
137 RtlDestroyAtomTable(WinSta->AtomTable);
138
139 RtlFreeUnicodeString(&WinSta->Name);
140 }
141
142 PVOID STDCALL
143 IntWinStaObjectFind(PVOID Object,
144 PWSTR Name,
145 ULONG Attributes)
146 {
147 PLIST_ENTRY Current;
148 PDESKTOP_OBJECT CurrentObject;
149 PWINSTATION_OBJECT WinStaObject = (PWINSTATION_OBJECT)Object;
150
151 DPRINT("WinStaObject (0x%X) Name (%wS)\n", WinStaObject, Name);
152
153 if (Name[0] == 0)
154 {
155 return NULL;
156 }
157
158 Current = WinStaObject->DesktopListHead.Flink;
159 while (Current != &WinStaObject->DesktopListHead)
160 {
161 CurrentObject = CONTAINING_RECORD(Current, DESKTOP_OBJECT, ListEntry);
162 DPRINT("Scanning %wZ for %wS\n", &CurrentObject->Name, Name);
163 if (Attributes & OBJ_CASE_INSENSITIVE)
164 {
165 if (_wcsicmp(CurrentObject->Name.Buffer, Name) == 0)
166 {
167 DPRINT("Found desktop at (0x%X)\n", CurrentObject);
168 return CurrentObject;
169 }
170 }
171 else
172 {
173 if (wcscmp(CurrentObject->Name.Buffer, Name) == 0)
174 {
175 DPRINT("Found desktop at (0x%X)\n", CurrentObject);
176 return CurrentObject;
177 }
178 }
179 Current = Current->Flink;
180 }
181
182 DPRINT("Returning NULL\n");
183
184 return NULL;
185 }
186
187 NTSTATUS
188 STDCALL
189 IntWinStaObjectParse(PVOID Object,
190 PVOID *NextObject,
191 PUNICODE_STRING FullPath,
192 PWSTR *Path,
193 ULONG Attributes)
194 {
195 PVOID FoundObject;
196 NTSTATUS Status;
197 PWSTR End;
198
199 DPRINT("Object (0x%X) Path (0x%X) *Path (%wS)\n", Object, Path, *Path);
200
201 *NextObject = NULL;
202
203 if ((Path == NULL) || ((*Path) == NULL))
204 {
205 return STATUS_SUCCESS;
206 }
207
208 End = wcschr((*Path) + 1, '\\');
209 if (End != NULL)
210 {
211 DPRINT("Name contains illegal characters\n");
212 return STATUS_UNSUCCESSFUL;
213 }
214
215 FoundObject = IntWinStaObjectFind(Object, (*Path) + 1, Attributes);
216 if (FoundObject == NULL)
217 {
218 DPRINT("Name was not found\n");
219 return STATUS_UNSUCCESSFUL;
220 }
221
222 Status = ObReferenceObjectByPointer(
223 FoundObject,
224 STANDARD_RIGHTS_REQUIRED,
225 NULL,
226 UserMode);
227
228 *NextObject = FoundObject;
229 *Path = NULL;
230
231 return Status;
232 }
233
234 /* PRIVATE FUNCTIONS **********************************************************/
235
236 /*
237 * IntGetFullWindowStationName
238 *
239 * Get a full window station object name from a name specified in
240 * NtUserCreateWindowStation, NtUserOpenWindowStation, NtUserCreateDesktop
241 * or NtUserOpenDesktop.
242 *
243 * Return Value
244 * TRUE on success, FALSE on failure.
245 */
246
247 BOOL FASTCALL
248 IntGetFullWindowStationName(
249 OUT PUNICODE_STRING FullName,
250 IN PUNICODE_STRING WinStaName,
251 IN OPTIONAL PUNICODE_STRING DesktopName)
252 {
253 PWCHAR Buffer;
254
255 FullName->Length = WINSTA_ROOT_NAME_LENGTH * sizeof(WCHAR);
256 if (WinStaName != NULL)
257 FullName->Length += WinStaName->Length + sizeof(WCHAR);
258 if (DesktopName != NULL)
259 FullName->Length += DesktopName->Length + sizeof(WCHAR);
260 FullName->Buffer = ExAllocatePoolWithTag(PagedPool, FullName->Length, TAG_STRING);
261 if (FullName->Buffer == NULL)
262 {
263 return FALSE;
264 }
265
266 Buffer = FullName->Buffer;
267 memcpy(Buffer, WINSTA_ROOT_NAME, WINSTA_ROOT_NAME_LENGTH * sizeof(WCHAR));
268 Buffer += WINSTA_ROOT_NAME_LENGTH;
269 if (WinStaName != NULL)
270 {
271 memcpy(Buffer, L"\\", sizeof(WCHAR));
272 Buffer ++;
273 memcpy(Buffer, WinStaName->Buffer, WinStaName->Length);
274
275 if (DesktopName != NULL)
276 {
277 Buffer += WinStaName->Length / sizeof(WCHAR);
278 memcpy(Buffer, L"\\", sizeof(WCHAR));
279 Buffer ++;
280 memcpy(Buffer, DesktopName->Buffer, DesktopName->Length);
281 }
282 }
283
284 return TRUE;
285 }
286
287 /*
288 * IntValidateWindowStationHandle
289 *
290 * Validates the window station handle.
291 *
292 * Remarks
293 * If the function succeeds, the handle remains referenced. If the
294 * fucntion fails, last error is set.
295 */
296
297 NTSTATUS FASTCALL
298 IntValidateWindowStationHandle(
299 HWINSTA WindowStation,
300 KPROCESSOR_MODE AccessMode,
301 ACCESS_MASK DesiredAccess,
302 PWINSTATION_OBJECT *Object)
303 {
304 NTSTATUS Status;
305
306 if (WindowStation == NULL)
307 {
308 // DPRINT1("Invalid window station handle\n");
309 SetLastWin32Error(ERROR_INVALID_HANDLE);
310 return STATUS_INVALID_HANDLE;
311 }
312
313 Status = ObReferenceObjectByHandle(
314 WindowStation,
315 DesiredAccess,
316 ExWindowStationObjectType,
317 AccessMode,
318 (PVOID*)Object,
319 NULL);
320
321 if (!NT_SUCCESS(Status))
322 SetLastNtError(Status);
323
324 return Status;
325 }
326
327 BOOL FASTCALL
328 IntGetWindowStationObject(PWINSTATION_OBJECT Object)
329 {
330 NTSTATUS Status;
331
332 Status = ObReferenceObjectByPointer(
333 Object,
334 KernelMode,
335 ExWindowStationObjectType,
336 0);
337
338 return NT_SUCCESS(Status);
339 }
340
341 BOOL FASTCALL
342 co_IntInitializeDesktopGraphics(VOID)
343 {
344 UNICODE_STRING DriverName;
345 if (! IntCreatePrimarySurface())
346 {
347 return FALSE;
348 }
349 RtlInitUnicodeString(&DriverName, L"DISPLAY");
350 ScreenDeviceContext = IntGdiCreateDC(&DriverName, NULL, NULL, NULL, FALSE);
351 if (NULL == ScreenDeviceContext)
352 {
353 IntDestroyPrimarySurface();
354 return FALSE;
355 }
356 DC_SetOwnership(ScreenDeviceContext, NULL);
357
358 UserAcquireOrReleaseInputOwnership(FALSE);
359
360 /* Setup the cursor */
361 co_IntLoadDefaultCursors();
362
363 return TRUE;
364 }
365
366 VOID FASTCALL
367 IntEndDesktopGraphics(VOID)
368 {
369 UserAcquireOrReleaseInputOwnership(TRUE);
370 if (NULL != ScreenDeviceContext)
371 {
372 DC_SetOwnership(ScreenDeviceContext, PsGetCurrentProcess());
373 NtGdiDeleteDC(ScreenDeviceContext);
374 ScreenDeviceContext = NULL;
375 }
376 IntHideDesktop(IntGetActiveDesktop());
377 IntDestroyPrimarySurface();
378 }
379
380 HDC FASTCALL
381 IntGetScreenDC(VOID)
382 {
383 return ScreenDeviceContext;
384 }
385
386 /* PUBLIC FUNCTIONS ***********************************************************/
387
388 /*
389 * NtUserCreateWindowStation
390 *
391 * Creates a new window station.
392 *
393 * Parameters
394 * lpszWindowStationName
395 * Pointer to a null-terminated string specifying the name of the
396 * window station to be created. Window station names are
397 * case-insensitive and cannot contain backslash characters (\).
398 * Only members of the Administrators group are allowed to specify a
399 * name.
400 *
401 * dwDesiredAccess
402 * Requested type of access
403 *
404 * lpSecurity
405 * Security descriptor
406 *
407 * Unknown3, Unknown4, Unknown5
408 * Unused
409 *
410 * Return Value
411 * If the function succeeds, the return value is a handle to the newly
412 * created window station. If the specified window station already
413 * exists, the function succeeds and returns a handle to the existing
414 * window station. If the function fails, the return value is NULL.
415 *
416 * Todo
417 * Correct the prototype to match the Windows one (with 7 parameters
418 * on Windows XP).
419 *
420 * Status
421 * @implemented
422 */
423
424 HWINSTA STDCALL
425 NtUserCreateWindowStation(
426 PUNICODE_STRING lpszWindowStationName,
427 ACCESS_MASK dwDesiredAccess,
428 LPSECURITY_ATTRIBUTES lpSecurity,
429 DWORD Unknown3,
430 DWORD Unknown4,
431 DWORD Unknown5)
432 {
433 PSYSTEM_CURSORINFO CurInfo;
434 UNICODE_STRING WindowStationName;
435 PWINSTATION_OBJECT WindowStationObject;
436 HWINSTA WindowStation;
437 OBJECT_ATTRIBUTES ObjectAttributes;
438 NTSTATUS Status;
439
440 /*
441 * Generate full window station name
442 */
443
444 if (!IntGetFullWindowStationName(&WindowStationName, lpszWindowStationName,
445 NULL))
446 {
447 SetLastNtError(STATUS_INSUFFICIENT_RESOURCES);
448 return 0;
449 }
450
451 /*
452 * Try to open already existing window station
453 */
454
455 DPRINT("Trying to open window station (%wZ)\n", &WindowStationName);
456
457 /* Initialize ObjectAttributes for the window station object */
458 InitializeObjectAttributes(
459 &ObjectAttributes,
460 &WindowStationName,
461 0,
462 NULL,
463 NULL);
464
465 Status = ObOpenObjectByName(
466 &ObjectAttributes,
467 ExWindowStationObjectType,
468 NULL,
469 KernelMode,
470 dwDesiredAccess,
471 NULL,
472 (PVOID*)&WindowStation);
473
474 if (NT_SUCCESS(Status))
475 {
476 DPRINT("Successfully opened window station (%wZ)\n", WindowStationName);
477 ExFreePool(WindowStationName.Buffer);
478 return (HWINSTA)WindowStation;
479 }
480
481 /*
482 * No existing window station found, try to create new one
483 */
484
485 DPRINT("Creating window station (%wZ)\n", &WindowStationName);
486
487 Status = ObCreateObject(
488 KernelMode,
489 ExWindowStationObjectType,
490 &ObjectAttributes,
491 ExGetPreviousMode(),
492 NULL,
493 sizeof(WINSTATION_OBJECT),
494 0,
495 0,
496 (PVOID*)&WindowStationObject);
497
498 if (!NT_SUCCESS(Status))
499 {
500 DPRINT1("Failed creating window station (%wZ)\n", &WindowStationName);
501 ExFreePool(WindowStationName.Buffer);
502 SetLastNtError(STATUS_INSUFFICIENT_RESOURCES);
503 return 0;
504 }
505
506 WindowStationObject->Name = *lpszWindowStationName;
507
508 Status = ObInsertObject(
509 (PVOID)WindowStationObject,
510 NULL,
511 STANDARD_RIGHTS_REQUIRED,
512 0,
513 NULL,
514 (PVOID*)&WindowStation);
515
516 if (!NT_SUCCESS(Status))
517 {
518 DPRINT1("Failed creating window station (%wZ)\n", &WindowStationName);
519 ExFreePool(WindowStationName.Buffer);
520 SetLastNtError(STATUS_INSUFFICIENT_RESOURCES);
521 ObDereferenceObject(WindowStationObject);
522 return 0;
523 }
524
525 /*
526 * Initialize the new window station object
527 */
528
529 if(!(CurInfo = ExAllocatePool(PagedPool, sizeof(SYSTEM_CURSORINFO))))
530 {
531 ExFreePool(WindowStationName.Buffer);
532 /* FIXME - Delete window station object */
533 ObDereferenceObject(WindowStationObject);
534 SetLastNtError(STATUS_INSUFFICIENT_RESOURCES);
535 return 0;
536 }
537
538 WindowStationObject->HandleTable = ObmCreateHandleTable();
539 if (!WindowStationObject->HandleTable)
540 {
541 DPRINT("Failed creating handle table\n");
542 ExFreePool(CurInfo);
543 ExFreePool(WindowStationName.Buffer);
544 /* FIXME - Delete window station object */
545 ObDereferenceObject(WindowStationObject);
546 SetLastNtError(STATUS_INSUFFICIENT_RESOURCES);
547 return 0;
548 }
549
550 InitHotKeys(WindowStationObject);
551
552 ExInitializeFastMutex(&CurInfo->CursorMutex);
553 CurInfo->Enabled = FALSE;
554 CurInfo->ButtonsDown = 0;
555 CurInfo->CursorClipInfo.IsClipped = FALSE;
556 CurInfo->LastBtnDown = 0;
557 CurInfo->CurrentCursorObject = NULL;
558 CurInfo->ShowingCursor = 0;
559
560 /* FIXME: Obtain the following information from the registry */
561 CurInfo->SwapButtons = FALSE;
562 CurInfo->DblClickSpeed = 500;
563 CurInfo->DblClickWidth = 4;
564 CurInfo->DblClickHeight = 4;
565
566 WindowStationObject->SystemCursor = CurInfo;
567
568 if (!IntSetupCurIconHandles(WindowStationObject))
569 {
570 DPRINT1("Setting up the Cursor/Icon Handle table failed!\n");
571 /* FIXME: Complain more loudly? */
572 }
573
574 DPRINT("Window station successfully created (%wZ)\n", lpszWindowStationName);
575 ExFreePool(WindowStationName.Buffer);
576 return WindowStation;
577 }
578
579 /*
580 * NtUserOpenWindowStation
581 *
582 * Opens an existing window station.
583 *
584 * Parameters
585 * lpszWindowStationName
586 * Name of the existing window station.
587 *
588 * dwDesiredAccess
589 * Requested type of access.
590 *
591 * Return Value
592 * If the function succeeds, the return value is the handle to the
593 * specified window station. If the function fails, the return value
594 * is NULL.
595 *
596 * Remarks
597 * The returned handle can be closed with NtUserCloseWindowStation.
598 *
599 * Status
600 * @implemented
601 */
602
603 HWINSTA STDCALL
604 NtUserOpenWindowStation(
605 PUNICODE_STRING lpszWindowStationName,
606 ACCESS_MASK dwDesiredAccess)
607 {
608 UNICODE_STRING WindowStationName;
609 HWINSTA WindowStation;
610 OBJECT_ATTRIBUTES ObjectAttributes;
611 NTSTATUS Status;
612
613 /*
614 * Generate full window station name
615 */
616
617 if (!IntGetFullWindowStationName(&WindowStationName, lpszWindowStationName,
618 NULL))
619 {
620 SetLastNtError(STATUS_INSUFFICIENT_RESOURCES);
621 return 0;
622 }
623
624 DPRINT("Trying to open window station (%wZ)\n", &WindowStationName);
625
626 /* Initialize ObjectAttributes for the window station object */
627 InitializeObjectAttributes(
628 &ObjectAttributes,
629 &WindowStationName,
630 0,
631 NULL,
632 NULL);
633
634 Status = ObOpenObjectByName(
635 &ObjectAttributes,
636 ExWindowStationObjectType,
637 NULL,
638 UserMode,
639 dwDesiredAccess,
640 NULL,
641 (PVOID*)&WindowStation);
642
643 if (!NT_SUCCESS(Status))
644 {
645 SetLastNtError(Status);
646 ExFreePool(WindowStationName.Buffer);
647 return 0;
648 }
649
650 DPRINT("Successfully opened window station (%wZ)\n", &WindowStationName);
651 ExFreePool(WindowStationName.Buffer);
652
653 return WindowStation;
654 }
655
656 /*
657 * NtUserCloseWindowStation
658 *
659 * Closes a window station handle.
660 *
661 * Parameters
662 * hWinSta
663 * Handle to the window station.
664 *
665 * Return Value
666 * Status
667 *
668 * Remarks
669 * The window station handle can be created with NtUserCreateWindowStation
670 * or NtUserOpenWindowStation. Attemps to close a handle to the window
671 * station assigned to the calling process will fail.
672 *
673 * Status
674 * @implemented
675 */
676
677 BOOL
678 STDCALL
679 NtUserCloseWindowStation(
680 HWINSTA hWinSta)
681 {
682 PWINSTATION_OBJECT Object;
683 NTSTATUS Status;
684
685 DPRINT("About to close window station handle (0x%X)\n", hWinSta);
686
687 Status = IntValidateWindowStationHandle(
688 hWinSta,
689 KernelMode,
690 0,
691 &Object);
692
693 if (!NT_SUCCESS(Status))
694 {
695 DPRINT("Validation of window station handle (0x%X) failed\n", hWinSta);
696 return FALSE;
697 }
698
699 #if 0
700 /* FIXME - free the cursor information when actually deleting the object!! */
701 ASSERT(Object->SystemCursor);
702 ExFreePool(Object->SystemCursor);
703 #endif
704
705 ObDereferenceObject(Object);
706
707 DPRINT("Closing window station handle (0x%X)\n", hWinSta);
708
709 Status = ZwClose(hWinSta);
710 if (!NT_SUCCESS(Status))
711 {
712 SetLastNtError(Status);
713 return FALSE;
714 }
715
716 return TRUE;
717 }
718
719 /*
720 * NtUserGetObjectInformation
721 *
722 * The NtUserGetObjectInformation function retrieves information about a
723 * window station or desktop object.
724 *
725 * Parameters
726 * hObj
727 * Handle to the window station or desktop object for which to
728 * return information. This can be a handle of type HDESK or HWINSTA
729 * (for example, a handle returned by NtUserCreateWindowStation,
730 * NtUserOpenWindowStation, NtUserCreateDesktop, or NtUserOpenDesktop).
731 *
732 * nIndex
733 * Specifies the object information to be retrieved.
734 *
735 * pvInfo
736 * Pointer to a buffer to receive the object information.
737 *
738 * nLength
739 * Specifies the size, in bytes, of the buffer pointed to by the
740 * pvInfo parameter.
741 *
742 * lpnLengthNeeded
743 * Pointer to a variable receiving the number of bytes required to
744 * store the requested information. If this variable's value is
745 * greater than the value of the nLength parameter when the function
746 * returns, the function returns FALSE, and none of the information
747 * is copied to the pvInfo buffer. If the value of the variable pointed
748 * to by lpnLengthNeeded is less than or equal to the value of nLength,
749 * the entire information block is copied.
750 *
751 * Return Value
752 * If the function succeeds, the return value is nonzero. If the function
753 * fails, the return value is zero.
754 *
755 * Status
756 * @unimplemented
757 */
758
759 BOOL STDCALL
760 NtUserGetObjectInformation(
761 HANDLE hObject,
762 DWORD nIndex,
763 PVOID pvInformation,
764 DWORD nLength,
765 PDWORD nLengthNeeded)
766 {
767 PWINSTATION_OBJECT WinStaObject = NULL;
768 PDESKTOP_OBJECT DesktopObject = NULL;
769 NTSTATUS Status;
770 PVOID pvData = NULL;
771 DWORD nDataSize = 0;
772
773 /* try windowstation */
774 DPRINT("Trying to open window station 0x%x\n", hObject);
775 Status = IntValidateWindowStationHandle(
776 hObject,
777 UserMode,/*ExGetPreviousMode(),*/
778 GENERIC_READ, /* FIXME: is this ok? */
779 &WinStaObject);
780
781
782 if (!NT_SUCCESS(Status) && Status != STATUS_OBJECT_TYPE_MISMATCH)
783 {
784 DPRINT("Failed: 0x%x\n", Status);
785 SetLastNtError(Status);
786 return FALSE;
787 }
788
789 if (Status == STATUS_OBJECT_TYPE_MISMATCH)
790 {
791 /* try desktop */
792 DPRINT("Trying to open desktop 0x%x\n", hObject);
793 Status = IntValidateDesktopHandle(
794 hObject,
795 UserMode,/*ExGetPreviousMode(),*/
796 GENERIC_READ, /* FIXME: is this ok? */
797 &DesktopObject);
798 if (!NT_SUCCESS(Status))
799 {
800 DPRINT("Failed: 0x%x\n", Status);
801 SetLastNtError(Status);
802 return FALSE;
803 }
804 }
805 DPRINT("WinSta or Desktop opened!!\n");
806
807 /* get data */
808 switch (nIndex)
809 {
810 case UOI_FLAGS:
811 Status = STATUS_NOT_IMPLEMENTED;
812 DPRINT1("UOI_FLAGS unimplemented!\n");
813 break;
814
815 case UOI_NAME:
816 if (WinStaObject != NULL)
817 {
818 pvData = WinStaObject->Name.Buffer;
819 nDataSize = WinStaObject->Name.Length+2;
820 Status = STATUS_SUCCESS;
821 }
822 else if (DesktopObject != NULL)
823 {
824 pvData = DesktopObject->Name.Buffer;
825 nDataSize = DesktopObject->Name.Length+2;
826 Status = STATUS_SUCCESS;
827 }
828 else
829 Status = STATUS_INVALID_PARAMETER;
830 break;
831
832 case UOI_TYPE:
833 if (WinStaObject != NULL)
834 {
835 pvData = L"WindowStation";
836 nDataSize = (wcslen(pvData) + 1) * sizeof(WCHAR);
837 Status = STATUS_SUCCESS;
838 }
839 else if (DesktopObject != NULL)
840 {
841 pvData = L"Desktop";
842 nDataSize = (wcslen(pvData) + 1) * sizeof(WCHAR);
843 Status = STATUS_SUCCESS;
844 }
845 else
846 Status = STATUS_INVALID_PARAMETER;
847 break;
848
849 case UOI_USER_SID:
850 Status = STATUS_NOT_IMPLEMENTED;
851 DPRINT1("UOI_USER_SID unimplemented!\n");
852 break;
853
854 default:
855 Status = STATUS_INVALID_PARAMETER;
856 break;
857 }
858
859 /* try to copy data to caller */
860 if (Status == STATUS_SUCCESS)
861 {
862 DPRINT("Trying to copy data to caller (len = %d, len needed = %d)\n", nLength, nDataSize);
863 *nLengthNeeded = nDataSize;
864 if (nLength >= nDataSize)
865 Status = MmCopyToCaller(pvInformation, pvData, nDataSize);
866 else
867 Status = STATUS_BUFFER_TOO_SMALL;
868 }
869
870 /* release objects */
871 if (WinStaObject != NULL)
872 ObDereferenceObject(WinStaObject);
873 if (DesktopObject != NULL)
874 ObDereferenceObject(DesktopObject);
875
876 SetLastNtError(Status);
877 return NT_SUCCESS(Status);
878 }
879
880 /*
881 * NtUserSetObjectInformation
882 *
883 * The NtUserSetObjectInformation function sets information about a
884 * window station or desktop object.
885 *
886 * Parameters
887 * hObj
888 * Handle to the window station or desktop object for which to set
889 * object information. This value can be a handle of type HDESK or
890 * HWINSTA.
891 *
892 * nIndex
893 * Specifies the object information to be set.
894 *
895 * pvInfo
896 * Pointer to a buffer containing the object information.
897 *
898 * nLength
899 * Specifies the size, in bytes, of the information contained in the
900 * buffer pointed to by pvInfo.
901 *
902 * Return Value
903 * If the function succeeds, the return value is nonzero. If the function
904 * fails the return value is zero.
905 *
906 * Status
907 * @unimplemented
908 */
909
910 BOOL
911 STDCALL
912 NtUserSetObjectInformation(
913 HANDLE hObject,
914 DWORD nIndex,
915 PVOID pvInformation,
916 DWORD nLength)
917 {
918 /* FIXME: ZwQueryObject */
919 /* FIXME: ZwSetInformationObject */
920 SetLastNtError(STATUS_UNSUCCESSFUL);
921 return FALSE;
922 }
923
924
925
926
927 HWINSTA FASTCALL
928 UserGetProcessWindowStation(VOID)
929 {
930 if(PsGetCurrentProcess() != CsrProcess)
931 {
932 return PsGetCurrentProcess()->Win32WindowStation;
933 }
934 else
935 {
936 /* FIXME - get the pointer to the window station by querying the parent of
937 the desktop of the calling thread (which is a window station),
938 then use ObFindHandleForObject() to find a suitable handle */
939 DPRINT1("CSRSS called NtUserGetProcessWindowStation()!!! returned NULL!\n");
940 return NULL;
941 }
942 }
943
944
945 /*
946 * NtUserGetProcessWindowStation
947 *
948 * Returns a handle to the current process window station.
949 *
950 * Return Value
951 * If the function succeeds, the return value is handle to the window
952 * station assigned to the current process. If the function fails, the
953 * return value is NULL.
954 *
955 * Status
956 * @implemented
957 */
958
959 HWINSTA STDCALL
960 NtUserGetProcessWindowStation(VOID)
961 {
962 return UserGetProcessWindowStation();
963 }
964
965 PWINSTATION_OBJECT FASTCALL
966 IntGetWinStaObj(VOID)
967 {
968 PWINSTATION_OBJECT WinStaObj;
969
970 /*
971 * just a temporary hack, this will be gone soon
972 */
973
974 if(PsGetWin32Thread() != NULL && PsGetWin32Thread()->Desktop != NULL)
975 {
976 WinStaObj = PsGetWin32Thread()->Desktop->WindowStation;
977 ObReferenceObjectByPointer(WinStaObj, KernelMode, ExWindowStationObjectType, 0);
978 }
979 else if(PsGetCurrentProcess() != CsrProcess)
980 {
981 NTSTATUS Status = IntValidateWindowStationHandle(PsGetCurrentProcess()->Win32WindowStation,
982 KernelMode,
983 0,
984 &WinStaObj);
985 if(!NT_SUCCESS(Status))
986 {
987 SetLastNtError(Status);
988 return NULL;
989 }
990 }
991 else
992 {
993 WinStaObj = NULL;
994 }
995
996 return WinStaObj;
997 }
998
999 /*
1000 * NtUserSetProcessWindowStation
1001 *
1002 * Assigns a window station to the current process.
1003 *
1004 * Parameters
1005 * hWinSta
1006 * Handle to the window station.
1007 *
1008 * Return Value
1009 * Status
1010 *
1011 * Status
1012 * @implemented
1013 */
1014
1015 BOOL STDCALL
1016 NtUserSetProcessWindowStation(HWINSTA hWindowStation)
1017 {
1018 HANDLE hOld;
1019 PWINSTATION_OBJECT NewWinSta;
1020 NTSTATUS Status;
1021
1022 DPRINT("About to set process window station with handle (0x%X)\n",
1023 hWindowStation);
1024
1025 if(PsGetCurrentProcess() == CsrProcess)
1026 {
1027 DPRINT1("CSRSS is not allowed to change it's window station!!!\n");
1028 SetLastWin32Error(ERROR_ACCESS_DENIED);
1029 return FALSE;
1030 }
1031
1032 Status = IntValidateWindowStationHandle(
1033 hWindowStation,
1034 KernelMode,
1035 0,
1036 &NewWinSta);
1037
1038 if (!NT_SUCCESS(Status))
1039 {
1040 DPRINT("Validation of window station handle (0x%X) failed\n",
1041 hWindowStation);
1042 SetLastNtError(Status);
1043 return FALSE;
1044 }
1045
1046 /*
1047 * FIXME - don't allow changing the window station if there are threads that are attached to desktops and own gui objects
1048 */
1049
1050 /* FIXME - dereference the old window station, etc... */
1051 hOld = InterlockedExchangePointer(&PsGetCurrentProcess()->Win32WindowStation, hWindowStation);
1052
1053 DPRINT("PsGetCurrentProcess()->Win32WindowStation 0x%X\n",
1054 PsGetCurrentProcess()->Win32WindowStation);
1055
1056 return TRUE;
1057 }
1058
1059 /*
1060 * NtUserLockWindowStation
1061 *
1062 * Locks switching desktops. Only the logon application is allowed to call this function.
1063 *
1064 * Status
1065 * @implemented
1066 */
1067
1068 BOOL STDCALL
1069 NtUserLockWindowStation(HWINSTA hWindowStation)
1070 {
1071 PWINSTATION_OBJECT Object;
1072 NTSTATUS Status;
1073
1074 DPRINT("About to set process window station with handle (0x%X)\n",
1075 hWindowStation);
1076
1077 if(PsGetWin32Process() != LogonProcess)
1078 {
1079 DPRINT1("Unauthorized process attempted to lock the window station!\n");
1080 SetLastWin32Error(ERROR_ACCESS_DENIED);
1081 return FALSE;
1082 }
1083
1084 Status = IntValidateWindowStationHandle(
1085 hWindowStation,
1086 KernelMode,
1087 0,
1088 &Object);
1089 if (!NT_SUCCESS(Status))
1090 {
1091 DPRINT("Validation of window station handle (0x%X) failed\n",
1092 hWindowStation);
1093 SetLastNtError(Status);
1094 return FALSE;
1095 }
1096
1097 Object->Flags |= WSS_LOCKED;
1098
1099 ObDereferenceObject(Object);
1100 return TRUE;
1101 }
1102
1103 /*
1104 * NtUserUnlockWindowStation
1105 *
1106 * Unlocks switching desktops. Only the logon application is allowed to call this function.
1107 *
1108 * Status
1109 * @implemented
1110 */
1111
1112 BOOL STDCALL
1113 NtUserUnlockWindowStation(HWINSTA hWindowStation)
1114 {
1115 PWINSTATION_OBJECT Object;
1116 NTSTATUS Status;
1117 BOOL Ret;
1118
1119 DPRINT("About to set process window station with handle (0x%X)\n",
1120 hWindowStation);
1121
1122 if(PsGetWin32Process() != LogonProcess)
1123 {
1124 DPRINT1("Unauthorized process attempted to unlock the window station!\n");
1125 SetLastWin32Error(ERROR_ACCESS_DENIED);
1126 return FALSE;
1127 }
1128
1129 Status = IntValidateWindowStationHandle(
1130 hWindowStation,
1131 KernelMode,
1132 0,
1133 &Object);
1134 if (!NT_SUCCESS(Status))
1135 {
1136 DPRINT("Validation of window station handle (0x%X) failed\n",
1137 hWindowStation);
1138 SetLastNtError(Status);
1139 return FALSE;
1140 }
1141
1142 Ret = (Object->Flags & WSS_LOCKED) == WSS_LOCKED;
1143 Object->Flags &= ~WSS_LOCKED;
1144
1145 ObDereferenceObject(Object);
1146 return Ret;
1147 }
1148
1149 /*
1150 * NtUserSetWindowStationUser
1151 *
1152 * Status
1153 * @unimplemented
1154 */
1155
1156 DWORD STDCALL
1157 NtUserSetWindowStationUser(
1158 DWORD Unknown0,
1159 DWORD Unknown1,
1160 DWORD Unknown2,
1161 DWORD Unknown3)
1162 {
1163 UNIMPLEMENTED
1164
1165 return 0;
1166 }
1167
1168 static NTSTATUS FASTCALL
1169 BuildWindowStationNameList(
1170 ULONG dwSize,
1171 PVOID lpBuffer,
1172 PULONG pRequiredSize)
1173 {
1174 OBJECT_ATTRIBUTES ObjectAttributes;
1175 NTSTATUS Status;
1176 HANDLE DirectoryHandle;
1177 UNICODE_STRING DirectoryName;
1178 char InitialBuffer[256], *Buffer;
1179 ULONG Context, ReturnLength, BufferSize;
1180 DWORD EntryCount;
1181 POBJECT_DIRECTORY_INFORMATION DirEntry;
1182 WCHAR NullWchar;
1183
1184 /*
1185 * Generate name of window station directory
1186 */
1187 if (!IntGetFullWindowStationName(&DirectoryName, NULL, NULL))
1188 {
1189 return STATUS_INSUFFICIENT_RESOURCES;
1190 }
1191
1192 /*
1193 * Try to open the directory.
1194 */
1195 InitializeObjectAttributes(
1196 &ObjectAttributes,
1197 &DirectoryName,
1198 OBJ_CASE_INSENSITIVE,
1199 NULL,
1200 NULL);
1201
1202 Status = ZwOpenDirectoryObject(
1203 &DirectoryHandle,
1204 DIRECTORY_QUERY,
1205 &ObjectAttributes);
1206
1207 ExFreePool(DirectoryName.Buffer);
1208
1209 if (!NT_SUCCESS(Status))
1210 {
1211 return Status;
1212 }
1213
1214 /* First try to query the directory using a fixed-size buffer */
1215 Context = 0;
1216 Buffer = NULL;
1217 Status = ZwQueryDirectoryObject(DirectoryHandle, InitialBuffer, sizeof(InitialBuffer),
1218 FALSE, TRUE, &Context, &ReturnLength);
1219 if (NT_SUCCESS(Status))
1220 {
1221 if (STATUS_NO_MORE_ENTRIES == ZwQueryDirectoryObject(DirectoryHandle, NULL, 0, FALSE,
1222 FALSE, &Context, NULL))
1223 {
1224 /* Our fixed-size buffer is large enough */
1225 Buffer = InitialBuffer;
1226 }
1227 }
1228
1229 if (NULL == Buffer)
1230 {
1231 /* Need a larger buffer, check how large exactly */
1232 Status = ZwQueryDirectoryObject(DirectoryHandle, NULL, 0, FALSE, TRUE, &Context,
1233 &ReturnLength);
1234 if (STATUS_BUFFER_TOO_SMALL == Status)
1235 {
1236 BufferSize = ReturnLength;
1237 Buffer = ExAllocatePoolWithTag(PagedPool, BufferSize, TAG_WINSTA);
1238 if (NULL == Buffer)
1239 {
1240 ObDereferenceObject(DirectoryHandle);
1241 return STATUS_NO_MEMORY;
1242 }
1243
1244 /* We should have a sufficiently large buffer now */
1245 Context = 0;
1246 Status = ZwQueryDirectoryObject(DirectoryHandle, Buffer, BufferSize,
1247 FALSE, TRUE, &Context, &ReturnLength);
1248 if (! NT_SUCCESS(Status) ||
1249 STATUS_NO_MORE_ENTRIES != ZwQueryDirectoryObject(DirectoryHandle, NULL, 0, FALSE,
1250 FALSE, &Context, NULL))
1251 {
1252 /* Something went wrong, maybe someone added a directory entry? Just give up. */
1253 ExFreePool(Buffer);
1254 ObDereferenceObject(DirectoryHandle);
1255 return NT_SUCCESS(Status) ? STATUS_INTERNAL_ERROR : Status;
1256 }
1257 }
1258 }
1259
1260 ZwClose(DirectoryHandle);
1261
1262 /*
1263 * Count the required size of buffer.
1264 */
1265 ReturnLength = sizeof(DWORD);
1266 EntryCount = 0;
1267 for (DirEntry = (POBJECT_DIRECTORY_INFORMATION) Buffer; 0 != DirEntry->ObjectName.Length;
1268 DirEntry++)
1269 {
1270 ReturnLength += DirEntry->ObjectName.Length + sizeof(WCHAR);
1271 EntryCount++;
1272 }
1273 DPRINT("Required size: %d Entry count: %d\n", ReturnLength, EntryCount);
1274 if (NULL != pRequiredSize)
1275 {
1276 Status = MmCopyToCaller(pRequiredSize, &ReturnLength, sizeof(ULONG));
1277 if (! NT_SUCCESS(Status))
1278 {
1279 if (Buffer != InitialBuffer)
1280 {
1281 ExFreePool(Buffer);
1282 }
1283 return STATUS_BUFFER_TOO_SMALL;
1284 }
1285 }
1286
1287 /*
1288 * Check if the supplied buffer is large enough.
1289 */
1290 if (dwSize < ReturnLength)
1291 {
1292 if (Buffer != InitialBuffer)
1293 {
1294 ExFreePool(Buffer);
1295 }
1296 return STATUS_BUFFER_TOO_SMALL;
1297 }
1298
1299 /*
1300 * Generate the resulting buffer contents.
1301 */
1302 Status = MmCopyToCaller(lpBuffer, &EntryCount, sizeof(DWORD));
1303 if (! NT_SUCCESS(Status))
1304 {
1305 if (Buffer != InitialBuffer)
1306 {
1307 ExFreePool(Buffer);
1308 }
1309 return Status;
1310 }
1311 lpBuffer = (PVOID) ((PCHAR) lpBuffer + sizeof(DWORD));
1312
1313 NullWchar = L'\0';
1314 for (DirEntry = (POBJECT_DIRECTORY_INFORMATION) Buffer; 0 != DirEntry->ObjectName.Length;
1315 DirEntry++)
1316 {
1317 Status = MmCopyToCaller(lpBuffer, DirEntry->ObjectName.Buffer, DirEntry->ObjectName.Length);
1318 if (! NT_SUCCESS(Status))
1319 {
1320 if (Buffer != InitialBuffer)
1321 {
1322 ExFreePool(Buffer);
1323 }
1324 return Status;
1325 }
1326 lpBuffer = (PVOID) ((PCHAR) lpBuffer + DirEntry->ObjectName.Length);
1327 Status = MmCopyToCaller(lpBuffer, &NullWchar, sizeof(WCHAR));
1328 if (! NT_SUCCESS(Status))
1329 {
1330 if (Buffer != InitialBuffer)
1331 {
1332 ExFreePool(Buffer);
1333 }
1334 return Status;
1335 }
1336 lpBuffer = (PVOID) ((PCHAR) lpBuffer + sizeof(WCHAR));
1337 }
1338
1339 /*
1340 * Clean up
1341 */
1342 if (NULL != Buffer && Buffer != InitialBuffer)
1343 {
1344 ExFreePool(Buffer);
1345 }
1346
1347 return STATUS_SUCCESS;
1348 }
1349
1350 static NTSTATUS FASTCALL
1351 BuildDesktopNameList(
1352 HWINSTA hWindowStation,
1353 ULONG dwSize,
1354 PVOID lpBuffer,
1355 PULONG pRequiredSize)
1356 {
1357 NTSTATUS Status;
1358 PWINSTATION_OBJECT WindowStation;
1359 KIRQL OldLevel;
1360 PLIST_ENTRY DesktopEntry;
1361 PDESKTOP_OBJECT DesktopObject;
1362 DWORD EntryCount;
1363 ULONG ReturnLength;
1364 WCHAR NullWchar;
1365
1366 Status = IntValidateWindowStationHandle(hWindowStation,
1367 KernelMode,
1368 0,
1369 &WindowStation);
1370 if (! NT_SUCCESS(Status))
1371 {
1372 return Status;
1373 }
1374
1375 KeAcquireSpinLock(&WindowStation->Lock, &OldLevel);
1376
1377 /*
1378 * Count the required size of buffer.
1379 */
1380 ReturnLength = sizeof(DWORD);
1381 EntryCount = 0;
1382 for (DesktopEntry = WindowStation->DesktopListHead.Flink;
1383 DesktopEntry != &WindowStation->DesktopListHead;
1384 DesktopEntry = DesktopEntry->Flink)
1385 {
1386 DesktopObject = CONTAINING_RECORD(DesktopEntry, DESKTOP_OBJECT, ListEntry);
1387 ReturnLength += DesktopObject->Name.Length + sizeof(WCHAR);
1388 EntryCount++;
1389 }
1390 DPRINT("Required size: %d Entry count: %d\n", ReturnLength, EntryCount);
1391 if (NULL != pRequiredSize)
1392 {
1393 Status = MmCopyToCaller(pRequiredSize, &ReturnLength, sizeof(ULONG));
1394 if (! NT_SUCCESS(Status))
1395 {
1396 KeReleaseSpinLock(&WindowStation->Lock, OldLevel);
1397 ObDereferenceObject(WindowStation);
1398 return STATUS_BUFFER_TOO_SMALL;
1399 }
1400 }
1401
1402 /*
1403 * Check if the supplied buffer is large enough.
1404 */
1405 if (dwSize < ReturnLength)
1406 {
1407 KeReleaseSpinLock(&WindowStation->Lock, OldLevel);
1408 ObDereferenceObject(WindowStation);
1409 return STATUS_BUFFER_TOO_SMALL;
1410 }
1411
1412 /*
1413 * Generate the resulting buffer contents.
1414 */
1415 Status = MmCopyToCaller(lpBuffer, &EntryCount, sizeof(DWORD));
1416 if (! NT_SUCCESS(Status))
1417 {
1418 KeReleaseSpinLock(&WindowStation->Lock, OldLevel);
1419 ObDereferenceObject(WindowStation);
1420 return Status;
1421 }
1422 lpBuffer = (PVOID) ((PCHAR) lpBuffer + sizeof(DWORD));
1423
1424 NullWchar = L'\0';
1425 for (DesktopEntry = WindowStation->DesktopListHead.Flink;
1426 DesktopEntry != &WindowStation->DesktopListHead;
1427 DesktopEntry = DesktopEntry->Flink)
1428 {
1429 DesktopObject = CONTAINING_RECORD(DesktopEntry, DESKTOP_OBJECT, ListEntry);
1430 Status = MmCopyToCaller(lpBuffer, DesktopObject->Name.Buffer, DesktopObject->Name.Length);
1431 if (! NT_SUCCESS(Status))
1432 {
1433 KeReleaseSpinLock(&WindowStation->Lock, OldLevel);
1434 ObDereferenceObject(WindowStation);
1435 return Status;
1436 }
1437 lpBuffer = (PVOID) ((PCHAR) lpBuffer + DesktopObject->Name.Length);
1438 Status = MmCopyToCaller(lpBuffer, &NullWchar, sizeof(WCHAR));
1439 if (! NT_SUCCESS(Status))
1440 {
1441 KeReleaseSpinLock(&WindowStation->Lock, OldLevel);
1442 ObDereferenceObject(WindowStation);
1443 return Status;
1444 }
1445 lpBuffer = (PVOID) ((PCHAR) lpBuffer + sizeof(WCHAR));
1446 }
1447
1448 /*
1449 * Clean up
1450 */
1451 KeReleaseSpinLock(&WindowStation->Lock, OldLevel);
1452 ObDereferenceObject(WindowStation);
1453
1454 return STATUS_SUCCESS;
1455 }
1456
1457 /*
1458 * NtUserBuildNameList
1459 *
1460 * Function used for enumeration of desktops or window stations.
1461 *
1462 * Parameters
1463 * hWinSta
1464 * For enumeration of window stations this parameter must be set to
1465 * zero. Otherwise it's handle for window station.
1466 *
1467 * dwSize
1468 * Size of buffer passed by caller.
1469 *
1470 * lpBuffer
1471 * Buffer passed by caller. If the function succedes, the buffer is
1472 * filled with window station/desktop count (in first DWORD) and
1473 * NULL-terminated window station/desktop names.
1474 *
1475 * pRequiredSize
1476 * If the function suceedes, this is the number of bytes copied.
1477 * Otherwise it's size of buffer needed for function to succeed.
1478 *
1479 * Status
1480 * @implemented
1481 */
1482
1483 NTSTATUS STDCALL
1484 NtUserBuildNameList(
1485 HWINSTA hWindowStation,
1486 ULONG dwSize,
1487 PVOID lpBuffer,
1488 PULONG pRequiredSize)
1489 {
1490 /* The WindowStation name list and desktop name list are build in completely
1491 different ways. Call the appropriate function */
1492 return NULL == hWindowStation ? BuildWindowStationNameList(dwSize, lpBuffer, pRequiredSize) :
1493 BuildDesktopNameList(hWindowStation, dwSize, lpBuffer, pRequiredSize);
1494 }
1495
1496 /* EOF */