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