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