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