[WIN32K]: Since I don't have time to properly fix the desktop version painting for...
[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 case WM_SETCURSOR:
668 {
669 PCURICON_OBJECT pcurOld, pcurNew;
670 pcurNew = UserGetCurIconObject(gDesktopCursor);
671 if (!pcurNew)
672 {
673 return TRUE;
674 }
675
676 pcurNew->CURSORF_flags |= CURSORF_CURRENT;
677 pcurOld = UserSetCursor(pcurNew, FALSE);
678 if (pcurOld)
679 {
680 pcurOld->CURSORF_flags &= ~CURSORF_CURRENT;
681 UserDereferenceObject(pcurOld);
682 }
683 return TRUE;
684 }
685
686 case WM_WINDOWPOSCHANGING:
687 {
688 PWINDOWPOS pWindowPos = (PWINDOWPOS)lParam;
689 if((pWindowPos->flags & SWP_SHOWWINDOW) != 0)
690 {
691 HDESK hdesk = IntGetDesktopObjectHandle(gpdeskInputDesktop);
692 IntSetThreadDesktop(hdesk, FALSE);
693 }
694 }
695
696 }
697 return TRUE; /* We are done. Do not do any callbacks to user mode */
698 }
699
700 BOOL FASTCALL
701 UserMessageWindowProc(PWND pwnd, UINT Msg, WPARAM wParam, LPARAM lParam, LRESULT *lResult)
702 {
703 *lResult = 0;
704
705 switch(Msg)
706 {
707 case WM_NCCREATE:
708 pwnd->fnid |= FNID_MESSAGEWND;
709 *lResult = (LRESULT)TRUE;
710 break;
711 case WM_DESTROY:
712 pwnd->fnid |= FNID_DESTROY;
713 break;
714 }
715
716 return TRUE; /* We are done. Do not do any callbacks to user mode */
717 }
718
719 VOID NTAPI DesktopThreadMain()
720 {
721 BOOL Ret;
722 MSG Msg;
723
724 gptiDesktopThread = PsGetCurrentThreadWin32Thread();
725
726 UserEnterExclusive();
727
728 /* Register system classes. This thread does not belong to any desktop so the
729 classes will be allocated from the shared heap */
730 UserRegisterSystemClasses();
731
732 while (TRUE)
733 {
734 Ret = co_IntGetPeekMessage(&Msg, 0, 0, 0, PM_REMOVE, TRUE);
735 if (Ret)
736 {
737 IntDispatchMessage(&Msg);
738 }
739 }
740
741 UserLeave();
742 }
743
744 HDC FASTCALL
745 UserGetDesktopDC(ULONG DcType, BOOL EmptyDC, BOOL ValidatehWnd)
746 {
747 PWND DesktopObject = 0;
748 HDC DesktopHDC = 0;
749
750 if (DcType == DC_TYPE_DIRECT)
751 {
752 DesktopObject = UserGetDesktopWindow();
753 DesktopHDC = (HDC)UserGetWindowDC(DesktopObject);
754 }
755 else
756 {
757 PMONITOR pMonitor = UserGetPrimaryMonitor();
758 DesktopHDC = IntGdiCreateDisplayDC(pMonitor->hDev, DcType, EmptyDC);
759 }
760
761 return DesktopHDC;
762 }
763
764 VOID APIENTRY
765 UserRedrawDesktop()
766 {
767 PWND Window = NULL;
768 PREGION Rgn;
769
770 Window = UserGetDesktopWindow();
771 Rgn = IntSysCreateRectpRgnIndirect(&Window->rcWindow);
772
773 IntInvalidateWindows( Window,
774 Rgn,
775 RDW_FRAME |
776 RDW_ERASE |
777 RDW_INVALIDATE |
778 RDW_ALLCHILDREN);
779
780 REGION_Delete(Rgn);
781 }
782
783
784 NTSTATUS FASTCALL
785 co_IntShowDesktop(PDESKTOP Desktop, ULONG Width, ULONG Height, BOOL bRedraw)
786 {
787 PWND pwnd = Desktop->pDeskInfo->spwnd;
788 UINT flags = SWP_NOACTIVATE|SWP_NOZORDER|SWP_SHOWWINDOW;
789 ASSERT(pwnd);
790
791 if(!bRedraw)
792 flags |= SWP_NOREDRAW;
793
794 co_WinPosSetWindowPos(pwnd, NULL, 0, 0, Width, Height, flags);
795
796 if(bRedraw)
797 co_UserRedrawWindow( pwnd, NULL, 0, RDW_UPDATENOW | RDW_ALLCHILDREN | RDW_INVALIDATE );
798
799 return STATUS_SUCCESS;
800 }
801
802 NTSTATUS FASTCALL
803 IntHideDesktop(PDESKTOP Desktop)
804 {
805 PWND DesktopWnd;
806
807 DesktopWnd = IntGetWindowObject(Desktop->DesktopWindow);
808 if (! DesktopWnd)
809 {
810 return ERROR_INVALID_WINDOW_HANDLE;
811 }
812 DesktopWnd->style &= ~WS_VISIBLE;
813
814 return STATUS_SUCCESS;
815 }
816
817 static
818 HWND* FASTCALL
819 UserBuildShellHookHwndList(PDESKTOP Desktop)
820 {
821 ULONG entries=0;
822 PLIST_ENTRY ListEntry;
823 PSHELL_HOOK_WINDOW Current;
824 HWND* list;
825
826 /* FIXME: If we save nb elements in desktop, we dont have to loop to find nb entries */
827 ListEntry = Desktop->ShellHookWindows.Flink;
828 while (ListEntry != &Desktop->ShellHookWindows)
829 {
830 ListEntry = ListEntry->Flink;
831 entries++;
832 }
833
834 if (!entries) return NULL;
835
836 list = ExAllocatePoolWithTag(PagedPool, sizeof(HWND) * (entries + 1), USERTAG_WINDOWLIST); /* alloc one extra for nullterm */
837 if (list)
838 {
839 HWND* cursor = list;
840
841 ListEntry = Desktop->ShellHookWindows.Flink;
842 while (ListEntry != &Desktop->ShellHookWindows)
843 {
844 Current = CONTAINING_RECORD(ListEntry, SHELL_HOOK_WINDOW, ListEntry);
845 ListEntry = ListEntry->Flink;
846 *cursor++ = Current->hWnd;
847 }
848
849 *cursor = NULL; /* Nullterm list */
850 }
851
852 return list;
853 }
854
855 /*
856 * Send the Message to the windows registered for ShellHook
857 * notifications. The lParam contents depend on the Message. See
858 * MSDN for more details (RegisterShellHookWindow)
859 */
860 VOID co_IntShellHookNotify(WPARAM Message, WPARAM wParam, LPARAM lParam)
861 {
862 PDESKTOP Desktop = IntGetActiveDesktop();
863 HWND* HwndList;
864
865 if (!gpsi->uiShellMsg)
866 {
867 gpsi->uiShellMsg = IntAddAtom(L"SHELLHOOK");
868
869 TRACE("MsgType = %x\n", gpsi->uiShellMsg);
870 if (!gpsi->uiShellMsg)
871 ERR("LastError: %x\n", EngGetLastError());
872 }
873
874 if (!Desktop)
875 {
876 TRACE("IntShellHookNotify: No desktop!\n");
877 return;
878 }
879
880 // Allow other devices have a shot at foreground.
881 if (Message == HSHELL_APPCOMMAND) ptiLastInput = NULL;
882
883 // FIXME: System Tray Support.
884
885 HwndList = UserBuildShellHookHwndList(Desktop);
886 if (HwndList)
887 {
888 HWND* cursor = HwndList;
889
890 for (; *cursor; cursor++)
891 {
892 TRACE("Sending notify\n");
893 UserPostMessage(*cursor,
894 gpsi->uiShellMsg,
895 Message,
896 (Message == HSHELL_LANGUAGE ? lParam : (LPARAM)wParam) );
897 /* co_IntPostOrSendMessage(*cursor,
898 gpsi->uiShellMsg,
899 Message,
900 (Message == HSHELL_LANGUAGE ? lParam : (LPARAM)wParam) );*/
901 }
902
903 ExFreePoolWithTag(HwndList, USERTAG_WINDOWLIST);
904 }
905
906 if (ISITHOOKED(WH_SHELL))
907 {
908 co_HOOK_CallHooks(WH_SHELL, Message, wParam, lParam);
909 }
910 }
911
912 /*
913 * Add the window to the ShellHookWindows list. The windows
914 * on that list get notifications that are important to shell
915 * type applications.
916 *
917 * TODO: Validate the window? I'm not sure if sending these messages to
918 * an unsuspecting application that is not your own is a nice thing to do.
919 */
920 BOOL IntRegisterShellHookWindow(HWND hWnd)
921 {
922 PTHREADINFO pti = PsGetCurrentThreadWin32Thread();
923 PDESKTOP Desktop = pti->rpdesk;
924 PSHELL_HOOK_WINDOW Entry;
925
926 TRACE("IntRegisterShellHookWindow\n");
927
928 /* First deregister the window, so we can be sure it's never twice in the
929 * list.
930 */
931 IntDeRegisterShellHookWindow(hWnd);
932
933 Entry = ExAllocatePoolWithTag(PagedPool,
934 sizeof(SHELL_HOOK_WINDOW),
935 TAG_WINSTA);
936
937 if (!Entry)
938 return FALSE;
939
940 Entry->hWnd = hWnd;
941
942 InsertTailList(&Desktop->ShellHookWindows, &Entry->ListEntry);
943
944 return TRUE;
945 }
946
947 /*
948 * Remove the window from the ShellHookWindows list. The windows
949 * on that list get notifications that are important to shell
950 * type applications.
951 */
952 BOOL IntDeRegisterShellHookWindow(HWND hWnd)
953 {
954 PTHREADINFO pti = PsGetCurrentThreadWin32Thread();
955 PDESKTOP Desktop = pti->rpdesk;
956 PLIST_ENTRY ListEntry;
957 PSHELL_HOOK_WINDOW Current;
958
959 ListEntry = Desktop->ShellHookWindows.Flink;
960 while (ListEntry != &Desktop->ShellHookWindows)
961 {
962 Current = CONTAINING_RECORD(ListEntry, SHELL_HOOK_WINDOW, ListEntry);
963 ListEntry = ListEntry->Flink;
964 if (Current->hWnd == hWnd)
965 {
966 RemoveEntryList(&Current->ListEntry);
967 ExFreePoolWithTag(Current, TAG_WINSTA);
968 return TRUE;
969 }
970 }
971
972 return FALSE;
973 }
974
975 static VOID
976 IntFreeDesktopHeap(IN OUT PDESKTOP Desktop)
977 {
978 /* FIXME: Disable until unmapping works in mm */
979 #if 0
980 if (Desktop->pheapDesktop != NULL)
981 {
982 MmUnmapViewInSessionSpace(Desktop->pheapDesktop);
983 Desktop->pheapDesktop = NULL;
984 }
985
986 if (Desktop->hsectionDesktop != NULL)
987 {
988 ObDereferenceObject(Desktop->hsectionDesktop);
989 Desktop->hsectionDesktop = NULL;
990 }
991 #endif
992 }
993
994 BOOL FASTCALL
995 IntPaintDesktop(HDC hDC)
996 {
997 RECTL Rect;
998 HBRUSH DesktopBrush, PreviousBrush;
999 HWND hWndDesktop;
1000 BOOL doPatBlt = TRUE;
1001 PWND WndDesktop;
1002 static WCHAR s_wszSafeMode[] = L"Safe Mode";
1003 int len;
1004 COLORREF color_old;
1005 UINT align_old;
1006 int mode_old;
1007
1008 if (GdiGetClipBox(hDC, &Rect) == ERROR)
1009 return FALSE;
1010
1011 hWndDesktop = IntGetDesktopWindow(); // rpdesk->DesktopWindow;
1012
1013 WndDesktop = UserGetWindowObject(hWndDesktop); // rpdesk->pDeskInfo->spwnd;
1014 if (!WndDesktop)
1015 return FALSE;
1016
1017 if (!UserGetSystemMetrics(SM_CLEANBOOT))
1018 {
1019 DesktopBrush = (HBRUSH)WndDesktop->pcls->hbrBackground;
1020
1021 /*
1022 * Paint desktop background
1023 */
1024 if (gspv.hbmWallpaper != NULL)
1025 {
1026 SIZE sz;
1027 int x, y;
1028 HDC hWallpaperDC;
1029
1030 sz.cx = WndDesktop->rcWindow.right - WndDesktop->rcWindow.left;
1031 sz.cy = WndDesktop->rcWindow.bottom - WndDesktop->rcWindow.top;
1032
1033 if (gspv.WallpaperMode == wmStretch ||
1034 gspv.WallpaperMode == wmTile)
1035 {
1036 x = 0;
1037 y = 0;
1038 }
1039 else
1040 {
1041 /* Find the upper left corner, can be negative if the bitmap is bigger then the screen */
1042 x = (sz.cx / 2) - (gspv.cxWallpaper / 2);
1043 y = (sz.cy / 2) - (gspv.cyWallpaper / 2);
1044 }
1045
1046 hWallpaperDC = NtGdiCreateCompatibleDC(hDC);
1047 if(hWallpaperDC != NULL)
1048 {
1049 HBITMAP hOldBitmap;
1050
1051 /* Fill in the area that the bitmap is not going to cover */
1052 if (x > 0 || y > 0)
1053 {
1054 /* FIXME: Clip out the bitmap
1055 can be replaced with "NtGdiPatBlt(hDC, x, y, WinSta->cxWallpaper, WinSta->cyWallpaper, PATCOPY | DSTINVERT);"
1056 once we support DSTINVERT */
1057 PreviousBrush = NtGdiSelectBrush(hDC, DesktopBrush);
1058 NtGdiPatBlt(hDC, Rect.left, Rect.top, Rect.right, Rect.bottom, PATCOPY);
1059 NtGdiSelectBrush(hDC, PreviousBrush);
1060 }
1061
1062 /* Do not fill the background after it is painted no matter the size of the picture */
1063 doPatBlt = FALSE;
1064
1065 hOldBitmap = NtGdiSelectBitmap(hWallpaperDC, gspv.hbmWallpaper);
1066
1067 if (gspv.WallpaperMode == wmStretch)
1068 {
1069 if(Rect.right && Rect.bottom)
1070 NtGdiStretchBlt(hDC,
1071 x,
1072 y,
1073 sz.cx,
1074 sz.cy,
1075 hWallpaperDC,
1076 0,
1077 0,
1078 gspv.cxWallpaper,
1079 gspv.cyWallpaper,
1080 SRCCOPY,
1081 0);
1082 }
1083 else if (gspv.WallpaperMode == wmTile)
1084 {
1085 /* Paint the bitmap across the screen then down */
1086 for(y = 0; y < Rect.bottom; y += gspv.cyWallpaper)
1087 {
1088 for(x = 0; x < Rect.right; x += gspv.cxWallpaper)
1089 {
1090 NtGdiBitBlt(hDC,
1091 x,
1092 y,
1093 gspv.cxWallpaper,
1094 gspv.cyWallpaper,
1095 hWallpaperDC,
1096 0,
1097 0,
1098 SRCCOPY,
1099 0,
1100 0);
1101 }
1102 }
1103 }
1104 else
1105 {
1106 NtGdiBitBlt(hDC,
1107 x,
1108 y,
1109 gspv.cxWallpaper,
1110 gspv.cyWallpaper,
1111 hWallpaperDC,
1112 0,
1113 0,
1114 SRCCOPY,
1115 0,
1116 0);
1117 }
1118 NtGdiSelectBitmap(hWallpaperDC, hOldBitmap);
1119 NtGdiDeleteObjectApp(hWallpaperDC);
1120 }
1121 }
1122 }
1123 else
1124 {
1125 /* Black desktop background in Safe Mode */
1126 DesktopBrush = StockObjects[BLACK_BRUSH];
1127 }
1128 /* Background is set to none, clear the screen */
1129 if (doPatBlt)
1130 {
1131 PreviousBrush = NtGdiSelectBrush(hDC, DesktopBrush);
1132 NtGdiPatBlt(hDC, Rect.left, Rect.top, Rect.right, Rect.bottom, PATCOPY);
1133 NtGdiSelectBrush(hDC, PreviousBrush);
1134 }
1135
1136 /*
1137 * Display system version on the desktop background
1138 */
1139
1140 if (g_PaintDesktopVersion || UserGetSystemMetrics(SM_CLEANBOOT))
1141 {
1142 static WCHAR s_wszVersion[256] = {0};
1143 RECTL rect;
1144
1145 if (*s_wszVersion)
1146 {
1147 len = wcslen(s_wszVersion);
1148 }
1149 else
1150 {
1151 len = GetSystemVersionString(s_wszVersion);
1152 }
1153
1154 if (len)
1155 {
1156 if (!UserSystemParametersInfo(SPI_GETWORKAREA, 0, &rect, 0))
1157 {
1158 rect.right = UserGetSystemMetrics(SM_CXSCREEN);
1159 rect.bottom = UserGetSystemMetrics(SM_CYSCREEN);
1160 }
1161
1162 color_old = IntGdiSetTextColor(hDC, RGB(255,255,255));
1163 align_old = IntGdiSetTextAlign(hDC, TA_RIGHT);
1164 mode_old = IntGdiSetBkMode(hDC, TRANSPARENT);
1165
1166 if(!UserGetSystemMetrics(SM_CLEANBOOT))
1167 {
1168 GreExtTextOutW(hDC, rect.right - 16, rect.bottom - 48, 0, NULL, s_wszVersion, len, NULL, 0);
1169 }
1170 else
1171 {
1172 /* Safe Mode */
1173
1174 /* Version information text in top center */
1175 IntGdiSetTextAlign(hDC, TA_CENTER | TA_TOP);
1176 GreExtTextOutW(hDC, (rect.right + rect.left)/2, rect.top + 3, 0, NULL, s_wszVersion, len, NULL, 0);
1177
1178 /* Safe Mode text in corners */
1179 len = wcslen(s_wszSafeMode);
1180 IntGdiSetTextAlign(hDC, TA_LEFT | TA_TOP);
1181 GreExtTextOutW(hDC, rect.left, rect.top + 3, 0, NULL, s_wszSafeMode, len, NULL, 0);
1182 IntGdiSetTextAlign(hDC, TA_RIGHT | TA_TOP);
1183 GreExtTextOutW(hDC, rect.right, rect.top + 3, 0, NULL, s_wszSafeMode, len, NULL, 0);
1184 IntGdiSetTextAlign(hDC, TA_LEFT | TA_BASELINE);
1185 GreExtTextOutW(hDC, rect.left, rect.bottom - 5, 0, NULL, s_wszSafeMode, len, NULL, 0);
1186 IntGdiSetTextAlign(hDC, TA_RIGHT | TA_BASELINE);
1187 GreExtTextOutW(hDC, rect.right, rect.bottom - 5, 0, NULL, s_wszSafeMode, len, NULL, 0);
1188 }
1189
1190 IntGdiSetBkMode(hDC, mode_old);
1191 IntGdiSetTextAlign(hDC, align_old);
1192 IntGdiSetTextColor(hDC, color_old);
1193 }
1194 }
1195 return TRUE;
1196 }
1197
1198 static NTSTATUS
1199 UserInitializeDesktop(PDESKTOP pdesk, PUNICODE_STRING DesktopName, PWINSTATION_OBJECT pwinsta)
1200 {
1201 PVOID DesktopHeapSystemBase = NULL;
1202 ULONG_PTR HeapSize = gdwDesktopSectionSize * 1024;
1203 SIZE_T DesktopInfoSize;
1204 ULONG i;
1205
1206 TRACE("UserInitializeDesktop desktop 0x%p with name %wZ\n", pdesk, DesktopName);
1207
1208 RtlZeroMemory(pdesk, sizeof(DESKTOP));
1209
1210 /* Link the desktop with the parent window station */
1211 pdesk->rpwinstaParent = pwinsta;
1212 InsertTailList(&pwinsta->DesktopListHead, &pdesk->ListEntry);
1213
1214 /* Create the desktop heap */
1215 pdesk->hsectionDesktop = NULL;
1216 pdesk->pheapDesktop = UserCreateHeap(&pdesk->hsectionDesktop,
1217 &DesktopHeapSystemBase,
1218 HeapSize);
1219 if (pdesk->pheapDesktop == NULL)
1220 {
1221 ERR("Failed to create desktop heap!\n");
1222 return STATUS_NO_MEMORY;
1223 }
1224
1225 /* Create DESKTOPINFO */
1226 DesktopInfoSize = sizeof(DESKTOPINFO) + DesktopName->Length + sizeof(WCHAR);
1227 pdesk->pDeskInfo = RtlAllocateHeap(pdesk->pheapDesktop,
1228 HEAP_NO_SERIALIZE | HEAP_ZERO_MEMORY,
1229 DesktopInfoSize);
1230 if (pdesk->pDeskInfo == NULL)
1231 {
1232 ERR("Failed to create the DESKTOP structure!\n");
1233 return STATUS_NO_MEMORY;
1234 }
1235
1236 /* Initialize the DESKTOPINFO */
1237 pdesk->pDeskInfo->pvDesktopBase = DesktopHeapSystemBase;
1238 pdesk->pDeskInfo->pvDesktopLimit = (PVOID)((ULONG_PTR)DesktopHeapSystemBase + HeapSize);
1239 RtlCopyMemory(pdesk->pDeskInfo->szDesktopName,
1240 DesktopName->Buffer,
1241 DesktopName->Length + sizeof(WCHAR));
1242 for (i = 0; i < NB_HOOKS; i++)
1243 {
1244 InitializeListHead(&pdesk->pDeskInfo->aphkStart[i]);
1245 }
1246
1247 InitializeListHead(&pdesk->ShellHookWindows);
1248 InitializeListHead(&pdesk->PtiList);
1249
1250 return STATUS_SUCCESS;
1251 }
1252
1253 /* SYSCALLS *******************************************************************/
1254
1255 /*
1256 * NtUserCreateDesktop
1257 *
1258 * Creates a new desktop.
1259 *
1260 * Parameters
1261 * poaAttribs
1262 * Object Attributes.
1263 *
1264 * lpszDesktopDevice
1265 * Name of the device.
1266 *
1267 * pDeviceMode
1268 * Device Mode.
1269 *
1270 * dwFlags
1271 * Interaction flags.
1272 *
1273 * dwDesiredAccess
1274 * Requested type of access.
1275 *
1276 *
1277 * Return Value
1278 * If the function succeeds, the return value is a handle to the newly
1279 * created desktop. If the specified desktop already exists, the function
1280 * succeeds and returns a handle to the existing desktop. When you are
1281 * finished using the handle, call the CloseDesktop function to close it.
1282 * If the function fails, the return value is NULL.
1283 *
1284 * Status
1285 * @implemented
1286 */
1287
1288 HDESK APIENTRY
1289 NtUserCreateDesktop(
1290 POBJECT_ATTRIBUTES ObjectAttributes,
1291 PUNICODE_STRING lpszDesktopDevice,
1292 LPDEVMODEW lpdmw,
1293 DWORD dwFlags,
1294 ACCESS_MASK dwDesiredAccess)
1295 {
1296 PDESKTOP pdesk = NULL;
1297 NTSTATUS Status = STATUS_SUCCESS;
1298 HDESK hdesk;
1299 BOOLEAN Context = FALSE;
1300 UNICODE_STRING ClassName;
1301 LARGE_STRING WindowName;
1302 BOOL NoHooks = FALSE;
1303 PWND pWnd = NULL;
1304 CREATESTRUCTW Cs;
1305 PTHREADINFO ptiCurrent;
1306 PCLS pcls;
1307
1308 DECLARE_RETURN(HDESK);
1309
1310 TRACE("Enter NtUserCreateDesktop\n");
1311 UserEnterExclusive();
1312
1313 ptiCurrent = PsGetCurrentThreadWin32Thread();
1314 ASSERT(ptiCurrent);
1315 ASSERT(gptiDesktopThread);
1316
1317 /* Turn off hooks when calling any CreateWindowEx from inside win32k. */
1318 NoHooks = (ptiCurrent->TIF_flags & TIF_DISABLEHOOKS);
1319 ptiCurrent->TIF_flags |= TIF_DISABLEHOOKS;
1320 ptiCurrent->pClientInfo->dwTIFlags = ptiCurrent->TIF_flags;
1321
1322 /*
1323 * Try to open already existing desktop
1324 */
1325 Status = ObOpenObjectByName(
1326 ObjectAttributes,
1327 ExDesktopObjectType,
1328 UserMode,
1329 NULL,
1330 dwDesiredAccess,
1331 (PVOID)&Context,
1332 (HANDLE*)&hdesk);
1333 if (!NT_SUCCESS(Status))
1334 {
1335 ERR("ObOpenObjectByName failed to open/create desktop\n");
1336 SetLastNtError(Status);
1337 RETURN(NULL);
1338 }
1339
1340 /* In case the object was not created (eg if it existed), return now */
1341 if (Context == FALSE)
1342 {
1343 TRACE("NtUserCreateDesktop opened desktop %wZ\n", ObjectAttributes->ObjectName);
1344 RETURN( hdesk);
1345 }
1346
1347 /* Reference the desktop */
1348 Status = ObReferenceObjectByHandle(hdesk,
1349 0,
1350 ExDesktopObjectType,
1351 KernelMode,
1352 (PVOID*)&pdesk,
1353 NULL);
1354 if (!NT_SUCCESS(Status))
1355 {
1356 ERR("Failed to reference desktop object\n");
1357 SetLastNtError(Status);
1358 RETURN(NULL);
1359 }
1360
1361 /* Get the desktop window class. The thread desktop does not belong to any desktop
1362 * so the classes created there (including the desktop class) are allocated in the shared heap
1363 * It would cause problems if we used a class that belongs to the caller
1364 */
1365 ClassName.Buffer = WC_DESKTOP;
1366 ClassName.Length = 0;
1367 pcls = IntGetAndReferenceClass(&ClassName, 0, TRUE);
1368 if (pcls == NULL)
1369 {
1370 ASSERT(FALSE);
1371 RETURN(NULL);
1372 }
1373
1374 RtlZeroMemory(&WindowName, sizeof(WindowName));
1375 RtlZeroMemory(&Cs, sizeof(Cs));
1376 Cs.x = UserGetSystemMetrics(SM_XVIRTUALSCREEN),
1377 Cs.y = UserGetSystemMetrics(SM_YVIRTUALSCREEN),
1378 Cs.cx = UserGetSystemMetrics(SM_CXVIRTUALSCREEN),
1379 Cs.cy = UserGetSystemMetrics(SM_CYVIRTUALSCREEN),
1380 Cs.style = WS_POPUP|WS_CLIPCHILDREN;
1381 Cs.hInstance = hModClient; // hModuleWin; // Server side winproc!
1382 Cs.lpszName = (LPCWSTR) &WindowName;
1383 Cs.lpszClass = (LPCWSTR) &ClassName;
1384
1385 /* Use IntCreateWindow instead of co_UserCreateWindowEx cause the later expects a thread with a desktop */
1386 pWnd = IntCreateWindow(&Cs, &WindowName, pcls, NULL, NULL, NULL, pdesk);
1387 if (pWnd == NULL)
1388 {
1389 ERR("Failed to create desktop window for the new desktop\n");
1390 RETURN(NULL);
1391 }
1392
1393 pdesk->dwSessionId = PsGetCurrentProcessSessionId();
1394 pdesk->DesktopWindow = pWnd->head.h;
1395 pdesk->pDeskInfo->spwnd = pWnd;
1396 pWnd->fnid = FNID_DESKTOP;
1397
1398 ClassName.Buffer = MAKEINTATOM(gpsi->atomSysClass[ICLS_HWNDMESSAGE]);
1399 ClassName.Length = 0;
1400 pcls = IntGetAndReferenceClass(&ClassName, 0, TRUE);
1401 if (pcls == NULL)
1402 {
1403 ASSERT(FALSE);
1404 RETURN(NULL);
1405 }
1406
1407 RtlZeroMemory(&WindowName, sizeof(WindowName));
1408 RtlZeroMemory(&Cs, sizeof(Cs));
1409 Cs.cx = Cs.cy = 100;
1410 Cs.style = WS_POPUP|WS_CLIPCHILDREN;
1411 Cs.hInstance = hModClient; // hModuleWin; // Server side winproc!
1412 Cs.lpszName = (LPCWSTR) &WindowName;
1413 Cs.lpszClass = (LPCWSTR) &ClassName;
1414 pWnd = IntCreateWindow(&Cs, &WindowName, pcls, NULL, NULL, NULL, pdesk);
1415 if (pWnd == NULL)
1416 {
1417 ERR("Failed to create message window for the new desktop\n");
1418 RETURN(NULL);
1419 }
1420
1421 pdesk->spwndMessage = pWnd;
1422 pWnd->fnid = FNID_MESSAGEWND;
1423
1424 /* Now,,,
1425 if !(WinStaObject->Flags & WSF_NOIO) is (not set) for desktop input output mode (see wiki)
1426 Create Tooltip. Saved in DesktopObject->spwndTooltip.
1427 Tooltip dwExStyle: WS_EX_TOOLWINDOW|WS_EX_TOPMOST
1428 hWndParent are spwndMessage. Use hModuleWin for server side winproc!
1429 The rest is same as message window.
1430 http://msdn.microsoft.com/en-us/library/bb760250(VS.85).aspx
1431 */
1432 RETURN( hdesk);
1433
1434 CLEANUP:
1435 if (pdesk != NULL)
1436 {
1437 ObDereferenceObject(pdesk);
1438 }
1439 if (_ret_ == NULL && hdesk != NULL)
1440 {
1441 ObCloseHandle(hdesk, UserMode);
1442 }
1443 if (!NoHooks)
1444 {
1445 ptiCurrent->TIF_flags &= ~TIF_DISABLEHOOKS;
1446 ptiCurrent->pClientInfo->dwTIFlags = ptiCurrent->TIF_flags;
1447 }
1448 TRACE("Leave NtUserCreateDesktop, ret=%p\n",_ret_);
1449 UserLeave();
1450 END_CLEANUP;
1451 }
1452
1453 /*
1454 * NtUserOpenDesktop
1455 *
1456 * Opens an existing desktop.
1457 *
1458 * Parameters
1459 * lpszDesktopName
1460 * Name of the existing desktop.
1461 *
1462 * dwFlags
1463 * Interaction flags.
1464 *
1465 * dwDesiredAccess
1466 * Requested type of access.
1467 *
1468 * Return Value
1469 * Handle to the desktop or zero on failure.
1470 *
1471 * Status
1472 * @implemented
1473 */
1474
1475 HDESK APIENTRY
1476 NtUserOpenDesktop(
1477 POBJECT_ATTRIBUTES ObjectAttributes,
1478 DWORD dwFlags,
1479 ACCESS_MASK dwDesiredAccess)
1480 {
1481 NTSTATUS Status;
1482 HDESK Desktop;
1483
1484 Status = ObOpenObjectByName(
1485 ObjectAttributes,
1486 ExDesktopObjectType,
1487 UserMode,
1488 NULL,
1489 dwDesiredAccess,
1490 NULL,
1491 (HANDLE*)&Desktop);
1492
1493 if (!NT_SUCCESS(Status))
1494 {
1495 ERR("Failed to open desktop\n");
1496 SetLastNtError(Status);
1497 return 0;
1498 }
1499
1500 TRACE("Opened desktop %S with handle 0x%p\n", ObjectAttributes->ObjectName->Buffer, Desktop);
1501
1502 return Desktop;
1503 }
1504
1505 /*
1506 * NtUserOpenInputDesktop
1507 *
1508 * Opens the input (interactive) desktop.
1509 *
1510 * Parameters
1511 * dwFlags
1512 * Interaction flags.
1513 *
1514 * fInherit
1515 * Inheritance option.
1516 *
1517 * dwDesiredAccess
1518 * Requested type of access.
1519 *
1520 * Return Value
1521 * Handle to the input desktop or zero on failure.
1522 *
1523 * Status
1524 * @implemented
1525 */
1526
1527 HDESK APIENTRY
1528 NtUserOpenInputDesktop(
1529 DWORD dwFlags,
1530 BOOL fInherit,
1531 ACCESS_MASK dwDesiredAccess)
1532 {
1533 NTSTATUS Status;
1534 HDESK hdesk = NULL;
1535 ULONG HandleAttributes = 0;
1536
1537 UserEnterExclusive();
1538 TRACE("Enter NtUserOpenInputDesktop gpdeskInputDesktop 0x%p\n",gpdeskInputDesktop);
1539
1540 if(fInherit) HandleAttributes = OBJ_INHERIT;
1541
1542 /* Create a new handle to the object */
1543 Status = ObOpenObjectByPointer(
1544 gpdeskInputDesktop,
1545 HandleAttributes,
1546 NULL,
1547 dwDesiredAccess,
1548 ExDesktopObjectType,
1549 UserMode,
1550 (PHANDLE)&hdesk);
1551
1552 if (!NT_SUCCESS(Status))
1553 {
1554 ERR("Failed to open input desktop object\n");
1555 SetLastNtError(Status);
1556 }
1557
1558 TRACE("NtUserOpenInputDesktop returning 0x%p\n",hdesk);
1559 UserLeave();
1560 return hdesk;
1561 }
1562
1563 /*
1564 * NtUserCloseDesktop
1565 *
1566 * Closes a desktop handle.
1567 *
1568 * Parameters
1569 * hDesktop
1570 * Handle to the desktop.
1571 *
1572 * Return Value
1573 * Status
1574 *
1575 * Remarks
1576 * The desktop handle can be created with NtUserCreateDesktop or
1577 * NtUserOpenDesktop. This function will fail if any thread in the calling
1578 * process is using the specified desktop handle or if the handle refers
1579 * to the initial desktop of the calling process.
1580 *
1581 * Status
1582 * @implemented
1583 */
1584
1585 BOOL APIENTRY
1586 NtUserCloseDesktop(HDESK hDesktop)
1587 {
1588 PDESKTOP pdesk;
1589 NTSTATUS Status;
1590 DECLARE_RETURN(BOOL);
1591
1592 TRACE("NtUserCloseDesktop called (0x%p)\n", hDesktop);
1593 UserEnterExclusive();
1594
1595 if( hDesktop == gptiCurrent->hdesk || hDesktop == gptiCurrent->ppi->hdeskStartup)
1596 {
1597 ERR("Attempted to close thread desktop\n");
1598 EngSetLastError(ERROR_BUSY);
1599 RETURN(FALSE);
1600 }
1601
1602 Status = IntValidateDesktopHandle( hDesktop, UserMode, 0, &pdesk);
1603 if (!NT_SUCCESS(Status))
1604 {
1605 ERR("Validation of desktop handle (0x%p) failed\n", hDesktop);
1606 RETURN(FALSE);
1607 }
1608
1609 ObDereferenceObject(pdesk);
1610
1611 Status = ZwClose(hDesktop);
1612 if (!NT_SUCCESS(Status))
1613 {
1614 ERR("Failed to close desktop handle 0x%p\n", hDesktop);
1615 SetLastNtError(Status);
1616 RETURN(FALSE);
1617 }
1618
1619 RETURN(TRUE);
1620
1621 CLEANUP:
1622 TRACE("Leave NtUserCloseDesktop, ret=%i\n",_ret_);
1623 UserLeave();
1624 END_CLEANUP;
1625 }
1626
1627 /*
1628 * NtUserPaintDesktop
1629 *
1630 * The NtUserPaintDesktop function fills the clipping region in the
1631 * specified device context with the desktop pattern or wallpaper. The
1632 * function is provided primarily for shell desktops.
1633 *
1634 * Parameters
1635 * hDC
1636 * Handle to the device context.
1637 *
1638 * Status
1639 * @implemented
1640 */
1641
1642 BOOL APIENTRY
1643 NtUserPaintDesktop(HDC hDC)
1644 {
1645 BOOL Ret;
1646 UserEnterExclusive();
1647 TRACE("Enter NtUserPaintDesktop\n");
1648 Ret = IntPaintDesktop(hDC);
1649 TRACE("Leave NtUserPaintDesktop, ret=%i\n",Ret);
1650 UserLeave();
1651 return Ret;
1652 }
1653
1654 /*
1655 * NtUserResolveDesktop
1656 *
1657 * The NtUserResolveDesktop function retrieves handles to the desktop and
1658 * the window station specified by the desktop path string.
1659 *
1660 * Parameters
1661 * ProcessHandle
1662 * Handle to a user process.
1663 *
1664 * DesktopPath
1665 * The desktop path string.
1666 *
1667 * Return Value
1668 * Handle to the desktop (direct return value) and
1669 * handle to the associated window station (by pointer).
1670 * NULL in case of failure.
1671 *
1672 * Remarks
1673 * Callable by CSRSS only.
1674 *
1675 * Status
1676 * @implemented
1677 */
1678
1679 HDESK
1680 APIENTRY
1681 NtUserResolveDesktop(
1682 IN HANDLE ProcessHandle,
1683 IN PUNICODE_STRING DesktopPath,
1684 DWORD dwUnknown,
1685 OUT HWINSTA* phWinSta)
1686 {
1687 NTSTATUS Status;
1688 PEPROCESS Process = NULL;
1689 HWINSTA hWinSta = NULL;
1690 HDESK hDesktop = NULL;
1691
1692 /* Allow only the Console Server to perform this operation (via CSRSS) */
1693 if (PsGetCurrentProcess() != gpepCSRSS)
1694 return NULL;
1695
1696 /* Get the process object the user handle was referencing */
1697 Status = ObReferenceObjectByHandle(ProcessHandle,
1698 PROCESS_QUERY_INFORMATION,
1699 *PsProcessType,
1700 UserMode,
1701 (PVOID*)&Process,
1702 NULL);
1703 if (!NT_SUCCESS(Status)) return NULL;
1704
1705 // UserEnterShared();
1706
1707 _SEH2_TRY
1708 {
1709 UNICODE_STRING CapturedDesktopPath;
1710
1711 /* Capture the user desktop path string */
1712 Status = IntSafeCopyUnicodeStringTerminateNULL(&CapturedDesktopPath,
1713 DesktopPath);
1714 if (!NT_SUCCESS(Status)) _SEH2_YIELD(goto Quit);
1715
1716 /* Call the internal function */
1717 Status = IntParseDesktopPath(Process,
1718 &CapturedDesktopPath,
1719 &hWinSta,
1720 &hDesktop);
1721 if (!NT_SUCCESS(Status))
1722 {
1723 ERR("IntParseDesktopPath failed, Status = 0x%08lx\n", Status);
1724 hWinSta = NULL;
1725 hDesktop = NULL;
1726 }
1727
1728 /* Return the window station handle */
1729 *phWinSta = hWinSta;
1730
1731 /* Free the captured string */
1732 if (CapturedDesktopPath.Buffer)
1733 ExFreePoolWithTag(CapturedDesktopPath.Buffer, TAG_STRING);
1734 }
1735 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1736 {
1737 Status = _SEH2_GetExceptionCode();
1738 }
1739 _SEH2_END;
1740
1741 Quit:
1742 // UserLeave();
1743
1744 /* Dereference the process object */
1745 ObDereferenceObject(Process);
1746
1747 /* Return the desktop handle */
1748 return hDesktop;
1749 }
1750
1751 /*
1752 * NtUserSwitchDesktop
1753 *
1754 * Sets the current input (interactive) desktop.
1755 *
1756 * Parameters
1757 * hDesktop
1758 * Handle to desktop.
1759 *
1760 * Return Value
1761 * Status
1762 *
1763 * Status
1764 * @unimplemented
1765 */
1766
1767 BOOL APIENTRY
1768 NtUserSwitchDesktop(HDESK hdesk)
1769 {
1770 PDESKTOP pdesk;
1771 NTSTATUS Status;
1772 BOOL bRedrawDesktop;
1773 DECLARE_RETURN(BOOL);
1774
1775 UserEnterExclusive();
1776 TRACE("Enter NtUserSwitchDesktop(0x%p)\n", hdesk);
1777
1778 Status = IntValidateDesktopHandle( hdesk, UserMode, 0, &pdesk);
1779 if (!NT_SUCCESS(Status))
1780 {
1781 ERR("Validation of desktop handle (0x%p) failed\n", hdesk);
1782 RETURN(FALSE);
1783 }
1784
1785 if (PsGetCurrentProcessSessionId() != pdesk->rpwinstaParent->dwSessionId)
1786 {
1787 ERR("NtUserSwitchDesktop called for a desktop of a different session\n");
1788 RETURN(FALSE);
1789 }
1790
1791 if(pdesk == gpdeskInputDesktop)
1792 {
1793 WARN("NtUserSwitchDesktop called for active desktop\n");
1794 RETURN(TRUE);
1795 }
1796
1797 /*
1798 * Don't allow applications switch the desktop if it's locked, unless the caller
1799 * is the logon application itself
1800 */
1801 if((pdesk->rpwinstaParent->Flags & WSS_LOCKED) &&
1802 gpidLogon != PsGetCurrentProcessId())
1803 {
1804 ObDereferenceObject(pdesk);
1805 ERR("Switching desktop 0x%p denied because the window station is locked!\n", hdesk);
1806 RETURN(FALSE);
1807 }
1808
1809 if(pdesk->rpwinstaParent != InputWindowStation)
1810 {
1811 ObDereferenceObject(pdesk);
1812 ERR("Switching desktop 0x%p denied because desktop doesn't belong to the interactive winsta!\n", hdesk);
1813 RETURN(FALSE);
1814 }
1815
1816 /* FIXME: Fail if the process is associated with a secured
1817 desktop such as Winlogon or Screen-Saver */
1818 /* FIXME: Connect to input device */
1819
1820 TRACE("Switching from desktop 0x%p to 0x%p\n", gpdeskInputDesktop, pdesk);
1821
1822 bRedrawDesktop = FALSE;
1823
1824 /* The first time SwitchDesktop is called, gpdeskInputDesktop is NULL */
1825 if(gpdeskInputDesktop != NULL)
1826 {
1827 if((gpdeskInputDesktop->pDeskInfo->spwnd->style & WS_VISIBLE) == WS_VISIBLE)
1828 bRedrawDesktop = TRUE;
1829
1830 /* Hide the previous desktop window */
1831 IntHideDesktop(gpdeskInputDesktop);
1832 }
1833
1834 /* Set the active desktop in the desktop's window station. */
1835 InputWindowStation->ActiveDesktop = pdesk;
1836
1837 /* Set the global state. */
1838 gpdeskInputDesktop = pdesk;
1839
1840 /* Show the new desktop window */
1841 co_IntShowDesktop(pdesk, UserGetSystemMetrics(SM_CXSCREEN), UserGetSystemMetrics(SM_CYSCREEN), bRedrawDesktop);
1842
1843 TRACE("SwitchDesktop gpdeskInputDesktop 0x%p\n",gpdeskInputDesktop);
1844 ObDereferenceObject(pdesk);
1845
1846 RETURN(TRUE);
1847
1848 CLEANUP:
1849 TRACE("Leave NtUserSwitchDesktop, ret=%i\n",_ret_);
1850 UserLeave();
1851 END_CLEANUP;
1852 }
1853
1854 /*
1855 * NtUserGetThreadDesktop
1856 *
1857 * Status
1858 * @implemented
1859 */
1860
1861 HDESK APIENTRY
1862 NtUserGetThreadDesktop(DWORD dwThreadId, DWORD Unknown1)
1863 {
1864 NTSTATUS Status;
1865 PETHREAD Thread;
1866 PDESKTOP DesktopObject;
1867 HDESK Ret, hThreadDesktop;
1868 OBJECT_HANDLE_INFORMATION HandleInformation;
1869 DECLARE_RETURN(HDESK);
1870
1871 UserEnterExclusive();
1872 TRACE("Enter NtUserGetThreadDesktop\n");
1873
1874 if(!dwThreadId)
1875 {
1876 EngSetLastError(ERROR_INVALID_PARAMETER);
1877 RETURN(0);
1878 }
1879
1880 Status = PsLookupThreadByThreadId((HANDLE)(DWORD_PTR)dwThreadId, &Thread);
1881 if(!NT_SUCCESS(Status))
1882 {
1883 EngSetLastError(ERROR_INVALID_PARAMETER);
1884 RETURN(0);
1885 }
1886
1887 if(Thread->ThreadsProcess == PsGetCurrentProcess())
1888 {
1889 /* Just return the handle, we queried the desktop handle of a thread running
1890 in the same context */
1891 Ret = ((PTHREADINFO)Thread->Tcb.Win32Thread)->hdesk;
1892 ObDereferenceObject(Thread);
1893 RETURN(Ret);
1894 }
1895
1896 /* Get the desktop handle and the desktop of the thread */
1897 if(!(hThreadDesktop = ((PTHREADINFO)Thread->Tcb.Win32Thread)->hdesk) ||
1898 !(DesktopObject = ((PTHREADINFO)Thread->Tcb.Win32Thread)->rpdesk))
1899 {
1900 ObDereferenceObject(Thread);
1901 ERR("Desktop information of thread 0x%x broken!?\n", dwThreadId);
1902 RETURN(NULL);
1903 }
1904
1905 /* We could just use DesktopObject instead of looking up the handle, but latter
1906 may be a bit safer (e.g. when the desktop is being destroyed */
1907 /* Switch into the context of the thread we're trying to get the desktop from,
1908 so we can use the handle */
1909 KeAttachProcess(&Thread->ThreadsProcess->Pcb);
1910 Status = ObReferenceObjectByHandle(hThreadDesktop,
1911 GENERIC_ALL,
1912 ExDesktopObjectType,
1913 UserMode,
1914 (PVOID*)&DesktopObject,
1915 &HandleInformation);
1916 KeDetachProcess();
1917
1918 /* The handle couldn't be found, there's nothing to get... */
1919 if(!NT_SUCCESS(Status))
1920 {
1921 ObDereferenceObject(Thread);
1922 RETURN(NULL);
1923 }
1924
1925 /* Lookup our handle table if we can find a handle to the desktop object,
1926 if not, create one */
1927 Ret = IntGetDesktopObjectHandle(DesktopObject);
1928
1929 /* All done, we got a valid handle to the desktop */
1930 ObDereferenceObject(DesktopObject);
1931 ObDereferenceObject(Thread);
1932 RETURN(Ret);
1933
1934 CLEANUP:
1935 TRACE("Leave NtUserGetThreadDesktop, ret=%p\n",_ret_);
1936 UserLeave();
1937 END_CLEANUP;
1938 }
1939
1940 static NTSTATUS
1941 IntUnmapDesktopView(IN PDESKTOP pdesk)
1942 {
1943 PPROCESSINFO ppi;
1944 PW32HEAP_USER_MAPPING HeapMapping, *PrevLink;
1945 NTSTATUS Status = STATUS_SUCCESS;
1946
1947 TRACE("IntUnmapDesktopView called for desktop object %p\n", pdesk);
1948
1949 ppi = PsGetCurrentProcessWin32Process();
1950
1951 /*
1952 * Unmap if we're the last thread using the desktop.
1953 * Start the search at the next mapping: skip the first entry
1954 * as it must be the global user heap mapping.
1955 */
1956 PrevLink = &ppi->HeapMappings.Next;
1957 HeapMapping = *PrevLink;
1958 while (HeapMapping != NULL)
1959 {
1960 if (HeapMapping->KernelMapping == (PVOID)pdesk->pheapDesktop)
1961 {
1962 if (--HeapMapping->Count == 0)
1963 {
1964 *PrevLink = HeapMapping->Next;
1965
1966 TRACE("ppi 0x%p unmapped heap of desktop 0x%p\n", ppi, pdesk);
1967 Status = MmUnmapViewOfSection(PsGetCurrentProcess(),
1968 HeapMapping->UserMapping);
1969
1970 ObDereferenceObject(pdesk);
1971
1972 UserHeapFree(HeapMapping);
1973 break;
1974 }
1975 }
1976
1977 PrevLink = &HeapMapping->Next;
1978 HeapMapping = HeapMapping->Next;
1979 }
1980
1981 return Status;
1982 }
1983
1984 static NTSTATUS
1985 IntMapDesktopView(IN PDESKTOP pdesk)
1986 {
1987 PPROCESSINFO ppi;
1988 PW32HEAP_USER_MAPPING HeapMapping, *PrevLink;
1989 PVOID UserBase = NULL;
1990 SIZE_T ViewSize = 0;
1991 LARGE_INTEGER Offset;
1992 NTSTATUS Status;
1993
1994 TRACE("IntMapDesktopView called for desktop object 0x%p\n", pdesk);
1995
1996 ppi = PsGetCurrentProcessWin32Process();
1997
1998 /*
1999 * Find out if another thread already mapped the desktop heap.
2000 * Start the search at the next mapping: skip the first entry
2001 * as it must be the global user heap mapping.
2002 */
2003 PrevLink = &ppi->HeapMappings.Next;
2004 HeapMapping = *PrevLink;
2005 while (HeapMapping != NULL)
2006 {
2007 if (HeapMapping->KernelMapping == (PVOID)pdesk->pheapDesktop)
2008 {
2009 HeapMapping->Count++;
2010 return STATUS_SUCCESS;
2011 }
2012
2013 PrevLink = &HeapMapping->Next;
2014 HeapMapping = HeapMapping->Next;
2015 }
2016
2017 /* We're the first, map the heap */
2018 Offset.QuadPart = 0;
2019 Status = MmMapViewOfSection(pdesk->hsectionDesktop,
2020 PsGetCurrentProcess(),
2021 &UserBase,
2022 0,
2023 0,
2024 &Offset,
2025 &ViewSize,
2026 ViewUnmap,
2027 SEC_NO_CHANGE,
2028 PAGE_EXECUTE_READ); /* Would prefer PAGE_READONLY, but thanks to RTL heaps... */
2029 if (!NT_SUCCESS(Status))
2030 {
2031 ERR("Failed to map desktop\n");
2032 return Status;
2033 }
2034
2035 TRACE("ppi 0x%p mapped heap of desktop 0x%p\n", ppi, pdesk);
2036
2037 /* Add the mapping */
2038 HeapMapping = UserHeapAlloc(sizeof(*HeapMapping));
2039 if (HeapMapping == NULL)
2040 {
2041 MmUnmapViewOfSection(PsGetCurrentProcess(), UserBase);
2042 ERR("UserHeapAlloc() failed!\n");
2043 return STATUS_NO_MEMORY;
2044 }
2045
2046 HeapMapping->Next = NULL;
2047 HeapMapping->KernelMapping = (PVOID)pdesk->pheapDesktop;
2048 HeapMapping->UserMapping = UserBase;
2049 HeapMapping->Limit = ViewSize;
2050 HeapMapping->Count = 1;
2051 *PrevLink = HeapMapping;
2052
2053 ObReferenceObject(pdesk);
2054
2055 return STATUS_SUCCESS;
2056 }
2057
2058 BOOL
2059 IntSetThreadDesktop(IN HDESK hDesktop,
2060 IN BOOL FreeOnFailure)
2061 {
2062 PDESKTOP pdesk = NULL, pdeskOld;
2063 HDESK hdeskOld;
2064 PTHREADINFO pti;
2065 NTSTATUS Status;
2066 PCLIENTTHREADINFO pctiOld, pctiNew = NULL;
2067 PCLIENTINFO pci;
2068
2069 ASSERT(NtCurrentTeb());
2070
2071 TRACE("IntSetThreadDesktop hDesktop:0x%p, FOF:%i\n",hDesktop, FreeOnFailure);
2072
2073 pti = PsGetCurrentThreadWin32Thread();
2074 pci = pti->pClientInfo;
2075
2076 /* If the caller gave us a desktop, ensure it is valid */
2077 if(hDesktop != NULL)
2078 {
2079 /* Validate the new desktop. */
2080 Status = IntValidateDesktopHandle( hDesktop, UserMode, 0, &pdesk);
2081 if (!NT_SUCCESS(Status))
2082 {
2083 ERR("Validation of desktop handle (0x%p) failed\n", hDesktop);
2084 return FALSE;
2085 }
2086
2087 if (pti->rpdesk == pdesk)
2088 {
2089 /* Nothing to do */
2090 ObDereferenceObject(pdesk);
2091 return TRUE;
2092 }
2093 }
2094
2095 /* Make sure that we don't own any window in the current desktop */
2096 if (!IsListEmpty(&pti->WindowListHead))
2097 {
2098 if(pdesk)
2099 ObDereferenceObject(pdesk);
2100 ERR("Attempted to change thread desktop although the thread has windows!\n");
2101 EngSetLastError(ERROR_BUSY);
2102 return FALSE;
2103 }
2104
2105 /* Desktop is being re-set so clear out foreground. */
2106 if (pti->rpdesk != pdesk && pti->MessageQueue == gpqForeground)
2107 {
2108 // Like above, there shouldn't be any windows, hooks or anything active on this threads desktop!
2109 IntSetFocusMessageQueue(NULL);
2110 }
2111
2112 /* Before doing the switch, map the new desktop heap and allocate the new pcti */
2113 if(pdesk != NULL)
2114 {
2115 Status = IntMapDesktopView(pdesk);
2116 if (!NT_SUCCESS(Status))
2117 {
2118 ERR("Failed to map desktop heap!\n");
2119 ObDereferenceObject(pdesk);
2120 SetLastNtError(Status);
2121 return FALSE;
2122 }
2123
2124 pctiNew = DesktopHeapAlloc( pdesk, sizeof(CLIENTTHREADINFO));
2125 if(pctiNew == NULL)
2126 {
2127 ERR("Failed to allocate new pcti\n");
2128 IntUnmapDesktopView(pdesk);
2129 ObDereferenceObject(pdesk);
2130 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
2131 return FALSE;
2132 }
2133 }
2134
2135 /* free all classes or move them to the shared heap */
2136 if(pti->rpdesk != NULL)
2137 {
2138 if(!IntCheckProcessDesktopClasses(pti->rpdesk, FreeOnFailure))
2139 {
2140 ERR("Failed to move process classes to shared heap!\n");
2141 if(pdesk)
2142 {
2143 DesktopHeapFree(pdesk, pctiNew);
2144 IntUnmapDesktopView(pdesk);
2145 ObDereferenceObject(pdesk);
2146 }
2147 return FALSE;
2148 }
2149 }
2150
2151 pdeskOld = pti->rpdesk;
2152 hdeskOld = pti->hdesk;
2153 if (pti->pcti != &pti->cti)
2154 pctiOld = pti->pcti;
2155 else
2156 pctiOld = NULL;
2157
2158 /* do the switch */
2159 if(pdesk != NULL)
2160 {
2161 pti->rpdesk = pdesk;
2162 pti->hdesk = hDesktop;
2163 pti->pDeskInfo = pti->rpdesk->pDeskInfo;
2164 pti->pcti = pctiNew;
2165
2166 pci->ulClientDelta = DesktopHeapGetUserDelta();
2167 pci->pDeskInfo = (PVOID)((ULONG_PTR)pti->pDeskInfo - pci->ulClientDelta);
2168 pci->pClientThreadInfo = (PVOID)((ULONG_PTR)pti->pcti - pci->ulClientDelta);
2169
2170 /* initialize the new pcti */
2171 if(pctiOld != NULL)
2172 {
2173 RtlCopyMemory(pctiNew, pctiOld, sizeof(CLIENTTHREADINFO));
2174 }
2175 else
2176 {
2177 RtlZeroMemory(pctiNew, sizeof(CLIENTTHREADINFO));
2178 pci->fsHooks = pti->fsHooks;
2179 pci->dwTIFlags = pti->TIF_flags;
2180 }
2181 }
2182 else
2183 {
2184 pti->rpdesk = NULL;
2185 pti->hdesk = NULL;
2186 pti->pDeskInfo = NULL;
2187 pti->pcti = &pti->cti; // Always point inside so there will be no crash when posting or sending msg's!
2188 pci->ulClientDelta = 0;
2189 pci->pDeskInfo = NULL;
2190 pci->pClientThreadInfo = NULL;
2191 }
2192
2193 /* clean up the old desktop */
2194 if(pdeskOld != NULL)
2195 {
2196 RemoveEntryList(&pti->PtiLink);
2197 if (pctiOld) DesktopHeapFree(pdeskOld, pctiOld);
2198 IntUnmapDesktopView(pdeskOld);
2199 ObDereferenceObject(pdeskOld);
2200 ZwClose(hdeskOld);
2201 }
2202
2203 if(pdesk)
2204 {
2205 InsertTailList(&pdesk->PtiList, &pti->PtiLink);
2206 }
2207
2208 TRACE("IntSetThreadDesktop: pti 0x%p ppi 0x%p switched from object 0x%p to 0x%p\n", pti, pti->ppi, pdeskOld, pdesk);
2209
2210 return TRUE;
2211 }
2212
2213 /*
2214 * NtUserSetThreadDesktop
2215 *
2216 * Status
2217 * @implemented
2218 */
2219
2220 BOOL APIENTRY
2221 NtUserSetThreadDesktop(HDESK hDesktop)
2222 {
2223 BOOL ret = FALSE;
2224
2225 UserEnterExclusive();
2226
2227 // FIXME: IntSetThreadDesktop validates the desktop handle, it should happen
2228 // here too and set the NT error level. Q. Is it necessary to have the validation
2229 // in IntSetThreadDesktop? Is it needed there too?
2230 if (hDesktop || (!hDesktop && PsGetCurrentProcess() == gpepCSRSS))
2231 ret = IntSetThreadDesktop(hDesktop, FALSE);
2232
2233 UserLeave();
2234
2235 return ret;
2236 }
2237
2238 /* EOF */