Synchronize with trunk's revision r57599.
[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(&Request);
1224 if (! NT_SUCCESS(Status))
1225 {
1226 CsrCloseHandle(Request.Data.CreateDesktopRequest.DesktopHandle);
1227 ERR("Failed to notify CSRSS about new desktop\n");
1228 ZwClose(Desktop);
1229 SetLastNtError(Status);
1230 RETURN( NULL);
1231 }
1232
1233 if (ptiCurrent && !ptiCurrent->rpdesk) IntSetThreadDesktop(Desktop,FALSE);
1234
1235 ClassName.Buffer = ((PWSTR)((ULONG_PTR)(WORD)(gpsi->atomSysClass[ICLS_HWNDMESSAGE])));
1236 ClassName.Length = 0;
1237 RtlZeroMemory(&WindowName, sizeof(WindowName));
1238
1239 RtlZeroMemory(&Cs, sizeof(Cs));
1240 Cs.cx = Cs.cy = 100;
1241 Cs.style = WS_POPUP|WS_CLIPCHILDREN;
1242 Cs.hInstance = hModClient; // hModuleWin; // Server side winproc! Leave it to Timo to not pass on notes!
1243 Cs.lpszName = (LPCWSTR) &WindowName;
1244 Cs.lpszClass = (LPCWSTR) &ClassName;
1245
1246 pWnd = co_UserCreateWindowEx(&Cs, &ClassName, &WindowName, NULL);
1247 if (!pWnd)
1248 {
1249 ERR("Failed to create Message window handle\n");
1250 }
1251 else
1252 {
1253 DesktopObject->spwndMessage = pWnd;
1254 }
1255
1256 /* Now,,,
1257 if !(WinStaObject->Flags & WSF_NOIO) is (not set) for desktop input output mode (see wiki)
1258 Create Tooltip. Saved in DesktopObject->spwndTooltip.
1259 Tooltip dwExStyle: WS_EX_TOOLWINDOW|WS_EX_TOPMOST
1260 hWndParent are spwndMessage. Use hModuleWin for server side winproc!
1261 The rest is same as message window.
1262 http://msdn.microsoft.com/en-us/library/bb760250(VS.85).aspx
1263 */
1264 RETURN( Desktop);
1265
1266 CLEANUP:
1267 if(DesktopName.Buffer != NULL)
1268 {
1269 ExFreePoolWithTag(DesktopName.Buffer, TAG_STRING);
1270 }
1271 if (!NoHooks && ptiCurrent)
1272 {
1273 ptiCurrent->TIF_flags &= ~TIF_DISABLEHOOKS;
1274 ptiCurrent->pClientInfo->dwTIFlags = ptiCurrent->TIF_flags;
1275 }
1276 TRACE("Leave NtUserCreateDesktop, ret=%p\n",_ret_);
1277 UserLeave();
1278 END_CLEANUP;
1279 }
1280
1281 /*
1282 * NtUserOpenDesktop
1283 *
1284 * Opens an existing desktop.
1285 *
1286 * Parameters
1287 * lpszDesktopName
1288 * Name of the existing desktop.
1289 *
1290 * dwFlags
1291 * Interaction flags.
1292 *
1293 * dwDesiredAccess
1294 * Requested type of access.
1295 *
1296 * Return Value
1297 * Handle to the desktop or zero on failure.
1298 *
1299 * Status
1300 * @implemented
1301 */
1302
1303 HDESK APIENTRY
1304 NtUserOpenDesktop(
1305 POBJECT_ATTRIBUTES ObjectAttributes,
1306 DWORD dwFlags,
1307 ACCESS_MASK dwDesiredAccess)
1308 {
1309 NTSTATUS Status;
1310 HDESK Desktop;
1311
1312 Status = ObOpenObjectByName(
1313 ObjectAttributes,
1314 ExDesktopObjectType,
1315 UserMode,
1316 NULL,
1317 dwDesiredAccess,
1318 NULL,
1319 (HANDLE*)&Desktop);
1320
1321 if (!NT_SUCCESS(Status))
1322 {
1323 ERR("Failed to open desktop\n");
1324 SetLastNtError(Status);
1325 return 0;
1326 }
1327
1328 TRACE("Opened desktop %S with handle 0x%p\n", ObjectAttributes->ObjectName->Buffer, Desktop);
1329
1330 return Desktop;
1331 }
1332
1333 /*
1334 * NtUserOpenInputDesktop
1335 *
1336 * Opens the input (interactive) desktop.
1337 *
1338 * Parameters
1339 * dwFlags
1340 * Interaction flags.
1341 *
1342 * fInherit
1343 * Inheritance option.
1344 *
1345 * dwDesiredAccess
1346 * Requested type of access.
1347 *
1348 * Return Value
1349 * Handle to the input desktop or zero on failure.
1350 *
1351 * Status
1352 * @implemented
1353 */
1354
1355 HDESK APIENTRY
1356 NtUserOpenInputDesktop(
1357 DWORD dwFlags,
1358 BOOL fInherit,
1359 ACCESS_MASK dwDesiredAccess)
1360 {
1361 PDESKTOP pdesk;
1362 NTSTATUS Status;
1363 HDESK hdesk = NULL;
1364
1365 UserEnterExclusive();
1366 TRACE("Enter NtUserOpenInputDesktop InputDesktopHandle 0x%p\n",InputDesktopHandle);
1367
1368 /* Get a pointer to the desktop object */
1369 Status = IntValidateDesktopHandle(InputDesktopHandle, UserMode, 0, &pdesk);
1370 if (!NT_SUCCESS(Status))
1371 {
1372 ERR("Validation of input desktop handle (0x%p) failed\n", InputDesktopHandle);
1373 goto Exit;
1374 }
1375
1376 /* Create a new handle to the object */
1377 Status = ObOpenObjectByPointer(
1378 pdesk,
1379 0,
1380 NULL,
1381 dwDesiredAccess,
1382 ExDesktopObjectType,
1383 UserMode,
1384 (PHANDLE)&hdesk);
1385
1386 ObDereferenceObject(pdesk);
1387
1388 if (!NT_SUCCESS(Status))
1389 {
1390 ERR("Failed to open input desktop object\n");
1391 SetLastNtError(Status);
1392 goto Exit;
1393 }
1394 Exit:
1395 TRACE("NtUserOpenInputDesktop returning 0x%p\n",hdesk);
1396 UserLeave();
1397 return hdesk;
1398 }
1399
1400 /*
1401 * NtUserCloseDesktop
1402 *
1403 * Closes a desktop handle.
1404 *
1405 * Parameters
1406 * hDesktop
1407 * Handle to the desktop.
1408 *
1409 * Return Value
1410 * Status
1411 *
1412 * Remarks
1413 * The desktop handle can be created with NtUserCreateDesktop or
1414 * NtUserOpenDesktop. This function will fail if any thread in the calling
1415 * process is using the specified desktop handle or if the handle refers
1416 * to the initial desktop of the calling process.
1417 *
1418 * Status
1419 * @implemented
1420 */
1421
1422 BOOL APIENTRY
1423 NtUserCloseDesktop(HDESK hDesktop)
1424 {
1425 PDESKTOP pdesk;
1426 NTSTATUS Status;
1427 DECLARE_RETURN(BOOL);
1428
1429 TRACE("NtUserCloseDesktop called (0x%p)\n", hDesktop);
1430 UserEnterExclusive();
1431
1432 if( hDesktop == gptiCurrent->hdesk || hDesktop == gptiCurrent->ppi->hdeskStartup)
1433 {
1434 ERR("Attempted to close thread desktop\n");
1435 EngSetLastError(ERROR_BUSY);
1436 RETURN(FALSE);
1437 }
1438
1439 Status = IntValidateDesktopHandle( hDesktop, UserMode, 0, &pdesk);
1440 if (!NT_SUCCESS(Status))
1441 {
1442 ERR("Validation of desktop handle (0x%p) failed\n", hDesktop);
1443 RETURN(FALSE);
1444 }
1445
1446 ObDereferenceObject(pdesk);
1447
1448 Status = ZwClose(hDesktop);
1449 if (!NT_SUCCESS(Status))
1450 {
1451 ERR("Failed to close desktop handle 0x%p\n", hDesktop);
1452 SetLastNtError(Status);
1453 RETURN(FALSE);
1454 }
1455
1456 RETURN(TRUE);
1457
1458 CLEANUP:
1459 TRACE("Leave NtUserCloseDesktop, ret=%i\n",_ret_);
1460 UserLeave();
1461 END_CLEANUP;
1462 }
1463
1464 /*
1465 * NtUserPaintDesktop
1466 *
1467 * The NtUserPaintDesktop function fills the clipping region in the
1468 * specified device context with the desktop pattern or wallpaper. The
1469 * function is provided primarily for shell desktops.
1470 *
1471 * Parameters
1472 * hDC
1473 * Handle to the device context.
1474 *
1475 * Status
1476 * @implemented
1477 */
1478
1479 BOOL APIENTRY
1480 NtUserPaintDesktop(HDC hDC)
1481 {
1482 BOOL Ret;
1483 UserEnterExclusive();
1484 TRACE("Enter NtUserPaintDesktop\n");
1485 Ret = IntPaintDesktop(hDC);
1486 TRACE("Leave NtUserPaintDesktop, ret=%i\n",Ret);
1487 UserLeave();
1488 return Ret;
1489 }
1490
1491 /*
1492 * NtUserSwitchDesktop
1493 *
1494 * Sets the current input (interactive) desktop.
1495 *
1496 * Parameters
1497 * hDesktop
1498 * Handle to desktop.
1499 *
1500 * Return Value
1501 * Status
1502 *
1503 * Status
1504 * @unimplemented
1505 */
1506
1507 BOOL APIENTRY
1508 NtUserSwitchDesktop(HDESK hdesk)
1509 {
1510 PDESKTOP pdesk;
1511 NTSTATUS Status;
1512 DECLARE_RETURN(BOOL);
1513
1514 UserEnterExclusive();
1515 TRACE("Enter NtUserSwitchDesktop(0x%p)\n", hdesk);
1516
1517 Status = IntValidateDesktopHandle( hdesk, UserMode, 0, &pdesk);
1518 if (!NT_SUCCESS(Status))
1519 {
1520 ERR("Validation of desktop handle (0x%p) failed\n", hdesk);
1521 RETURN(FALSE);
1522 }
1523
1524 /*
1525 * Don't allow applications switch the desktop if it's locked, unless the caller
1526 * is the logon application itself
1527 */
1528 if((pdesk->rpwinstaParent->Flags & WSS_LOCKED) &&
1529 LogonProcess != PsGetCurrentProcessWin32Process())
1530 {
1531 ObDereferenceObject(pdesk);
1532 ERR("Switching desktop 0x%p denied because the window station is locked!\n", hdesk);
1533 RETURN(FALSE);
1534 }
1535
1536 if(pdesk->rpwinstaParent != InputWindowStation)
1537 {
1538 ObDereferenceObject(pdesk);
1539 ERR("Switching desktop 0x%p denied because desktop doesn't belong to the interactive winsta!\n", hdesk);
1540 RETURN(FALSE);
1541 }
1542
1543 /* FIXME: Fail if the process is associated with a secured
1544 desktop such as Winlogon or Screen-Saver */
1545 /* FIXME: Connect to input device */
1546
1547 /* Set the active desktop in the desktop's window station. */
1548 InputWindowStation->ActiveDesktop = pdesk;
1549
1550 /* Set the global state. */
1551 InputDesktop = pdesk;
1552 InputDesktopHandle = hdesk;
1553 TRACE("SwitchDesktop InputDesktopHandle 0x%p\n",InputDesktopHandle);
1554 ObDereferenceObject(pdesk);
1555
1556 RETURN(TRUE);
1557
1558 CLEANUP:
1559 TRACE("Leave NtUserSwitchDesktop, ret=%i\n",_ret_);
1560 UserLeave();
1561 END_CLEANUP;
1562 }
1563
1564 /*
1565 * NtUserGetThreadDesktop
1566 *
1567 * Status
1568 * @implemented
1569 */
1570
1571 HDESK APIENTRY
1572 NtUserGetThreadDesktop(DWORD dwThreadId, DWORD Unknown1)
1573 {
1574 NTSTATUS Status;
1575 PETHREAD Thread;
1576 PDESKTOP DesktopObject;
1577 HDESK Ret, hThreadDesktop;
1578 OBJECT_HANDLE_INFORMATION HandleInformation;
1579 DECLARE_RETURN(HDESK);
1580
1581 UserEnterExclusive();
1582 TRACE("Enter NtUserGetThreadDesktop\n");
1583
1584 if(!dwThreadId)
1585 {
1586 EngSetLastError(ERROR_INVALID_PARAMETER);
1587 RETURN(0);
1588 }
1589
1590 Status = PsLookupThreadByThreadId((HANDLE)(DWORD_PTR)dwThreadId, &Thread);
1591 if(!NT_SUCCESS(Status))
1592 {
1593 EngSetLastError(ERROR_INVALID_PARAMETER);
1594 RETURN(0);
1595 }
1596
1597 if(Thread->ThreadsProcess == PsGetCurrentProcess())
1598 {
1599 /* Just return the handle, we queried the desktop handle of a thread running
1600 in the same context */
1601 Ret = ((PTHREADINFO)Thread->Tcb.Win32Thread)->hdesk;
1602 ObDereferenceObject(Thread);
1603 RETURN(Ret);
1604 }
1605
1606 /* Get the desktop handle and the desktop of the thread */
1607 if(!(hThreadDesktop = ((PTHREADINFO)Thread->Tcb.Win32Thread)->hdesk) ||
1608 !(DesktopObject = ((PTHREADINFO)Thread->Tcb.Win32Thread)->rpdesk))
1609 {
1610 ObDereferenceObject(Thread);
1611 ERR("Desktop information of thread 0x%x broken!?\n", dwThreadId);
1612 RETURN(NULL);
1613 }
1614
1615 /* We could just use DesktopObject instead of looking up the handle, but latter
1616 may be a bit safer (e.g. when the desktop is being destroyed */
1617 /* Switch into the context of the thread we're trying to get the desktop from,
1618 so we can use the handle */
1619 KeAttachProcess(&Thread->ThreadsProcess->Pcb);
1620 Status = ObReferenceObjectByHandle(hThreadDesktop,
1621 GENERIC_ALL,
1622 ExDesktopObjectType,
1623 UserMode,
1624 (PVOID*)&DesktopObject,
1625 &HandleInformation);
1626 KeDetachProcess();
1627
1628 /* The handle couldn't be found, there's nothing to get... */
1629 if(!NT_SUCCESS(Status))
1630 {
1631 ObDereferenceObject(Thread);
1632 RETURN(NULL);
1633 }
1634
1635 /* Lookup our handle table if we can find a handle to the desktop object,
1636 if not, create one */
1637 Ret = IntGetDesktopObjectHandle(DesktopObject);
1638
1639 /* All done, we got a valid handle to the desktop */
1640 ObDereferenceObject(DesktopObject);
1641 ObDereferenceObject(Thread);
1642 RETURN(Ret);
1643
1644 CLEANUP:
1645 TRACE("Leave NtUserGetThreadDesktop, ret=%p\n",_ret_);
1646 UserLeave();
1647 END_CLEANUP;
1648 }
1649
1650 static NTSTATUS
1651 IntUnmapDesktopView(IN PDESKTOP pdesk)
1652 {
1653 PPROCESSINFO ppi;
1654 PW32HEAP_USER_MAPPING HeapMapping, *PrevLink;
1655 NTSTATUS Status = STATUS_SUCCESS;
1656
1657 TRACE("IntUnmapDesktopView called for desktop object %p\n", pdesk);
1658
1659 ppi = PsGetCurrentProcessWin32Process();
1660 PrevLink = &ppi->HeapMappings.Next;
1661
1662 /* Unmap if we're the last thread using the desktop */
1663 HeapMapping = *PrevLink;
1664 while (HeapMapping != NULL)
1665 {
1666 if (HeapMapping->KernelMapping == (PVOID)pdesk->pheapDesktop)
1667 {
1668 if (--HeapMapping->Count == 0)
1669 {
1670 *PrevLink = HeapMapping->Next;
1671
1672 TRACE("ppi 0x%p unmapped heap of desktop 0x%p\n", ppi, pdesk);
1673 Status = MmUnmapViewOfSection(PsGetCurrentProcess(),
1674 HeapMapping->UserMapping);
1675
1676 ObDereferenceObject(pdesk);
1677
1678 UserHeapFree(HeapMapping);
1679 break;
1680 }
1681 }
1682
1683 PrevLink = &HeapMapping->Next;
1684 HeapMapping = HeapMapping->Next;
1685 }
1686
1687 return Status;
1688 }
1689
1690 static NTSTATUS
1691 IntMapDesktopView(IN PDESKTOP pdesk)
1692 {
1693 PPROCESSINFO ppi;
1694 PW32HEAP_USER_MAPPING HeapMapping, *PrevLink;
1695 PVOID UserBase = NULL;
1696 SIZE_T ViewSize = 0;
1697 LARGE_INTEGER Offset;
1698 NTSTATUS Status;
1699
1700 TRACE("IntMapDesktopView called for desktop object 0x%p\n", pdesk);
1701
1702 ppi = PsGetCurrentProcessWin32Process();
1703 PrevLink = &ppi->HeapMappings.Next;
1704
1705 /* Find out if another thread already mapped the desktop heap */
1706 HeapMapping = *PrevLink;
1707 while (HeapMapping != NULL)
1708 {
1709 if (HeapMapping->KernelMapping == (PVOID)pdesk->pheapDesktop)
1710 {
1711 HeapMapping->Count++;
1712 return STATUS_SUCCESS;
1713 }
1714
1715 PrevLink = &HeapMapping->Next;
1716 HeapMapping = HeapMapping->Next;
1717 }
1718
1719 /* We're the first, map the heap */
1720 Offset.QuadPart = 0;
1721 Status = MmMapViewOfSection(pdesk->hsectionDesktop,
1722 PsGetCurrentProcess(),
1723 &UserBase,
1724 0,
1725 0,
1726 &Offset,
1727 &ViewSize,
1728 ViewUnmap,
1729 SEC_NO_CHANGE,
1730 PAGE_EXECUTE_READ); /* Would prefer PAGE_READONLY, but thanks to RTL heaps... */
1731 if (!NT_SUCCESS(Status))
1732 {
1733 ERR("Failed to map desktop\n");
1734 return Status;
1735 }
1736
1737 TRACE("ppi 0x%p mapped heap of desktop 0x%p\n", ppi, pdesk);
1738
1739 /* Add the mapping */
1740 HeapMapping = UserHeapAlloc(sizeof(W32HEAP_USER_MAPPING));
1741 if (HeapMapping == NULL)
1742 {
1743 MmUnmapViewOfSection(PsGetCurrentProcess(), UserBase);
1744 ERR("UserHeapAlloc() failed!\n");
1745 return STATUS_NO_MEMORY;
1746 }
1747
1748 HeapMapping->Next = NULL;
1749 HeapMapping->KernelMapping = (PVOID)pdesk->pheapDesktop;
1750 HeapMapping->UserMapping = UserBase;
1751 HeapMapping->Limit = ViewSize;
1752 HeapMapping->Count = 1;
1753 *PrevLink = HeapMapping;
1754
1755 ObReferenceObject(pdesk);
1756
1757 return STATUS_SUCCESS;
1758 }
1759
1760 BOOL
1761 IntSetThreadDesktop(IN HDESK hDesktop,
1762 IN BOOL FreeOnFailure)
1763 {
1764 PDESKTOP pdesk = NULL, pdeskOld;
1765 HDESK hdeskOld;
1766 PTHREADINFO pti;
1767 NTSTATUS Status;
1768 PCLIENTTHREADINFO pctiOld, pctiNew = NULL;
1769 PCLIENTINFO pci;
1770
1771 ASSERT(NtCurrentTeb());
1772
1773 TRACE("IntSetThreadDesktop hDesktop:0x%p, FOF:%i\n",hDesktop, FreeOnFailure);
1774
1775 pti = PsGetCurrentThreadWin32Thread();
1776 pci = pti->pClientInfo;
1777
1778 /* If the caller gave us a desktop, ensure it is valid */
1779 if(hDesktop != NULL)
1780 {
1781 /* Validate the new desktop. */
1782 Status = IntValidateDesktopHandle( hDesktop, UserMode, 0, &pdesk);
1783 if (!NT_SUCCESS(Status))
1784 {
1785 ERR("Validation of desktop handle (0x%p) failed\n", hDesktop);
1786 return FALSE;
1787 }
1788
1789 if (pti->rpdesk == pdesk)
1790 {
1791 /* Nothing to do */
1792 ObDereferenceObject(pdesk);
1793 return TRUE;
1794 }
1795 }
1796
1797 /* Make sure that we don't own any window in the current desktop */
1798 if (!IsListEmpty(&pti->WindowListHead))
1799 {
1800 if(pdesk)
1801 ObDereferenceObject(pdesk);
1802 ERR("Attempted to change thread desktop although the thread has windows!\n");
1803 EngSetLastError(ERROR_BUSY);
1804 return FALSE;
1805 }
1806
1807 /* Desktop is being re-set so clear out foreground. */
1808 if (pti->rpdesk != pdesk && pti->MessageQueue == gpqForeground)
1809 {
1810 // Like above, there shouldn't be any windows, hooks or anything active on this threads desktop!
1811 IntSetFocusMessageQueue(NULL);
1812 }
1813
1814 /* Before doing the switch, map the new desktop heap and allocate the new pcti */
1815 if(pdesk != NULL)
1816 {
1817 Status = IntMapDesktopView(pdesk);
1818 if (!NT_SUCCESS(Status))
1819 {
1820 ERR("Failed to map desktop heap!\n");
1821 ObDereferenceObject(pdesk);
1822 SetLastNtError(Status);
1823 return FALSE;
1824 }
1825
1826 pctiNew = DesktopHeapAlloc( pdesk, sizeof(CLIENTTHREADINFO));
1827 if(pctiNew == NULL)
1828 {
1829 ERR("Failed to allocate new pcti\n");
1830 IntUnmapDesktopView(pdesk);
1831 ObDereferenceObject(pdesk);
1832 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
1833 return FALSE;
1834 }
1835 }
1836
1837 /* free all classes or move them to the shared heap */
1838 if(pti->rpdesk != NULL)
1839 {
1840 if(!IntCheckProcessDesktopClasses(pti->rpdesk, FreeOnFailure))
1841 {
1842 ERR("Failed to move process classes to shared heap!\n");
1843 if(pdesk)
1844 {
1845 DesktopHeapFree(pdesk, pctiNew);
1846 IntUnmapDesktopView(pdesk);
1847 ObDereferenceObject(pdesk);
1848 }
1849 return FALSE;
1850 }
1851 }
1852
1853 pdeskOld = pti->rpdesk;
1854 hdeskOld = pti->hdesk;
1855 if (pti->pcti != &pti->cti)
1856 pctiOld = pti->pcti;
1857 else
1858 pctiOld = NULL;
1859
1860 /* do the switch */
1861 if(pdesk != NULL)
1862 {
1863 pti->rpdesk = pdesk;
1864 pti->hdesk = hDesktop;
1865 pti->pDeskInfo = pti->rpdesk->pDeskInfo;
1866 pti->pcti = pctiNew;
1867
1868 pci->ulClientDelta = DesktopHeapGetUserDelta();
1869 pci->pDeskInfo = (PVOID)((ULONG_PTR)pti->pDeskInfo - pci->ulClientDelta);
1870 pci->pClientThreadInfo = (PVOID)((ULONG_PTR)pti->pcti - pci->ulClientDelta);
1871
1872 /* initialize the new pcti */
1873 if(pctiOld != NULL)
1874 {
1875 RtlCopyMemory(pctiNew, pctiOld, sizeof(CLIENTTHREADINFO));
1876 }
1877 else
1878 {
1879 RtlZeroMemory(pctiNew, sizeof(CLIENTTHREADINFO));
1880 pci->fsHooks = pti->fsHooks;
1881 pci->dwTIFlags = pti->TIF_flags;
1882 }
1883 }
1884 else
1885 {
1886 pti->rpdesk = NULL;
1887 pti->hdesk = NULL;
1888 pti->pDeskInfo = NULL;
1889 pti->pcti = &pti->cti; // Always point inside so there will be no crash when posting or sending msg's!
1890 pci->ulClientDelta = 0;
1891 pci->pDeskInfo = NULL;
1892 pci->pClientThreadInfo = NULL;
1893 }
1894
1895 /* clean up the old desktop */
1896 if(pdeskOld != NULL)
1897 {
1898 RemoveEntryList(&pti->PtiLink);
1899 if (pctiOld) DesktopHeapFree(pdeskOld, pctiOld);
1900 IntUnmapDesktopView(pdeskOld);
1901 ObDereferenceObject(pdeskOld);
1902 ZwClose(hdeskOld);
1903 }
1904
1905 if(pdesk)
1906 {
1907 InsertTailList(&pdesk->PtiList, &pti->PtiLink);
1908 }
1909
1910 TRACE("IntSetThreadDesktop: pti 0x%p ppi 0x%p switched from object 0x%p to 0x%p\n", pti, pti->ppi, pdeskOld, pdesk);
1911
1912 return TRUE;
1913 }
1914
1915 /*
1916 * NtUserSetThreadDesktop
1917 *
1918 * Status
1919 * @implemented
1920 */
1921
1922 BOOL APIENTRY
1923 NtUserSetThreadDesktop(HDESK hDesktop)
1924 {
1925 BOOL ret = FALSE;
1926
1927 UserEnterExclusive();
1928
1929 // FIXME: IntSetThreadDesktop validates the desktop handle, it should happen
1930 // here too and set the NT error level. Q. Is it necessary to have the validation
1931 // in IntSetThreadDesktop? Is it needed there too?
1932 if (hDesktop || (!hDesktop && CsrProcess == PsGetCurrentProcess()))
1933 ret = IntSetThreadDesktop(hDesktop, FALSE);
1934
1935 UserLeave();
1936
1937 return ret;
1938 }
1939
1940 /* EOF */