[WIN32SS/USER32]
[reactos.git] / win32ss / user / ntuser / desktop.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS Win32k subsystem
4 * PURPOSE: Desktops
5 * FILE: subsystems/win32/win32k/ntuser/desktop.c
6 * PROGRAMER: Casper S. Hornstrup (chorns@users.sourceforge.net)
7 */
8
9 /* INCLUDES ******************************************************************/
10
11 #include <win32k.h>
12 DBG_DEFAULT_CHANNEL(UserDesktop);
13
14 static
15 VOID
16 IntFreeDesktopHeap(
17 IN OUT PDESKTOP Desktop
18 );
19
20 /* GLOBALS *******************************************************************/
21
22 /* Currently active desktop */
23 PDESKTOP InputDesktop = NULL;
24 HDESK InputDesktopHandle = NULL;
25 HDC ScreenDeviceContext = NULL;
26
27 /* OBJECT CALLBACKS **********************************************************/
28
29 NTSTATUS
30 APIENTRY
31 IntDesktopObjectParse(IN PVOID ParseObject,
32 IN PVOID ObjectType,
33 IN OUT PACCESS_STATE AccessState,
34 IN KPROCESSOR_MODE AccessMode,
35 IN ULONG Attributes,
36 IN OUT PUNICODE_STRING CompleteName,
37 IN OUT PUNICODE_STRING RemainingName,
38 IN OUT PVOID Context OPTIONAL,
39 IN PSECURITY_QUALITY_OF_SERVICE SecurityQos OPTIONAL,
40 OUT PVOID *Object)
41 {
42 NTSTATUS Status;
43 PDESKTOP Desktop;
44 OBJECT_ATTRIBUTES ObjectAttributes;
45 PLIST_ENTRY NextEntry, ListHead;
46 PWINSTATION_OBJECT WinStaObject = (PWINSTATION_OBJECT)ParseObject;
47 PUNICODE_STRING DesktopName;
48 PBOOLEAN pContext = (PBOOLEAN) Context;
49
50 if(pContext)
51 *pContext = FALSE;
52
53 /* Set the list pointers and loop the window station */
54 ListHead = &WinStaObject->DesktopListHead;
55 NextEntry = ListHead->Flink;
56 while (NextEntry != ListHead)
57 {
58 /* Get the current desktop */
59 Desktop = CONTAINING_RECORD(NextEntry, DESKTOP, ListEntry);
60
61 /* Get its name */
62 DesktopName = GET_DESKTOP_NAME(Desktop);
63 if (DesktopName)
64 {
65 /* Compare the name */
66 if (RtlEqualUnicodeString(RemainingName,
67 DesktopName,
68 (Attributes & OBJ_CASE_INSENSITIVE)))
69 {
70 /* We found a match. Did this come from a create? */
71 if (Context)
72 {
73 /* Unless OPEN_IF was given, fail with an error */
74 if (!(Attributes & OBJ_OPENIF))
75 {
76 /* Name collision */
77 return STATUS_OBJECT_NAME_COLLISION;
78 }
79 else
80 {
81 /* Otherwise, return with a warning only */
82 Status = STATUS_OBJECT_NAME_EXISTS;
83 }
84 }
85 else
86 {
87 /* This was a real open, so this is OK */
88 Status = STATUS_SUCCESS;
89 }
90
91 /* Reference the desktop and return it */
92 ObReferenceObject(Desktop);
93 *Object = Desktop;
94 return Status;
95 }
96 }
97
98 /* Go to the next desktop */
99 NextEntry = NextEntry->Flink;
100 }
101
102 /* If we got here but this isn't a create, then fail */
103 if (!Context) return STATUS_OBJECT_NAME_NOT_FOUND;
104
105 /* Create the desktop object */
106 InitializeObjectAttributes(&ObjectAttributes, RemainingName, 0, NULL, NULL);
107 Status = ObCreateObject(KernelMode,
108 ExDesktopObjectType,
109 &ObjectAttributes,
110 KernelMode,
111 NULL,
112 sizeof(DESKTOP),
113 0,
114 0,
115 (PVOID)&Desktop);
116 if (!NT_SUCCESS(Status)) return Status;
117
118 /* Initialize shell hook window list and set the parent */
119 RtlZeroMemory(Desktop, sizeof(DESKTOP));
120 InitializeListHead(&Desktop->ShellHookWindows);
121 Desktop->rpwinstaParent = (PWINSTATION_OBJECT)ParseObject;
122
123 /* Put the desktop on the window station's list of associated desktops */
124 InsertTailList(&Desktop->rpwinstaParent->DesktopListHead,
125 &Desktop->ListEntry);
126
127 /* Set the desktop object and return success */
128 *Object = Desktop;
129 *pContext = TRUE;
130 return STATUS_SUCCESS;
131 }
132
133 VOID APIENTRY
134 IntDesktopObjectDelete(PWIN32_DELETEMETHOD_PARAMETERS Parameters)
135 {
136 PDESKTOP Desktop = (PDESKTOP)Parameters->Object;
137
138 TRACE("Deleting desktop object 0x%p\n", Desktop);
139
140 /* Remove the desktop from the window station's list of associcated desktops */
141 RemoveEntryList(&Desktop->ListEntry);
142
143 IntFreeDesktopHeap(Desktop);
144 }
145
146 NTSTATUS NTAPI
147 IntDesktopOkToClose(PWIN32_OKAYTOCLOSEMETHOD_PARAMETERS Parameters)
148 {
149 PTHREADINFO pti;
150
151 pti = PsGetCurrentThreadWin32Thread();
152
153 if( pti == NULL)
154 {
155 /* This happens when we leak desktop handles */
156 return STATUS_SUCCESS;
157 }
158
159 /* Do not allow the current desktop or the initial desktop to be closed */
160 if( Parameters->Handle == pti->ppi->hdeskStartup ||
161 Parameters->Handle == pti->hdesk)
162 {
163 return STATUS_ACCESS_DENIED;
164 }
165
166 return STATUS_SUCCESS;
167 }
168
169 /* PRIVATE FUNCTIONS **********************************************************/
170
171 INIT_FUNCTION
172 NTSTATUS
173 NTAPI
174 InitDesktopImpl(VOID)
175 {
176 GENERIC_MAPPING IntDesktopMapping = { DESKTOP_READ,
177 DESKTOP_WRITE,
178 DESKTOP_EXECUTE,
179 DESKTOP_ALL_ACCESS};
180
181 /* Set Desktop Object Attributes */
182 ExDesktopObjectType->TypeInfo.DefaultNonPagedPoolCharge = sizeof(DESKTOP);
183 ExDesktopObjectType->TypeInfo.GenericMapping = IntDesktopMapping;
184 ExDesktopObjectType->TypeInfo.ValidAccessMask = DESKTOP_ALL_ACCESS;
185 return STATUS_SUCCESS;
186 }
187
188 static int GetSystemVersionString(LPWSTR buffer)
189 {
190 RTL_OSVERSIONINFOEXW versionInfo;
191 int len;
192
193 versionInfo.dwOSVersionInfoSize = sizeof(RTL_OSVERSIONINFOEXW);
194
195 if (!NT_SUCCESS(RtlGetVersion((PRTL_OSVERSIONINFOW)&versionInfo)))
196 return 0;
197
198 if (versionInfo.dwMajorVersion <= 4)
199 len = swprintf(buffer,
200 L"ReactOS Version %d.%d %s Build %d",
201 versionInfo.dwMajorVersion, versionInfo.dwMinorVersion,
202 versionInfo.szCSDVersion, versionInfo.dwBuildNumber&0xFFFF);
203 else
204 len = swprintf(buffer,
205 L"ReactOS %s (Build %d)",
206 versionInfo.szCSDVersion, versionInfo.dwBuildNumber&0xFFFF);
207
208 return len;
209 }
210
211
212 NTSTATUS FASTCALL
213 IntParseDesktopPath(PEPROCESS Process,
214 PUNICODE_STRING DesktopPath,
215 HWINSTA *hWinSta,
216 HDESK *hDesktop)
217 {
218 OBJECT_ATTRIBUTES ObjectAttributes;
219 UNICODE_STRING ObjectName;
220 NTSTATUS Status;
221 WCHAR wstrWinstaFullName[MAX_PATH], *pwstrWinsta = NULL, *pwstrDesktop = NULL;
222
223 ASSERT(hWinSta);
224 ASSERT(hDesktop);
225 ASSERT(DesktopPath);
226
227 *hWinSta = NULL;
228 *hDesktop = NULL;
229
230 if(DesktopPath->Buffer != NULL && DesktopPath->Length > sizeof(WCHAR))
231 {
232 /*
233 * Parse the desktop path string which can be in the form "WinSta\Desktop"
234 * or just "Desktop". In latter case WinSta0 will be used.
235 */
236
237 pwstrDesktop = wcschr(DesktopPath->Buffer, L'\\');
238 if(pwstrDesktop != NULL)
239 {
240 *pwstrDesktop = 0;
241 pwstrDesktop++;
242 pwstrWinsta = DesktopPath->Buffer;
243 }
244 else
245 {
246 pwstrDesktop = DesktopPath->Buffer;
247 pwstrWinsta = NULL;
248 }
249
250 TRACE("IntParseDesktopPath pwstrWinsta:%S pwstrDesktop:%S\n", pwstrWinsta, pwstrDesktop);
251 }
252
253 #if 0
254 /* Search the process handle table for (inherited) window station
255 handles, use a more appropriate one than WinSta0 if possible. */
256 if (!ObFindHandleForObject(Process,
257 NULL,
258 ExWindowStationObjectType,
259 NULL,
260 (PHANDLE)hWinSta))
261 #endif
262 {
263 /* We had no luck searching for opened handles, use WinSta0 now */
264 if(!pwstrWinsta)
265 pwstrWinsta = L"WinSta0";
266 }
267
268 #if 0
269 /* Search the process handle table for (inherited) desktop
270 handles, use a more appropriate one than Default if possible. */
271 if (!ObFindHandleForObject(Process,
272 NULL,
273 ExDesktopObjectType,
274 NULL,
275 (PHANDLE)hDesktop))
276 #endif
277 {
278 /* We had no luck searching for opened handles, use Desktop now */
279 if(!pwstrDesktop)
280 pwstrDesktop = L"Default";
281 }
282
283 if(*hWinSta == NULL)
284 {
285 swprintf(wstrWinstaFullName, L"%wZ\\%ws", &gustrWindowStationsDir, pwstrWinsta);
286 RtlInitUnicodeString( &ObjectName, wstrWinstaFullName);
287
288 TRACE("parsed initial winsta: %wZ\n", &ObjectName);
289
290 /* Open the window station */
291 InitializeObjectAttributes(&ObjectAttributes,
292 &ObjectName,
293 OBJ_CASE_INSENSITIVE,
294 NULL,
295 NULL);
296
297 Status = ObOpenObjectByName(&ObjectAttributes,
298 ExWindowStationObjectType,
299 KernelMode,
300 NULL,
301 WINSTA_ACCESS_ALL,
302 NULL,
303 (HANDLE*)hWinSta);
304
305 if(!NT_SUCCESS(Status))
306 {
307 SetLastNtError(Status);
308 ERR("Failed to reference window station %wZ PID: --!\n", &ObjectName );
309 return Status;
310 }
311 }
312
313 if(*hDesktop == NULL)
314 {
315 RtlInitUnicodeString(&ObjectName, pwstrDesktop);
316
317 TRACE("parsed initial desktop: %wZ\n", &ObjectName);
318
319 /* Open the desktop object */
320 InitializeObjectAttributes(&ObjectAttributes,
321 &ObjectName,
322 OBJ_CASE_INSENSITIVE,
323 *hWinSta,
324 NULL);
325
326 Status = ObOpenObjectByName(&ObjectAttributes,
327 ExDesktopObjectType,
328 KernelMode,
329 NULL,
330 DESKTOP_ALL_ACCESS,
331 NULL,
332 (HANDLE*)hDesktop);
333
334 if(!NT_SUCCESS(Status))
335 {
336 *hDesktop = NULL;
337 NtClose(*hWinSta);
338 *hWinSta = NULL;
339 SetLastNtError(Status);
340 ERR("Failed to reference desktop %wZ PID: --!\n", &ObjectName);
341 return Status;
342 }
343 }
344 return STATUS_SUCCESS;
345 }
346
347 /*
348 * IntValidateDesktopHandle
349 *
350 * Validates the desktop handle.
351 *
352 * Remarks
353 * If the function succeeds, the handle remains referenced. If the
354 * fucntion fails, last error is set.
355 */
356
357 NTSTATUS FASTCALL
358 IntValidateDesktopHandle(
359 HDESK Desktop,
360 KPROCESSOR_MODE AccessMode,
361 ACCESS_MASK DesiredAccess,
362 PDESKTOP *Object)
363 {
364 NTSTATUS Status;
365
366 Status = ObReferenceObjectByHandle(
367 Desktop,
368 DesiredAccess,
369 ExDesktopObjectType,
370 AccessMode,
371 (PVOID*)Object,
372 NULL);
373
374 TRACE("IntValidateDesktopHandle: handle:0x%p obj:0x%p access:0x%x Status:0x%lx\n",
375 Desktop, *Object, DesiredAccess, Status);
376
377 if (!NT_SUCCESS(Status))
378 SetLastNtError(Status);
379
380 return Status;
381 }
382
383 PDESKTOP FASTCALL
384 IntGetActiveDesktop(VOID)
385 {
386 return InputDesktop;
387 }
388
389 /*
390 * Returns or creates a handle to the desktop object
391 */
392 HDESK FASTCALL
393 IntGetDesktopObjectHandle(PDESKTOP DesktopObject)
394 {
395 NTSTATUS Status;
396 HDESK Ret;
397
398 ASSERT(DesktopObject);
399
400 if (!ObFindHandleForObject(PsGetCurrentProcess(),
401 DesktopObject,
402 ExDesktopObjectType,
403 NULL,
404 (PHANDLE)&Ret))
405 {
406 Status = ObOpenObjectByPointer(DesktopObject,
407 0,
408 NULL,
409 0,
410 ExDesktopObjectType,
411 UserMode,
412 (PHANDLE)&Ret);
413 if(!NT_SUCCESS(Status))
414 {
415 /* Unable to create a handle */
416 ERR("Unable to create a desktop handle\n");
417 return NULL;
418 }
419 }
420 else
421 {
422 ERR("Got handle: %p\n", Ret);
423 }
424
425 return Ret;
426 }
427
428 PUSER_MESSAGE_QUEUE FASTCALL
429 IntGetFocusMessageQueue(VOID)
430 {
431 PDESKTOP pdo = IntGetActiveDesktop();
432 if (!pdo)
433 {
434 TRACE("No active desktop\n");
435 return(NULL);
436 }
437 return (PUSER_MESSAGE_QUEUE)pdo->ActiveMessageQueue;
438 }
439
440 VOID FASTCALL
441 IntSetFocusMessageQueue(PUSER_MESSAGE_QUEUE NewQueue)
442 {
443 PUSER_MESSAGE_QUEUE Old;
444 PDESKTOP pdo = IntGetActiveDesktop();
445 if (!pdo)
446 {
447 TRACE("No active desktop\n");
448 return;
449 }
450 if(NewQueue != NULL)
451 {
452 if(NewQueue->Desktop != NULL)
453 {
454 TRACE("Message Queue already attached to another desktop!\n");
455 return;
456 }
457 IntReferenceMessageQueue(NewQueue);
458 (void)InterlockedExchangePointer((PVOID*)&NewQueue->Desktop, pdo);
459 }
460 Old = (PUSER_MESSAGE_QUEUE)InterlockedExchangePointer((PVOID*)&pdo->ActiveMessageQueue, NewQueue);
461 if(Old != NULL)
462 {
463 (void)InterlockedExchangePointer((PVOID*)&Old->Desktop, 0);
464 IntDereferenceMessageQueue(Old);
465 gpqForegroundPrev = Old;
466 }
467 // Only one Q can have active foreground even when there are more than one desktop.
468 if (NewQueue) gpqForeground = pdo->ActiveMessageQueue;
469 else gpqForeground = NULL;
470 }
471
472 PWND FASTCALL
473 IntGetThreadDesktopWindow(PTHREADINFO pti)
474 {
475 if (!pti) pti = PsGetCurrentThreadWin32Thread();
476 if (pti->pDeskInfo) return pti->pDeskInfo->spwnd;
477 return NULL;
478 }
479
480 PWND FASTCALL co_GetDesktopWindow(PWND pWnd)
481 {
482 if (pWnd->head.rpdesk &&
483 pWnd->head.rpdesk->pDeskInfo)
484 return pWnd->head.rpdesk->pDeskInfo->spwnd;
485 return NULL;
486 }
487
488 HWND FASTCALL IntGetDesktopWindow(VOID)
489 {
490 PDESKTOP pdo = IntGetActiveDesktop();
491 if (!pdo)
492 {
493 TRACE("No active desktop\n");
494 return NULL;
495 }
496 return pdo->DesktopWindow;
497 }
498
499 PWND FASTCALL UserGetDesktopWindow(VOID)
500 {
501 PDESKTOP pdo = IntGetActiveDesktop();
502
503 if (!pdo)
504 {
505 TRACE("No active desktop\n");
506 return NULL;
507 }
508 // return pdo->pDeskInfo->spwnd;
509 return UserGetWindowObject(pdo->DesktopWindow);
510 }
511
512 HWND FASTCALL IntGetMessageWindow(VOID)
513 {
514 PDESKTOP pdo = IntGetActiveDesktop();
515
516 if (!pdo)
517 {
518 TRACE("No active desktop\n");
519 return NULL;
520 }
521 return pdo->spwndMessage->head.h;
522 }
523
524 PWND FASTCALL UserGetMessageWindow(VOID)
525 {
526 PDESKTOP pdo = IntGetActiveDesktop();
527
528 if (!pdo)
529 {
530 TRACE("No active desktop\n");
531 return NULL;
532 }
533 return pdo->spwndMessage;
534 }
535
536 HWND FASTCALL IntGetCurrentThreadDesktopWindow(VOID)
537 {
538 PTHREADINFO pti = PsGetCurrentThreadWin32Thread();
539 PDESKTOP pdo = pti->rpdesk;
540 if (NULL == pdo)
541 {
542 ERR("Thread doesn't have a desktop\n");
543 return NULL;
544 }
545 return pdo->DesktopWindow;
546 }
547
548 /* PUBLIC FUNCTIONS ***********************************************************/
549
550 BOOL FASTCALL
551 DesktopWindowProc(PWND Wnd, UINT Msg, WPARAM wParam, LPARAM lParam, LRESULT *lResult)
552 {
553 PAINTSTRUCT Ps;
554 ULONG Value;
555 //ERR("DesktopWindowProc\n");
556
557 *lResult = 0;
558
559 switch (Msg)
560 {
561 case WM_NCCREATE:
562 if (!Wnd->fnid)
563 {
564 Wnd->fnid = FNID_DESKTOP;
565 }
566 *lResult = (LRESULT)TRUE;
567 return TRUE;
568
569 case WM_CREATE:
570 Value = HandleToULong(PsGetCurrentProcessId());
571 // Save Process ID
572 co_UserSetWindowLong(UserHMGetHandle(Wnd), DT_GWL_PROCESSID, Value, FALSE);
573 Value = HandleToULong(PsGetCurrentThreadId());
574 // Save Thread ID
575 co_UserSetWindowLong(UserHMGetHandle(Wnd), DT_GWL_THREADID, Value, FALSE);
576 case WM_CLOSE:
577 return TRUE;
578
579 case WM_DISPLAYCHANGE:
580 co_WinPosSetWindowPos(Wnd, 0, 0, 0, LOWORD(lParam), HIWORD(lParam), SWP_NOZORDER | SWP_NOACTIVATE);
581 return TRUE;
582
583 case WM_ERASEBKGND:
584 IntPaintDesktop((HDC)wParam);
585 *lResult = 1;
586 return TRUE;
587
588 case WM_PAINT:
589 {
590 if (IntBeginPaint(Wnd, &Ps))
591 {
592 IntEndPaint(Wnd, &Ps);
593 }
594 return TRUE;
595 }
596 case WM_SYSCOLORCHANGE:
597 co_UserRedrawWindow(Wnd, NULL, NULL, RDW_INVALIDATE|RDW_ERASE|RDW_ALLCHILDREN);
598 return TRUE;
599 }
600 return FALSE; // Not processed so go with callback.
601 }
602
603 HDC FASTCALL
604 UserGetDesktopDC(ULONG DcType, BOOL EmptyDC, BOOL ValidatehWnd)
605 {
606 PWND DesktopObject = 0;
607 HDC DesktopHDC = 0;
608
609 if (DcType == DC_TYPE_DIRECT)
610 {
611 DesktopObject = UserGetDesktopWindow();
612 DesktopHDC = (HDC)UserGetWindowDC(DesktopObject);
613 }
614 else
615 {
616 PMONITOR pMonitor = UserGetPrimaryMonitor();
617 DesktopHDC = IntGdiCreateDisplayDC(pMonitor->hDev, DcType, EmptyDC);
618 }
619
620 return DesktopHDC;
621 }
622
623 VOID APIENTRY
624 UserRedrawDesktop()
625 {
626 PWND Window = NULL;
627 HRGN hRgn;
628
629 Window = UserGetDesktopWindow();
630 hRgn = IntSysCreateRectRgnIndirect(&Window->rcWindow);
631
632 IntInvalidateWindows( Window,
633 hRgn,
634 RDW_FRAME |
635 RDW_ERASE |
636 RDW_INVALIDATE |
637 RDW_ALLCHILDREN);
638
639 GreDeleteObject(hRgn);
640 }
641
642
643 NTSTATUS FASTCALL
644 co_IntShowDesktop(PDESKTOP Desktop, ULONG Width, ULONG Height)
645 {
646 PWND Window;
647
648 Window = IntGetWindowObject(Desktop->DesktopWindow);
649
650 if (!Window)
651 {
652 ERR("No Desktop window.\n");
653 return STATUS_UNSUCCESSFUL;
654 }
655 co_WinPosSetWindowPos(Window, NULL, 0, 0, Width, Height, SWP_NOACTIVATE|SWP_NOZORDER|SWP_SHOWWINDOW);
656
657 co_UserRedrawWindow( Window, NULL, 0, RDW_UPDATENOW | RDW_ALLCHILDREN);
658 return STATUS_SUCCESS;
659 }
660
661 NTSTATUS FASTCALL
662 IntHideDesktop(PDESKTOP Desktop)
663 {
664 PWND DesktopWnd;
665
666 DesktopWnd = IntGetWindowObject(Desktop->DesktopWindow);
667 if (! DesktopWnd)
668 {
669 return ERROR_INVALID_WINDOW_HANDLE;
670 }
671 DesktopWnd->style &= ~WS_VISIBLE;
672
673 return STATUS_SUCCESS;
674 }
675
676 static
677 HWND* FASTCALL
678 UserBuildShellHookHwndList(PDESKTOP Desktop)
679 {
680 ULONG entries=0;
681 PSHELL_HOOK_WINDOW Current;
682 HWND* list;
683
684 /* FIXME: If we save nb elements in desktop, we dont have to loop to find nb entries */
685 LIST_FOR_EACH(Current, &Desktop->ShellHookWindows, SHELL_HOOK_WINDOW, ListEntry)
686 entries++;
687
688 if (!entries) return NULL;
689
690 list = ExAllocatePoolWithTag(PagedPool, sizeof(HWND) * (entries + 1), USERTAG_WINDOWLIST); /* alloc one extra for nullterm */
691 if (list)
692 {
693 HWND* cursor = list;
694
695 LIST_FOR_EACH(Current, &Desktop->ShellHookWindows, SHELL_HOOK_WINDOW, ListEntry)
696 *cursor++ = Current->hWnd;
697
698 *cursor = NULL; /* Nullterm list */
699 }
700
701 return list;
702 }
703
704 /*
705 * Send the Message to the windows registered for ShellHook
706 * notifications. The lParam contents depend on the Message. See
707 * MSDN for more details (RegisterShellHookWindow)
708 */
709 VOID co_IntShellHookNotify(WPARAM Message, WPARAM wParam, LPARAM lParam)
710 {
711 PDESKTOP Desktop = IntGetActiveDesktop();
712 HWND* HwndList;
713
714 if (!gpsi->uiShellMsg)
715 {
716 gpsi->uiShellMsg = IntAddAtom(L"SHELLHOOK");
717
718 TRACE("MsgType = %x\n", gpsi->uiShellMsg);
719 if (!gpsi->uiShellMsg)
720 ERR("LastError: %x\n", EngGetLastError());
721 }
722
723 if (!Desktop)
724 {
725 TRACE("IntShellHookNotify: No desktop!\n");
726 return;
727 }
728
729 // FIXME: System Tray Support.
730
731 HwndList = UserBuildShellHookHwndList(Desktop);
732 if (HwndList)
733 {
734 HWND* cursor = HwndList;
735
736 for (; *cursor; cursor++)
737 {
738 TRACE("Sending notify\n");
739 co_IntPostOrSendMessage(*cursor,
740 gpsi->uiShellMsg,
741 Message,
742 (Message == HSHELL_LANGUAGE ? lParam : (LPARAM)wParam) );
743 }
744
745 ExFreePoolWithTag(HwndList, USERTAG_WINDOWLIST);
746 }
747
748 if (ISITHOOKED(WH_SHELL))
749 {
750 co_HOOK_CallHooks(WH_SHELL, Message, wParam, lParam);
751 }
752 }
753
754 /*
755 * Add the window to the ShellHookWindows list. The windows
756 * on that list get notifications that are important to shell
757 * type applications.
758 *
759 * TODO: Validate the window? I'm not sure if sending these messages to
760 * an unsuspecting application that is not your own is a nice thing to do.
761 */
762 BOOL IntRegisterShellHookWindow(HWND hWnd)
763 {
764 PTHREADINFO pti = PsGetCurrentThreadWin32Thread();
765 PDESKTOP Desktop = pti->rpdesk;
766 PSHELL_HOOK_WINDOW Entry;
767
768 TRACE("IntRegisterShellHookWindow\n");
769
770 /* First deregister the window, so we can be sure it's never twice in the
771 * list.
772 */
773 IntDeRegisterShellHookWindow(hWnd);
774
775 Entry = ExAllocatePoolWithTag(PagedPool,
776 sizeof(SHELL_HOOK_WINDOW),
777 TAG_WINSTA);
778
779 if (!Entry)
780 return FALSE;
781
782 Entry->hWnd = hWnd;
783
784 InsertTailList(&Desktop->ShellHookWindows, &Entry->ListEntry);
785
786 return TRUE;
787 }
788
789 /*
790 * Remove the window from the ShellHookWindows list. The windows
791 * on that list get notifications that are important to shell
792 * type applications.
793 */
794 BOOL IntDeRegisterShellHookWindow(HWND hWnd)
795 {
796 PTHREADINFO pti = PsGetCurrentThreadWin32Thread();
797 PDESKTOP Desktop = pti->rpdesk;
798 PSHELL_HOOK_WINDOW Current;
799
800 LIST_FOR_EACH(Current, &Desktop->ShellHookWindows, SHELL_HOOK_WINDOW, ListEntry)
801 {
802 if (Current->hWnd == hWnd)
803 {
804 RemoveEntryList(&Current->ListEntry);
805 ExFreePoolWithTag(Current, TAG_WINSTA);
806 return TRUE;
807 }
808 }
809
810 return FALSE;
811 }
812
813 static VOID
814 IntFreeDesktopHeap(IN OUT PDESKTOP Desktop)
815 {
816 if (Desktop->pheapDesktop != NULL)
817 {
818 MmUnmapViewInSessionSpace(Desktop->pheapDesktop);
819 Desktop->pheapDesktop = NULL;
820 }
821
822 if (Desktop->hsectionDesktop != NULL)
823 {
824 ObDereferenceObject(Desktop->hsectionDesktop);
825 Desktop->hsectionDesktop = NULL;
826 }
827 }
828
829 BOOL FASTCALL
830 IntPaintDesktop(HDC hDC)
831 {
832 RECTL Rect;
833 HBRUSH DesktopBrush, PreviousBrush;
834 HWND hWndDesktop;
835 BOOL doPatBlt = TRUE;
836 PWND WndDesktop;
837 static WCHAR s_wszSafeMode[] = L"Safe Mode";
838 int len;
839 COLORREF color_old;
840 UINT align_old;
841 int mode_old;
842
843 GdiGetClipBox(hDC, &Rect);
844
845 hWndDesktop = IntGetDesktopWindow(); // rpdesk->DesktopWindow;
846
847 WndDesktop = UserGetWindowObject(hWndDesktop); // rpdesk->pDeskInfo->spwnd;
848 if (!WndDesktop)
849 {
850 return FALSE;
851 }
852
853 if (!UserGetSystemMetrics(SM_CLEANBOOT))
854 {
855 DesktopBrush = (HBRUSH)WndDesktop->pcls->hbrBackground;
856
857 /*
858 * Paint desktop background
859 */
860 if (gspv.hbmWallpaper != NULL)
861 {
862 SIZE sz;
863 int x, y;
864 HDC hWallpaperDC;
865
866 sz.cx = WndDesktop->rcWindow.right - WndDesktop->rcWindow.left;
867 sz.cy = WndDesktop->rcWindow.bottom - WndDesktop->rcWindow.top;
868
869 if (gspv.WallpaperMode == wmStretch ||
870 gspv.WallpaperMode == wmTile)
871 {
872 x = 0;
873 y = 0;
874 }
875 else
876 {
877 /* Find the upper left corner, can be negtive if the bitmap is bigger then the screen */
878 x = (sz.cx / 2) - (gspv.cxWallpaper / 2);
879 y = (sz.cy / 2) - (gspv.cyWallpaper / 2);
880 }
881
882 hWallpaperDC = NtGdiCreateCompatibleDC(hDC);
883 if(hWallpaperDC != NULL)
884 {
885 HBITMAP hOldBitmap;
886
887 /* Fill in the area that the bitmap is not going to cover */
888 if (x > 0 || y > 0)
889 {
890 /* FIXME: Clip out the bitmap
891 can be replaced with "NtGdiPatBlt(hDC, x, y, WinSta->cxWallpaper, WinSta->cyWallpaper, PATCOPY | DSTINVERT);"
892 once we support DSTINVERT */
893 PreviousBrush = NtGdiSelectBrush(hDC, DesktopBrush);
894 NtGdiPatBlt(hDC, Rect.left, Rect.top, Rect.right, Rect.bottom, PATCOPY);
895 NtGdiSelectBrush(hDC, PreviousBrush);
896 }
897
898 /*Do not fill the background after it is painted no matter the size of the picture */
899 doPatBlt = FALSE;
900
901 hOldBitmap = NtGdiSelectBitmap(hWallpaperDC, gspv.hbmWallpaper);
902
903 if (gspv.WallpaperMode == wmStretch)
904 {
905 if(Rect.right && Rect.bottom)
906 NtGdiStretchBlt(hDC,
907 x,
908 y,
909 sz.cx,
910 sz.cy,
911 hWallpaperDC,
912 0,
913 0,
914 gspv.cxWallpaper,
915 gspv.cyWallpaper,
916 SRCCOPY,
917 0);
918
919 }
920 else if (gspv.WallpaperMode == wmTile)
921 {
922 /* Paint the bitmap across the screen then down */
923 for(y = 0; y < Rect.bottom; y += gspv.cyWallpaper)
924 {
925 for(x = 0; x < Rect.right; x += gspv.cxWallpaper)
926 {
927 NtGdiBitBlt(hDC,
928 x,
929 y,
930 gspv.cxWallpaper,
931 gspv.cyWallpaper,
932 hWallpaperDC,
933 0,
934 0,
935 SRCCOPY,
936 0,
937 0);
938 }
939 }
940 }
941 else
942 {
943 NtGdiBitBlt(hDC,
944 x,
945 y,
946 gspv.cxWallpaper,
947 gspv.cyWallpaper,
948 hWallpaperDC,
949 0,
950 0,
951 SRCCOPY,
952 0,
953 0);
954 }
955 NtGdiSelectBitmap(hWallpaperDC, hOldBitmap);
956 NtGdiDeleteObjectApp(hWallpaperDC);
957 }
958 }
959 }
960 else
961 {
962 /* Black desktop background in Safe Mode */
963 DesktopBrush = StockObjects[BLACK_BRUSH];
964 }
965 /* Back ground is set to none, clear the screen */
966 if (doPatBlt)
967 {
968 PreviousBrush = NtGdiSelectBrush(hDC, DesktopBrush);
969 NtGdiPatBlt(hDC, Rect.left, Rect.top, Rect.right, Rect.bottom, PATCOPY);
970 NtGdiSelectBrush(hDC, PreviousBrush);
971 }
972
973 /*
974 * Display system version on the desktop background
975 */
976
977 if (g_PaintDesktopVersion||UserGetSystemMetrics(SM_CLEANBOOT))
978 {
979 static WCHAR s_wszVersion[256] = {0};
980 RECTL rect;
981
982 if (*s_wszVersion)
983 {
984 len = wcslen(s_wszVersion);
985 }
986 else
987 {
988 len = GetSystemVersionString(s_wszVersion);
989 }
990
991 if (len)
992 {
993 if (!UserSystemParametersInfo(SPI_GETWORKAREA, 0, &rect, 0))
994 {
995 rect.right = UserGetSystemMetrics(SM_CXSCREEN);
996 rect.bottom = UserGetSystemMetrics(SM_CYSCREEN);
997 }
998
999 color_old = IntGdiSetTextColor(hDC, RGB(255,255,255));
1000 align_old = IntGdiSetTextAlign(hDC, TA_RIGHT);
1001 mode_old = IntGdiSetBkMode(hDC, TRANSPARENT);
1002
1003 if(!UserGetSystemMetrics(SM_CLEANBOOT))
1004 {
1005 GreExtTextOutW(hDC, rect.right-16, rect.bottom-48, 0, NULL, s_wszVersion, len, NULL, 0);
1006 }
1007 else
1008 {
1009 /* Safe Mode */
1010 /* Version information text in top center */
1011 IntGdiSetTextAlign(hDC, TA_CENTER|TA_TOP);
1012 GreExtTextOutW(hDC, (rect.right+rect.left)/2, rect.top, 0, NULL, s_wszVersion, len, NULL, 0);
1013 /* Safe Mode text in corners */
1014 len = wcslen(s_wszSafeMode);
1015 IntGdiSetTextAlign(hDC, TA_RIGHT|TA_TOP);
1016 GreExtTextOutW(hDC, rect.right, rect.top, 0, NULL, s_wszSafeMode, len, NULL, 0);
1017 IntGdiSetTextAlign(hDC, TA_RIGHT|TA_BASELINE);
1018 GreExtTextOutW(hDC, rect.right, rect.bottom, 0, NULL, s_wszSafeMode, len, NULL, 0);
1019 IntGdiSetTextAlign(hDC, TA_LEFT|TA_TOP);
1020 GreExtTextOutW(hDC, rect.left, rect.top, 0, NULL, s_wszSafeMode, len, NULL, 0);
1021 IntGdiSetTextAlign(hDC, TA_LEFT|TA_BASELINE);
1022 GreExtTextOutW(hDC, rect.left, rect.bottom, 0, NULL, s_wszSafeMode, len, NULL, 0);
1023 }
1024
1025 IntGdiSetBkMode(hDC, mode_old);
1026 IntGdiSetTextAlign(hDC, align_old);
1027 IntGdiSetTextColor(hDC, color_old);
1028 }
1029 }
1030 return TRUE;
1031 }
1032
1033 /* SYSCALLS *******************************************************************/
1034
1035 /*
1036 * NtUserCreateDesktop
1037 *
1038 * Creates a new desktop.
1039 *
1040 * Parameters
1041 * poaAttribs
1042 * Object Attributes.
1043 *
1044 * lpszDesktopDevice
1045 * Name of the device.
1046 *
1047 * pDeviceMode
1048 * Device Mode.
1049 *
1050 * dwFlags
1051 * Interaction flags.
1052 *
1053 * dwDesiredAccess
1054 * Requested type of access.
1055 *
1056 *
1057 * Return Value
1058 * If the function succeeds, the return value is a handle to the newly
1059 * created desktop. If the specified desktop already exists, the function
1060 * succeeds and returns a handle to the existing desktop. When you are
1061 * finished using the handle, call the CloseDesktop function to close it.
1062 * If the function fails, the return value is NULL.
1063 *
1064 * Status
1065 * @implemented
1066 */
1067
1068 HDESK APIENTRY
1069 NtUserCreateDesktop(
1070 POBJECT_ATTRIBUTES ObjectAttributes,
1071 PUNICODE_STRING lpszDesktopDevice,
1072 LPDEVMODEW lpdmw,
1073 DWORD dwFlags,
1074 ACCESS_MASK dwDesiredAccess)
1075 {
1076 PDESKTOP DesktopObject;
1077 UNICODE_STRING DesktopName;
1078 NTSTATUS Status = STATUS_SUCCESS;
1079 HDESK Desktop;
1080 CSR_API_MESSAGE Request;
1081 PVOID DesktopHeapSystemBase = NULL;
1082 SIZE_T DesktopInfoSize;
1083 BOOLEAN Context;
1084 ULONG_PTR HeapSize = 400 * 1024; /* FIXME: Windows uses 200KB by default */
1085 UNICODE_STRING ClassName;
1086 LARGE_STRING WindowName;
1087 BOOL NoHooks = FALSE;
1088 PWND pWnd = NULL;
1089 CREATESTRUCTW Cs;
1090 INT i;
1091 PTHREADINFO ptiCurrent;
1092 DECLARE_RETURN(HDESK);
1093
1094 TRACE("Enter NtUserCreateDesktop\n");
1095 UserEnterExclusive();
1096
1097 ptiCurrent = PsGetCurrentThreadWin32Thread();
1098 if (ptiCurrent)
1099 {
1100 /* Turn off hooks when calling any CreateWindowEx from inside win32k. */
1101 NoHooks = (ptiCurrent->TIF_flags & TIF_DISABLEHOOKS);
1102 ptiCurrent->TIF_flags |= TIF_DISABLEHOOKS;
1103 ptiCurrent->pClientInfo->dwTIFlags = ptiCurrent->TIF_flags;
1104 }
1105 /*else
1106 {ERR("NtUserCreateDesktop: No ptiCurrent\n");}*/
1107 DesktopName.Buffer = NULL;
1108
1109 /*
1110 * Try to open already existing desktop
1111 */
1112
1113 Status = ObOpenObjectByName(
1114 ObjectAttributes,
1115 ExDesktopObjectType,
1116 UserMode,
1117 NULL,
1118 dwDesiredAccess,
1119 (PVOID)&Context,
1120 (HANDLE*)&Desktop);
1121 if (!NT_SUCCESS(Status)) RETURN(NULL);
1122
1123 /* In case the object was not created (eg if it existed), return now */
1124 if (Context == FALSE)
1125 {
1126 TRACE("NtUserCreateDesktop opened desktop %wZ\n", ObjectAttributes->ObjectName);
1127 RETURN( Desktop);
1128 }
1129
1130 /* Capture desktop name */
1131 _SEH2_TRY
1132 {
1133 ProbeForRead( ObjectAttributes, sizeof(OBJECT_ATTRIBUTES), 1);
1134
1135 Status = IntSafeCopyUnicodeStringTerminateNULL(&DesktopName, ObjectAttributes->ObjectName);
1136 }
1137 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1138 {
1139 Status = _SEH2_GetExceptionCode();
1140 }
1141 _SEH2_END
1142
1143 if (! NT_SUCCESS(Status))
1144 {
1145 ERR("Failed reading Object Attributes from user space.\n");
1146 SetLastNtError(Status);
1147 RETURN( NULL);
1148 }
1149
1150 /* Reference the desktop */
1151 Status = ObReferenceObjectByHandle(Desktop,
1152 0,
1153 ExDesktopObjectType,
1154 KernelMode,
1155 (PVOID)&DesktopObject,
1156 NULL);
1157 if (!NT_SUCCESS(Status))
1158 {
1159 ERR("Failed to reference desktop object\n");
1160 RETURN(NULL);
1161 }
1162
1163 TRACE("NtUserCreateDesktop created desktop 0x%p with name %wZ\n", DesktopObject, &DesktopName);
1164
1165 DesktopObject->hsectionDesktop = NULL;
1166 DesktopObject->pheapDesktop = UserCreateHeap(&DesktopObject->hsectionDesktop,
1167 &DesktopHeapSystemBase,
1168 HeapSize);
1169 if (DesktopObject->pheapDesktop == NULL)
1170 {
1171 ObDereferenceObject(DesktopObject);
1172 ERR("Failed to create desktop heap!\n");
1173 RETURN(NULL);
1174 }
1175
1176 DesktopInfoSize = sizeof(DESKTOPINFO) + DesktopName.Length + sizeof(WCHAR);
1177
1178 DesktopObject->pDeskInfo = RtlAllocateHeap(DesktopObject->pheapDesktop,
1179 HEAP_NO_SERIALIZE,
1180 DesktopInfoSize);
1181
1182 if (DesktopObject->pDeskInfo == NULL)
1183 {
1184 ObDereferenceObject(DesktopObject);
1185 ERR("Failed to create the DESKTOP structure!\n");
1186 RETURN(NULL);
1187 }
1188
1189 RtlZeroMemory(DesktopObject->pDeskInfo,
1190 DesktopInfoSize);
1191
1192 DesktopObject->pDeskInfo->pvDesktopBase = DesktopHeapSystemBase;
1193 DesktopObject->pDeskInfo->pvDesktopLimit = (PVOID)((ULONG_PTR)DesktopHeapSystemBase + HeapSize);
1194 RtlCopyMemory(DesktopObject->pDeskInfo->szDesktopName,
1195 DesktopName.Buffer,
1196 DesktopName.Length + sizeof(WCHAR));
1197
1198 /* Initialize some local (to win32k) desktop state. */
1199 InitializeListHead(&DesktopObject->PtiList);
1200 DesktopObject->ActiveMessageQueue = NULL;
1201
1202 /* Setup Global Hooks. */
1203 for (i = 0; i < NB_HOOKS; i++)
1204 {
1205 InitializeListHead(&DesktopObject->pDeskInfo->aphkStart[i]);
1206 }
1207
1208 /*
1209 * Create a handle for CSRSS and notify CSRSS for Creating Desktop Background Windows and Threads.
1210 */
1211 Request.ApiNumber = CSR_CREATE_API_NUMBER(CSR_GUI, CREATE_DESKTOP);
1212 Status = CsrInsertObject(Desktop,
1213 GENERIC_ALL,
1214 (HANDLE*)&Request.Data.CreateDesktopRequest.DesktopHandle);
1215 if (! NT_SUCCESS(Status))
1216 {
1217 ERR("Failed to create desktop handle for CSRSS\n");
1218 ZwClose(Desktop);
1219 SetLastNtError(Status);
1220 RETURN( NULL);
1221 }
1222
1223 Status = co_CsrNotify((PCSR_API_MESSAGE)&Request,
1224 sizeof(CSR_API_MESSAGE));
1225 if (! NT_SUCCESS(Status))
1226 {
1227 CsrCloseHandle(Request.Data.CreateDesktopRequest.DesktopHandle);
1228 ERR("Failed to notify CSRSS about new desktop\n");
1229 ZwClose(Desktop);
1230 SetLastNtError(Status);
1231 RETURN( NULL);
1232 }
1233
1234 if (ptiCurrent && !ptiCurrent->rpdesk) IntSetThreadDesktop(Desktop,FALSE);
1235
1236 ClassName.Buffer = ((PWSTR)((ULONG_PTR)(WORD)(gpsi->atomSysClass[ICLS_HWNDMESSAGE])));
1237 ClassName.Length = 0;
1238 RtlZeroMemory(&WindowName, sizeof(WindowName));
1239
1240 RtlZeroMemory(&Cs, sizeof(Cs));
1241 Cs.cx = Cs.cy = 100;
1242 Cs.style = WS_POPUP|WS_CLIPCHILDREN;
1243 Cs.hInstance = hModClient; // hModuleWin; // Server side winproc! Leave it to Timo to not pass on notes!
1244 Cs.lpszName = (LPCWSTR) &WindowName;
1245 Cs.lpszClass = (LPCWSTR) &ClassName;
1246
1247 pWnd = co_UserCreateWindowEx(&Cs, &ClassName, &WindowName, NULL);
1248 if (!pWnd)
1249 {
1250 ERR("Failed to create Message window handle\n");
1251 }
1252 else
1253 {
1254 DesktopObject->spwndMessage = pWnd;
1255 }
1256
1257 /* Now,,,
1258 if !(WinStaObject->Flags & WSF_NOIO) is (not set) for desktop input output mode (see wiki)
1259 Create Tooltip. Saved in DesktopObject->spwndTooltip.
1260 Tooltip dwExStyle: WS_EX_TOOLWINDOW|WS_EX_TOPMOST
1261 hWndParent are spwndMessage. Use hModuleWin for server side winproc!
1262 The rest is same as message window.
1263 http://msdn.microsoft.com/en-us/library/bb760250(VS.85).aspx
1264 */
1265 RETURN( Desktop);
1266
1267 CLEANUP:
1268 if(DesktopName.Buffer != NULL)
1269 {
1270 ExFreePoolWithTag(DesktopName.Buffer, TAG_STRING);
1271 }
1272 if (!NoHooks && ptiCurrent)
1273 {
1274 ptiCurrent->TIF_flags &= ~TIF_DISABLEHOOKS;
1275 ptiCurrent->pClientInfo->dwTIFlags = ptiCurrent->TIF_flags;
1276 }
1277 TRACE("Leave NtUserCreateDesktop, ret=%p\n",_ret_);
1278 UserLeave();
1279 END_CLEANUP;
1280 }
1281
1282 /*
1283 * NtUserOpenDesktop
1284 *
1285 * Opens an existing desktop.
1286 *
1287 * Parameters
1288 * lpszDesktopName
1289 * Name of the existing desktop.
1290 *
1291 * dwFlags
1292 * Interaction flags.
1293 *
1294 * dwDesiredAccess
1295 * Requested type of access.
1296 *
1297 * Return Value
1298 * Handle to the desktop or zero on failure.
1299 *
1300 * Status
1301 * @implemented
1302 */
1303
1304 HDESK APIENTRY
1305 NtUserOpenDesktop(
1306 POBJECT_ATTRIBUTES ObjectAttributes,
1307 DWORD dwFlags,
1308 ACCESS_MASK dwDesiredAccess)
1309 {
1310 NTSTATUS Status;
1311 HDESK Desktop;
1312
1313 Status = ObOpenObjectByName(
1314 ObjectAttributes,
1315 ExDesktopObjectType,
1316 UserMode,
1317 NULL,
1318 dwDesiredAccess,
1319 NULL,
1320 (HANDLE*)&Desktop);
1321
1322 if (!NT_SUCCESS(Status))
1323 {
1324 ERR("Failed to open desktop\n");
1325 SetLastNtError(Status);
1326 return 0;
1327 }
1328
1329 TRACE("Opened desktop %S with handle 0x%p\n", ObjectAttributes->ObjectName->Buffer, Desktop);
1330
1331 return Desktop;
1332 }
1333
1334 /*
1335 * NtUserOpenInputDesktop
1336 *
1337 * Opens the input (interactive) desktop.
1338 *
1339 * Parameters
1340 * dwFlags
1341 * Interaction flags.
1342 *
1343 * fInherit
1344 * Inheritance option.
1345 *
1346 * dwDesiredAccess
1347 * Requested type of access.
1348 *
1349 * Return Value
1350 * Handle to the input desktop or zero on failure.
1351 *
1352 * Status
1353 * @implemented
1354 */
1355
1356 HDESK APIENTRY
1357 NtUserOpenInputDesktop(
1358 DWORD dwFlags,
1359 BOOL fInherit,
1360 ACCESS_MASK dwDesiredAccess)
1361 {
1362 PDESKTOP pdesk;
1363 NTSTATUS Status;
1364 HDESK hdesk = NULL;
1365
1366 UserEnterExclusive();
1367 TRACE("Enter NtUserOpenInputDesktop InputDesktopHandle 0x%p\n",InputDesktopHandle);
1368
1369 /* Get a pointer to the desktop object */
1370 Status = IntValidateDesktopHandle(InputDesktopHandle, UserMode, 0, &pdesk);
1371 if (!NT_SUCCESS(Status))
1372 {
1373 ERR("Validation of input desktop handle (0x%p) failed\n", InputDesktopHandle);
1374 goto Exit;
1375 }
1376
1377 /* Create a new handle to the object */
1378 Status = ObOpenObjectByPointer(
1379 pdesk,
1380 0,
1381 NULL,
1382 dwDesiredAccess,
1383 ExDesktopObjectType,
1384 UserMode,
1385 (PHANDLE)&hdesk);
1386
1387 ObDereferenceObject(pdesk);
1388
1389 if (!NT_SUCCESS(Status))
1390 {
1391 ERR("Failed to open input desktop object\n");
1392 SetLastNtError(Status);
1393 goto Exit;
1394 }
1395 Exit:
1396 TRACE("NtUserOpenInputDesktop returning 0x%p\n",hdesk);
1397 UserLeave();
1398 return hdesk;
1399 }
1400
1401 /*
1402 * NtUserCloseDesktop
1403 *
1404 * Closes a desktop handle.
1405 *
1406 * Parameters
1407 * hDesktop
1408 * Handle to the desktop.
1409 *
1410 * Return Value
1411 * Status
1412 *
1413 * Remarks
1414 * The desktop handle can be created with NtUserCreateDesktop or
1415 * NtUserOpenDesktop. This function will fail if any thread in the calling
1416 * process is using the specified desktop handle or if the handle refers
1417 * to the initial desktop of the calling process.
1418 *
1419 * Status
1420 * @implemented
1421 */
1422
1423 BOOL APIENTRY
1424 NtUserCloseDesktop(HDESK hDesktop)
1425 {
1426 PDESKTOP pdesk;
1427 NTSTATUS Status;
1428 DECLARE_RETURN(BOOL);
1429
1430 TRACE("NtUserCloseDesktop called (0x%p)\n", hDesktop);
1431 UserEnterExclusive();
1432
1433 if( hDesktop == gptiCurrent->hdesk || hDesktop == gptiCurrent->ppi->hdeskStartup)
1434 {
1435 ERR("Attempted to close thread desktop\n");
1436 EngSetLastError(ERROR_BUSY);
1437 RETURN(FALSE);
1438 }
1439
1440 Status = IntValidateDesktopHandle( hDesktop, UserMode, 0, &pdesk);
1441 if (!NT_SUCCESS(Status))
1442 {
1443 ERR("Validation of desktop handle (0x%p) failed\n", hDesktop);
1444 RETURN(FALSE);
1445 }
1446
1447 ObDereferenceObject(pdesk);
1448
1449 Status = ZwClose(hDesktop);
1450 if (!NT_SUCCESS(Status))
1451 {
1452 ERR("Failed to close desktop handle 0x%p\n", hDesktop);
1453 SetLastNtError(Status);
1454 RETURN(FALSE);
1455 }
1456
1457 RETURN(TRUE);
1458
1459 CLEANUP:
1460 TRACE("Leave NtUserCloseDesktop, ret=%i\n",_ret_);
1461 UserLeave();
1462 END_CLEANUP;
1463 }
1464
1465 /*
1466 * NtUserPaintDesktop
1467 *
1468 * The NtUserPaintDesktop function fills the clipping region in the
1469 * specified device context with the desktop pattern or wallpaper. The
1470 * function is provided primarily for shell desktops.
1471 *
1472 * Parameters
1473 * hDC
1474 * Handle to the device context.
1475 *
1476 * Status
1477 * @implemented
1478 */
1479
1480 BOOL APIENTRY
1481 NtUserPaintDesktop(HDC hDC)
1482 {
1483 BOOL Ret;
1484 UserEnterExclusive();
1485 TRACE("Enter NtUserPaintDesktop\n");
1486 Ret = IntPaintDesktop(hDC);
1487 TRACE("Leave NtUserPaintDesktop, ret=%i\n",Ret);
1488 UserLeave();
1489 return Ret;
1490 }
1491
1492 /*
1493 * NtUserSwitchDesktop
1494 *
1495 * Sets the current input (interactive) desktop.
1496 *
1497 * Parameters
1498 * hDesktop
1499 * Handle to desktop.
1500 *
1501 * Return Value
1502 * Status
1503 *
1504 * Status
1505 * @unimplemented
1506 */
1507
1508 BOOL APIENTRY
1509 NtUserSwitchDesktop(HDESK hdesk)
1510 {
1511 PDESKTOP pdesk;
1512 NTSTATUS Status;
1513 DECLARE_RETURN(BOOL);
1514
1515 UserEnterExclusive();
1516 TRACE("Enter NtUserSwitchDesktop(0x%p)\n", hdesk);
1517
1518 Status = IntValidateDesktopHandle( hdesk, UserMode, 0, &pdesk);
1519 if (!NT_SUCCESS(Status))
1520 {
1521 ERR("Validation of desktop handle (0x%p) failed\n", hdesk);
1522 RETURN(FALSE);
1523 }
1524
1525 /*
1526 * Don't allow applications switch the desktop if it's locked, unless the caller
1527 * is the logon application itself
1528 */
1529 if((pdesk->rpwinstaParent->Flags & WSS_LOCKED) &&
1530 LogonProcess != PsGetCurrentProcessWin32Process())
1531 {
1532 ObDereferenceObject(pdesk);
1533 ERR("Switching desktop 0x%p denied because the window station is locked!\n", hdesk);
1534 RETURN(FALSE);
1535 }
1536
1537 if(pdesk->rpwinstaParent != InputWindowStation)
1538 {
1539 ObDereferenceObject(pdesk);
1540 ERR("Switching desktop 0x%p denied because desktop doesn't belong to the interactive winsta!\n", hdesk);
1541 RETURN(FALSE);
1542 }
1543
1544 /* FIXME: Fail if the process is associated with a secured
1545 desktop such as Winlogon or Screen-Saver */
1546 /* FIXME: Connect to input device */
1547
1548 /* Set the active desktop in the desktop's window station. */
1549 InputWindowStation->ActiveDesktop = pdesk;
1550
1551 /* Set the global state. */
1552 InputDesktop = pdesk;
1553 InputDesktopHandle = hdesk;
1554 TRACE("SwitchDesktop InputDesktopHandle 0x%p\n",InputDesktopHandle);
1555 ObDereferenceObject(pdesk);
1556
1557 RETURN(TRUE);
1558
1559 CLEANUP:
1560 TRACE("Leave NtUserSwitchDesktop, ret=%i\n",_ret_);
1561 UserLeave();
1562 END_CLEANUP;
1563 }
1564
1565 /*
1566 * NtUserGetThreadDesktop
1567 *
1568 * Status
1569 * @implemented
1570 */
1571
1572 HDESK APIENTRY
1573 NtUserGetThreadDesktop(DWORD dwThreadId, DWORD Unknown1)
1574 {
1575 NTSTATUS Status;
1576 PETHREAD Thread;
1577 PDESKTOP DesktopObject;
1578 HDESK Ret, hThreadDesktop;
1579 OBJECT_HANDLE_INFORMATION HandleInformation;
1580 DECLARE_RETURN(HDESK);
1581
1582 UserEnterExclusive();
1583 TRACE("Enter NtUserGetThreadDesktop\n");
1584
1585 if(!dwThreadId)
1586 {
1587 EngSetLastError(ERROR_INVALID_PARAMETER);
1588 RETURN(0);
1589 }
1590
1591 Status = PsLookupThreadByThreadId((HANDLE)(DWORD_PTR)dwThreadId, &Thread);
1592 if(!NT_SUCCESS(Status))
1593 {
1594 EngSetLastError(ERROR_INVALID_PARAMETER);
1595 RETURN(0);
1596 }
1597
1598 if(Thread->ThreadsProcess == PsGetCurrentProcess())
1599 {
1600 /* Just return the handle, we queried the desktop handle of a thread running
1601 in the same context */
1602 Ret = ((PTHREADINFO)Thread->Tcb.Win32Thread)->hdesk;
1603 ObDereferenceObject(Thread);
1604 RETURN(Ret);
1605 }
1606
1607 /* Get the desktop handle and the desktop of the thread */
1608 if(!(hThreadDesktop = ((PTHREADINFO)Thread->Tcb.Win32Thread)->hdesk) ||
1609 !(DesktopObject = ((PTHREADINFO)Thread->Tcb.Win32Thread)->rpdesk))
1610 {
1611 ObDereferenceObject(Thread);
1612 ERR("Desktop information of thread 0x%x broken!?\n", dwThreadId);
1613 RETURN(NULL);
1614 }
1615
1616 /* We could just use DesktopObject instead of looking up the handle, but latter
1617 may be a bit safer (e.g. when the desktop is being destroyed */
1618 /* Switch into the context of the thread we're trying to get the desktop from,
1619 so we can use the handle */
1620 KeAttachProcess(&Thread->ThreadsProcess->Pcb);
1621 Status = ObReferenceObjectByHandle(hThreadDesktop,
1622 GENERIC_ALL,
1623 ExDesktopObjectType,
1624 UserMode,
1625 (PVOID*)&DesktopObject,
1626 &HandleInformation);
1627 KeDetachProcess();
1628
1629 /* The handle couldn't be found, there's nothing to get... */
1630 if(!NT_SUCCESS(Status))
1631 {
1632 ObDereferenceObject(Thread);
1633 RETURN(NULL);
1634 }
1635
1636 /* Lookup our handle table if we can find a handle to the desktop object,
1637 if not, create one */
1638 Ret = IntGetDesktopObjectHandle(DesktopObject);
1639
1640 /* All done, we got a valid handle to the desktop */
1641 ObDereferenceObject(DesktopObject);
1642 ObDereferenceObject(Thread);
1643 RETURN(Ret);
1644
1645 CLEANUP:
1646 TRACE("Leave NtUserGetThreadDesktop, ret=%p\n",_ret_);
1647 UserLeave();
1648 END_CLEANUP;
1649 }
1650
1651 static NTSTATUS
1652 IntUnmapDesktopView(IN PDESKTOP pdesk)
1653 {
1654 PPROCESSINFO ppi;
1655 PW32HEAP_USER_MAPPING HeapMapping, *PrevLink;
1656 NTSTATUS Status = STATUS_SUCCESS;
1657
1658 TRACE("IntUnmapDesktopView called for desktop object %p\n", pdesk);
1659
1660 ppi = PsGetCurrentProcessWin32Process();
1661 PrevLink = &ppi->HeapMappings.Next;
1662
1663 /* Unmap if we're the last thread using the desktop */
1664 HeapMapping = *PrevLink;
1665 while (HeapMapping != NULL)
1666 {
1667 if (HeapMapping->KernelMapping == (PVOID)pdesk->pheapDesktop)
1668 {
1669 if (--HeapMapping->Count == 0)
1670 {
1671 *PrevLink = HeapMapping->Next;
1672
1673 TRACE("ppi 0x%p unmapped heap of desktop 0x%p\n", ppi, pdesk);
1674 Status = MmUnmapViewOfSection(PsGetCurrentProcess(),
1675 HeapMapping->UserMapping);
1676
1677 ObDereferenceObject(pdesk);
1678
1679 UserHeapFree(HeapMapping);
1680 break;
1681 }
1682 }
1683
1684 PrevLink = &HeapMapping->Next;
1685 HeapMapping = HeapMapping->Next;
1686 }
1687
1688 return Status;
1689 }
1690
1691 static NTSTATUS
1692 IntMapDesktopView(IN PDESKTOP pdesk)
1693 {
1694 PPROCESSINFO ppi;
1695 PW32HEAP_USER_MAPPING HeapMapping, *PrevLink;
1696 PVOID UserBase = NULL;
1697 SIZE_T ViewSize = 0;
1698 LARGE_INTEGER Offset;
1699 NTSTATUS Status;
1700
1701 TRACE("IntMapDesktopView called for desktop object 0x%p\n", pdesk);
1702
1703 ppi = PsGetCurrentProcessWin32Process();
1704 PrevLink = &ppi->HeapMappings.Next;
1705
1706 /* Find out if another thread already mapped the desktop heap */
1707 HeapMapping = *PrevLink;
1708 while (HeapMapping != NULL)
1709 {
1710 if (HeapMapping->KernelMapping == (PVOID)pdesk->pheapDesktop)
1711 {
1712 HeapMapping->Count++;
1713 return STATUS_SUCCESS;
1714 }
1715
1716 PrevLink = &HeapMapping->Next;
1717 HeapMapping = HeapMapping->Next;
1718 }
1719
1720 /* We're the first, map the heap */
1721 Offset.QuadPart = 0;
1722 Status = MmMapViewOfSection(pdesk->hsectionDesktop,
1723 PsGetCurrentProcess(),
1724 &UserBase,
1725 0,
1726 0,
1727 &Offset,
1728 &ViewSize,
1729 ViewUnmap,
1730 SEC_NO_CHANGE,
1731 PAGE_EXECUTE_READ); /* Would prefer PAGE_READONLY, but thanks to RTL heaps... */
1732 if (!NT_SUCCESS(Status))
1733 {
1734 ERR("Failed to map desktop\n");
1735 return Status;
1736 }
1737
1738 TRACE("ppi 0x%p mapped heap of desktop 0x%p\n", ppi, pdesk);
1739
1740 /* Add the mapping */
1741 HeapMapping = UserHeapAlloc(sizeof(W32HEAP_USER_MAPPING));
1742 if (HeapMapping == NULL)
1743 {
1744 MmUnmapViewOfSection(PsGetCurrentProcess(), UserBase);
1745 ERR("UserHeapAlloc() failed!\n");
1746 return STATUS_NO_MEMORY;
1747 }
1748
1749 HeapMapping->Next = NULL;
1750 HeapMapping->KernelMapping = (PVOID)pdesk->pheapDesktop;
1751 HeapMapping->UserMapping = UserBase;
1752 HeapMapping->Limit = ViewSize;
1753 HeapMapping->Count = 1;
1754 *PrevLink = HeapMapping;
1755
1756 ObReferenceObject(pdesk);
1757
1758 return STATUS_SUCCESS;
1759 }
1760
1761 BOOL
1762 IntSetThreadDesktop(IN HDESK hDesktop,
1763 IN BOOL FreeOnFailure)
1764 {
1765 PDESKTOP pdesk = NULL, pdeskOld;
1766 HDESK hdeskOld;
1767 PTHREADINFO pti;
1768 NTSTATUS Status;
1769 PCLIENTTHREADINFO pctiOld, pctiNew = NULL;
1770 PCLIENTINFO pci;
1771
1772 ASSERT(NtCurrentTeb());
1773
1774 TRACE("IntSetThreadDesktop hDesktop:0x%p, FOF:%i\n",hDesktop, FreeOnFailure);
1775
1776 pti = PsGetCurrentThreadWin32Thread();
1777 pci = pti->pClientInfo;
1778
1779 /* If the caller gave us a desktop, ensure it is valid */
1780 if(hDesktop != NULL)
1781 {
1782 /* Validate the new desktop. */
1783 Status = IntValidateDesktopHandle( hDesktop, UserMode, 0, &pdesk);
1784 if (!NT_SUCCESS(Status))
1785 {
1786 ERR("Validation of desktop handle (0x%p) failed\n", hDesktop);
1787 return FALSE;
1788 }
1789
1790 if (pti->rpdesk == pdesk)
1791 {
1792 /* Nothing to do */
1793 ObDereferenceObject(pdesk);
1794 return TRUE;
1795 }
1796 }
1797
1798 /* Make sure that we don't own any window in the current desktop */
1799 if (!IsListEmpty(&pti->WindowListHead))
1800 {
1801 if(pdesk)
1802 ObDereferenceObject(pdesk);
1803 ERR("Attempted to change thread desktop although the thread has windows!\n");
1804 EngSetLastError(ERROR_BUSY);
1805 return FALSE;
1806 }
1807
1808 /* Desktop is being re-set so clear out foreground. */
1809 if (pti->rpdesk != pdesk && pti->MessageQueue == gpqForeground)
1810 {
1811 // Like above, there shouldn't be any windows, hooks or anything active on this threads desktop!
1812 IntSetFocusMessageQueue(NULL);
1813 }
1814
1815 /* Before doing the switch, map the new desktop heap and allocate the new pcti */
1816 if(pdesk != NULL)
1817 {
1818 Status = IntMapDesktopView(pdesk);
1819 if (!NT_SUCCESS(Status))
1820 {
1821 ERR("Failed to map desktop heap!\n");
1822 ObDereferenceObject(pdesk);
1823 SetLastNtError(Status);
1824 return FALSE;
1825 }
1826
1827 pctiNew = DesktopHeapAlloc( pdesk, sizeof(CLIENTTHREADINFO));
1828 if(pctiNew == NULL)
1829 {
1830 ERR("Failed to allocate new pcti\n");
1831 IntUnmapDesktopView(pdesk);
1832 ObDereferenceObject(pdesk);
1833 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
1834 return FALSE;
1835 }
1836 }
1837
1838 /* free all classes or move them to the shared heap */
1839 if(pti->rpdesk != NULL)
1840 {
1841 if(!IntCheckProcessDesktopClasses(pti->rpdesk, FreeOnFailure))
1842 {
1843 ERR("Failed to move process classes to shared heap!\n");
1844 if(pdesk)
1845 {
1846 DesktopHeapFree(pdesk, pctiNew);
1847 IntUnmapDesktopView(pdesk);
1848 ObDereferenceObject(pdesk);
1849 }
1850 return FALSE;
1851 }
1852 }
1853
1854 pdeskOld = pti->rpdesk;
1855 hdeskOld = pti->hdesk;
1856 if (pti->pcti != &pti->cti)
1857 pctiOld = pti->pcti;
1858 else
1859 pctiOld = NULL;
1860
1861 /* do the switch */
1862 if(pdesk != NULL)
1863 {
1864 pti->rpdesk = pdesk;
1865 pti->hdesk = hDesktop;
1866 pti->pDeskInfo = pti->rpdesk->pDeskInfo;
1867 pti->pcti = pctiNew;
1868
1869 pci->ulClientDelta = DesktopHeapGetUserDelta();
1870 pci->pDeskInfo = (PVOID)((ULONG_PTR)pti->pDeskInfo - pci->ulClientDelta);
1871 pci->pClientThreadInfo = (PVOID)((ULONG_PTR)pti->pcti - pci->ulClientDelta);
1872
1873 /* initialize the new pcti */
1874 if(pctiOld != NULL)
1875 {
1876 RtlCopyMemory(pctiNew, pctiOld, sizeof(CLIENTTHREADINFO));
1877 }
1878 else
1879 {
1880 RtlZeroMemory(pctiNew, sizeof(CLIENTTHREADINFO));
1881 pci->fsHooks = pti->fsHooks;
1882 pci->dwTIFlags = pti->TIF_flags;
1883 }
1884 }
1885 else
1886 {
1887 pti->rpdesk = NULL;
1888 pti->hdesk = NULL;
1889 pti->pDeskInfo = NULL;
1890 pti->pcti = &pti->cti; // Always point inside so there will be no crash when posting or sending msg's!
1891 pci->ulClientDelta = 0;
1892 pci->pDeskInfo = NULL;
1893 pci->pClientThreadInfo = NULL;
1894 }
1895
1896 /* clean up the old desktop */
1897 if(pdeskOld != NULL)
1898 {
1899 RemoveEntryList(&pti->PtiLink);
1900 if (pctiOld) DesktopHeapFree(pdeskOld, pctiOld);
1901 IntUnmapDesktopView(pdeskOld);
1902 ObDereferenceObject(pdeskOld);
1903 ZwClose(hdeskOld);
1904 }
1905
1906 if(pdesk)
1907 {
1908 InsertTailList(&pdesk->PtiList, &pti->PtiLink);
1909 }
1910
1911 TRACE("IntSetThreadDesktop: pti 0x%p ppi 0x%p switched from object 0x%p to 0x%p\n", pti, pti->ppi, pdeskOld, pdesk);
1912
1913 return TRUE;
1914 }
1915
1916 /*
1917 * NtUserSetThreadDesktop
1918 *
1919 * Status
1920 * @implemented
1921 */
1922
1923 BOOL APIENTRY
1924 NtUserSetThreadDesktop(HDESK hDesktop)
1925 {
1926 BOOL ret = FALSE;
1927
1928 UserEnterExclusive();
1929
1930 // FIXME: IntSetThreadDesktop validates the desktop handle, it should happen
1931 // here too and set the NT error level. Q. Is it necessary to have the validation
1932 // in IntSetThreadDesktop? Is it needed there too?
1933 if (hDesktop || (!hDesktop && CsrProcess == PsGetCurrentProcess()))
1934 ret = IntSetThreadDesktop(hDesktop, FALSE);
1935
1936 UserLeave();
1937
1938 return ret;
1939 }
1940
1941 /* EOF */