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