2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS Win32k subsystem
5 * FILE: subsystems/win32/win32k/ntuser/desktop.c
6 * PROGRAMMER: Casper S. Hornstrup (chorns@users.sourceforge.net)
9 /* INCLUDES ******************************************************************/
12 DBG_DEFAULT_CHANNEL(UserDesktop
);
14 #include <reactos/buildno.h>
17 UserInitializeDesktop(PDESKTOP pdesk
, PUNICODE_STRING DesktopName
, PWINSTATION_OBJECT pwinsta
);
20 IntMapDesktopView(IN PDESKTOP pdesk
);
23 IntUnmapDesktopView(IN PDESKTOP pdesk
);
26 IntFreeDesktopHeap(IN PDESKTOP pdesk
);
28 /* GLOBALS *******************************************************************/
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.
34 /* Currently active desktop */
35 PDESKTOP gpdeskInputDesktop
= NULL
;
36 HDC ScreenDeviceContext
= NULL
;
37 PTHREADINFO gptiDesktopThread
= NULL
;
38 HCURSOR gDesktopCursor
= NULL
;
40 /* OBJECT CALLBACKS **********************************************************/
44 IntDesktopObjectParse(IN PVOID ParseObject
,
46 IN OUT PACCESS_STATE AccessState
,
47 IN KPROCESSOR_MODE AccessMode
,
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
,
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
;
66 /* Set the list pointers and loop the window station */
67 ListHead
= &WinStaObject
->DesktopListHead
;
68 NextEntry
= ListHead
->Flink
;
69 while (NextEntry
!= ListHead
)
71 /* Get the current desktop */
72 Desktop
= CONTAINING_RECORD(NextEntry
, DESKTOP
, ListEntry
);
74 /* Get the desktop name */
75 ASSERT(Desktop
->pDeskInfo
!= NULL
);
76 RtlInitUnicodeString(&DesktopName
, Desktop
->pDeskInfo
->szDesktopName
);
78 /* Compare the name */
79 if (RtlEqualUnicodeString(RemainingName
,
81 (Attributes
& OBJ_CASE_INSENSITIVE
) != 0))
83 /* We found a match. Did this come from a create? */
86 /* Unless OPEN_IF was given, fail with an error */
87 if (!(Attributes
& OBJ_OPENIF
))
90 return STATUS_OBJECT_NAME_COLLISION
;
94 /* Otherwise, return with a warning only */
95 Status
= STATUS_OBJECT_NAME_EXISTS
;
100 /* This was a real open, so this is OK */
101 Status
= STATUS_SUCCESS
;
104 /* Reference the desktop and return it */
105 ObReferenceObject(Desktop
);
110 /* Go to the next desktop */
111 NextEntry
= NextEntry
->Flink
;
114 /* If we got here but this isn't a create, just fail */
115 if (!Context
) return STATUS_OBJECT_NAME_NOT_FOUND
;
117 /* Create the desktop object */
118 InitializeObjectAttributes(&ObjectAttributes
, RemainingName
, 0, NULL
, NULL
);
119 Status
= ObCreateObject(KernelMode
,
128 if (!NT_SUCCESS(Status
)) return Status
;
130 /* Initialize the desktop */
131 Status
= UserInitializeDesktop(Desktop
, RemainingName
, WinStaObject
);
132 if (!NT_SUCCESS(Status
))
134 ObDereferenceObject(Desktop
);
138 /* Set the desktop object and return success */
141 return STATUS_SUCCESS
;
146 IntDesktopObjectDelete(
147 _In_ PVOID Parameters
)
149 PWIN32_DELETEMETHOD_PARAMETERS DeleteParameters
= Parameters
;
150 PDESKTOP pdesk
= (PDESKTOP
)DeleteParameters
->Object
;
152 TRACE("Deleting desktop object 0x%p\n", pdesk
);
154 if (pdesk
->pDeskInfo
&&
155 pdesk
->pDeskInfo
->spwnd
)
157 ASSERT(pdesk
->pDeskInfo
->spwnd
->spwndChild
== NULL
);
158 co_UserDestroyWindow(pdesk
->pDeskInfo
->spwnd
);
161 if (pdesk
->spwndMessage
)
162 co_UserDestroyWindow(pdesk
->spwndMessage
);
164 /* Remove the desktop from the window station's list of associcated desktops */
165 RemoveEntryList(&pdesk
->ListEntry
);
168 IntFreeDesktopHeap(pdesk
);
170 ObDereferenceObject(pdesk
->rpwinstaParent
);
172 return STATUS_SUCCESS
;
178 _In_ PVOID Parameters
)
180 PWIN32_OKAYTOCLOSEMETHOD_PARAMETERS OkToCloseParameters
= Parameters
;
181 PTHREADINFO pti
= PsGetCurrentThreadWin32Thread();
185 /* This happens when we leak desktop handles */
186 return STATUS_SUCCESS
;
189 /* Do not allow the current desktop or the initial desktop to be closed */
190 if (OkToCloseParameters
->Handle
== pti
->ppi
->hdeskStartup
||
191 OkToCloseParameters
->Handle
== pti
->hdesk
)
193 return STATUS_ACCESS_DENIED
;
196 return STATUS_SUCCESS
;
201 IntDesktopObjectOpen(
202 _In_ PVOID Parameters
)
204 PWIN32_OPENMETHOD_PARAMETERS OpenParameters
= Parameters
;
205 PPROCESSINFO ppi
= PsGetProcessWin32Process(OpenParameters
->Process
);
207 return STATUS_SUCCESS
;
209 return IntMapDesktopView((PDESKTOP
)OpenParameters
->Object
);
214 IntDesktopObjectClose(
215 _In_ PVOID Parameters
)
217 PWIN32_CLOSEMETHOD_PARAMETERS CloseParameters
= Parameters
;
218 PPROCESSINFO ppi
= PsGetProcessWin32Process(CloseParameters
->Process
);
221 /* This happens when the process leaks desktop handles.
222 * At this point the PPROCESSINFO is already destroyed */
223 return STATUS_SUCCESS
;
226 return IntUnmapDesktopView((PDESKTOP
)CloseParameters
->Object
);
230 /* PRIVATE FUNCTIONS **********************************************************/
235 InitDesktopImpl(VOID
)
237 GENERIC_MAPPING IntDesktopMapping
= { DESKTOP_READ
,
242 /* Set Desktop Object Attributes */
243 ExDesktopObjectType
->TypeInfo
.DefaultNonPagedPoolCharge
= sizeof(DESKTOP
);
244 ExDesktopObjectType
->TypeInfo
.GenericMapping
= IntDesktopMapping
;
245 ExDesktopObjectType
->TypeInfo
.ValidAccessMask
= DESKTOP_ALL_ACCESS
;
246 return STATUS_SUCCESS
;
250 GetSystemVersionString(OUT PWSTR pwszzVersion
,
252 IN BOOLEAN InSafeMode
,
253 IN BOOLEAN AppendNtSystemRoot
)
257 RTL_OSVERSIONINFOEXW VerInfo
;
258 UNICODE_STRING CSDVersionString
;
260 UNICODE_STRING CurBuildNmString
;
262 RTL_QUERY_REGISTRY_TABLE VersionConfigurationTable
[2] =
266 RTL_QUERY_REGISTRY_DIRECT
,
275 RTL_QUERY_REGISTRY_DIRECT
,
276 L
"CurrentBuildNumber",
285 WCHAR VersionBuffer
[256];
288 VerInfo
.dwOSVersionInfoSize
= sizeof(VerInfo
);
291 * This call is uniquely used to retrieve the current CSD numbers.
292 * All the rest (major, minor, ...) is either retrieved from the
293 * SharedUserData structure, or from the registry.
295 RtlGetVersion((PRTL_OSVERSIONINFOW
)&VerInfo
);
298 * In kernel-mode, szCSDVersion is not initialized. Initialize it
299 * and query its value from the registry.
301 RtlZeroMemory(VerInfo
.szCSDVersion
, sizeof(VerInfo
.szCSDVersion
));
302 RtlInitEmptyUnicodeString(&CSDVersionString
,
303 VerInfo
.szCSDVersion
,
304 sizeof(VerInfo
.szCSDVersion
));
305 Status
= RtlQueryRegistryValues(RTL_REGISTRY_WINDOWS_NT
,
307 VersionConfigurationTable
,
310 if (!NT_SUCCESS(Status
))
312 /* Indicate nothing is there */
313 CSDVersionString
.Length
= 0;
316 CSDVersionString
.Buffer
[CSDVersionString
.Length
/ sizeof(WCHAR
)] = UNICODE_NULL
;
318 EndBuffer
= VersionBuffer
;
319 if ( /* VerInfo.wServicePackMajor != 0 && */ CSDVersionString
.Length
)
321 /* Print the version string */
322 Status
= RtlStringCbPrintfExW(VersionBuffer
,
323 sizeof(VersionBuffer
),
329 if (!NT_SUCCESS(Status
))
331 /* No version, NULL-terminate the string */
332 *EndBuffer
= UNICODE_NULL
;
337 /* No version, NULL-terminate the string */
338 *EndBuffer
= UNICODE_NULL
;
343 /* String for Safe Mode */
344 Status
= RtlStringCchPrintfW(pwszzVersion
,
346 L
"ReactOS Version %S %S (NT %u.%u Build %u%s)\n",
348 KERNEL_VERSION_BUILD_STR
, // Same as the "BuildLab" string in the registry
349 SharedUserData
->NtMajorVersion
,
350 SharedUserData
->NtMinorVersion
,
351 (VerInfo
.dwBuildNumber
& 0xFFFF),
354 if (AppendNtSystemRoot
&& NT_SUCCESS(Status
))
356 Status
= RtlStringCbPrintfW(VersionBuffer
,
357 sizeof(VersionBuffer
),
359 SharedUserData
->NtSystemRoot
);
360 if (NT_SUCCESS(Status
))
362 /* Replace the last newline by a NULL, before concatenating */
363 EndBuffer
= wcsrchr(pwszzVersion
, L
'\n');
364 if (EndBuffer
) *EndBuffer
= UNICODE_NULL
;
366 /* The concatenated string has a terminating newline */
367 Status
= RtlStringCchCatW(pwszzVersion
,
370 if (!NT_SUCCESS(Status
))
372 /* Concatenation failed, put back the newline */
373 if (EndBuffer
) *EndBuffer
= L
'\n';
377 /* Override any failures as the NtSystemRoot string is optional */
378 Status
= STATUS_SUCCESS
;
383 /* Multi-string for Normal Mode */
384 Status
= RtlStringCchPrintfW(pwszzVersion
,
386 L
"ReactOS Version %S\n"
388 L
"Reporting NT %u.%u (Build %u%s)\n",
390 KERNEL_VERSION_BUILD_STR
, // Same as the "BuildLab" string in the registry
391 SharedUserData
->NtMajorVersion
,
392 SharedUserData
->NtMinorVersion
,
393 (VerInfo
.dwBuildNumber
& 0xFFFF),
396 if (AppendNtSystemRoot
&& NT_SUCCESS(Status
))
398 Status
= RtlStringCbPrintfW(VersionBuffer
,
399 sizeof(VersionBuffer
),
401 SharedUserData
->NtSystemRoot
);
402 if (NT_SUCCESS(Status
))
404 Status
= RtlStringCchCatW(pwszzVersion
,
409 /* Override any failures as the NtSystemRoot string is optional */
410 Status
= STATUS_SUCCESS
;
414 if (!NT_SUCCESS(Status
))
416 /* Fall-back string */
417 Status
= RtlStringCchPrintfW(pwszzVersion
,
419 L
"ReactOS Version %S %S\n",
421 KERNEL_VERSION_BUILD_STR
);
422 if (!NT_SUCCESS(Status
))
424 /* General failure, NULL-terminate the string */
425 pwszzVersion
[0] = UNICODE_NULL
;
430 * Convert the string separators (newlines) into NULLs
431 * and NULL-terminate the multi-string.
433 while (*pwszzVersion
)
435 EndBuffer
= wcschr(pwszzVersion
, L
'\n');
436 if (!EndBuffer
) break;
437 pwszzVersion
= EndBuffer
;
439 *pwszzVersion
++ = UNICODE_NULL
;
441 *pwszzVersion
= UNICODE_NULL
;
448 IntParseDesktopPath(PEPROCESS Process
,
449 PUNICODE_STRING DesktopPath
,
453 OBJECT_ATTRIBUTES ObjectAttributes
;
454 UNICODE_STRING ObjectName
;
456 WCHAR wstrWinstaFullName
[MAX_PATH
], *pwstrWinsta
= NULL
, *pwstrDesktop
= NULL
;
465 if (DesktopPath
->Buffer
!= NULL
&& DesktopPath
->Length
> sizeof(WCHAR
))
468 * Parse the desktop path string which can be in the form "WinSta\Desktop"
469 * or just "Desktop". In latter case WinSta0 will be used.
472 pwstrDesktop
= wcschr(DesktopPath
->Buffer
, L
'\\');
473 if (pwstrDesktop
!= NULL
)
477 pwstrWinsta
= DesktopPath
->Buffer
;
481 pwstrDesktop
= DesktopPath
->Buffer
;
485 TRACE("IntParseDesktopPath pwstrWinsta:%S pwstrDesktop:%S\n", pwstrWinsta
, pwstrDesktop
);
489 /* Search the process handle table for (inherited) window station
490 handles, use a more appropriate one than WinSta0 if possible. */
491 if (!ObFindHandleForObject(Process
,
493 ExWindowStationObjectType
,
498 /* We had no luck searching for opened handles, use WinSta0 now */
500 pwstrWinsta
= L
"WinSta0";
504 /* Search the process handle table for (inherited) desktop
505 handles, use a more appropriate one than Default if possible. */
506 if (!ObFindHandleForObject(Process
,
513 /* We had no luck searching for opened handles, use Desktop now */
515 pwstrDesktop
= L
"Default";
518 if (*hWinSta
== NULL
)
520 swprintf(wstrWinstaFullName
, L
"%wZ\\%ws", &gustrWindowStationsDir
, pwstrWinsta
);
521 RtlInitUnicodeString( &ObjectName
, wstrWinstaFullName
);
523 TRACE("parsed initial winsta: %wZ\n", &ObjectName
);
525 /* Open the window station */
526 InitializeObjectAttributes(&ObjectAttributes
,
528 OBJ_CASE_INSENSITIVE
,
532 Status
= ObOpenObjectByName(&ObjectAttributes
,
533 ExWindowStationObjectType
,
540 if (!NT_SUCCESS(Status
))
542 SetLastNtError(Status
);
543 ERR("Failed to reference window station %wZ PID: --!\n", &ObjectName
);
548 if (*hDesktop
== NULL
)
550 RtlInitUnicodeString(&ObjectName
, pwstrDesktop
);
552 TRACE("parsed initial desktop: %wZ\n", &ObjectName
);
554 /* Open the desktop object */
555 InitializeObjectAttributes(&ObjectAttributes
,
557 OBJ_CASE_INSENSITIVE
,
561 Status
= ObOpenObjectByName(&ObjectAttributes
,
569 if (!NT_SUCCESS(Status
))
574 SetLastNtError(Status
);
575 ERR("Failed to reference desktop %wZ PID: --!\n", &ObjectName
);
579 return STATUS_SUCCESS
;
583 * IntValidateDesktopHandle
585 * Validates the desktop handle.
588 * If the function succeeds, the handle remains referenced. If the
589 * fucntion fails, last error is set.
593 IntValidateDesktopHandle(
595 KPROCESSOR_MODE AccessMode
,
596 ACCESS_MASK DesiredAccess
,
601 Status
= ObReferenceObjectByHandle(
609 TRACE("IntValidateDesktopHandle: handle:0x%p obj:0x%p access:0x%x Status:0x%lx\n",
610 Desktop
, *Object
, DesiredAccess
, Status
);
612 if (!NT_SUCCESS(Status
))
613 SetLastNtError(Status
);
619 IntGetActiveDesktop(VOID
)
621 return gpdeskInputDesktop
;
625 * Returns or creates a handle to the desktop object
628 IntGetDesktopObjectHandle(PDESKTOP DesktopObject
)
633 ASSERT(DesktopObject
);
635 if (!ObFindHandleForObject(PsGetCurrentProcess(),
641 Status
= ObOpenObjectByPointer(DesktopObject
,
648 if (!NT_SUCCESS(Status
))
650 /* Unable to create a handle */
651 ERR("Unable to create a desktop handle\n");
657 TRACE("Got handle: %p\n", Ret
);
663 PUSER_MESSAGE_QUEUE FASTCALL
664 IntGetFocusMessageQueue(VOID
)
666 PDESKTOP pdo
= IntGetActiveDesktop();
669 TRACE("No active desktop\n");
672 return (PUSER_MESSAGE_QUEUE
)pdo
->ActiveMessageQueue
;
676 IntSetFocusMessageQueue(PUSER_MESSAGE_QUEUE NewQueue
)
678 PUSER_MESSAGE_QUEUE Old
;
679 PDESKTOP pdo
= IntGetActiveDesktop();
682 TRACE("No active desktop\n");
685 if (NewQueue
!= NULL
)
687 if (NewQueue
->Desktop
!= NULL
)
689 TRACE("Message Queue already attached to another desktop!\n");
692 IntReferenceMessageQueue(NewQueue
);
693 (void)InterlockedExchangePointer((PVOID
*)&NewQueue
->Desktop
, pdo
);
695 Old
= (PUSER_MESSAGE_QUEUE
)InterlockedExchangePointer((PVOID
*)&pdo
->ActiveMessageQueue
, NewQueue
);
698 (void)InterlockedExchangePointer((PVOID
*)&Old
->Desktop
, 0);
699 gpqForegroundPrev
= Old
;
700 IntDereferenceMessageQueue(Old
);
702 // Only one Q can have active foreground even when there are more than one desktop.
705 gpqForeground
= pdo
->ActiveMessageQueue
;
709 gpqForeground
= NULL
;
710 ERR("ptiLastInput is CLEARED!!\n");
711 ptiLastInput
= NULL
; // ReactOS hacks,,,, should check for process death.
716 IntGetThreadDesktopWindow(PTHREADINFO pti
)
718 if (!pti
) pti
= PsGetCurrentThreadWin32Thread();
719 if (pti
->pDeskInfo
) return pti
->pDeskInfo
->spwnd
;
723 PWND FASTCALL
co_GetDesktopWindow(PWND pWnd
)
725 if (pWnd
->head
.rpdesk
&&
726 pWnd
->head
.rpdesk
->pDeskInfo
)
727 return pWnd
->head
.rpdesk
->pDeskInfo
->spwnd
;
731 HWND FASTCALL
IntGetDesktopWindow(VOID
)
733 PDESKTOP pdo
= IntGetActiveDesktop();
736 TRACE("No active desktop\n");
739 return pdo
->DesktopWindow
;
742 PWND FASTCALL
UserGetDesktopWindow(VOID
)
744 PDESKTOP pdo
= IntGetActiveDesktop();
748 TRACE("No active desktop\n");
751 // return pdo->pDeskInfo->spwnd;
752 return UserGetWindowObject(pdo
->DesktopWindow
);
755 HWND FASTCALL
IntGetMessageWindow(VOID
)
757 PDESKTOP pdo
= IntGetActiveDesktop();
761 TRACE("No active desktop\n");
764 return pdo
->spwndMessage
->head
.h
;
767 PWND FASTCALL
UserGetMessageWindow(VOID
)
769 PDESKTOP pdo
= IntGetActiveDesktop();
773 TRACE("No active desktop\n");
776 return pdo
->spwndMessage
;
779 HWND FASTCALL
IntGetCurrentThreadDesktopWindow(VOID
)
781 PTHREADINFO pti
= PsGetCurrentThreadWin32Thread();
782 PDESKTOP pdo
= pti
->rpdesk
;
785 ERR("Thread doesn't have a desktop\n");
788 return pdo
->DesktopWindow
;
791 /* PUBLIC FUNCTIONS ***********************************************************/
794 DesktopWindowProc(PWND Wnd
, UINT Msg
, WPARAM wParam
, LPARAM lParam
, LRESULT
*lResult
)
798 //ERR("DesktopWindowProc\n");
807 Wnd
->fnid
= FNID_DESKTOP
;
809 *lResult
= (LRESULT
)TRUE
;
813 Value
= HandleToULong(PsGetCurrentProcessId());
815 co_UserSetWindowLong(UserHMGetHandle(Wnd
), DT_GWL_PROCESSID
, Value
, FALSE
);
816 Value
= HandleToULong(PsGetCurrentThreadId());
818 co_UserSetWindowLong(UserHMGetHandle(Wnd
), DT_GWL_THREADID
, Value
, FALSE
);
822 case WM_DISPLAYCHANGE
:
823 co_WinPosSetWindowPos(Wnd
, 0, 0, 0, LOWORD(lParam
), HIWORD(lParam
), SWP_NOZORDER
| SWP_NOACTIVATE
);
827 IntPaintDesktop((HDC
)wParam
);
833 if (IntBeginPaint(Wnd
, &Ps
))
835 IntEndPaint(Wnd
, &Ps
);
839 case WM_SYSCOLORCHANGE
:
840 co_UserRedrawWindow(Wnd
, NULL
, NULL
, RDW_INVALIDATE
|RDW_ERASE
|RDW_ALLCHILDREN
);
845 PCURICON_OBJECT pcurOld
, pcurNew
;
846 pcurNew
= UserGetCurIconObject(gDesktopCursor
);
852 pcurNew
->CURSORF_flags
|= CURSORF_CURRENT
;
853 pcurOld
= UserSetCursor(pcurNew
, FALSE
);
856 pcurOld
->CURSORF_flags
&= ~CURSORF_CURRENT
;
857 UserDereferenceObject(pcurOld
);
862 case WM_WINDOWPOSCHANGING
:
864 PWINDOWPOS pWindowPos
= (PWINDOWPOS
)lParam
;
865 if ((pWindowPos
->flags
& SWP_SHOWWINDOW
) != 0)
867 HDESK hdesk
= IntGetDesktopObjectHandle(gpdeskInputDesktop
);
868 IntSetThreadDesktop(hdesk
, FALSE
);
873 TRACE("DWP calling IDWP Msg %d\n",Msg
);
874 //*lResult = IntDefWindowProc(Wnd, Msg, wParam, lParam, FALSE);
876 return TRUE
; /* We are done. Do not do any callbacks to user mode */
880 UserMessageWindowProc(PWND pwnd
, UINT Msg
, WPARAM wParam
, LPARAM lParam
, LRESULT
*lResult
)
887 pwnd
->fnid
|= FNID_MESSAGEWND
;
888 *lResult
= (LRESULT
)TRUE
;
891 pwnd
->fnid
|= FNID_DESTROY
;
894 ERR("UMWP calling IDWP\n");
895 *lResult
= IntDefWindowProc(pwnd
, Msg
, wParam
, lParam
, FALSE
);
898 return TRUE
; /* We are done. Do not do any callbacks to user mode */
901 VOID NTAPI
DesktopThreadMain(VOID
)
906 gptiDesktopThread
= PsGetCurrentThreadWin32Thread();
908 UserEnterExclusive();
910 /* Register system classes. This thread does not belong to any desktop so the
911 classes will be allocated from the shared heap */
912 UserRegisterSystemClasses();
916 Ret
= co_IntGetPeekMessage(&Msg
, 0, 0, 0, PM_REMOVE
, TRUE
);
919 IntDispatchMessage(&Msg
);
927 UserGetDesktopDC(ULONG DcType
, BOOL EmptyDC
, BOOL ValidatehWnd
)
929 PWND DesktopObject
= 0;
932 if (DcType
== DC_TYPE_DIRECT
)
934 DesktopObject
= UserGetDesktopWindow();
935 DesktopHDC
= (HDC
)UserGetWindowDC(DesktopObject
);
939 PMONITOR pMonitor
= UserGetPrimaryMonitor();
940 DesktopHDC
= IntGdiCreateDisplayDC(pMonitor
->hDev
, DcType
, EmptyDC
);
947 UserRedrawDesktop(VOID
)
952 Window
= UserGetDesktopWindow();
953 Rgn
= IntSysCreateRectpRgnIndirect(&Window
->rcWindow
);
955 IntInvalidateWindows( Window
,
967 co_IntShowDesktop(PDESKTOP Desktop
, ULONG Width
, ULONG Height
, BOOL bRedraw
)
969 PWND pwnd
= Desktop
->pDeskInfo
->spwnd
;
970 UINT flags
= SWP_NOACTIVATE
|SWP_NOZORDER
|SWP_SHOWWINDOW
;
974 flags
|= SWP_NOREDRAW
;
976 co_WinPosSetWindowPos(pwnd
, NULL
, 0, 0, Width
, Height
, flags
);
979 co_UserRedrawWindow( pwnd
, NULL
, 0, RDW_UPDATENOW
| RDW_ALLCHILDREN
| RDW_INVALIDATE
);
981 return STATUS_SUCCESS
;
985 IntHideDesktop(PDESKTOP Desktop
)
989 DesktopWnd
= IntGetWindowObject(Desktop
->DesktopWindow
);
992 return ERROR_INVALID_WINDOW_HANDLE
;
994 DesktopWnd
->style
&= ~WS_VISIBLE
;
996 return STATUS_SUCCESS
;
1001 UserBuildShellHookHwndList(PDESKTOP Desktop
)
1004 PLIST_ENTRY ListEntry
;
1005 PSHELL_HOOK_WINDOW Current
;
1008 /* FIXME: If we save nb elements in desktop, we don't have to loop to find nb entries */
1009 ListEntry
= Desktop
->ShellHookWindows
.Flink
;
1010 while (ListEntry
!= &Desktop
->ShellHookWindows
)
1012 ListEntry
= ListEntry
->Flink
;
1016 if (!entries
) return NULL
;
1018 list
= ExAllocatePoolWithTag(PagedPool
, sizeof(HWND
) * (entries
+ 1), USERTAG_WINDOWLIST
); /* alloc one extra for nullterm */
1021 HWND
* cursor
= list
;
1023 ListEntry
= Desktop
->ShellHookWindows
.Flink
;
1024 while (ListEntry
!= &Desktop
->ShellHookWindows
)
1026 Current
= CONTAINING_RECORD(ListEntry
, SHELL_HOOK_WINDOW
, ListEntry
);
1027 ListEntry
= ListEntry
->Flink
;
1028 *cursor
++ = Current
->hWnd
;
1031 *cursor
= NULL
; /* Nullterm list */
1038 * Send the Message to the windows registered for ShellHook
1039 * notifications. The lParam contents depend on the Message. See
1040 * MSDN for more details (RegisterShellHookWindow)
1042 VOID
co_IntShellHookNotify(WPARAM Message
, WPARAM wParam
, LPARAM lParam
)
1044 PDESKTOP Desktop
= IntGetActiveDesktop();
1047 if (!gpsi
->uiShellMsg
)
1049 gpsi
->uiShellMsg
= IntAddAtom(L
"SHELLHOOK");
1051 TRACE("MsgType = %x\n", gpsi
->uiShellMsg
);
1052 if (!gpsi
->uiShellMsg
)
1053 ERR("LastError: %x\n", EngGetLastError());
1058 TRACE("IntShellHookNotify: No desktop!\n");
1062 // Allow other devices have a shot at foreground.
1063 if (Message
== HSHELL_APPCOMMAND
) ptiLastInput
= NULL
;
1065 // FIXME: System Tray Support.
1067 HwndList
= UserBuildShellHookHwndList(Desktop
);
1070 HWND
* cursor
= HwndList
;
1072 for (; *cursor
; cursor
++)
1074 TRACE("Sending notify\n");
1075 UserPostMessage(*cursor
,
1078 (Message
== HSHELL_LANGUAGE
? lParam
: (LPARAM
)wParam
) );
1079 /* co_IntPostOrSendMessage(*cursor,
1082 (Message == HSHELL_LANGUAGE ? lParam : (LPARAM)wParam) );*/
1085 ExFreePoolWithTag(HwndList
, USERTAG_WINDOWLIST
);
1088 if (ISITHOOKED(WH_SHELL
))
1090 co_HOOK_CallHooks(WH_SHELL
, Message
, wParam
, lParam
);
1095 * Add the window to the ShellHookWindows list. The windows
1096 * on that list get notifications that are important to shell
1097 * type applications.
1099 * TODO: Validate the window? I'm not sure if sending these messages to
1100 * an unsuspecting application that is not your own is a nice thing to do.
1102 BOOL
IntRegisterShellHookWindow(HWND hWnd
)
1104 PTHREADINFO pti
= PsGetCurrentThreadWin32Thread();
1105 PDESKTOP Desktop
= pti
->rpdesk
;
1106 PSHELL_HOOK_WINDOW Entry
;
1108 TRACE("IntRegisterShellHookWindow\n");
1110 /* First deregister the window, so we can be sure it's never twice in the
1113 IntDeRegisterShellHookWindow(hWnd
);
1115 Entry
= ExAllocatePoolWithTag(PagedPool
,
1116 sizeof(SHELL_HOOK_WINDOW
),
1124 InsertTailList(&Desktop
->ShellHookWindows
, &Entry
->ListEntry
);
1130 * Remove the window from the ShellHookWindows list. The windows
1131 * on that list get notifications that are important to shell
1132 * type applications.
1134 BOOL
IntDeRegisterShellHookWindow(HWND hWnd
)
1136 PTHREADINFO pti
= PsGetCurrentThreadWin32Thread();
1137 PDESKTOP Desktop
= pti
->rpdesk
;
1138 PLIST_ENTRY ListEntry
;
1139 PSHELL_HOOK_WINDOW Current
;
1141 ListEntry
= Desktop
->ShellHookWindows
.Flink
;
1142 while (ListEntry
!= &Desktop
->ShellHookWindows
)
1144 Current
= CONTAINING_RECORD(ListEntry
, SHELL_HOOK_WINDOW
, ListEntry
);
1145 ListEntry
= ListEntry
->Flink
;
1146 if (Current
->hWnd
== hWnd
)
1148 RemoveEntryList(&Current
->ListEntry
);
1149 ExFreePoolWithTag(Current
, TAG_WINSTA
);
1158 IntFreeDesktopHeap(IN OUT PDESKTOP Desktop
)
1160 /* FIXME: Disable until unmapping works in mm */
1162 if (Desktop
->pheapDesktop
!= NULL
)
1164 MmUnmapViewInSessionSpace(Desktop
->pheapDesktop
);
1165 Desktop
->pheapDesktop
= NULL
;
1168 if (Desktop
->hsectionDesktop
!= NULL
)
1170 ObDereferenceObject(Desktop
->hsectionDesktop
);
1171 Desktop
->hsectionDesktop
= NULL
;
1177 IntPaintDesktop(HDC hDC
)
1179 static WCHAR s_wszSafeMode
[] = L
"Safe Mode"; // FIXME: Localize!
1182 HBRUSH DesktopBrush
, PreviousBrush
;
1184 BOOL doPatBlt
= TRUE
;
1188 if (GdiGetClipBox(hDC
, &Rect
) == ERROR
)
1191 hWndDesktop
= IntGetDesktopWindow(); // rpdesk->DesktopWindow;
1193 WndDesktop
= UserGetWindowObject(hWndDesktop
); // rpdesk->pDeskInfo->spwnd;
1197 /* Retrieve the current SafeMode state */
1198 InSafeMode
= (UserGetSystemMetrics(SM_CLEANBOOT
) != 0); // gpsi->aiSysMet[SM_CLEANBOOT];
1202 DesktopBrush
= (HBRUSH
)WndDesktop
->pcls
->hbrBackground
;
1205 * Paint desktop background
1207 if (gspv
.hbmWallpaper
!= NULL
)
1213 sz
.cx
= WndDesktop
->rcWindow
.right
- WndDesktop
->rcWindow
.left
;
1214 sz
.cy
= WndDesktop
->rcWindow
.bottom
- WndDesktop
->rcWindow
.top
;
1216 if (gspv
.WallpaperMode
== wmStretch
||
1217 gspv
.WallpaperMode
== wmTile
)
1224 /* Find the upper left corner, can be negative if the bitmap is bigger than the screen */
1225 x
= (sz
.cx
/ 2) - (gspv
.cxWallpaper
/ 2);
1226 y
= (sz
.cy
/ 2) - (gspv
.cyWallpaper
/ 2);
1229 hWallpaperDC
= NtGdiCreateCompatibleDC(hDC
);
1230 if (hWallpaperDC
!= NULL
)
1234 /* Fill in the area that the bitmap is not going to cover */
1237 /* FIXME: Clip out the bitmap
1238 can be replaced with "NtGdiPatBlt(hDC, x, y, gspv.cxWallpaper, gspv.cyWallpaper, PATCOPY | DSTINVERT);"
1239 once we support DSTINVERT */
1240 PreviousBrush
= NtGdiSelectBrush(hDC
, DesktopBrush
);
1241 NtGdiPatBlt(hDC
, Rect
.left
, Rect
.top
, Rect
.right
, Rect
.bottom
, PATCOPY
);
1242 NtGdiSelectBrush(hDC
, PreviousBrush
);
1245 /* Do not fill the background after it is painted no matter the size of the picture */
1248 hOldBitmap
= NtGdiSelectBitmap(hWallpaperDC
, gspv
.hbmWallpaper
);
1250 if (gspv
.WallpaperMode
== wmStretch
)
1252 if (Rect
.right
&& Rect
.bottom
)
1253 NtGdiStretchBlt(hDC
,
1266 else if (gspv
.WallpaperMode
== wmTile
)
1268 /* Paint the bitmap across the screen then down */
1269 for (y
= 0; y
< Rect
.bottom
; y
+= gspv
.cyWallpaper
)
1271 for (x
= 0; x
< Rect
.right
; x
+= gspv
.cxWallpaper
)
1301 NtGdiSelectBitmap(hWallpaperDC
, hOldBitmap
);
1302 NtGdiDeleteObjectApp(hWallpaperDC
);
1308 /* Black desktop background in Safe Mode */
1309 DesktopBrush
= StockObjects
[BLACK_BRUSH
];
1312 /* Background is set to none, clear the screen */
1315 PreviousBrush
= NtGdiSelectBrush(hDC
, DesktopBrush
);
1316 NtGdiPatBlt(hDC
, Rect
.left
, Rect
.top
, Rect
.right
, Rect
.bottom
, PATCOPY
);
1317 NtGdiSelectBrush(hDC
, PreviousBrush
);
1321 * Display the system version on the desktop background
1323 if (InSafeMode
|| g_AlwaysDisplayVersion
|| g_PaintDesktopVersion
)
1326 static WCHAR wszzVersion
[1024] = L
"\0";
1328 /* Only used in normal mode */
1329 // We expect at most 4 strings (3 for version, 1 for optional NtSystemRoot)
1330 static POLYTEXTW VerStrs
[4] = {{0},{0},{0},{0}};
1334 HFONT hFont1
= NULL
, hFont2
= NULL
, hOldFont
= NULL
;
1335 COLORREF crText
, color_old
;
1340 if (!UserSystemParametersInfo(SPI_GETWORKAREA
, 0, &Rect
, 0))
1342 Rect
.right
= UserGetSystemMetrics(SM_CXSCREEN
);
1343 Rect
.bottom
= UserGetSystemMetrics(SM_CYSCREEN
);
1347 * Set up the fonts (otherwise use default ones)
1350 /* Font for the principal version string */
1351 hFont1
= GreCreateFontIndirectW(&gspv
.ncm
.lfCaptionFont
);
1352 /* Font for the secondary version strings */
1353 hFont2
= GreCreateFontIndirectW(&gspv
.ncm
.lfMenuFont
);
1356 hOldFont
= NtGdiSelectFont(hDC
, hFont1
);
1358 if (gspv
.hbmWallpaper
== NULL
)
1360 /* Retrieve the brush fill colour */
1361 // TODO: The following code constitutes "GreGetBrushColor".
1362 PreviousBrush
= NtGdiSelectBrush(hDC
, DesktopBrush
);
1363 pdc
= DC_LockDc(hDC
);
1366 crText
= pdc
->eboFill
.ulRGBColor
;
1371 crText
= RGB(0, 0, 0);
1373 NtGdiSelectBrush(hDC
, PreviousBrush
);
1375 /* Adjust text colour according to the brush */
1376 if (GetRValue(crText
) + GetGValue(crText
) + GetBValue(crText
) > 128 * 3)
1377 crText
= RGB(0, 0, 0);
1379 crText
= RGB(255, 255, 255);
1383 /* Always use white when the text is displayed on top of a wallpaper */
1384 crText
= RGB(255, 255, 255);
1387 color_old
= IntGdiSetTextColor(hDC
, crText
);
1388 align_old
= IntGdiSetTextAlign(hDC
, TA_RIGHT
);
1389 mode_old
= IntGdiSetBkMode(hDC
, TRANSPARENT
);
1391 /* Display the system version information */
1394 Status
= GetSystemVersionString(wszzVersion
,
1395 ARRAYSIZE(wszzVersion
),
1397 g_AlwaysDisplayVersion
);
1398 if (!InSafeMode
&& NT_SUCCESS(Status
) && *wszzVersion
)
1400 PWCHAR pstr
= wszzVersion
;
1401 for (i
= 0; (i
< ARRAYSIZE(VerStrs
)) && *pstr
; ++i
)
1403 VerStrs
[i
].n
= wcslen(pstr
);
1404 VerStrs
[i
].lpstr
= pstr
;
1405 pstr
+= (VerStrs
[i
].n
+ 1);
1411 Status
= STATUS_SUCCESS
;
1413 if (NT_SUCCESS(Status
) && *wszzVersion
)
1418 LONG TotalHeight
= 0;
1420 /* Normal Mode: multiple version information text separated by newlines */
1421 IntGdiSetTextAlign(hDC
, TA_RIGHT
| TA_BOTTOM
);
1423 /* Compute the heights of the strings */
1424 if (hFont1
) NtGdiSelectFont(hDC
, hFont1
);
1425 for (i
= 0; i
< ARRAYSIZE(VerStrs
); ++i
)
1427 if (!VerStrs
[i
].lpstr
|| !*VerStrs
[i
].lpstr
|| (VerStrs
[i
].n
== 0))
1430 GreGetTextExtentW(hDC
, VerStrs
[i
].lpstr
, VerStrs
[i
].n
, &Size
, 1);
1431 VerStrs
[i
].y
= Size
.cy
; // Store the string height
1432 TotalHeight
+= Size
.cy
;
1434 /* While the first string was using hFont1, all the others use hFont2 */
1435 if (hFont2
) NtGdiSelectFont(hDC
, hFont2
);
1437 /* The total height must not exceed the screen height */
1438 TotalHeight
= min(TotalHeight
, Rect
.bottom
);
1440 /* Display the strings */
1441 if (hFont1
) NtGdiSelectFont(hDC
, hFont1
);
1442 for (i
= 0; i
< ARRAYSIZE(VerStrs
); ++i
)
1444 if (!VerStrs
[i
].lpstr
|| !*VerStrs
[i
].lpstr
|| (VerStrs
[i
].n
== 0))
1447 TotalHeight
-= VerStrs
[i
].y
;
1450 Rect
.bottom
- TotalHeight
- 5,
1456 /* While the first string was using hFont1, all the others use hFont2 */
1457 if (hFont2
) NtGdiSelectFont(hDC
, hFont2
);
1462 if (hFont1
) NtGdiSelectFont(hDC
, hFont1
);
1464 /* Safe Mode: single version information text in top center */
1465 len
= wcslen(wszzVersion
);
1467 IntGdiSetTextAlign(hDC
, TA_CENTER
| TA_TOP
);
1468 GreExtTextOutW(hDC
, (Rect
.right
+ Rect
.left
)/2, Rect
.top
+ 3, 0, NULL
, wszzVersion
, len
, NULL
, 0);
1474 if (hFont1
) NtGdiSelectFont(hDC
, hFont1
);
1476 /* Print Safe Mode text in corners */
1477 len
= wcslen(s_wszSafeMode
);
1479 IntGdiSetTextAlign(hDC
, TA_LEFT
| TA_TOP
);
1480 GreExtTextOutW(hDC
, Rect
.left
, Rect
.top
+ 3, 0, NULL
, s_wszSafeMode
, len
, NULL
, 0);
1481 IntGdiSetTextAlign(hDC
, TA_RIGHT
| TA_TOP
);
1482 GreExtTextOutW(hDC
, Rect
.right
, Rect
.top
+ 3, 0, NULL
, s_wszSafeMode
, len
, NULL
, 0);
1483 IntGdiSetTextAlign(hDC
, TA_LEFT
| TA_BOTTOM
);
1484 GreExtTextOutW(hDC
, Rect
.left
, Rect
.bottom
- 5, 0, NULL
, s_wszSafeMode
, len
, NULL
, 0);
1485 IntGdiSetTextAlign(hDC
, TA_RIGHT
| TA_BOTTOM
);
1486 GreExtTextOutW(hDC
, Rect
.right
, Rect
.bottom
- 5, 0, NULL
, s_wszSafeMode
, len
, NULL
, 0);
1489 IntGdiSetBkMode(hDC
, mode_old
);
1490 IntGdiSetTextAlign(hDC
, align_old
);
1491 IntGdiSetTextColor(hDC
, color_old
);
1494 GreDeleteObject(hFont2
);
1498 NtGdiSelectFont(hDC
, hOldFont
);
1499 GreDeleteObject(hFont1
);
1507 UserInitializeDesktop(PDESKTOP pdesk
, PUNICODE_STRING DesktopName
, PWINSTATION_OBJECT pwinsta
)
1509 PVOID DesktopHeapSystemBase
= NULL
;
1510 ULONG_PTR HeapSize
= gdwDesktopSectionSize
* 1024;
1511 SIZE_T DesktopInfoSize
;
1514 TRACE("UserInitializeDesktop desktop 0x%p with name %wZ\n", pdesk
, DesktopName
);
1516 RtlZeroMemory(pdesk
, sizeof(DESKTOP
));
1518 /* Link the desktop with the parent window station */
1519 ObReferenceObject(pwinsta
);
1520 pdesk
->rpwinstaParent
= pwinsta
;
1521 InsertTailList(&pwinsta
->DesktopListHead
, &pdesk
->ListEntry
);
1523 /* Create the desktop heap */
1524 pdesk
->hsectionDesktop
= NULL
;
1525 pdesk
->pheapDesktop
= UserCreateHeap(&pdesk
->hsectionDesktop
,
1526 &DesktopHeapSystemBase
,
1528 if (pdesk
->pheapDesktop
== NULL
)
1530 ERR("Failed to create desktop heap!\n");
1531 return STATUS_NO_MEMORY
;
1534 /* Create DESKTOPINFO */
1535 DesktopInfoSize
= sizeof(DESKTOPINFO
) + DesktopName
->Length
+ sizeof(WCHAR
);
1536 pdesk
->pDeskInfo
= RtlAllocateHeap(pdesk
->pheapDesktop
,
1537 HEAP_NO_SERIALIZE
| HEAP_ZERO_MEMORY
,
1539 if (pdesk
->pDeskInfo
== NULL
)
1541 ERR("Failed to create the DESKTOP structure!\n");
1542 return STATUS_NO_MEMORY
;
1545 /* Initialize the DESKTOPINFO */
1546 pdesk
->pDeskInfo
->pvDesktopBase
= DesktopHeapSystemBase
;
1547 pdesk
->pDeskInfo
->pvDesktopLimit
= (PVOID
)((ULONG_PTR
)DesktopHeapSystemBase
+ HeapSize
);
1548 RtlCopyMemory(pdesk
->pDeskInfo
->szDesktopName
,
1549 DesktopName
->Buffer
,
1550 DesktopName
->Length
+ sizeof(WCHAR
));
1551 for (i
= 0; i
< NB_HOOKS
; i
++)
1553 InitializeListHead(&pdesk
->pDeskInfo
->aphkStart
[i
]);
1556 InitializeListHead(&pdesk
->ShellHookWindows
);
1557 InitializeListHead(&pdesk
->PtiList
);
1559 return STATUS_SUCCESS
;
1562 /* SYSCALLS *******************************************************************/
1565 * NtUserCreateDesktop
1567 * Creates a new desktop.
1571 * Object Attributes.
1574 * Name of the device.
1580 * Interaction flags.
1583 * Requested type of access.
1587 * If the function succeeds, the return value is a handle to the newly
1588 * created desktop. If the specified desktop already exists, the function
1589 * succeeds and returns a handle to the existing desktop. When you are
1590 * finished using the handle, call the CloseDesktop function to close it.
1591 * If the function fails, the return value is NULL.
1598 NtUserCreateDesktop(
1599 POBJECT_ATTRIBUTES ObjectAttributes
,
1600 PUNICODE_STRING lpszDesktopDevice
,
1603 ACCESS_MASK dwDesiredAccess
)
1605 PDESKTOP pdesk
= NULL
;
1606 NTSTATUS Status
= STATUS_SUCCESS
;
1608 BOOLEAN Context
= FALSE
;
1609 UNICODE_STRING ClassName
;
1610 LARGE_STRING WindowName
;
1611 BOOL NoHooks
= FALSE
;
1614 PTHREADINFO ptiCurrent
;
1617 DECLARE_RETURN(HDESK
);
1619 TRACE("Enter NtUserCreateDesktop\n");
1620 UserEnterExclusive();
1622 ptiCurrent
= PsGetCurrentThreadWin32Thread();
1624 ASSERT(gptiDesktopThread
);
1626 /* Turn off hooks when calling any CreateWindowEx from inside win32k. */
1627 NoHooks
= (ptiCurrent
->TIF_flags
& TIF_DISABLEHOOKS
);
1628 ptiCurrent
->TIF_flags
|= TIF_DISABLEHOOKS
;
1629 ptiCurrent
->pClientInfo
->dwTIFlags
= ptiCurrent
->TIF_flags
;
1632 * Try to open already existing desktop
1634 Status
= ObOpenObjectByName(
1636 ExDesktopObjectType
,
1642 if (!NT_SUCCESS(Status
))
1644 ERR("ObOpenObjectByName failed to open/create desktop\n");
1645 SetLastNtError(Status
);
1649 /* In case the object was not created (eg if it existed), return now */
1650 if (Context
== FALSE
)
1652 TRACE("NtUserCreateDesktop opened desktop %wZ\n", ObjectAttributes
->ObjectName
);
1656 /* Reference the desktop */
1657 Status
= ObReferenceObjectByHandle(hdesk
,
1659 ExDesktopObjectType
,
1663 if (!NT_SUCCESS(Status
))
1665 ERR("Failed to reference desktop object\n");
1666 SetLastNtError(Status
);
1670 /* Get the desktop window class. The thread desktop does not belong to any desktop
1671 * so the classes created there (including the desktop class) are allocated in the shared heap
1672 * It would cause problems if we used a class that belongs to the caller
1674 ClassName
.Buffer
= WC_DESKTOP
;
1675 ClassName
.Length
= 0;
1676 pcls
= IntGetAndReferenceClass(&ClassName
, 0, TRUE
);
1683 RtlZeroMemory(&WindowName
, sizeof(WindowName
));
1684 RtlZeroMemory(&Cs
, sizeof(Cs
));
1685 Cs
.x
= UserGetSystemMetrics(SM_XVIRTUALSCREEN
),
1686 Cs
.y
= UserGetSystemMetrics(SM_YVIRTUALSCREEN
),
1687 Cs
.cx
= UserGetSystemMetrics(SM_CXVIRTUALSCREEN
),
1688 Cs
.cy
= UserGetSystemMetrics(SM_CYVIRTUALSCREEN
),
1689 Cs
.style
= WS_POPUP
|WS_CLIPCHILDREN
;
1690 Cs
.hInstance
= hModClient
; // hModuleWin; // Server side winproc!
1691 Cs
.lpszName
= (LPCWSTR
) &WindowName
;
1692 Cs
.lpszClass
= (LPCWSTR
) &ClassName
;
1694 /* Use IntCreateWindow instead of co_UserCreateWindowEx cause the later expects a thread with a desktop */
1695 pWnd
= IntCreateWindow(&Cs
, &WindowName
, pcls
, NULL
, NULL
, NULL
, pdesk
);
1698 ERR("Failed to create desktop window for the new desktop\n");
1702 pdesk
->dwSessionId
= PsGetCurrentProcessSessionId();
1703 pdesk
->DesktopWindow
= pWnd
->head
.h
;
1704 pdesk
->pDeskInfo
->spwnd
= pWnd
;
1705 pWnd
->fnid
= FNID_DESKTOP
;
1707 ClassName
.Buffer
= MAKEINTATOM(gpsi
->atomSysClass
[ICLS_HWNDMESSAGE
]);
1708 ClassName
.Length
= 0;
1709 pcls
= IntGetAndReferenceClass(&ClassName
, 0, TRUE
);
1716 RtlZeroMemory(&WindowName
, sizeof(WindowName
));
1717 RtlZeroMemory(&Cs
, sizeof(Cs
));
1718 Cs
.cx
= Cs
.cy
= 100;
1719 Cs
.style
= WS_POPUP
|WS_CLIPCHILDREN
;
1720 Cs
.hInstance
= hModClient
; // hModuleWin; // Server side winproc!
1721 Cs
.lpszName
= (LPCWSTR
) &WindowName
;
1722 Cs
.lpszClass
= (LPCWSTR
) &ClassName
;
1723 pWnd
= IntCreateWindow(&Cs
, &WindowName
, pcls
, NULL
, NULL
, NULL
, pdesk
);
1726 ERR("Failed to create message window for the new desktop\n");
1730 pdesk
->spwndMessage
= pWnd
;
1731 pWnd
->fnid
= FNID_MESSAGEWND
;
1734 if !(WinStaObject->Flags & WSF_NOIO) is (not set) for desktop input output mode (see wiki)
1735 Create Tooltip. Saved in DesktopObject->spwndTooltip.
1736 Tooltip dwExStyle: WS_EX_TOOLWINDOW|WS_EX_TOPMOST
1737 hWndParent are spwndMessage. Use hModuleWin for server side winproc!
1738 The rest is same as message window.
1739 http://msdn.microsoft.com/en-us/library/bb760250(VS.85).aspx
1746 ObDereferenceObject(pdesk
);
1748 if (_ret_
== NULL
&& hdesk
!= NULL
)
1750 ObCloseHandle(hdesk
, UserMode
);
1754 ptiCurrent
->TIF_flags
&= ~TIF_DISABLEHOOKS
;
1755 ptiCurrent
->pClientInfo
->dwTIFlags
= ptiCurrent
->TIF_flags
;
1757 TRACE("Leave NtUserCreateDesktop, ret=%p\n",_ret_
);
1765 * Opens an existing desktop.
1769 * Name of the existing desktop.
1772 * Interaction flags.
1775 * Requested type of access.
1778 * Handle to the desktop or zero on failure.
1786 POBJECT_ATTRIBUTES ObjectAttributes
,
1788 ACCESS_MASK dwDesiredAccess
)
1793 Status
= ObOpenObjectByName(
1795 ExDesktopObjectType
,
1802 if (!NT_SUCCESS(Status
))
1804 ERR("Failed to open desktop\n");
1805 SetLastNtError(Status
);
1809 TRACE("Opened desktop %S with handle 0x%p\n", ObjectAttributes
->ObjectName
->Buffer
, Desktop
);
1815 * NtUserOpenInputDesktop
1817 * Opens the input (interactive) desktop.
1821 * Interaction flags.
1824 * Inheritance option.
1827 * Requested type of access.
1830 * Handle to the input desktop or zero on failure.
1837 NtUserOpenInputDesktop(
1840 ACCESS_MASK dwDesiredAccess
)
1844 ULONG HandleAttributes
= 0;
1846 UserEnterExclusive();
1847 TRACE("Enter NtUserOpenInputDesktop gpdeskInputDesktop 0x%p\n",gpdeskInputDesktop
);
1849 if (fInherit
) HandleAttributes
= OBJ_INHERIT
;
1851 /* Create a new handle to the object */
1852 Status
= ObOpenObjectByPointer(
1857 ExDesktopObjectType
,
1861 if (!NT_SUCCESS(Status
))
1863 ERR("Failed to open input desktop object\n");
1864 SetLastNtError(Status
);
1867 TRACE("NtUserOpenInputDesktop returning 0x%p\n",hdesk
);
1873 * NtUserCloseDesktop
1875 * Closes a desktop handle.
1879 * Handle to the desktop.
1885 * The desktop handle can be created with NtUserCreateDesktop or
1886 * NtUserOpenDesktop. This function will fail if any thread in the calling
1887 * process is using the specified desktop handle or if the handle refers
1888 * to the initial desktop of the calling process.
1895 NtUserCloseDesktop(HDESK hDesktop
)
1899 DECLARE_RETURN(BOOL
);
1901 TRACE("NtUserCloseDesktop called (0x%p)\n", hDesktop
);
1902 UserEnterExclusive();
1904 if (hDesktop
== gptiCurrent
->hdesk
|| hDesktop
== gptiCurrent
->ppi
->hdeskStartup
)
1906 ERR("Attempted to close thread desktop\n");
1907 EngSetLastError(ERROR_BUSY
);
1911 Status
= IntValidateDesktopHandle( hDesktop
, UserMode
, 0, &pdesk
);
1912 if (!NT_SUCCESS(Status
))
1914 ERR("Validation of desktop handle (0x%p) failed\n", hDesktop
);
1918 ObDereferenceObject(pdesk
);
1920 Status
= ZwClose(hDesktop
);
1921 if (!NT_SUCCESS(Status
))
1923 ERR("Failed to close desktop handle 0x%p\n", hDesktop
);
1924 SetLastNtError(Status
);
1931 TRACE("Leave NtUserCloseDesktop, ret=%i\n",_ret_
);
1937 * NtUserPaintDesktop
1939 * The NtUserPaintDesktop function fills the clipping region in the
1940 * specified device context with the desktop pattern or wallpaper. The
1941 * function is provided primarily for shell desktops.
1945 * Handle to the device context.
1952 NtUserPaintDesktop(HDC hDC
)
1955 UserEnterExclusive();
1956 TRACE("Enter NtUserPaintDesktop\n");
1957 Ret
= IntPaintDesktop(hDC
);
1958 TRACE("Leave NtUserPaintDesktop, ret=%i\n",Ret
);
1964 * NtUserResolveDesktop
1966 * The NtUserResolveDesktop function retrieves handles to the desktop and
1967 * the window station specified by the desktop path string.
1971 * Handle to a user process.
1974 * The desktop path string.
1977 * Handle to the desktop (direct return value) and
1978 * handle to the associated window station (by pointer).
1979 * NULL in case of failure.
1982 * Callable by CSRSS only.
1990 NtUserResolveDesktop(
1991 IN HANDLE ProcessHandle
,
1992 IN PUNICODE_STRING DesktopPath
,
1994 OUT HWINSTA
* phWinSta
)
1997 PEPROCESS Process
= NULL
;
1998 HWINSTA hWinSta
= NULL
;
1999 HDESK hDesktop
= NULL
;
2001 /* Allow only the Console Server to perform this operation (via CSRSS) */
2002 if (PsGetCurrentProcess() != gpepCSRSS
)
2005 /* Get the process object the user handle was referencing */
2006 Status
= ObReferenceObjectByHandle(ProcessHandle
,
2007 PROCESS_QUERY_INFORMATION
,
2012 if (!NT_SUCCESS(Status
)) return NULL
;
2014 // UserEnterShared();
2018 UNICODE_STRING CapturedDesktopPath
;
2020 /* Capture the user desktop path string */
2021 Status
= IntSafeCopyUnicodeStringTerminateNULL(&CapturedDesktopPath
,
2023 if (!NT_SUCCESS(Status
)) _SEH2_YIELD(goto Quit
);
2025 /* Call the internal function */
2026 Status
= IntParseDesktopPath(Process
,
2027 &CapturedDesktopPath
,
2030 if (!NT_SUCCESS(Status
))
2032 ERR("IntParseDesktopPath failed, Status = 0x%08lx\n", Status
);
2037 /* Return the window station handle */
2038 *phWinSta
= hWinSta
;
2040 /* Free the captured string */
2041 if (CapturedDesktopPath
.Buffer
)
2042 ExFreePoolWithTag(CapturedDesktopPath
.Buffer
, TAG_STRING
);
2044 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2046 Status
= _SEH2_GetExceptionCode();
2053 /* Dereference the process object */
2054 ObDereferenceObject(Process
);
2056 /* Return the desktop handle */
2061 * NtUserSwitchDesktop
2063 * Sets the current input (interactive) desktop.
2067 * Handle to desktop.
2077 NtUserSwitchDesktop(HDESK hdesk
)
2081 BOOL bRedrawDesktop
;
2082 DECLARE_RETURN(BOOL
);
2084 UserEnterExclusive();
2085 TRACE("Enter NtUserSwitchDesktop(0x%p)\n", hdesk
);
2087 Status
= IntValidateDesktopHandle( hdesk
, UserMode
, 0, &pdesk
);
2088 if (!NT_SUCCESS(Status
))
2090 ERR("Validation of desktop handle (0x%p) failed\n", hdesk
);
2094 if (PsGetCurrentProcessSessionId() != pdesk
->rpwinstaParent
->dwSessionId
)
2096 ObDereferenceObject(pdesk
);
2097 ERR("NtUserSwitchDesktop called for a desktop of a different session\n");
2101 if (pdesk
== gpdeskInputDesktop
)
2103 ObDereferenceObject(pdesk
);
2104 WARN("NtUserSwitchDesktop called for active desktop\n");
2109 * Don't allow applications switch the desktop if it's locked, unless the caller
2110 * is the logon application itself
2112 if ((pdesk
->rpwinstaParent
->Flags
& WSS_LOCKED
) &&
2113 gpidLogon
!= PsGetCurrentProcessId())
2115 ObDereferenceObject(pdesk
);
2116 ERR("Switching desktop 0x%p denied because the window station is locked!\n", hdesk
);
2120 if (pdesk
->rpwinstaParent
!= InputWindowStation
)
2122 ObDereferenceObject(pdesk
);
2123 ERR("Switching desktop 0x%p denied because desktop doesn't belong to the interactive winsta!\n", hdesk
);
2127 /* FIXME: Fail if the process is associated with a secured
2128 desktop such as Winlogon or Screen-Saver */
2129 /* FIXME: Connect to input device */
2131 TRACE("Switching from desktop 0x%p to 0x%p\n", gpdeskInputDesktop
, pdesk
);
2133 bRedrawDesktop
= FALSE
;
2135 /* The first time SwitchDesktop is called, gpdeskInputDesktop is NULL */
2136 if (gpdeskInputDesktop
!= NULL
)
2138 if ((gpdeskInputDesktop
->pDeskInfo
->spwnd
->style
& WS_VISIBLE
) == WS_VISIBLE
)
2139 bRedrawDesktop
= TRUE
;
2141 /* Hide the previous desktop window */
2142 IntHideDesktop(gpdeskInputDesktop
);
2145 /* Set the active desktop in the desktop's window station. */
2146 InputWindowStation
->ActiveDesktop
= pdesk
;
2148 /* Set the global state. */
2149 gpdeskInputDesktop
= pdesk
;
2151 /* Show the new desktop window */
2152 co_IntShowDesktop(pdesk
, UserGetSystemMetrics(SM_CXSCREEN
), UserGetSystemMetrics(SM_CYSCREEN
), bRedrawDesktop
);
2154 TRACE("SwitchDesktop gpdeskInputDesktop 0x%p\n",gpdeskInputDesktop
);
2155 ObDereferenceObject(pdesk
);
2160 TRACE("Leave NtUserSwitchDesktop, ret=%i\n",_ret_
);
2166 * NtUserGetThreadDesktop
2173 NtUserGetThreadDesktop(DWORD dwThreadId
, DWORD Unknown1
)
2177 PDESKTOP DesktopObject
;
2178 HDESK Ret
, hThreadDesktop
;
2179 OBJECT_HANDLE_INFORMATION HandleInformation
;
2180 DECLARE_RETURN(HDESK
);
2182 UserEnterExclusive();
2183 TRACE("Enter NtUserGetThreadDesktop\n");
2187 EngSetLastError(ERROR_INVALID_PARAMETER
);
2191 Status
= PsLookupThreadByThreadId((HANDLE
)(DWORD_PTR
)dwThreadId
, &Thread
);
2192 if (!NT_SUCCESS(Status
))
2194 EngSetLastError(ERROR_INVALID_PARAMETER
);
2198 if (Thread
->ThreadsProcess
== PsGetCurrentProcess())
2200 /* Just return the handle, we queried the desktop handle of a thread running
2201 in the same context */
2202 Ret
= ((PTHREADINFO
)Thread
->Tcb
.Win32Thread
)->hdesk
;
2203 ObDereferenceObject(Thread
);
2207 /* Get the desktop handle and the desktop of the thread */
2208 if (!(hThreadDesktop
= ((PTHREADINFO
)Thread
->Tcb
.Win32Thread
)->hdesk
) ||
2209 !(DesktopObject
= ((PTHREADINFO
)Thread
->Tcb
.Win32Thread
)->rpdesk
))
2211 ObDereferenceObject(Thread
);
2212 ERR("Desktop information of thread 0x%x broken!?\n", dwThreadId
);
2216 /* We could just use DesktopObject instead of looking up the handle, but latter
2217 may be a bit safer (e.g. when the desktop is being destroyed */
2218 /* Switch into the context of the thread we're trying to get the desktop from,
2219 so we can use the handle */
2220 KeAttachProcess(&Thread
->ThreadsProcess
->Pcb
);
2221 Status
= ObReferenceObjectByHandle(hThreadDesktop
,
2223 ExDesktopObjectType
,
2225 (PVOID
*)&DesktopObject
,
2226 &HandleInformation
);
2229 /* The handle couldn't be found, there's nothing to get... */
2230 if (!NT_SUCCESS(Status
))
2232 ObDereferenceObject(Thread
);
2236 /* Lookup our handle table if we can find a handle to the desktop object,
2237 if not, create one */
2238 Ret
= IntGetDesktopObjectHandle(DesktopObject
);
2240 /* All done, we got a valid handle to the desktop */
2241 ObDereferenceObject(DesktopObject
);
2242 ObDereferenceObject(Thread
);
2246 TRACE("Leave NtUserGetThreadDesktop, ret=%p\n",_ret_
);
2252 IntUnmapDesktopView(IN PDESKTOP pdesk
)
2255 PW32HEAP_USER_MAPPING HeapMapping
, *PrevLink
;
2256 NTSTATUS Status
= STATUS_SUCCESS
;
2258 TRACE("IntUnmapDesktopView called for desktop object %p\n", pdesk
);
2260 ppi
= PsGetCurrentProcessWin32Process();
2263 * Unmap if we're the last thread using the desktop.
2264 * Start the search at the next mapping: skip the first entry
2265 * as it must be the global user heap mapping.
2267 PrevLink
= &ppi
->HeapMappings
.Next
;
2268 HeapMapping
= *PrevLink
;
2269 while (HeapMapping
!= NULL
)
2271 if (HeapMapping
->KernelMapping
== (PVOID
)pdesk
->pheapDesktop
)
2273 if (--HeapMapping
->Count
== 0)
2275 *PrevLink
= HeapMapping
->Next
;
2277 TRACE("ppi 0x%p unmapped heap of desktop 0x%p\n", ppi
, pdesk
);
2278 Status
= MmUnmapViewOfSection(PsGetCurrentProcess(),
2279 HeapMapping
->UserMapping
);
2281 ObDereferenceObject(pdesk
);
2283 UserHeapFree(HeapMapping
);
2288 PrevLink
= &HeapMapping
->Next
;
2289 HeapMapping
= HeapMapping
->Next
;
2296 IntMapDesktopView(IN PDESKTOP pdesk
)
2299 PW32HEAP_USER_MAPPING HeapMapping
, *PrevLink
;
2300 PVOID UserBase
= NULL
;
2301 SIZE_T ViewSize
= 0;
2302 LARGE_INTEGER Offset
;
2305 TRACE("IntMapDesktopView called for desktop object 0x%p\n", pdesk
);
2307 ppi
= PsGetCurrentProcessWin32Process();
2310 * Find out if another thread already mapped the desktop heap.
2311 * Start the search at the next mapping: skip the first entry
2312 * as it must be the global user heap mapping.
2314 PrevLink
= &ppi
->HeapMappings
.Next
;
2315 HeapMapping
= *PrevLink
;
2316 while (HeapMapping
!= NULL
)
2318 if (HeapMapping
->KernelMapping
== (PVOID
)pdesk
->pheapDesktop
)
2320 HeapMapping
->Count
++;
2321 return STATUS_SUCCESS
;
2324 PrevLink
= &HeapMapping
->Next
;
2325 HeapMapping
= HeapMapping
->Next
;
2328 /* We're the first, map the heap */
2329 Offset
.QuadPart
= 0;
2330 Status
= MmMapViewOfSection(pdesk
->hsectionDesktop
,
2331 PsGetCurrentProcess(),
2339 PAGE_EXECUTE_READ
); /* Would prefer PAGE_READONLY, but thanks to RTL heaps... */
2340 if (!NT_SUCCESS(Status
))
2342 ERR("Failed to map desktop\n");
2346 TRACE("ppi 0x%p mapped heap of desktop 0x%p\n", ppi
, pdesk
);
2348 /* Add the mapping */
2349 HeapMapping
= UserHeapAlloc(sizeof(*HeapMapping
));
2350 if (HeapMapping
== NULL
)
2352 MmUnmapViewOfSection(PsGetCurrentProcess(), UserBase
);
2353 ERR("UserHeapAlloc() failed!\n");
2354 return STATUS_NO_MEMORY
;
2357 HeapMapping
->Next
= NULL
;
2358 HeapMapping
->KernelMapping
= (PVOID
)pdesk
->pheapDesktop
;
2359 HeapMapping
->UserMapping
= UserBase
;
2360 HeapMapping
->Limit
= ViewSize
;
2361 HeapMapping
->Count
= 1;
2362 *PrevLink
= HeapMapping
;
2364 ObReferenceObject(pdesk
);
2366 return STATUS_SUCCESS
;
2370 IntSetThreadDesktop(IN HDESK hDesktop
,
2371 IN BOOL FreeOnFailure
)
2373 PDESKTOP pdesk
= NULL
, pdeskOld
;
2377 PCLIENTTHREADINFO pctiOld
, pctiNew
= NULL
;
2380 ASSERT(NtCurrentTeb());
2382 TRACE("IntSetThreadDesktop hDesktop:0x%p, FOF:%i\n",hDesktop
, FreeOnFailure
);
2384 pti
= PsGetCurrentThreadWin32Thread();
2385 pci
= pti
->pClientInfo
;
2387 /* If the caller gave us a desktop, ensure it is valid */
2388 if (hDesktop
!= NULL
)
2390 /* Validate the new desktop. */
2391 Status
= IntValidateDesktopHandle( hDesktop
, UserMode
, 0, &pdesk
);
2392 if (!NT_SUCCESS(Status
))
2394 ERR("Validation of desktop handle (0x%p) failed\n", hDesktop
);
2398 if (pti
->rpdesk
== pdesk
)
2401 ObDereferenceObject(pdesk
);
2406 /* Make sure that we don't own any window in the current desktop */
2407 if (!IsListEmpty(&pti
->WindowListHead
))
2410 ObDereferenceObject(pdesk
);
2411 ERR("Attempted to change thread desktop although the thread has windows!\n");
2412 EngSetLastError(ERROR_BUSY
);
2416 /* Desktop is being re-set so clear out foreground. */
2417 if (pti
->rpdesk
!= pdesk
&& pti
->MessageQueue
== gpqForeground
)
2419 // Like above, there shouldn't be any windows, hooks or anything active on this threads desktop!
2420 IntSetFocusMessageQueue(NULL
);
2423 /* Before doing the switch, map the new desktop heap and allocate the new pcti */
2426 Status
= IntMapDesktopView(pdesk
);
2427 if (!NT_SUCCESS(Status
))
2429 ERR("Failed to map desktop heap!\n");
2430 ObDereferenceObject(pdesk
);
2431 SetLastNtError(Status
);
2435 pctiNew
= DesktopHeapAlloc( pdesk
, sizeof(CLIENTTHREADINFO
));
2436 if (pctiNew
== NULL
)
2438 ERR("Failed to allocate new pcti\n");
2439 IntUnmapDesktopView(pdesk
);
2440 ObDereferenceObject(pdesk
);
2441 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY
);
2446 /* free all classes or move them to the shared heap */
2447 if (pti
->rpdesk
!= NULL
)
2449 if (!IntCheckProcessDesktopClasses(pti
->rpdesk
, FreeOnFailure
))
2451 ERR("Failed to move process classes to shared heap!\n");
2454 DesktopHeapFree(pdesk
, pctiNew
);
2455 IntUnmapDesktopView(pdesk
);
2456 ObDereferenceObject(pdesk
);
2462 pdeskOld
= pti
->rpdesk
;
2463 hdeskOld
= pti
->hdesk
;
2464 if (pti
->pcti
!= &pti
->cti
)
2465 pctiOld
= pti
->pcti
;
2472 pti
->rpdesk
= pdesk
;
2473 pti
->hdesk
= hDesktop
;
2474 pti
->pDeskInfo
= pti
->rpdesk
->pDeskInfo
;
2475 pti
->pcti
= pctiNew
;
2477 pci
->ulClientDelta
= DesktopHeapGetUserDelta();
2478 pci
->pDeskInfo
= (PVOID
)((ULONG_PTR
)pti
->pDeskInfo
- pci
->ulClientDelta
);
2479 pci
->pClientThreadInfo
= (PVOID
)((ULONG_PTR
)pti
->pcti
- pci
->ulClientDelta
);
2481 /* initialize the new pcti */
2482 if (pctiOld
!= NULL
)
2484 RtlCopyMemory(pctiNew
, pctiOld
, sizeof(CLIENTTHREADINFO
));
2488 RtlZeroMemory(pctiNew
, sizeof(CLIENTTHREADINFO
));
2489 pci
->fsHooks
= pti
->fsHooks
;
2490 pci
->dwTIFlags
= pti
->TIF_flags
;
2497 pti
->pDeskInfo
= NULL
;
2498 pti
->pcti
= &pti
->cti
; // Always point inside so there will be no crash when posting or sending msg's!
2499 pci
->ulClientDelta
= 0;
2500 pci
->pDeskInfo
= NULL
;
2501 pci
->pClientThreadInfo
= NULL
;
2504 /* clean up the old desktop */
2505 if (pdeskOld
!= NULL
)
2507 RemoveEntryList(&pti
->PtiLink
);
2508 if (pctiOld
) DesktopHeapFree(pdeskOld
, pctiOld
);
2509 IntUnmapDesktopView(pdeskOld
);
2510 ObDereferenceObject(pdeskOld
);
2516 InsertTailList(&pdesk
->PtiList
, &pti
->PtiLink
);
2519 TRACE("IntSetThreadDesktop: pti 0x%p ppi 0x%p switched from object 0x%p to 0x%p\n", pti
, pti
->ppi
, pdeskOld
, pdesk
);
2525 * NtUserSetThreadDesktop
2532 NtUserSetThreadDesktop(HDESK hDesktop
)
2536 UserEnterExclusive();
2538 // FIXME: IntSetThreadDesktop validates the desktop handle, it should happen
2539 // here too and set the NT error level. Q. Is it necessary to have the validation
2540 // in IntSetThreadDesktop? Is it needed there too?
2541 if (hDesktop
|| (!hDesktop
&& PsGetCurrentProcess() == gpepCSRSS
))
2542 ret
= IntSetThreadDesktop(hDesktop
, FALSE
);