[Win32k]
[reactos.git] / reactos / 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%x\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: %d!\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: %d!\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%x obj:0x%x access:0x%x Status:0x%x\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: %lx\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 }
470
471 HWND FASTCALL IntGetDesktopWindow(VOID)
472 {
473 PDESKTOP pdo = IntGetActiveDesktop();
474 if (!pdo)
475 {
476 TRACE("No active desktop\n");
477 return NULL;
478 }
479 return pdo->DesktopWindow;
480 }
481
482 PWND FASTCALL UserGetDesktopWindow(VOID)
483 {
484 PDESKTOP pdo = IntGetActiveDesktop();
485
486 if (!pdo)
487 {
488 TRACE("No active desktop\n");
489 return NULL;
490 }
491 // return pdo->pDeskInfo->spwnd;
492 return UserGetWindowObject(pdo->DesktopWindow);
493 }
494
495 HWND FASTCALL IntGetMessageWindow(VOID)
496 {
497 PDESKTOP pdo = IntGetActiveDesktop();
498
499 if (!pdo)
500 {
501 TRACE("No active desktop\n");
502 return NULL;
503 }
504 return pdo->spwndMessage->head.h;
505 }
506
507 PWND FASTCALL UserGetMessageWindow(VOID)
508 {
509 PDESKTOP pdo = IntGetActiveDesktop();
510
511 if (!pdo)
512 {
513 TRACE("No active desktop\n");
514 return NULL;
515 }
516 return pdo->spwndMessage;
517 }
518
519 HWND FASTCALL IntGetCurrentThreadDesktopWindow(VOID)
520 {
521 PTHREADINFO pti = PsGetCurrentThreadWin32Thread();
522 PDESKTOP pdo = pti->rpdesk;
523 if (NULL == pdo)
524 {
525 ERR("Thread doesn't have a desktop\n");
526 return NULL;
527 }
528 return pdo->DesktopWindow;
529 }
530
531 /* PUBLIC FUNCTIONS ***********************************************************/
532
533 HDC FASTCALL
534 UserGetDesktopDC(ULONG DcType, BOOL EmptyDC, BOOL ValidatehWnd)
535 {
536 PWND DesktopObject = 0;
537 HDC DesktopHDC = 0;
538
539 if (DcType == DC_TYPE_DIRECT)
540 {
541 DesktopObject = UserGetDesktopWindow();
542 DesktopHDC = (HDC)UserGetWindowDC(DesktopObject);
543 }
544 else
545 {
546 PMONITOR pMonitor = UserGetPrimaryMonitor();
547 DesktopHDC = IntGdiCreateDisplayDC(pMonitor->hDev, DcType, EmptyDC);
548 }
549
550 return DesktopHDC;
551 }
552
553 VOID APIENTRY
554 UserRedrawDesktop()
555 {
556 PWND Window = NULL;
557 HRGN hRgn;
558
559 Window = UserGetDesktopWindow();
560 hRgn = IntSysCreateRectRgnIndirect(&Window->rcWindow);
561
562 IntInvalidateWindows( Window,
563 hRgn,
564 RDW_FRAME |
565 RDW_ERASE |
566 RDW_INVALIDATE |
567 RDW_ALLCHILDREN);
568
569 GreDeleteObject(hRgn);
570 }
571
572
573 NTSTATUS FASTCALL
574 co_IntShowDesktop(PDESKTOP Desktop, ULONG Width, ULONG Height)
575 {
576 //#if 0
577 CSR_API_MESSAGE Request;
578
579 Request.Type = MAKE_CSR_API(SHOW_DESKTOP, CSR_GUI);
580 Request.Data.ShowDesktopRequest.DesktopWindow = Desktop->DesktopWindow;
581 Request.Data.ShowDesktopRequest.Width = Width;
582 Request.Data.ShowDesktopRequest.Height = Height;
583
584 return co_CsrNotify(&Request);
585 //#endif
586 #if 0 // DESKTOPWNDPROC
587 PWND Window;
588
589 Window = IntGetWindowObject(Desktop->DesktopWindow);
590
591 if (!Window)
592 {
593 ERR("No Desktop window.\n");
594 return STATUS_UNSUCCESSFUL;
595 }
596 co_WinPosSetWindowPos(Window, NULL, 0, 0, Width, Height, SWP_NOACTIVATE|SWP_NOZORDER|SWP_SHOWWINDOW);
597
598 co_UserRedrawWindow( Window, NULL, 0, RDW_UPDATENOW | RDW_ALLCHILDREN);
599 return STATUS_SUCCESS;
600 #endif
601 }
602
603 NTSTATUS FASTCALL
604 IntHideDesktop(PDESKTOP Desktop)
605 {
606 PWND DesktopWnd;
607
608 DesktopWnd = IntGetWindowObject(Desktop->DesktopWindow);
609 if (! DesktopWnd)
610 {
611 return ERROR_INVALID_WINDOW_HANDLE;
612 }
613 DesktopWnd->style &= ~WS_VISIBLE;
614
615 return STATUS_SUCCESS;
616 }
617
618 static
619 HWND* FASTCALL
620 UserBuildShellHookHwndList(PDESKTOP Desktop)
621 {
622 ULONG entries=0;
623 PSHELL_HOOK_WINDOW Current;
624 HWND* list;
625
626 /* FIXME: If we save nb elements in desktop, we dont have to loop to find nb entries */
627 LIST_FOR_EACH(Current, &Desktop->ShellHookWindows, SHELL_HOOK_WINDOW, ListEntry)
628 entries++;
629
630 if (!entries) return NULL;
631
632 list = ExAllocatePoolWithTag(PagedPool, sizeof(HWND) * (entries + 1), USERTAG_WINDOWLIST); /* alloc one extra for nullterm */
633 if (list)
634 {
635 HWND* cursor = list;
636
637 LIST_FOR_EACH(Current, &Desktop->ShellHookWindows, SHELL_HOOK_WINDOW, ListEntry)
638 *cursor++ = Current->hWnd;
639
640 *cursor = NULL; /* Nullterm list */
641 }
642
643 return list;
644 }
645
646 /*
647 * Send the Message to the windows registered for ShellHook
648 * notifications. The lParam contents depend on the Message. See
649 * MSDN for more details (RegisterShellHookWindow)
650 */
651 VOID co_IntShellHookNotify(WPARAM Message, LPARAM lParam)
652 {
653 PDESKTOP Desktop = IntGetActiveDesktop();
654 HWND* HwndList;
655
656 if (!gpsi->uiShellMsg)
657 {
658 gpsi->uiShellMsg = IntAddAtom(L"SHELLHOOK");
659
660 TRACE("MsgType = %x\n", gpsi->uiShellMsg);
661 if (!gpsi->uiShellMsg)
662 ERR("LastError: %x\n", EngGetLastError());
663 }
664
665 if (!Desktop)
666 {
667 TRACE("IntShellHookNotify: No desktop!\n");
668 return;
669 }
670
671 HwndList = UserBuildShellHookHwndList(Desktop);
672 if (HwndList)
673 {
674 HWND* cursor = HwndList;
675
676 for (; *cursor; cursor++)
677 {
678 TRACE("Sending notify\n");
679 co_IntPostOrSendMessage(*cursor,
680 gpsi->uiShellMsg,
681 Message,
682 lParam);
683 }
684
685 ExFreePool(HwndList);
686 }
687
688 }
689
690 /*
691 * Add the window to the ShellHookWindows list. The windows
692 * on that list get notifications that are important to shell
693 * type applications.
694 *
695 * TODO: Validate the window? I'm not sure if sending these messages to
696 * an unsuspecting application that is not your own is a nice thing to do.
697 */
698 BOOL IntRegisterShellHookWindow(HWND hWnd)
699 {
700 PTHREADINFO pti = PsGetCurrentThreadWin32Thread();
701 PDESKTOP Desktop = pti->rpdesk;
702 PSHELL_HOOK_WINDOW Entry;
703
704 TRACE("IntRegisterShellHookWindow\n");
705
706 /* First deregister the window, so we can be sure it's never twice in the
707 * list.
708 */
709 IntDeRegisterShellHookWindow(hWnd);
710
711 Entry = ExAllocatePoolWithTag(PagedPool,
712 sizeof(SHELL_HOOK_WINDOW),
713 TAG_WINSTA);
714
715 if (!Entry)
716 return FALSE;
717
718 Entry->hWnd = hWnd;
719
720 InsertTailList(&Desktop->ShellHookWindows, &Entry->ListEntry);
721
722 return TRUE;
723 }
724
725 /*
726 * Remove the window from the ShellHookWindows list. The windows
727 * on that list get notifications that are important to shell
728 * type applications.
729 */
730 BOOL IntDeRegisterShellHookWindow(HWND hWnd)
731 {
732 PTHREADINFO pti = PsGetCurrentThreadWin32Thread();
733 PDESKTOP Desktop = pti->rpdesk;
734 PSHELL_HOOK_WINDOW Current;
735
736 LIST_FOR_EACH(Current, &Desktop->ShellHookWindows, SHELL_HOOK_WINDOW, ListEntry)
737 {
738 if (Current->hWnd == hWnd)
739 {
740 RemoveEntryList(&Current->ListEntry);
741 ExFreePool(Current);
742 return TRUE;
743 }
744 }
745
746 return FALSE;
747 }
748
749 static VOID
750 IntFreeDesktopHeap(IN OUT PDESKTOP Desktop)
751 {
752 if (Desktop->hsectionDesktop != NULL)
753 {
754 ObDereferenceObject(Desktop->hsectionDesktop);
755 Desktop->hsectionDesktop = NULL;
756 }
757 }
758
759 BOOL FASTCALL
760 IntPaintDesktop(HDC hDC)
761 {
762 RECTL Rect;
763 HBRUSH DesktopBrush, PreviousBrush;
764 HWND hWndDesktop;
765 BOOL doPatBlt = TRUE;
766 PWND WndDesktop;
767 static WCHAR s_wszSafeMode[] = L"Safe Mode";
768 int len;
769 COLORREF color_old;
770 UINT align_old;
771 int mode_old;
772
773 GdiGetClipBox(hDC, &Rect);
774
775 hWndDesktop = IntGetDesktopWindow(); // rpdesk->DesktopWindow;
776
777 WndDesktop = UserGetWindowObject(hWndDesktop); // rpdesk->pDeskInfo->spwnd;
778 if (!WndDesktop)
779 {
780 return FALSE;
781 }
782
783 if (!UserGetSystemMetrics(SM_CLEANBOOT))
784 {
785 DesktopBrush = (HBRUSH)WndDesktop->pcls->hbrBackground;
786
787 /*
788 * Paint desktop background
789 */
790 if (gspv.hbmWallpaper != NULL)
791 {
792 SIZE sz;
793 int x, y;
794 HDC hWallpaperDC;
795
796 sz.cx = WndDesktop->rcWindow.right - WndDesktop->rcWindow.left;
797 sz.cy = WndDesktop->rcWindow.bottom - WndDesktop->rcWindow.top;
798
799 if (gspv.WallpaperMode == wmStretch ||
800 gspv.WallpaperMode == wmTile)
801 {
802 x = 0;
803 y = 0;
804 }
805 else
806 {
807 /* Find the upper left corner, can be negtive if the bitmap is bigger then the screen */
808 x = (sz.cx / 2) - (gspv.cxWallpaper / 2);
809 y = (sz.cy / 2) - (gspv.cyWallpaper / 2);
810 }
811
812 hWallpaperDC = NtGdiCreateCompatibleDC(hDC);
813 if(hWallpaperDC != NULL)
814 {
815 HBITMAP hOldBitmap;
816
817 /* Fill in the area that the bitmap is not going to cover */
818 if (x > 0 || y > 0)
819 {
820 /* FIXME: Clip out the bitmap
821 can be replaced with "NtGdiPatBlt(hDC, x, y, WinSta->cxWallpaper, WinSta->cyWallpaper, PATCOPY | DSTINVERT);"
822 once we support DSTINVERT */
823 PreviousBrush = NtGdiSelectBrush(hDC, DesktopBrush);
824 NtGdiPatBlt(hDC, Rect.left, Rect.top, Rect.right, Rect.bottom, PATCOPY);
825 NtGdiSelectBrush(hDC, PreviousBrush);
826 }
827
828 /*Do not fill the background after it is painted no matter the size of the picture */
829 doPatBlt = FALSE;
830
831 hOldBitmap = NtGdiSelectBitmap(hWallpaperDC, gspv.hbmWallpaper);
832
833 if (gspv.WallpaperMode == wmStretch)
834 {
835 if(Rect.right && Rect.bottom)
836 NtGdiStretchBlt(hDC,
837 x,
838 y,
839 sz.cx,
840 sz.cy,
841 hWallpaperDC,
842 0,
843 0,
844 gspv.cxWallpaper,
845 gspv.cyWallpaper,
846 SRCCOPY,
847 0);
848
849 }
850 else if (gspv.WallpaperMode == wmTile)
851 {
852 /* Paint the bitmap across the screen then down */
853 for(y = 0; y < Rect.bottom; y += gspv.cyWallpaper)
854 {
855 for(x = 0; x < Rect.right; x += gspv.cxWallpaper)
856 {
857 NtGdiBitBlt(hDC,
858 x,
859 y,
860 gspv.cxWallpaper,
861 gspv.cyWallpaper,
862 hWallpaperDC,
863 0,
864 0,
865 SRCCOPY,
866 0,
867 0);
868 }
869 }
870 }
871 else
872 {
873 NtGdiBitBlt(hDC,
874 x,
875 y,
876 gspv.cxWallpaper,
877 gspv.cyWallpaper,
878 hWallpaperDC,
879 0,
880 0,
881 SRCCOPY,
882 0,
883 0);
884 }
885 NtGdiSelectBitmap(hWallpaperDC, hOldBitmap);
886 NtGdiDeleteObjectApp(hWallpaperDC);
887 }
888 }
889 }
890 else
891 {
892 /* Black desktop background in Safe Mode */
893 DesktopBrush = StockObjects[BLACK_BRUSH];
894 }
895
896 /* Back ground is set to none, clear the screen */
897 if (doPatBlt)
898 {
899 PreviousBrush = NtGdiSelectBrush(hDC, DesktopBrush);
900 NtGdiPatBlt(hDC, Rect.left, Rect.top, Rect.right, Rect.bottom, PATCOPY);
901 NtGdiSelectBrush(hDC, PreviousBrush);
902 }
903
904 /*
905 * Display system version on the desktop background
906 */
907
908 if (g_PaintDesktopVersion||UserGetSystemMetrics(SM_CLEANBOOT))
909 {
910 static WCHAR s_wszVersion[256] = {0};
911 RECTL rect;
912
913 if (*s_wszVersion)
914 {
915 len = wcslen(s_wszVersion);
916 }
917 else
918 {
919 len = GetSystemVersionString(s_wszVersion);
920 }
921
922 if (len)
923 {
924 if (!UserSystemParametersInfo(SPI_GETWORKAREA, 0, &rect, 0))
925 {
926 rect.right = UserGetSystemMetrics(SM_CXSCREEN);
927 rect.bottom = UserGetSystemMetrics(SM_CYSCREEN);
928 }
929
930 color_old = IntGdiSetTextColor(hDC, RGB(255,255,255));
931 align_old = IntGdiSetTextAlign(hDC, TA_RIGHT);
932 mode_old = IntGdiSetBkMode(hDC, TRANSPARENT);
933
934 if(!UserGetSystemMetrics(SM_CLEANBOOT))
935 {
936 GreExtTextOutW(hDC, rect.right-16, rect.bottom-48, 0, NULL, s_wszVersion, len, NULL, 0);
937 }
938 else
939 {
940 /* Safe Mode */
941 /* Version information text in top center */
942 IntGdiSetTextAlign(hDC, TA_CENTER|TA_TOP);
943 GreExtTextOutW(hDC, (rect.right+rect.left)/2, rect.top, 0, NULL, s_wszVersion, len, NULL, 0);
944 /* Safe Mode text in corners */
945 len = wcslen(s_wszSafeMode);
946 IntGdiSetTextAlign(hDC, TA_RIGHT|TA_TOP);
947 GreExtTextOutW(hDC, rect.right, rect.top, 0, NULL, s_wszSafeMode, len, NULL, 0);
948 IntGdiSetTextAlign(hDC, TA_RIGHT|TA_BASELINE);
949 GreExtTextOutW(hDC, rect.right, rect.bottom, 0, NULL, s_wszSafeMode, len, NULL, 0);
950 IntGdiSetTextAlign(hDC, TA_LEFT|TA_TOP);
951 GreExtTextOutW(hDC, rect.left, rect.top, 0, NULL, s_wszSafeMode, len, NULL, 0);
952 IntGdiSetTextAlign(hDC, TA_LEFT|TA_BASELINE);
953 GreExtTextOutW(hDC, rect.left, rect.bottom, 0, NULL, s_wszSafeMode, len, NULL, 0);
954
955 }
956
957
958 IntGdiSetBkMode(hDC, mode_old);
959 IntGdiSetTextAlign(hDC, align_old);
960 IntGdiSetTextColor(hDC, color_old);
961 }
962 }
963
964 return TRUE;
965 }
966
967
968 /* SYSCALLS *******************************************************************/
969
970 /*
971 * NtUserCreateDesktop
972 *
973 * Creates a new desktop.
974 *
975 * Parameters
976 * poaAttribs
977 * Object Attributes.
978 *
979 * lpszDesktopDevice
980 * Name of the device.
981 *
982 * pDeviceMode
983 * Device Mode.
984 *
985 * dwFlags
986 * Interaction flags.
987 *
988 * dwDesiredAccess
989 * Requested type of access.
990 *
991 *
992 * Return Value
993 * If the function succeeds, the return value is a handle to the newly
994 * created desktop. If the specified desktop already exists, the function
995 * succeeds and returns a handle to the existing desktop. When you are
996 * finished using the handle, call the CloseDesktop function to close it.
997 * If the function fails, the return value is NULL.
998 *
999 * Status
1000 * @implemented
1001 */
1002
1003 HDESK APIENTRY
1004 NtUserCreateDesktop(
1005 POBJECT_ATTRIBUTES ObjectAttributes,
1006 PUNICODE_STRING lpszDesktopDevice,
1007 LPDEVMODEW lpdmw,
1008 DWORD dwFlags,
1009 ACCESS_MASK dwDesiredAccess)
1010 {
1011 PDESKTOP DesktopObject;
1012 UNICODE_STRING DesktopName;
1013 NTSTATUS Status = STATUS_SUCCESS;
1014 HDESK Desktop;
1015 CSR_API_MESSAGE Request;
1016 PVOID DesktopHeapSystemBase = NULL;
1017 SIZE_T DesktopInfoSize;
1018 BOOLEAN Context;
1019 ULONG_PTR HeapSize = 4 * 1024 * 1024; /* FIXME */
1020 UNICODE_STRING ClassName;
1021 LARGE_STRING WindowName;
1022 BOOL NoHooks = FALSE;
1023 PWND pWnd = NULL;
1024 CREATESTRUCTW Cs;
1025 INT i;
1026 PTHREADINFO ptiCurrent;
1027 DECLARE_RETURN(HDESK);
1028
1029 TRACE("Enter NtUserCreateDesktop\n");
1030 UserEnterExclusive();
1031
1032 ptiCurrent = PsGetCurrentThreadWin32Thread();
1033 if (ptiCurrent)
1034 {
1035 /* Turn off hooks when calling any CreateWindowEx from inside win32k. */
1036 NoHooks = (ptiCurrent->TIF_flags & TIF_DISABLEHOOKS);
1037 ptiCurrent->TIF_flags |= TIF_DISABLEHOOKS;
1038 ptiCurrent->pClientInfo->dwTIFlags = ptiCurrent->TIF_flags;
1039 }
1040 DesktopName.Buffer = NULL;
1041
1042 /*
1043 * Try to open already existing desktop
1044 */
1045
1046 Status = ObOpenObjectByName(
1047 ObjectAttributes,
1048 ExDesktopObjectType,
1049 UserMode,
1050 NULL,
1051 dwDesiredAccess,
1052 (PVOID)&Context,
1053 (HANDLE*)&Desktop);
1054 if (!NT_SUCCESS(Status)) RETURN(NULL);
1055
1056 /* In case the object was not created (eg if it existed), return now */
1057 if (Context == FALSE)
1058 {
1059 TRACE("NtUserCreateDesktop opened desktop %wZ\n", ObjectAttributes->ObjectName);
1060 RETURN( Desktop);
1061 }
1062
1063 /* Capture desktop name */
1064 _SEH2_TRY
1065 {
1066 ProbeForRead( ObjectAttributes, sizeof(OBJECT_ATTRIBUTES), 1);
1067
1068 Status = IntSafeCopyUnicodeStringTerminateNULL(&DesktopName, ObjectAttributes->ObjectName);
1069 }
1070 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1071 {
1072 Status = _SEH2_GetExceptionCode();
1073 }
1074 _SEH2_END
1075
1076 if (! NT_SUCCESS(Status))
1077 {
1078 ERR("Failed reading Object Attributes from user space.\n");
1079 SetLastNtError(Status);
1080 RETURN( NULL);
1081 }
1082
1083 /* Reference the desktop */
1084 Status = ObReferenceObjectByHandle(Desktop,
1085 0,
1086 ExDesktopObjectType,
1087 KernelMode,
1088 (PVOID)&DesktopObject,
1089 NULL);
1090 if (!NT_SUCCESS(Status))
1091 {
1092 ERR("Failed to reference desktop object\n");
1093 RETURN(NULL);
1094 }
1095
1096 TRACE("NtUserCreateDesktop created desktop 0x%x with name %wZ\n", DesktopObject, &DesktopName);
1097
1098 DesktopObject->hsectionDesktop = NULL;
1099 DesktopObject->pheapDesktop = UserCreateHeap(&DesktopObject->hsectionDesktop,
1100 &DesktopHeapSystemBase,
1101 HeapSize);
1102 if (DesktopObject->pheapDesktop == NULL)
1103 {
1104 ObDereferenceObject(DesktopObject);
1105 ERR("Failed to create desktop heap!\n");
1106 RETURN(NULL);
1107 }
1108
1109 DesktopInfoSize = sizeof(DESKTOPINFO) + DesktopName.Length + sizeof(WCHAR);
1110
1111 DesktopObject->pDeskInfo = RtlAllocateHeap(DesktopObject->pheapDesktop,
1112 HEAP_NO_SERIALIZE,
1113 DesktopInfoSize);
1114
1115 if (DesktopObject->pDeskInfo == NULL)
1116 {
1117 ObDereferenceObject(DesktopObject);
1118 ERR("Failed to create the DESKTOP structure!\n");
1119 RETURN(NULL);
1120 }
1121
1122 RtlZeroMemory(DesktopObject->pDeskInfo,
1123 DesktopInfoSize);
1124
1125 DesktopObject->pDeskInfo->pvDesktopBase = DesktopHeapSystemBase;
1126 DesktopObject->pDeskInfo->pvDesktopLimit = (PVOID)((ULONG_PTR)DesktopHeapSystemBase + HeapSize);
1127 RtlCopyMemory(DesktopObject->pDeskInfo->szDesktopName,
1128 DesktopName.Buffer,
1129 DesktopName.Length + sizeof(WCHAR));
1130
1131 /* Initialize some local (to win32k) desktop state. */
1132 InitializeListHead(&DesktopObject->PtiList);
1133 DesktopObject->ActiveMessageQueue = NULL;
1134
1135 /* Setup Global Hooks. */
1136 for (i = 0; i < NB_HOOKS; i++)
1137 {
1138 InitializeListHead(&DesktopObject->pDeskInfo->aphkStart[i]);
1139 }
1140 //#if 0
1141 /*
1142 * Create a handle for CSRSS and notify CSRSS for Creating Desktop Background Window.
1143 */
1144 Request.Type = MAKE_CSR_API(CREATE_DESKTOP, CSR_GUI);
1145 Status = CsrInsertObject(Desktop,
1146 GENERIC_ALL,
1147 (HANDLE*)&Request.Data.CreateDesktopRequest.DesktopHandle);
1148 if (! NT_SUCCESS(Status))
1149 {
1150 ERR("Failed to create desktop handle for CSRSS\n");
1151 ZwClose(Desktop);
1152 SetLastNtError(Status);
1153 RETURN( NULL);
1154 }
1155
1156 Status = co_CsrNotify(&Request);
1157 if (! NT_SUCCESS(Status))
1158 {
1159 CsrCloseHandle(Request.Data.CreateDesktopRequest.DesktopHandle);
1160 ERR("Failed to notify CSRSS about new desktop\n");
1161 ZwClose(Desktop);
1162 SetLastNtError(Status);
1163 RETURN( NULL);
1164 }
1165 //#endif
1166 if (!ptiCurrent->rpdesk) IntSetThreadDesktop(Desktop,FALSE);
1167
1168 /*
1169 Based on wine/server/window.c in get_desktop_window.
1170 */
1171
1172 #if 0 // DESKTOPWNDPROC from Revision 6908 Dedicated to GvG!
1173 // Turn on when server side proc is ready.
1174 //
1175 // Create desktop window.
1176 //
1177 /*
1178 Currently this works but it hangs in
1179 co_IntShowDesktop->co_UserRedrawWindow->co_IntPaintWindows->co_IntSendMessage
1180 Commenting out co_UserRedrawWindow will allow it to run with gui issues.
1181 */
1182 ClassName.Buffer = ((PWSTR)((ULONG_PTR)(WORD)(gpsi->atomSysClass[ICLS_DESKTOP])));
1183 ClassName.Length = 0;
1184 RtlZeroMemory(&WindowName, sizeof(WindowName));
1185
1186 RtlZeroMemory(&Cs, sizeof(Cs));
1187 Cs.x = UserGetSystemMetrics(SM_XVIRTUALSCREEN);
1188 Cs.y = UserGetSystemMetrics(SM_YVIRTUALSCREEN);
1189 Cs.cx = UserGetSystemMetrics(SM_CXVIRTUALSCREEN);
1190 Cs.cy = UserGetSystemMetrics(SM_CYVIRTUALSCREEN);
1191 Cs.style = WS_POPUP|WS_CLIPCHILDREN;
1192 Cs.hInstance = hModClient; // Experimental mode... hModuleWin; // Server side winproc!
1193 Cs.lpszName = (LPCWSTR) &WindowName;
1194 Cs.lpszClass = (LPCWSTR) &ClassName;
1195
1196 pWnd = co_UserCreateWindowEx(&Cs, &ClassName, &WindowName, NULL);
1197 if (!pWnd)
1198 {
1199 ERR("Failed to create Desktop window handle\n");
1200 }
1201 else
1202 { // Should be set in window.c, Justin Case,
1203 if (!DesktopObject->DesktopWindow)
1204 {
1205 DesktopObject->pDeskInfo->spwnd = pWnd;
1206 DesktopObject->DesktopWindow = UserHMGetHandle(pWnd);
1207 }
1208 }
1209 #endif
1210 ClassName.Buffer = ((PWSTR)((ULONG_PTR)(WORD)(gpsi->atomSysClass[ICLS_HWNDMESSAGE])));
1211 ClassName.Length = 0;
1212 RtlZeroMemory(&WindowName, sizeof(WindowName));
1213
1214 RtlZeroMemory(&Cs, sizeof(Cs));
1215 Cs.cx = Cs.cy = 100;
1216 Cs.style = WS_POPUP|WS_CLIPCHILDREN;
1217 Cs.hInstance = hModClient; // hModuleWin; // Server side winproc! Leave it to Timo to not pass on notes!
1218 Cs.lpszName = (LPCWSTR) &WindowName;
1219 Cs.lpszClass = (LPCWSTR) &ClassName;
1220
1221 pWnd = co_UserCreateWindowEx(&Cs, &ClassName, &WindowName, NULL);
1222 if (!pWnd)
1223 {
1224 ERR("Failed to create Message window handle\n");
1225 }
1226 else
1227 {
1228 DesktopObject->spwndMessage = pWnd;
1229 }
1230
1231 /* Now,,,
1232 if !(WinStaObject->Flags & WSF_NOIO) is (not set) for desktop input output mode (see wiki)
1233 Create Tooltip. Saved in DesktopObject->spwndTooltip.
1234 Tooltip dwExStyle: WS_EX_TOOLWINDOW|WS_EX_TOPMOST
1235 hWndParent are spwndMessage. Use hModuleWin for server side winproc!
1236 The rest is same as message window.
1237 http://msdn.microsoft.com/en-us/library/bb760250(VS.85).aspx
1238 */
1239 RETURN( Desktop);
1240
1241 CLEANUP:
1242 if(DesktopName.Buffer != NULL)
1243 {
1244 ExFreePoolWithTag(DesktopName.Buffer, TAG_STRING);
1245 }
1246 if (!NoHooks && ptiCurrent)
1247 {
1248 ptiCurrent->TIF_flags &= ~TIF_DISABLEHOOKS;
1249 ptiCurrent->pClientInfo->dwTIFlags = ptiCurrent->TIF_flags;
1250 }
1251 TRACE("Leave NtUserCreateDesktop, ret=%i\n",_ret_);
1252 UserLeave();
1253 END_CLEANUP;
1254 }
1255
1256 /*
1257 * NtUserOpenDesktop
1258 *
1259 * Opens an existing desktop.
1260 *
1261 * Parameters
1262 * lpszDesktopName
1263 * Name of the existing desktop.
1264 *
1265 * dwFlags
1266 * Interaction flags.
1267 *
1268 * dwDesiredAccess
1269 * Requested type of access.
1270 *
1271 * Return Value
1272 * Handle to the desktop or zero on failure.
1273 *
1274 * Status
1275 * @implemented
1276 */
1277
1278 HDESK APIENTRY
1279 NtUserOpenDesktop(
1280 POBJECT_ATTRIBUTES ObjectAttributes,
1281 DWORD dwFlags,
1282 ACCESS_MASK dwDesiredAccess)
1283 {
1284 NTSTATUS Status;
1285 HDESK Desktop;
1286
1287 Status = ObOpenObjectByName(
1288 ObjectAttributes,
1289 ExDesktopObjectType,
1290 UserMode,
1291 NULL,
1292 dwDesiredAccess,
1293 NULL,
1294 (HANDLE*)&Desktop);
1295
1296 if (!NT_SUCCESS(Status))
1297 {
1298 ERR("Failed to open desktop\n");
1299 SetLastNtError(Status);
1300 return 0;
1301 }
1302
1303 TRACE("Opened desktop %S with handle 0x%x\n", ObjectAttributes->ObjectName->Buffer, Desktop);
1304
1305 return Desktop;
1306 }
1307
1308 /*
1309 * NtUserOpenInputDesktop
1310 *
1311 * Opens the input (interactive) desktop.
1312 *
1313 * Parameters
1314 * dwFlags
1315 * Interaction flags.
1316 *
1317 * fInherit
1318 * Inheritance option.
1319 *
1320 * dwDesiredAccess
1321 * Requested type of access.
1322 *
1323 * Return Value
1324 * Handle to the input desktop or zero on failure.
1325 *
1326 * Status
1327 * @implemented
1328 */
1329
1330 HDESK APIENTRY
1331 NtUserOpenInputDesktop(
1332 DWORD dwFlags,
1333 BOOL fInherit,
1334 ACCESS_MASK dwDesiredAccess)
1335 {
1336 PDESKTOP pdesk;
1337 NTSTATUS Status;
1338 HDESK hdesk;
1339
1340 TRACE("Enter NtUserOpenInputDesktop\n");
1341
1342 /* Get a pointer to the desktop object */
1343 Status = IntValidateDesktopHandle(InputDesktopHandle, UserMode, 0, &pdesk);
1344 if (!NT_SUCCESS(Status))
1345 {
1346 TRACE("Validation of input desktop handle (0x%X) failed\n", InputDesktop);
1347 return NULL;
1348 }
1349
1350 /* Create a new handle to the object */
1351 Status = ObOpenObjectByPointer(
1352 pdesk,
1353 0,
1354 NULL,
1355 dwDesiredAccess,
1356 ExDesktopObjectType,
1357 UserMode,
1358 (PHANDLE)&hdesk);
1359
1360 ObDereferenceObject(pdesk);
1361
1362 if (!NT_SUCCESS(Status))
1363 {
1364 ERR("Failed to open input desktop object\n");
1365 SetLastNtError(Status);
1366 return NULL;
1367 }
1368
1369 TRACE("NtUserOpenInputDesktop returning 0x%x\n",hdesk);
1370 return hdesk;
1371 }
1372
1373 /*
1374 * NtUserCloseDesktop
1375 *
1376 * Closes a desktop handle.
1377 *
1378 * Parameters
1379 * hDesktop
1380 * Handle to the desktop.
1381 *
1382 * Return Value
1383 * Status
1384 *
1385 * Remarks
1386 * The desktop handle can be created with NtUserCreateDesktop or
1387 * NtUserOpenDesktop. This function will fail if any thread in the calling
1388 * process is using the specified desktop handle or if the handle refers
1389 * to the initial desktop of the calling process.
1390 *
1391 * Status
1392 * @implemented
1393 */
1394
1395 BOOL APIENTRY
1396 NtUserCloseDesktop(HDESK hDesktop)
1397 {
1398 PDESKTOP pdesk;
1399 NTSTATUS Status;
1400 PTHREADINFO pti;
1401 DECLARE_RETURN(BOOL);
1402
1403 pti = PsGetCurrentThreadWin32Thread();
1404
1405 TRACE("NtUserCloseDesktop called (0x%x)\n", hDesktop);
1406 UserEnterExclusive();
1407
1408 if( hDesktop == pti->hdesk || hDesktop == pti->ppi->hdeskStartup)
1409 {
1410 ERR("Attempted to close thread desktop\n");
1411 EngSetLastError(ERROR_BUSY);
1412 RETURN(FALSE);
1413 }
1414
1415 Status = IntValidateDesktopHandle( hDesktop, UserMode, 0, &pdesk);
1416 if (!NT_SUCCESS(Status))
1417 {
1418 ERR("Validation of desktop handle (0x%X) failed\n", hDesktop);
1419 RETURN(FALSE);
1420 }
1421
1422 ObDereferenceObject(pdesk);
1423
1424 Status = ZwClose(hDesktop);
1425 if (!NT_SUCCESS(Status))
1426 {
1427 ERR("Failed to close desktop handle 0x%x\n", hDesktop);
1428 SetLastNtError(Status);
1429 RETURN(FALSE);
1430 }
1431
1432 RETURN(TRUE);
1433
1434 CLEANUP:
1435 TRACE("Leave NtUserCloseDesktop, ret=%i\n",_ret_);
1436 UserLeave();
1437 END_CLEANUP;
1438 }
1439
1440 /*
1441 * NtUserPaintDesktop
1442 *
1443 * The NtUserPaintDesktop function fills the clipping region in the
1444 * specified device context with the desktop pattern or wallpaper. The
1445 * function is provided primarily for shell desktops.
1446 *
1447 * Parameters
1448 * hDC
1449 * Handle to the device context.
1450 *
1451 * Status
1452 * @implemented
1453 */
1454
1455 BOOL APIENTRY
1456 NtUserPaintDesktop(HDC hDC)
1457 {
1458 BOOL Ret;
1459 UserEnterExclusive();
1460 TRACE("Enter NtUserPaintDesktop\n");
1461 Ret = IntPaintDesktop(hDC);
1462 TRACE("Leave NtUserPaintDesktop, ret=%i\n",Ret);
1463 UserLeave();
1464 return Ret;
1465 }
1466
1467 /*
1468 * NtUserSwitchDesktop
1469 *
1470 * Sets the current input (interactive) desktop.
1471 *
1472 * Parameters
1473 * hDesktop
1474 * Handle to desktop.
1475 *
1476 * Return Value
1477 * Status
1478 *
1479 * Status
1480 * @unimplemented
1481 */
1482
1483 BOOL APIENTRY
1484 NtUserSwitchDesktop(HDESK hdesk)
1485 {
1486 PDESKTOP pdesk;
1487 NTSTATUS Status;
1488 DECLARE_RETURN(BOOL);
1489
1490 UserEnterExclusive();
1491 TRACE("Enter NtUserSwitchDesktop(0x%x)\n", hdesk);
1492
1493 Status = IntValidateDesktopHandle( hdesk, UserMode, 0, &pdesk);
1494 if (!NT_SUCCESS(Status))
1495 {
1496 ERR("Validation of desktop handle (0x%X) failed\n", hdesk);
1497 RETURN(FALSE);
1498 }
1499
1500 /*
1501 * Don't allow applications switch the desktop if it's locked, unless the caller
1502 * is the logon application itself
1503 */
1504 if((pdesk->rpwinstaParent->Flags & WSS_LOCKED) &&
1505 LogonProcess != PsGetCurrentProcessWin32Process())
1506 {
1507 ObDereferenceObject(pdesk);
1508 ERR("Switching desktop 0x%x denied because the window station is locked!\n", hdesk);
1509 RETURN(FALSE);
1510 }
1511
1512 if(pdesk->rpwinstaParent != InputWindowStation)
1513 {
1514 ObDereferenceObject(pdesk);
1515 ERR("Switching desktop 0x%x denied because desktop doesn't belong to the interactive winsta!\n", hdesk);
1516 RETURN(FALSE);
1517 }
1518
1519 /* FIXME: Fail if the process is associated with a secured
1520 desktop such as Winlogon or Screen-Saver */
1521 /* FIXME: Connect to input device */
1522
1523 /* Set the active desktop in the desktop's window station. */
1524 InputWindowStation->ActiveDesktop = pdesk;
1525
1526 /* Set the global state. */
1527 InputDesktop = pdesk;
1528 InputDesktopHandle = hdesk;
1529
1530 ObDereferenceObject(pdesk);
1531
1532 RETURN(TRUE);
1533
1534 CLEANUP:
1535 TRACE("Leave NtUserSwitchDesktop, ret=%i\n",_ret_);
1536 UserLeave();
1537 END_CLEANUP;
1538 }
1539
1540 /*
1541 * NtUserGetThreadDesktop
1542 *
1543 * Status
1544 * @implemented
1545 */
1546
1547 HDESK APIENTRY
1548 NtUserGetThreadDesktop(DWORD dwThreadId, DWORD Unknown1)
1549 {
1550 NTSTATUS Status;
1551 PETHREAD Thread;
1552 PDESKTOP DesktopObject;
1553 HDESK Ret, hThreadDesktop;
1554 OBJECT_HANDLE_INFORMATION HandleInformation;
1555 DECLARE_RETURN(HDESK);
1556
1557 UserEnterExclusive();
1558 TRACE("Enter NtUserGetThreadDesktop\n");
1559
1560 if(!dwThreadId)
1561 {
1562 EngSetLastError(ERROR_INVALID_PARAMETER);
1563 RETURN(0);
1564 }
1565
1566 Status = PsLookupThreadByThreadId((HANDLE)(DWORD_PTR)dwThreadId, &Thread);
1567 if(!NT_SUCCESS(Status))
1568 {
1569 EngSetLastError(ERROR_INVALID_PARAMETER);
1570 RETURN(0);
1571 }
1572
1573 if(Thread->ThreadsProcess == PsGetCurrentProcess())
1574 {
1575 /* Just return the handle, we queried the desktop handle of a thread running
1576 in the same context */
1577 Ret = ((PTHREADINFO)Thread->Tcb.Win32Thread)->hdesk;
1578 ObDereferenceObject(Thread);
1579 RETURN(Ret);
1580 }
1581
1582 /* Get the desktop handle and the desktop of the thread */
1583 if(!(hThreadDesktop = ((PTHREADINFO)Thread->Tcb.Win32Thread)->hdesk) ||
1584 !(DesktopObject = ((PTHREADINFO)Thread->Tcb.Win32Thread)->rpdesk))
1585 {
1586 ObDereferenceObject(Thread);
1587 ERR("Desktop information of thread 0x%x broken!?\n", dwThreadId);
1588 RETURN(NULL);
1589 }
1590
1591 /* We could just use DesktopObject instead of looking up the handle, but latter
1592 may be a bit safer (e.g. when the desktop is being destroyed */
1593 /* Switch into the context of the thread we're trying to get the desktop from,
1594 so we can use the handle */
1595 KeAttachProcess(&Thread->ThreadsProcess->Pcb);
1596 Status = ObReferenceObjectByHandle(hThreadDesktop,
1597 GENERIC_ALL,
1598 ExDesktopObjectType,
1599 UserMode,
1600 (PVOID*)&DesktopObject,
1601 &HandleInformation);
1602 KeDetachProcess();
1603
1604 /* The handle couldn't be found, there's nothing to get... */
1605 if(!NT_SUCCESS(Status))
1606 {
1607 ObDereferenceObject(Thread);
1608 RETURN(NULL);
1609 }
1610
1611 /* Lookup our handle table if we can find a handle to the desktop object,
1612 if not, create one */
1613 Ret = IntGetDesktopObjectHandle(DesktopObject);
1614
1615 /* All done, we got a valid handle to the desktop */
1616 ObDereferenceObject(DesktopObject);
1617 ObDereferenceObject(Thread);
1618 RETURN(Ret);
1619
1620 CLEANUP:
1621 TRACE("Leave NtUserGetThreadDesktop, ret=%i\n",_ret_);
1622 UserLeave();
1623 END_CLEANUP;
1624 }
1625
1626 static NTSTATUS
1627 IntUnmapDesktopView(IN PDESKTOP pdesk)
1628 {
1629 PPROCESSINFO ppi;
1630 PW32HEAP_USER_MAPPING HeapMapping, *PrevLink;
1631 NTSTATUS Status = STATUS_SUCCESS;
1632
1633 TRACE("IntUnmapDesktopView called for desktop object %p\n", pdesk);
1634
1635 ppi = PsGetCurrentProcessWin32Process();
1636 PrevLink = &ppi->HeapMappings.Next;
1637
1638 /* Unmap if we're the last thread using the desktop */
1639 HeapMapping = *PrevLink;
1640 while (HeapMapping != NULL)
1641 {
1642 if (HeapMapping->KernelMapping == (PVOID)pdesk->pheapDesktop)
1643 {
1644 if (--HeapMapping->Count == 0)
1645 {
1646 *PrevLink = HeapMapping->Next;
1647
1648 TRACE("ppi 0x%x unmapped heap of desktop 0x%x\n", ppi, pdesk);
1649 Status = MmUnmapViewOfSection(PsGetCurrentProcess(),
1650 HeapMapping->UserMapping);
1651
1652 ObDereferenceObject(pdesk);
1653
1654 UserHeapFree(HeapMapping);
1655 break;
1656 }
1657 }
1658
1659 PrevLink = &HeapMapping->Next;
1660 HeapMapping = HeapMapping->Next;
1661 }
1662
1663 return Status;
1664 }
1665
1666 static NTSTATUS
1667 IntMapDesktopView(IN PDESKTOP pdesk)
1668 {
1669 PPROCESSINFO ppi;
1670 PW32HEAP_USER_MAPPING HeapMapping, *PrevLink;
1671 PVOID UserBase = NULL;
1672 SIZE_T ViewSize = 0;
1673 LARGE_INTEGER Offset;
1674 NTSTATUS Status;
1675
1676 TRACE("IntMapDesktopView called for desktop object 0x%x\n", pdesk);
1677
1678 ppi = PsGetCurrentProcessWin32Process();
1679 PrevLink = &ppi->HeapMappings.Next;
1680
1681 /* Find out if another thread already mapped the desktop heap */
1682 HeapMapping = *PrevLink;
1683 while (HeapMapping != NULL)
1684 {
1685 if (HeapMapping->KernelMapping == (PVOID)pdesk->pheapDesktop)
1686 {
1687 HeapMapping->Count++;
1688 return STATUS_SUCCESS;
1689 }
1690
1691 PrevLink = &HeapMapping->Next;
1692 HeapMapping = HeapMapping->Next;
1693 }
1694
1695 /* We're the first, map the heap */
1696 Offset.QuadPart = 0;
1697 Status = MmMapViewOfSection(pdesk->hsectionDesktop,
1698 PsGetCurrentProcess(),
1699 &UserBase,
1700 0,
1701 0,
1702 &Offset,
1703 &ViewSize,
1704 ViewUnmap,
1705 SEC_NO_CHANGE,
1706 PAGE_EXECUTE_READ); /* Would prefer PAGE_READONLY, but thanks to RTL heaps... */
1707 if (!NT_SUCCESS(Status))
1708 {
1709 ERR("Failed to map desktop\n");
1710 return Status;
1711 }
1712
1713 TRACE("ppi 0x%x mapped heap of desktop 0x%x\n", ppi, pdesk);
1714
1715 /* Add the mapping */
1716 HeapMapping = UserHeapAlloc(sizeof(W32HEAP_USER_MAPPING));
1717 if (HeapMapping == NULL)
1718 {
1719 MmUnmapViewOfSection(PsGetCurrentProcess(), UserBase);
1720 ERR("UserHeapAlloc() failed!\n");
1721 return STATUS_NO_MEMORY;
1722 }
1723
1724 HeapMapping->Next = NULL;
1725 HeapMapping->KernelMapping = (PVOID)pdesk->pheapDesktop;
1726 HeapMapping->UserMapping = UserBase;
1727 HeapMapping->Limit = ViewSize;
1728 HeapMapping->Count = 1;
1729 *PrevLink = HeapMapping;
1730
1731 ObReferenceObject(pdesk);
1732
1733 return STATUS_SUCCESS;
1734 }
1735
1736 BOOL
1737 IntSetThreadDesktop(IN HDESK hDesktop,
1738 IN BOOL FreeOnFailure)
1739 {
1740 PDESKTOP pdesk = NULL, pdeskOld;
1741 HDESK hdeskOld;
1742 PTHREADINFO pti;
1743 NTSTATUS Status;
1744 PCLIENTTHREADINFO pctiOld, pctiNew = NULL;
1745 PCLIENTINFO pci;
1746
1747 ASSERT(NtCurrentTeb());
1748
1749 TRACE("IntSetThreadDesktop hDesktop:0x%x, FOF:%d\n",hDesktop, FreeOnFailure);
1750
1751 pti = PsGetCurrentThreadWin32Thread();
1752 pci = pti->pClientInfo;
1753
1754 /* If the caller gave us a desktop, ensure it is valid */
1755 if(hDesktop != NULL)
1756 {
1757 /* Validate the new desktop. */
1758 Status = IntValidateDesktopHandle( hDesktop, UserMode, 0, &pdesk);
1759 if (!NT_SUCCESS(Status))
1760 {
1761 ERR("Validation of desktop handle (0x%X) failed\n", hDesktop);
1762 return FALSE;
1763 }
1764
1765 if (pti->rpdesk == pdesk)
1766 {
1767 /* Nothing to do */
1768 ObDereferenceObject(pdesk);
1769 return TRUE;
1770 }
1771 }
1772
1773 /* Make sure that we don't own any window in the current desktop */
1774 if (!IsListEmpty(&pti->WindowListHead))
1775 {
1776 if(pdesk)
1777 ObDereferenceObject(pdesk);
1778 ERR("Attempted to change thread desktop although the thread has windows!\n");
1779 EngSetLastError(ERROR_BUSY);
1780 return FALSE;
1781 }
1782
1783 /* Before doing the switch, map the new desktop heap and allocate the new pcti */
1784 if(pdesk != NULL)
1785 {
1786 Status = IntMapDesktopView(pdesk);
1787 if (!NT_SUCCESS(Status))
1788 {
1789 ERR("Failed to map desktop heap!\n");
1790 ObDereferenceObject(pdesk);
1791 SetLastNtError(Status);
1792 return FALSE;
1793 }
1794
1795 pctiNew = DesktopHeapAlloc( pdesk, sizeof(CLIENTTHREADINFO));
1796 if(pctiNew == NULL)
1797 {
1798 ERR("Failed to allocate new pcti\n");
1799 IntUnmapDesktopView(pdesk);
1800 ObDereferenceObject(pdesk);
1801 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
1802 return FALSE;
1803 }
1804 }
1805
1806 /* free all classes or move them to the shared heap */
1807 if(pti->rpdesk != NULL)
1808 {
1809 if(!IntCheckProcessDesktopClasses(pti->rpdesk, FreeOnFailure))
1810 {
1811 ERR("Failed to move process classes to shared heap!\n");
1812 if(pdesk)
1813 {
1814 DesktopHeapFree(pdesk, pctiNew);
1815 IntUnmapDesktopView(pdesk);
1816 ObDereferenceObject(pdesk);
1817 }
1818 return FALSE;
1819 }
1820 }
1821
1822 pdeskOld = pti->rpdesk;
1823 hdeskOld = pti->hdesk;
1824 if (pti->pcti != &pti->cti)
1825 pctiOld = pti->pcti;
1826 else
1827 pctiOld = NULL;
1828
1829 /* do the switch */
1830 if(pdesk != NULL)
1831 {
1832 pti->rpdesk = pdesk;
1833 pti->hdesk = hDesktop;
1834 pti->pDeskInfo = pti->rpdesk->pDeskInfo;
1835 pti->pcti = pctiNew;
1836
1837 pci->ulClientDelta = DesktopHeapGetUserDelta();
1838 pci->pDeskInfo = (PVOID)((ULONG_PTR)pti->pDeskInfo - pci->ulClientDelta);
1839 pci->pClientThreadInfo = (PVOID)((ULONG_PTR)pti->pcti - pci->ulClientDelta);
1840
1841 /* initialize the new pcti */
1842 if(pctiOld != NULL)
1843 {
1844 RtlCopyMemory(pctiNew, pctiOld, sizeof(CLIENTTHREADINFO));
1845 }
1846 else
1847 {
1848 RtlZeroMemory(pctiNew, sizeof(CLIENTTHREADINFO));
1849 }
1850 }
1851 else
1852 {
1853 pti->rpdesk = NULL;
1854 pti->hdesk = NULL;
1855 pti->pDeskInfo = NULL;
1856 pti->pcti = &pti->cti; // Always point inside so there will be no crash when posting or sending msg's!
1857 pci->ulClientDelta = 0;
1858 pci->pDeskInfo = NULL;
1859 pci->pClientThreadInfo = NULL;
1860 }
1861
1862 /* clean up the old desktop */
1863 if(pdeskOld != NULL)
1864 {
1865 RemoveEntryList(&pti->PtiLink);
1866 if (pctiOld) DesktopHeapFree(pdeskOld, pctiOld);
1867 IntUnmapDesktopView(pdeskOld);
1868 ObDereferenceObject(pdeskOld);
1869 ZwClose(hdeskOld);
1870 }
1871
1872 if(pdesk)
1873 {
1874 InsertTailList(&pdesk->PtiList, &pti->PtiLink);
1875 }
1876
1877 TRACE("IntSetThreadDesktop: pti 0x%x ppi 0x%x switched from object 0x%x to 0x%x\n", pti, pti->ppi, pdeskOld, pdesk);
1878
1879 return TRUE;
1880 }
1881
1882 /*
1883 * NtUserSetThreadDesktop
1884 *
1885 * Status
1886 * @implemented
1887 */
1888
1889 BOOL APIENTRY
1890 NtUserSetThreadDesktop(HDESK hDesktop)
1891 {
1892 BOOL ret = FALSE;
1893
1894 UserEnterExclusive();
1895
1896 // FIXME: IntSetThreadDesktop validates the desktop handle, it should happen
1897 // here too and set the NT error level. Q. Is it necessary to have the validation
1898 // in IntSetThreadDesktop? Is it needed there too?
1899 if (hDesktop || (!hDesktop && CsrProcess == PsGetCurrentProcess()))
1900 ret = IntSetThreadDesktop(hDesktop, FALSE);
1901
1902 UserLeave();
1903
1904 return ret;
1905 }
1906
1907 /* EOF */