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 BuildLabString
;
259 UNICODE_STRING CSDVersionString
;
260 RTL_QUERY_REGISTRY_TABLE VersionConfigurationTable
[] =
264 RTL_QUERY_REGISTRY_DIRECT
,
271 RTL_QUERY_REGISTRY_DIRECT
,
280 WCHAR BuildLabBuffer
[256];
281 WCHAR VersionBuffer
[256];
284 VerInfo
.dwOSVersionInfoSize
= sizeof(VerInfo
);
287 * This call is uniquely used to retrieve the current CSD numbers.
288 * All the rest (major, minor, ...) is either retrieved from the
289 * SharedUserData structure, or from the registry.
291 RtlGetVersion((PRTL_OSVERSIONINFOW
)&VerInfo
);
294 * - Retrieve the BuildLab string from the registry (set by the kernel).
295 * - In kernel-mode, szCSDVersion is not initialized. Initialize it
296 * and query its value from the registry.
298 RtlZeroMemory(BuildLabBuffer
, sizeof(BuildLabBuffer
));
299 RtlInitEmptyUnicodeString(&BuildLabString
,
301 sizeof(BuildLabBuffer
));
302 RtlZeroMemory(VerInfo
.szCSDVersion
, sizeof(VerInfo
.szCSDVersion
));
303 RtlInitEmptyUnicodeString(&CSDVersionString
,
304 VerInfo
.szCSDVersion
,
305 sizeof(VerInfo
.szCSDVersion
));
306 Status
= RtlQueryRegistryValues(RTL_REGISTRY_WINDOWS_NT
,
308 VersionConfigurationTable
,
311 if (!NT_SUCCESS(Status
))
313 /* Indicate nothing is there */
314 BuildLabString
.Length
= 0;
315 CSDVersionString
.Length
= 0;
317 /* NULL-terminate the strings */
318 BuildLabString
.Buffer
[BuildLabString
.Length
/ sizeof(WCHAR
)] = UNICODE_NULL
;
319 CSDVersionString
.Buffer
[CSDVersionString
.Length
/ sizeof(WCHAR
)] = UNICODE_NULL
;
321 EndBuffer
= VersionBuffer
;
322 if ( /* VerInfo.wServicePackMajor != 0 && */ CSDVersionString
.Length
)
324 /* Print the version string */
325 Status
= RtlStringCbPrintfExW(VersionBuffer
,
326 sizeof(VersionBuffer
),
332 if (!NT_SUCCESS(Status
))
334 /* No version, NULL-terminate the string */
335 *EndBuffer
= UNICODE_NULL
;
340 /* No version, NULL-terminate the string */
341 *EndBuffer
= UNICODE_NULL
;
346 /* String for Safe Mode */
347 Status
= RtlStringCchPrintfW(pwszzVersion
,
349 L
"ReactOS Version %S %wZ (NT %u.%u Build %u%s)\n",
352 SharedUserData
->NtMajorVersion
,
353 SharedUserData
->NtMinorVersion
,
354 (VerInfo
.dwBuildNumber
& 0xFFFF),
357 if (AppendNtSystemRoot
&& NT_SUCCESS(Status
))
359 Status
= RtlStringCbPrintfW(VersionBuffer
,
360 sizeof(VersionBuffer
),
362 SharedUserData
->NtSystemRoot
);
363 if (NT_SUCCESS(Status
))
365 /* Replace the last newline by a NULL, before concatenating */
366 EndBuffer
= wcsrchr(pwszzVersion
, L
'\n');
367 if (EndBuffer
) *EndBuffer
= UNICODE_NULL
;
369 /* The concatenated string has a terminating newline */
370 Status
= RtlStringCchCatW(pwszzVersion
,
373 if (!NT_SUCCESS(Status
))
375 /* Concatenation failed, put back the newline */
376 if (EndBuffer
) *EndBuffer
= L
'\n';
380 /* Override any failures as the NtSystemRoot string is optional */
381 Status
= STATUS_SUCCESS
;
386 /* Multi-string for Normal Mode */
387 Status
= RtlStringCchPrintfW(pwszzVersion
,
389 L
"ReactOS Version %S\n"
391 L
"Reporting NT %u.%u (Build %u%s)\n",
394 SharedUserData
->NtMajorVersion
,
395 SharedUserData
->NtMinorVersion
,
396 (VerInfo
.dwBuildNumber
& 0xFFFF),
399 if (AppendNtSystemRoot
&& NT_SUCCESS(Status
))
401 Status
= RtlStringCbPrintfW(VersionBuffer
,
402 sizeof(VersionBuffer
),
404 SharedUserData
->NtSystemRoot
);
405 if (NT_SUCCESS(Status
))
407 Status
= RtlStringCchCatW(pwszzVersion
,
412 /* Override any failures as the NtSystemRoot string is optional */
413 Status
= STATUS_SUCCESS
;
417 if (!NT_SUCCESS(Status
))
419 /* Fall-back string */
420 Status
= RtlStringCchPrintfW(pwszzVersion
,
422 L
"ReactOS Version %S %wZ\n",
425 if (!NT_SUCCESS(Status
))
427 /* General failure, NULL-terminate the string */
428 pwszzVersion
[0] = UNICODE_NULL
;
433 * Convert the string separators (newlines) into NULLs
434 * and NULL-terminate the multi-string.
436 while (*pwszzVersion
)
438 EndBuffer
= wcschr(pwszzVersion
, L
'\n');
439 if (!EndBuffer
) break;
440 pwszzVersion
= EndBuffer
;
442 *pwszzVersion
++ = UNICODE_NULL
;
444 *pwszzVersion
= UNICODE_NULL
;
451 IntParseDesktopPath(PEPROCESS Process
,
452 PUNICODE_STRING DesktopPath
,
456 OBJECT_ATTRIBUTES ObjectAttributes
;
457 UNICODE_STRING ObjectName
;
459 WCHAR wstrWinstaFullName
[MAX_PATH
], *pwstrWinsta
= NULL
, *pwstrDesktop
= NULL
;
468 if (DesktopPath
->Buffer
!= NULL
&& DesktopPath
->Length
> sizeof(WCHAR
))
471 * Parse the desktop path string which can be in the form "WinSta\Desktop"
472 * or just "Desktop". In latter case WinSta0 will be used.
475 pwstrDesktop
= wcschr(DesktopPath
->Buffer
, L
'\\');
476 if (pwstrDesktop
!= NULL
)
480 pwstrWinsta
= DesktopPath
->Buffer
;
484 pwstrDesktop
= DesktopPath
->Buffer
;
488 TRACE("IntParseDesktopPath pwstrWinsta:%S pwstrDesktop:%S\n", pwstrWinsta
, pwstrDesktop
);
492 /* Search the process handle table for (inherited) window station
493 handles, use a more appropriate one than WinSta0 if possible. */
494 if (!ObFindHandleForObject(Process
,
496 ExWindowStationObjectType
,
501 /* We had no luck searching for opened handles, use WinSta0 now */
503 pwstrWinsta
= L
"WinSta0";
507 /* Search the process handle table for (inherited) desktop
508 handles, use a more appropriate one than Default if possible. */
509 if (!ObFindHandleForObject(Process
,
516 /* We had no luck searching for opened handles, use Desktop now */
518 pwstrDesktop
= L
"Default";
521 if (*hWinSta
== NULL
)
523 swprintf(wstrWinstaFullName
, L
"%wZ\\%ws", &gustrWindowStationsDir
, pwstrWinsta
);
524 RtlInitUnicodeString( &ObjectName
, wstrWinstaFullName
);
526 TRACE("parsed initial winsta: %wZ\n", &ObjectName
);
528 /* Open the window station */
529 InitializeObjectAttributes(&ObjectAttributes
,
531 OBJ_CASE_INSENSITIVE
,
535 Status
= ObOpenObjectByName(&ObjectAttributes
,
536 ExWindowStationObjectType
,
543 if (!NT_SUCCESS(Status
))
545 SetLastNtError(Status
);
546 ERR("Failed to reference window station %wZ PID: --!\n", &ObjectName
);
551 if (*hDesktop
== NULL
)
553 RtlInitUnicodeString(&ObjectName
, pwstrDesktop
);
555 TRACE("parsed initial desktop: %wZ\n", &ObjectName
);
557 /* Open the desktop object */
558 InitializeObjectAttributes(&ObjectAttributes
,
560 OBJ_CASE_INSENSITIVE
,
564 Status
= ObOpenObjectByName(&ObjectAttributes
,
572 if (!NT_SUCCESS(Status
))
577 SetLastNtError(Status
);
578 ERR("Failed to reference desktop %wZ PID: --!\n", &ObjectName
);
582 return STATUS_SUCCESS
;
586 * IntValidateDesktopHandle
588 * Validates the desktop handle.
591 * If the function succeeds, the handle remains referenced. If the
592 * fucntion fails, last error is set.
596 IntValidateDesktopHandle(
598 KPROCESSOR_MODE AccessMode
,
599 ACCESS_MASK DesiredAccess
,
604 Status
= ObReferenceObjectByHandle(
612 TRACE("IntValidateDesktopHandle: handle:0x%p obj:0x%p access:0x%x Status:0x%lx\n",
613 Desktop
, *Object
, DesiredAccess
, Status
);
615 if (!NT_SUCCESS(Status
))
616 SetLastNtError(Status
);
622 IntGetActiveDesktop(VOID
)
624 return gpdeskInputDesktop
;
628 * Returns or creates a handle to the desktop object
631 IntGetDesktopObjectHandle(PDESKTOP DesktopObject
)
636 ASSERT(DesktopObject
);
638 if (!ObFindHandleForObject(PsGetCurrentProcess(),
644 Status
= ObOpenObjectByPointer(DesktopObject
,
651 if (!NT_SUCCESS(Status
))
653 /* Unable to create a handle */
654 ERR("Unable to create a desktop handle\n");
660 TRACE("Got handle: %p\n", Ret
);
666 PUSER_MESSAGE_QUEUE FASTCALL
667 IntGetFocusMessageQueue(VOID
)
669 PDESKTOP pdo
= IntGetActiveDesktop();
672 TRACE("No active desktop\n");
675 return (PUSER_MESSAGE_QUEUE
)pdo
->ActiveMessageQueue
;
679 IntSetFocusMessageQueue(PUSER_MESSAGE_QUEUE NewQueue
)
681 PUSER_MESSAGE_QUEUE Old
;
682 PDESKTOP pdo
= IntGetActiveDesktop();
685 TRACE("No active desktop\n");
688 if (NewQueue
!= NULL
)
690 if (NewQueue
->Desktop
!= NULL
)
692 TRACE("Message Queue already attached to another desktop!\n");
695 IntReferenceMessageQueue(NewQueue
);
696 (void)InterlockedExchangePointer((PVOID
*)&NewQueue
->Desktop
, pdo
);
698 Old
= (PUSER_MESSAGE_QUEUE
)InterlockedExchangePointer((PVOID
*)&pdo
->ActiveMessageQueue
, NewQueue
);
701 (void)InterlockedExchangePointer((PVOID
*)&Old
->Desktop
, 0);
702 gpqForegroundPrev
= Old
;
703 IntDereferenceMessageQueue(Old
);
705 // Only one Q can have active foreground even when there are more than one desktop.
708 gpqForeground
= pdo
->ActiveMessageQueue
;
712 gpqForeground
= NULL
;
713 ERR("ptiLastInput is CLEARED!!\n");
714 ptiLastInput
= NULL
; // ReactOS hacks,,,, should check for process death.
719 IntGetThreadDesktopWindow(PTHREADINFO pti
)
721 if (!pti
) pti
= PsGetCurrentThreadWin32Thread();
722 if (pti
->pDeskInfo
) return pti
->pDeskInfo
->spwnd
;
726 PWND FASTCALL
co_GetDesktopWindow(PWND pWnd
)
728 if (pWnd
->head
.rpdesk
&&
729 pWnd
->head
.rpdesk
->pDeskInfo
)
730 return pWnd
->head
.rpdesk
->pDeskInfo
->spwnd
;
734 HWND FASTCALL
IntGetDesktopWindow(VOID
)
736 PDESKTOP pdo
= IntGetActiveDesktop();
739 TRACE("No active desktop\n");
742 return pdo
->DesktopWindow
;
745 PWND FASTCALL
UserGetDesktopWindow(VOID
)
747 PDESKTOP pdo
= IntGetActiveDesktop();
751 TRACE("No active desktop\n");
754 // return pdo->pDeskInfo->spwnd;
755 return UserGetWindowObject(pdo
->DesktopWindow
);
758 HWND FASTCALL
IntGetMessageWindow(VOID
)
760 PDESKTOP pdo
= IntGetActiveDesktop();
764 TRACE("No active desktop\n");
767 return pdo
->spwndMessage
->head
.h
;
770 PWND FASTCALL
UserGetMessageWindow(VOID
)
772 PDESKTOP pdo
= IntGetActiveDesktop();
776 TRACE("No active desktop\n");
779 return pdo
->spwndMessage
;
782 HWND FASTCALL
IntGetCurrentThreadDesktopWindow(VOID
)
784 PTHREADINFO pti
= PsGetCurrentThreadWin32Thread();
785 PDESKTOP pdo
= pti
->rpdesk
;
788 ERR("Thread doesn't have a desktop\n");
791 return pdo
->DesktopWindow
;
794 /* PUBLIC FUNCTIONS ***********************************************************/
797 DesktopWindowProc(PWND Wnd
, UINT Msg
, WPARAM wParam
, LPARAM lParam
, LRESULT
*lResult
)
801 //ERR("DesktopWindowProc\n");
810 Wnd
->fnid
= FNID_DESKTOP
;
812 *lResult
= (LRESULT
)TRUE
;
816 Value
= HandleToULong(PsGetCurrentProcessId());
818 co_UserSetWindowLong(UserHMGetHandle(Wnd
), DT_GWL_PROCESSID
, Value
, FALSE
);
819 Value
= HandleToULong(PsGetCurrentThreadId());
821 co_UserSetWindowLong(UserHMGetHandle(Wnd
), DT_GWL_THREADID
, Value
, FALSE
);
825 case WM_DISPLAYCHANGE
:
826 co_WinPosSetWindowPos(Wnd
, 0, 0, 0, LOWORD(lParam
), HIWORD(lParam
), SWP_NOZORDER
| SWP_NOACTIVATE
);
830 IntPaintDesktop((HDC
)wParam
);
836 if (IntBeginPaint(Wnd
, &Ps
))
838 IntEndPaint(Wnd
, &Ps
);
842 case WM_SYSCOLORCHANGE
:
843 co_UserRedrawWindow(Wnd
, NULL
, NULL
, RDW_INVALIDATE
|RDW_ERASE
|RDW_ALLCHILDREN
);
848 PCURICON_OBJECT pcurOld
, pcurNew
;
849 pcurNew
= UserGetCurIconObject(gDesktopCursor
);
855 pcurNew
->CURSORF_flags
|= CURSORF_CURRENT
;
856 pcurOld
= UserSetCursor(pcurNew
, FALSE
);
859 pcurOld
->CURSORF_flags
&= ~CURSORF_CURRENT
;
860 UserDereferenceObject(pcurOld
);
865 case WM_WINDOWPOSCHANGING
:
867 PWINDOWPOS pWindowPos
= (PWINDOWPOS
)lParam
;
868 if ((pWindowPos
->flags
& SWP_SHOWWINDOW
) != 0)
870 HDESK hdesk
= IntGetDesktopObjectHandle(gpdeskInputDesktop
);
871 IntSetThreadDesktop(hdesk
, FALSE
);
876 TRACE("DWP calling IDWP Msg %d\n",Msg
);
877 //*lResult = IntDefWindowProc(Wnd, Msg, wParam, lParam, FALSE);
879 return TRUE
; /* We are done. Do not do any callbacks to user mode */
883 UserMessageWindowProc(PWND pwnd
, UINT Msg
, WPARAM wParam
, LPARAM lParam
, LRESULT
*lResult
)
890 pwnd
->fnid
|= FNID_MESSAGEWND
;
891 *lResult
= (LRESULT
)TRUE
;
894 pwnd
->fnid
|= FNID_DESTROY
;
897 ERR("UMWP calling IDWP\n");
898 *lResult
= IntDefWindowProc(pwnd
, Msg
, wParam
, lParam
, FALSE
);
901 return TRUE
; /* We are done. Do not do any callbacks to user mode */
904 VOID NTAPI
DesktopThreadMain(VOID
)
909 gptiDesktopThread
= PsGetCurrentThreadWin32Thread();
911 UserEnterExclusive();
913 /* Register system classes. This thread does not belong to any desktop so the
914 classes will be allocated from the shared heap */
915 UserRegisterSystemClasses();
919 Ret
= co_IntGetPeekMessage(&Msg
, 0, 0, 0, PM_REMOVE
, TRUE
);
922 IntDispatchMessage(&Msg
);
930 UserGetDesktopDC(ULONG DcType
, BOOL EmptyDC
, BOOL ValidatehWnd
)
932 PWND DesktopObject
= 0;
935 UserEnterExclusive();
937 if (DcType
== DC_TYPE_DIRECT
)
939 DesktopObject
= UserGetDesktopWindow();
940 DesktopHDC
= (HDC
)UserGetWindowDC(DesktopObject
);
944 PMONITOR pMonitor
= UserGetPrimaryMonitor();
945 DesktopHDC
= IntGdiCreateDisplayDC(pMonitor
->hDev
, DcType
, EmptyDC
);
954 UserRedrawDesktop(VOID
)
959 Window
= UserGetDesktopWindow();
960 Rgn
= IntSysCreateRectpRgnIndirect(&Window
->rcWindow
);
962 IntInvalidateWindows( Window
,
974 co_IntShowDesktop(PDESKTOP Desktop
, ULONG Width
, ULONG Height
, BOOL bRedraw
)
976 PWND pwnd
= Desktop
->pDeskInfo
->spwnd
;
977 UINT flags
= SWP_NOACTIVATE
|SWP_NOZORDER
|SWP_SHOWWINDOW
;
981 flags
|= SWP_NOREDRAW
;
983 co_WinPosSetWindowPos(pwnd
, NULL
, 0, 0, Width
, Height
, flags
);
986 co_UserRedrawWindow( pwnd
, NULL
, 0, RDW_UPDATENOW
| RDW_ALLCHILDREN
| RDW_INVALIDATE
);
988 return STATUS_SUCCESS
;
992 IntHideDesktop(PDESKTOP Desktop
)
996 DesktopWnd
= IntGetWindowObject(Desktop
->DesktopWindow
);
999 return ERROR_INVALID_WINDOW_HANDLE
;
1001 DesktopWnd
->style
&= ~WS_VISIBLE
;
1003 return STATUS_SUCCESS
;
1008 UserBuildShellHookHwndList(PDESKTOP Desktop
)
1011 PLIST_ENTRY ListEntry
;
1012 PSHELL_HOOK_WINDOW Current
;
1015 /* FIXME: If we save nb elements in desktop, we don't have to loop to find nb entries */
1016 ListEntry
= Desktop
->ShellHookWindows
.Flink
;
1017 while (ListEntry
!= &Desktop
->ShellHookWindows
)
1019 ListEntry
= ListEntry
->Flink
;
1023 if (!entries
) return NULL
;
1025 list
= ExAllocatePoolWithTag(PagedPool
, sizeof(HWND
) * (entries
+ 1), USERTAG_WINDOWLIST
); /* alloc one extra for nullterm */
1028 HWND
* cursor
= list
;
1030 ListEntry
= Desktop
->ShellHookWindows
.Flink
;
1031 while (ListEntry
!= &Desktop
->ShellHookWindows
)
1033 Current
= CONTAINING_RECORD(ListEntry
, SHELL_HOOK_WINDOW
, ListEntry
);
1034 ListEntry
= ListEntry
->Flink
;
1035 *cursor
++ = Current
->hWnd
;
1038 *cursor
= NULL
; /* Nullterm list */
1045 * Send the Message to the windows registered for ShellHook
1046 * notifications. The lParam contents depend on the Message. See
1047 * MSDN for more details (RegisterShellHookWindow)
1049 VOID
co_IntShellHookNotify(WPARAM Message
, WPARAM wParam
, LPARAM lParam
)
1051 PDESKTOP Desktop
= IntGetActiveDesktop();
1054 if (!gpsi
->uiShellMsg
)
1056 gpsi
->uiShellMsg
= IntAddAtom(L
"SHELLHOOK");
1058 TRACE("MsgType = %x\n", gpsi
->uiShellMsg
);
1059 if (!gpsi
->uiShellMsg
)
1060 ERR("LastError: %x\n", EngGetLastError());
1065 TRACE("IntShellHookNotify: No desktop!\n");
1069 // Allow other devices have a shot at foreground.
1070 if (Message
== HSHELL_APPCOMMAND
) ptiLastInput
= NULL
;
1072 // FIXME: System Tray Support.
1074 HwndList
= UserBuildShellHookHwndList(Desktop
);
1077 HWND
* cursor
= HwndList
;
1079 for (; *cursor
; cursor
++)
1081 TRACE("Sending notify\n");
1082 UserPostMessage(*cursor
,
1085 (Message
== HSHELL_LANGUAGE
? lParam
: (LPARAM
)wParam
) );
1086 /* co_IntPostOrSendMessage(*cursor,
1089 (Message == HSHELL_LANGUAGE ? lParam : (LPARAM)wParam) );*/
1092 ExFreePoolWithTag(HwndList
, USERTAG_WINDOWLIST
);
1095 if (ISITHOOKED(WH_SHELL
))
1097 co_HOOK_CallHooks(WH_SHELL
, Message
, wParam
, lParam
);
1102 * Add the window to the ShellHookWindows list. The windows
1103 * on that list get notifications that are important to shell
1104 * type applications.
1106 * TODO: Validate the window? I'm not sure if sending these messages to
1107 * an unsuspecting application that is not your own is a nice thing to do.
1109 BOOL
IntRegisterShellHookWindow(HWND hWnd
)
1111 PTHREADINFO pti
= PsGetCurrentThreadWin32Thread();
1112 PDESKTOP Desktop
= pti
->rpdesk
;
1113 PSHELL_HOOK_WINDOW Entry
;
1115 TRACE("IntRegisterShellHookWindow\n");
1117 /* First deregister the window, so we can be sure it's never twice in the
1120 IntDeRegisterShellHookWindow(hWnd
);
1122 Entry
= ExAllocatePoolWithTag(PagedPool
,
1123 sizeof(SHELL_HOOK_WINDOW
),
1131 InsertTailList(&Desktop
->ShellHookWindows
, &Entry
->ListEntry
);
1137 * Remove the window from the ShellHookWindows list. The windows
1138 * on that list get notifications that are important to shell
1139 * type applications.
1141 BOOL
IntDeRegisterShellHookWindow(HWND hWnd
)
1143 PTHREADINFO pti
= PsGetCurrentThreadWin32Thread();
1144 PDESKTOP Desktop
= pti
->rpdesk
;
1145 PLIST_ENTRY ListEntry
;
1146 PSHELL_HOOK_WINDOW Current
;
1148 ListEntry
= Desktop
->ShellHookWindows
.Flink
;
1149 while (ListEntry
!= &Desktop
->ShellHookWindows
)
1151 Current
= CONTAINING_RECORD(ListEntry
, SHELL_HOOK_WINDOW
, ListEntry
);
1152 ListEntry
= ListEntry
->Flink
;
1153 if (Current
->hWnd
== hWnd
)
1155 RemoveEntryList(&Current
->ListEntry
);
1156 ExFreePoolWithTag(Current
, TAG_WINSTA
);
1165 IntFreeDesktopHeap(IN OUT PDESKTOP Desktop
)
1167 /* FIXME: Disable until unmapping works in mm */
1169 if (Desktop
->pheapDesktop
!= NULL
)
1171 MmUnmapViewInSessionSpace(Desktop
->pheapDesktop
);
1172 Desktop
->pheapDesktop
= NULL
;
1175 if (Desktop
->hsectionDesktop
!= NULL
)
1177 ObDereferenceObject(Desktop
->hsectionDesktop
);
1178 Desktop
->hsectionDesktop
= NULL
;
1184 IntPaintDesktop(HDC hDC
)
1186 static WCHAR s_wszSafeMode
[] = L
"Safe Mode"; // FIXME: Localize!
1189 HBRUSH DesktopBrush
, PreviousBrush
;
1191 BOOL doPatBlt
= TRUE
;
1195 if (GdiGetClipBox(hDC
, &Rect
) == ERROR
)
1198 hWndDesktop
= IntGetDesktopWindow(); // rpdesk->DesktopWindow;
1200 WndDesktop
= UserGetWindowObject(hWndDesktop
); // rpdesk->pDeskInfo->spwnd;
1204 /* Retrieve the current SafeMode state */
1205 InSafeMode
= (UserGetSystemMetrics(SM_CLEANBOOT
) != 0); // gpsi->aiSysMet[SM_CLEANBOOT];
1209 DesktopBrush
= (HBRUSH
)WndDesktop
->pcls
->hbrBackground
;
1212 * Paint desktop background
1214 if (gspv
.hbmWallpaper
!= NULL
)
1220 sz
.cx
= WndDesktop
->rcWindow
.right
- WndDesktop
->rcWindow
.left
;
1221 sz
.cy
= WndDesktop
->rcWindow
.bottom
- WndDesktop
->rcWindow
.top
;
1223 if (gspv
.WallpaperMode
== wmStretch
||
1224 gspv
.WallpaperMode
== wmTile
)
1231 /* Find the upper left corner, can be negative if the bitmap is bigger than the screen */
1232 x
= (sz
.cx
/ 2) - (gspv
.cxWallpaper
/ 2);
1233 y
= (sz
.cy
/ 2) - (gspv
.cyWallpaper
/ 2);
1236 hWallpaperDC
= NtGdiCreateCompatibleDC(hDC
);
1237 if (hWallpaperDC
!= NULL
)
1241 /* Fill in the area that the bitmap is not going to cover */
1244 /* FIXME: Clip out the bitmap
1245 can be replaced with "NtGdiPatBlt(hDC, x, y, gspv.cxWallpaper, gspv.cyWallpaper, PATCOPY | DSTINVERT);"
1246 once we support DSTINVERT */
1247 PreviousBrush
= NtGdiSelectBrush(hDC
, DesktopBrush
);
1248 NtGdiPatBlt(hDC
, Rect
.left
, Rect
.top
, Rect
.right
, Rect
.bottom
, PATCOPY
);
1249 NtGdiSelectBrush(hDC
, PreviousBrush
);
1252 /* Do not fill the background after it is painted no matter the size of the picture */
1255 hOldBitmap
= NtGdiSelectBitmap(hWallpaperDC
, gspv
.hbmWallpaper
);
1257 if (gspv
.WallpaperMode
== wmStretch
)
1259 if (Rect
.right
&& Rect
.bottom
)
1260 NtGdiStretchBlt(hDC
,
1273 else if (gspv
.WallpaperMode
== wmTile
)
1275 /* Paint the bitmap across the screen then down */
1276 for (y
= 0; y
< Rect
.bottom
; y
+= gspv
.cyWallpaper
)
1278 for (x
= 0; x
< Rect
.right
; x
+= gspv
.cxWallpaper
)
1308 NtGdiSelectBitmap(hWallpaperDC
, hOldBitmap
);
1309 NtGdiDeleteObjectApp(hWallpaperDC
);
1315 /* Black desktop background in Safe Mode */
1316 DesktopBrush
= StockObjects
[BLACK_BRUSH
];
1319 /* Background is set to none, clear the screen */
1322 PreviousBrush
= NtGdiSelectBrush(hDC
, DesktopBrush
);
1323 NtGdiPatBlt(hDC
, Rect
.left
, Rect
.top
, Rect
.right
, Rect
.bottom
, PATCOPY
);
1324 NtGdiSelectBrush(hDC
, PreviousBrush
);
1328 * Display the system version on the desktop background
1330 if (InSafeMode
|| g_AlwaysDisplayVersion
|| g_PaintDesktopVersion
)
1333 static WCHAR wszzVersion
[1024] = L
"\0";
1335 /* Only used in normal mode */
1336 // We expect at most 4 strings (3 for version, 1 for optional NtSystemRoot)
1337 static POLYTEXTW VerStrs
[4] = {{0},{0},{0},{0}};
1341 HFONT hFont1
= NULL
, hFont2
= NULL
, hOldFont
= NULL
;
1342 COLORREF crText
, color_old
;
1347 if (!UserSystemParametersInfo(SPI_GETWORKAREA
, 0, &Rect
, 0))
1349 Rect
.left
= Rect
.top
= 0;
1350 Rect
.right
= UserGetSystemMetrics(SM_CXSCREEN
);
1351 Rect
.bottom
= UserGetSystemMetrics(SM_CYSCREEN
);
1355 RECTL_vOffsetRect(&Rect
, -Rect
.left
, -Rect
.top
);
1359 * Set up the fonts (otherwise use default ones)
1362 /* Font for the principal version string */
1363 hFont1
= GreCreateFontIndirectW(&gspv
.ncm
.lfCaptionFont
);
1364 /* Font for the secondary version strings */
1365 hFont2
= GreCreateFontIndirectW(&gspv
.ncm
.lfMenuFont
);
1368 hOldFont
= NtGdiSelectFont(hDC
, hFont1
);
1370 if (gspv
.hbmWallpaper
== NULL
)
1372 /* Retrieve the brush fill colour */
1373 // TODO: The following code constitutes "GreGetBrushColor".
1374 PreviousBrush
= NtGdiSelectBrush(hDC
, DesktopBrush
);
1375 pdc
= DC_LockDc(hDC
);
1378 crText
= pdc
->eboFill
.ulRGBColor
;
1383 crText
= RGB(0, 0, 0);
1385 NtGdiSelectBrush(hDC
, PreviousBrush
);
1387 /* Adjust text colour according to the brush */
1388 if (GetRValue(crText
) + GetGValue(crText
) + GetBValue(crText
) > 128 * 3)
1389 crText
= RGB(0, 0, 0);
1391 crText
= RGB(255, 255, 255);
1395 /* Always use white when the text is displayed on top of a wallpaper */
1396 crText
= RGB(255, 255, 255);
1399 color_old
= IntGdiSetTextColor(hDC
, crText
);
1400 align_old
= IntGdiSetTextAlign(hDC
, TA_RIGHT
);
1401 mode_old
= IntGdiSetBkMode(hDC
, TRANSPARENT
);
1403 /* Display the system version information */
1406 Status
= GetSystemVersionString(wszzVersion
,
1407 ARRAYSIZE(wszzVersion
),
1409 g_AlwaysDisplayVersion
);
1410 if (!InSafeMode
&& NT_SUCCESS(Status
) && *wszzVersion
)
1412 PWCHAR pstr
= wszzVersion
;
1413 for (i
= 0; (i
< ARRAYSIZE(VerStrs
)) && *pstr
; ++i
)
1415 VerStrs
[i
].n
= wcslen(pstr
);
1416 VerStrs
[i
].lpstr
= pstr
;
1417 pstr
+= (VerStrs
[i
].n
+ 1);
1423 Status
= STATUS_SUCCESS
;
1425 if (NT_SUCCESS(Status
) && *wszzVersion
)
1430 LONG TotalHeight
= 0;
1432 /* Normal Mode: multiple version information text separated by newlines */
1433 IntGdiSetTextAlign(hDC
, TA_RIGHT
| TA_BOTTOM
);
1435 /* Compute the heights of the strings */
1436 if (hFont1
) NtGdiSelectFont(hDC
, hFont1
);
1437 for (i
= 0; i
< ARRAYSIZE(VerStrs
); ++i
)
1439 if (!VerStrs
[i
].lpstr
|| !*VerStrs
[i
].lpstr
|| (VerStrs
[i
].n
== 0))
1442 GreGetTextExtentW(hDC
, VerStrs
[i
].lpstr
, VerStrs
[i
].n
, &Size
, 1);
1443 VerStrs
[i
].y
= Size
.cy
; // Store the string height
1444 TotalHeight
+= Size
.cy
;
1446 /* While the first string was using hFont1, all the others use hFont2 */
1447 if (hFont2
) NtGdiSelectFont(hDC
, hFont2
);
1449 /* The total height must not exceed the screen height */
1450 TotalHeight
= min(TotalHeight
, Rect
.bottom
);
1452 /* Display the strings */
1453 if (hFont1
) NtGdiSelectFont(hDC
, hFont1
);
1454 for (i
= 0; i
< ARRAYSIZE(VerStrs
); ++i
)
1456 if (!VerStrs
[i
].lpstr
|| !*VerStrs
[i
].lpstr
|| (VerStrs
[i
].n
== 0))
1459 TotalHeight
-= VerStrs
[i
].y
;
1462 Rect
.bottom
- TotalHeight
- 5,
1468 /* While the first string was using hFont1, all the others use hFont2 */
1469 if (hFont2
) NtGdiSelectFont(hDC
, hFont2
);
1474 if (hFont1
) NtGdiSelectFont(hDC
, hFont1
);
1476 /* Safe Mode: single version information text in top center */
1477 len
= wcslen(wszzVersion
);
1479 IntGdiSetTextAlign(hDC
, TA_CENTER
| TA_TOP
);
1480 GreExtTextOutW(hDC
, (Rect
.right
+ Rect
.left
)/2, Rect
.top
+ 3, 0, NULL
, wszzVersion
, len
, NULL
, 0);
1486 if (hFont1
) NtGdiSelectFont(hDC
, hFont1
);
1488 /* Print Safe Mode text in corners */
1489 len
= wcslen(s_wszSafeMode
);
1491 IntGdiSetTextAlign(hDC
, TA_LEFT
| TA_TOP
);
1492 GreExtTextOutW(hDC
, Rect
.left
, Rect
.top
+ 3, 0, NULL
, s_wszSafeMode
, len
, NULL
, 0);
1493 IntGdiSetTextAlign(hDC
, TA_RIGHT
| TA_TOP
);
1494 GreExtTextOutW(hDC
, Rect
.right
, Rect
.top
+ 3, 0, NULL
, s_wszSafeMode
, len
, NULL
, 0);
1495 IntGdiSetTextAlign(hDC
, TA_LEFT
| TA_BOTTOM
);
1496 GreExtTextOutW(hDC
, Rect
.left
, Rect
.bottom
- 5, 0, NULL
, s_wszSafeMode
, len
, NULL
, 0);
1497 IntGdiSetTextAlign(hDC
, TA_RIGHT
| TA_BOTTOM
);
1498 GreExtTextOutW(hDC
, Rect
.right
, Rect
.bottom
- 5, 0, NULL
, s_wszSafeMode
, len
, NULL
, 0);
1501 IntGdiSetBkMode(hDC
, mode_old
);
1502 IntGdiSetTextAlign(hDC
, align_old
);
1503 IntGdiSetTextColor(hDC
, color_old
);
1506 GreDeleteObject(hFont2
);
1510 NtGdiSelectFont(hDC
, hOldFont
);
1511 GreDeleteObject(hFont1
);
1519 UserInitializeDesktop(PDESKTOP pdesk
, PUNICODE_STRING DesktopName
, PWINSTATION_OBJECT pwinsta
)
1521 PVOID DesktopHeapSystemBase
= NULL
;
1522 ULONG_PTR HeapSize
= gdwDesktopSectionSize
* 1024;
1523 SIZE_T DesktopInfoSize
;
1526 TRACE("UserInitializeDesktop desktop 0x%p with name %wZ\n", pdesk
, DesktopName
);
1528 RtlZeroMemory(pdesk
, sizeof(DESKTOP
));
1530 /* Link the desktop with the parent window station */
1531 ObReferenceObject(pwinsta
);
1532 pdesk
->rpwinstaParent
= pwinsta
;
1533 InsertTailList(&pwinsta
->DesktopListHead
, &pdesk
->ListEntry
);
1535 /* Create the desktop heap */
1536 pdesk
->hsectionDesktop
= NULL
;
1537 pdesk
->pheapDesktop
= UserCreateHeap(&pdesk
->hsectionDesktop
,
1538 &DesktopHeapSystemBase
,
1540 if (pdesk
->pheapDesktop
== NULL
)
1542 ERR("Failed to create desktop heap!\n");
1543 return STATUS_NO_MEMORY
;
1546 /* Create DESKTOPINFO */
1547 DesktopInfoSize
= sizeof(DESKTOPINFO
) + DesktopName
->Length
+ sizeof(WCHAR
);
1548 pdesk
->pDeskInfo
= RtlAllocateHeap(pdesk
->pheapDesktop
,
1549 HEAP_NO_SERIALIZE
| HEAP_ZERO_MEMORY
,
1551 if (pdesk
->pDeskInfo
== NULL
)
1553 ERR("Failed to create the DESKTOP structure!\n");
1554 return STATUS_NO_MEMORY
;
1557 /* Initialize the DESKTOPINFO */
1558 pdesk
->pDeskInfo
->pvDesktopBase
= DesktopHeapSystemBase
;
1559 pdesk
->pDeskInfo
->pvDesktopLimit
= (PVOID
)((ULONG_PTR
)DesktopHeapSystemBase
+ HeapSize
);
1560 RtlCopyMemory(pdesk
->pDeskInfo
->szDesktopName
,
1561 DesktopName
->Buffer
,
1562 DesktopName
->Length
+ sizeof(WCHAR
));
1563 for (i
= 0; i
< NB_HOOKS
; i
++)
1565 InitializeListHead(&pdesk
->pDeskInfo
->aphkStart
[i
]);
1568 InitializeListHead(&pdesk
->ShellHookWindows
);
1569 InitializeListHead(&pdesk
->PtiList
);
1571 return STATUS_SUCCESS
;
1574 /* SYSCALLS *******************************************************************/
1577 * NtUserCreateDesktop
1579 * Creates a new desktop.
1583 * Object Attributes.
1586 * Name of the device.
1592 * Interaction flags.
1595 * Requested type of access.
1599 * If the function succeeds, the return value is a handle to the newly
1600 * created desktop. If the specified desktop already exists, the function
1601 * succeeds and returns a handle to the existing desktop. When you are
1602 * finished using the handle, call the CloseDesktop function to close it.
1603 * If the function fails, the return value is NULL.
1610 NtUserCreateDesktop(
1611 POBJECT_ATTRIBUTES ObjectAttributes
,
1612 PUNICODE_STRING lpszDesktopDevice
,
1615 ACCESS_MASK dwDesiredAccess
)
1617 PDESKTOP pdesk
= NULL
;
1618 NTSTATUS Status
= STATUS_SUCCESS
;
1620 BOOLEAN Context
= FALSE
;
1621 UNICODE_STRING ClassName
;
1622 LARGE_STRING WindowName
;
1623 BOOL NoHooks
= FALSE
;
1626 PTHREADINFO ptiCurrent
;
1629 DECLARE_RETURN(HDESK
);
1631 TRACE("Enter NtUserCreateDesktop\n");
1632 UserEnterExclusive();
1634 ptiCurrent
= PsGetCurrentThreadWin32Thread();
1636 ASSERT(gptiDesktopThread
);
1638 /* Turn off hooks when calling any CreateWindowEx from inside win32k. */
1639 NoHooks
= (ptiCurrent
->TIF_flags
& TIF_DISABLEHOOKS
);
1640 ptiCurrent
->TIF_flags
|= TIF_DISABLEHOOKS
;
1641 ptiCurrent
->pClientInfo
->dwTIFlags
= ptiCurrent
->TIF_flags
;
1644 * Try to open already existing desktop
1646 Status
= ObOpenObjectByName(
1648 ExDesktopObjectType
,
1654 if (!NT_SUCCESS(Status
))
1656 ERR("ObOpenObjectByName failed to open/create desktop\n");
1657 SetLastNtError(Status
);
1661 /* In case the object was not created (eg if it existed), return now */
1662 if (Context
== FALSE
)
1664 TRACE("NtUserCreateDesktop opened desktop %wZ\n", ObjectAttributes
->ObjectName
);
1668 /* Reference the desktop */
1669 Status
= ObReferenceObjectByHandle(hdesk
,
1671 ExDesktopObjectType
,
1675 if (!NT_SUCCESS(Status
))
1677 ERR("Failed to reference desktop object\n");
1678 SetLastNtError(Status
);
1682 /* Get the desktop window class. The thread desktop does not belong to any desktop
1683 * so the classes created there (including the desktop class) are allocated in the shared heap
1684 * It would cause problems if we used a class that belongs to the caller
1686 ClassName
.Buffer
= WC_DESKTOP
;
1687 ClassName
.Length
= 0;
1688 pcls
= IntGetAndReferenceClass(&ClassName
, 0, TRUE
);
1695 RtlZeroMemory(&WindowName
, sizeof(WindowName
));
1696 RtlZeroMemory(&Cs
, sizeof(Cs
));
1697 Cs
.x
= UserGetSystemMetrics(SM_XVIRTUALSCREEN
),
1698 Cs
.y
= UserGetSystemMetrics(SM_YVIRTUALSCREEN
),
1699 Cs
.cx
= UserGetSystemMetrics(SM_CXVIRTUALSCREEN
),
1700 Cs
.cy
= UserGetSystemMetrics(SM_CYVIRTUALSCREEN
),
1701 Cs
.style
= WS_POPUP
|WS_CLIPCHILDREN
;
1702 Cs
.hInstance
= hModClient
; // hModuleWin; // Server side winproc!
1703 Cs
.lpszName
= (LPCWSTR
) &WindowName
;
1704 Cs
.lpszClass
= (LPCWSTR
) &ClassName
;
1706 /* Use IntCreateWindow instead of co_UserCreateWindowEx cause the later expects a thread with a desktop */
1707 pWnd
= IntCreateWindow(&Cs
, &WindowName
, pcls
, NULL
, NULL
, NULL
, pdesk
);
1710 ERR("Failed to create desktop window for the new desktop\n");
1714 pdesk
->dwSessionId
= PsGetCurrentProcessSessionId();
1715 pdesk
->DesktopWindow
= pWnd
->head
.h
;
1716 pdesk
->pDeskInfo
->spwnd
= pWnd
;
1717 pWnd
->fnid
= FNID_DESKTOP
;
1719 ClassName
.Buffer
= MAKEINTATOM(gpsi
->atomSysClass
[ICLS_HWNDMESSAGE
]);
1720 ClassName
.Length
= 0;
1721 pcls
= IntGetAndReferenceClass(&ClassName
, 0, TRUE
);
1728 RtlZeroMemory(&WindowName
, sizeof(WindowName
));
1729 RtlZeroMemory(&Cs
, sizeof(Cs
));
1730 Cs
.cx
= Cs
.cy
= 100;
1731 Cs
.style
= WS_POPUP
|WS_CLIPCHILDREN
;
1732 Cs
.hInstance
= hModClient
; // hModuleWin; // Server side winproc!
1733 Cs
.lpszName
= (LPCWSTR
) &WindowName
;
1734 Cs
.lpszClass
= (LPCWSTR
) &ClassName
;
1735 pWnd
= IntCreateWindow(&Cs
, &WindowName
, pcls
, NULL
, NULL
, NULL
, pdesk
);
1738 ERR("Failed to create message window for the new desktop\n");
1742 pdesk
->spwndMessage
= pWnd
;
1743 pWnd
->fnid
= FNID_MESSAGEWND
;
1746 if !(WinStaObject->Flags & WSF_NOIO) is (not set) for desktop input output mode (see wiki)
1747 Create Tooltip. Saved in DesktopObject->spwndTooltip.
1748 Tooltip dwExStyle: WS_EX_TOOLWINDOW|WS_EX_TOPMOST
1749 hWndParent are spwndMessage. Use hModuleWin for server side winproc!
1750 The rest is same as message window.
1751 http://msdn.microsoft.com/en-us/library/bb760250(VS.85).aspx
1758 ObDereferenceObject(pdesk
);
1760 if (_ret_
== NULL
&& hdesk
!= NULL
)
1762 ObCloseHandle(hdesk
, UserMode
);
1766 ptiCurrent
->TIF_flags
&= ~TIF_DISABLEHOOKS
;
1767 ptiCurrent
->pClientInfo
->dwTIFlags
= ptiCurrent
->TIF_flags
;
1769 TRACE("Leave NtUserCreateDesktop, ret=%p\n",_ret_
);
1777 * Opens an existing desktop.
1781 * Name of the existing desktop.
1784 * Interaction flags.
1787 * Requested type of access.
1790 * Handle to the desktop or zero on failure.
1798 POBJECT_ATTRIBUTES ObjectAttributes
,
1800 ACCESS_MASK dwDesiredAccess
)
1805 Status
= ObOpenObjectByName(
1807 ExDesktopObjectType
,
1814 if (!NT_SUCCESS(Status
))
1816 ERR("Failed to open desktop\n");
1817 SetLastNtError(Status
);
1821 TRACE("Opened desktop %S with handle 0x%p\n", ObjectAttributes
->ObjectName
->Buffer
, Desktop
);
1827 * NtUserOpenInputDesktop
1829 * Opens the input (interactive) desktop.
1833 * Interaction flags.
1836 * Inheritance option.
1839 * Requested type of access.
1842 * Handle to the input desktop or zero on failure.
1849 NtUserOpenInputDesktop(
1852 ACCESS_MASK dwDesiredAccess
)
1856 ULONG HandleAttributes
= 0;
1858 UserEnterExclusive();
1859 TRACE("Enter NtUserOpenInputDesktop gpdeskInputDesktop 0x%p\n",gpdeskInputDesktop
);
1861 if (fInherit
) HandleAttributes
= OBJ_INHERIT
;
1863 /* Create a new handle to the object */
1864 Status
= ObOpenObjectByPointer(
1869 ExDesktopObjectType
,
1873 if (!NT_SUCCESS(Status
))
1875 ERR("Failed to open input desktop object\n");
1876 SetLastNtError(Status
);
1879 TRACE("NtUserOpenInputDesktop returning 0x%p\n",hdesk
);
1885 * NtUserCloseDesktop
1887 * Closes a desktop handle.
1891 * Handle to the desktop.
1897 * The desktop handle can be created with NtUserCreateDesktop or
1898 * NtUserOpenDesktop. This function will fail if any thread in the calling
1899 * process is using the specified desktop handle or if the handle refers
1900 * to the initial desktop of the calling process.
1907 NtUserCloseDesktop(HDESK hDesktop
)
1911 DECLARE_RETURN(BOOL
);
1913 TRACE("NtUserCloseDesktop called (0x%p)\n", hDesktop
);
1914 UserEnterExclusive();
1916 if (hDesktop
== gptiCurrent
->hdesk
|| hDesktop
== gptiCurrent
->ppi
->hdeskStartup
)
1918 ERR("Attempted to close thread desktop\n");
1919 EngSetLastError(ERROR_BUSY
);
1923 Status
= IntValidateDesktopHandle( hDesktop
, UserMode
, 0, &pdesk
);
1924 if (!NT_SUCCESS(Status
))
1926 ERR("Validation of desktop handle (0x%p) failed\n", hDesktop
);
1930 ObDereferenceObject(pdesk
);
1932 Status
= ZwClose(hDesktop
);
1933 if (!NT_SUCCESS(Status
))
1935 ERR("Failed to close desktop handle 0x%p\n", hDesktop
);
1936 SetLastNtError(Status
);
1943 TRACE("Leave NtUserCloseDesktop, ret=%i\n",_ret_
);
1949 * NtUserPaintDesktop
1951 * The NtUserPaintDesktop function fills the clipping region in the
1952 * specified device context with the desktop pattern or wallpaper. The
1953 * function is provided primarily for shell desktops.
1957 * Handle to the device context.
1964 NtUserPaintDesktop(HDC hDC
)
1967 UserEnterExclusive();
1968 TRACE("Enter NtUserPaintDesktop\n");
1969 Ret
= IntPaintDesktop(hDC
);
1970 TRACE("Leave NtUserPaintDesktop, ret=%i\n",Ret
);
1976 * NtUserResolveDesktop
1978 * The NtUserResolveDesktop function retrieves handles to the desktop and
1979 * the window station specified by the desktop path string.
1983 * Handle to a user process.
1986 * The desktop path string.
1989 * Handle to the desktop (direct return value) and
1990 * handle to the associated window station (by pointer).
1991 * NULL in case of failure.
1994 * Callable by CSRSS only.
2002 NtUserResolveDesktop(
2003 IN HANDLE ProcessHandle
,
2004 IN PUNICODE_STRING DesktopPath
,
2006 OUT HWINSTA
* phWinSta
)
2009 PEPROCESS Process
= NULL
;
2010 HWINSTA hWinSta
= NULL
;
2011 HDESK hDesktop
= NULL
;
2013 /* Allow only the Console Server to perform this operation (via CSRSS) */
2014 if (PsGetCurrentProcess() != gpepCSRSS
)
2017 /* Get the process object the user handle was referencing */
2018 Status
= ObReferenceObjectByHandle(ProcessHandle
,
2019 PROCESS_QUERY_INFORMATION
,
2024 if (!NT_SUCCESS(Status
)) return NULL
;
2026 // UserEnterShared();
2030 UNICODE_STRING CapturedDesktopPath
;
2032 /* Capture the user desktop path string */
2033 Status
= IntSafeCopyUnicodeStringTerminateNULL(&CapturedDesktopPath
,
2035 if (!NT_SUCCESS(Status
)) _SEH2_YIELD(goto Quit
);
2037 /* Call the internal function */
2038 Status
= IntParseDesktopPath(Process
,
2039 &CapturedDesktopPath
,
2042 if (!NT_SUCCESS(Status
))
2044 ERR("IntParseDesktopPath failed, Status = 0x%08lx\n", Status
);
2049 /* Return the window station handle */
2050 *phWinSta
= hWinSta
;
2052 /* Free the captured string */
2053 if (CapturedDesktopPath
.Buffer
)
2054 ExFreePoolWithTag(CapturedDesktopPath
.Buffer
, TAG_STRING
);
2056 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2058 Status
= _SEH2_GetExceptionCode();
2065 /* Dereference the process object */
2066 ObDereferenceObject(Process
);
2068 /* Return the desktop handle */
2073 * NtUserSwitchDesktop
2075 * Sets the current input (interactive) desktop.
2079 * Handle to desktop.
2089 NtUserSwitchDesktop(HDESK hdesk
)
2093 BOOL bRedrawDesktop
;
2094 DECLARE_RETURN(BOOL
);
2096 UserEnterExclusive();
2097 TRACE("Enter NtUserSwitchDesktop(0x%p)\n", hdesk
);
2099 Status
= IntValidateDesktopHandle( hdesk
, UserMode
, 0, &pdesk
);
2100 if (!NT_SUCCESS(Status
))
2102 ERR("Validation of desktop handle (0x%p) failed\n", hdesk
);
2106 if (PsGetCurrentProcessSessionId() != pdesk
->rpwinstaParent
->dwSessionId
)
2108 ObDereferenceObject(pdesk
);
2109 ERR("NtUserSwitchDesktop called for a desktop of a different session\n");
2113 if (pdesk
== gpdeskInputDesktop
)
2115 ObDereferenceObject(pdesk
);
2116 WARN("NtUserSwitchDesktop called for active desktop\n");
2121 * Don't allow applications switch the desktop if it's locked, unless the caller
2122 * is the logon application itself
2124 if ((pdesk
->rpwinstaParent
->Flags
& WSS_LOCKED
) &&
2125 gpidLogon
!= PsGetCurrentProcessId())
2127 ObDereferenceObject(pdesk
);
2128 ERR("Switching desktop 0x%p denied because the window station is locked!\n", hdesk
);
2132 if (pdesk
->rpwinstaParent
!= InputWindowStation
)
2134 ObDereferenceObject(pdesk
);
2135 ERR("Switching desktop 0x%p denied because desktop doesn't belong to the interactive winsta!\n", hdesk
);
2139 /* FIXME: Fail if the process is associated with a secured
2140 desktop such as Winlogon or Screen-Saver */
2141 /* FIXME: Connect to input device */
2143 TRACE("Switching from desktop 0x%p to 0x%p\n", gpdeskInputDesktop
, pdesk
);
2145 bRedrawDesktop
= FALSE
;
2147 /* The first time SwitchDesktop is called, gpdeskInputDesktop is NULL */
2148 if (gpdeskInputDesktop
!= NULL
)
2150 if ((gpdeskInputDesktop
->pDeskInfo
->spwnd
->style
& WS_VISIBLE
) == WS_VISIBLE
)
2151 bRedrawDesktop
= TRUE
;
2153 /* Hide the previous desktop window */
2154 IntHideDesktop(gpdeskInputDesktop
);
2157 /* Set the active desktop in the desktop's window station. */
2158 InputWindowStation
->ActiveDesktop
= pdesk
;
2160 /* Set the global state. */
2161 gpdeskInputDesktop
= pdesk
;
2163 /* Show the new desktop window */
2164 co_IntShowDesktop(pdesk
, UserGetSystemMetrics(SM_CXSCREEN
), UserGetSystemMetrics(SM_CYSCREEN
), bRedrawDesktop
);
2166 TRACE("SwitchDesktop gpdeskInputDesktop 0x%p\n",gpdeskInputDesktop
);
2167 ObDereferenceObject(pdesk
);
2172 TRACE("Leave NtUserSwitchDesktop, ret=%i\n",_ret_
);
2178 * NtUserGetThreadDesktop
2185 NtUserGetThreadDesktop(DWORD dwThreadId
, DWORD Unknown1
)
2189 PDESKTOP DesktopObject
;
2190 HDESK Ret
, hThreadDesktop
;
2191 OBJECT_HANDLE_INFORMATION HandleInformation
;
2192 DECLARE_RETURN(HDESK
);
2194 UserEnterExclusive();
2195 TRACE("Enter NtUserGetThreadDesktop\n");
2199 EngSetLastError(ERROR_INVALID_PARAMETER
);
2203 Status
= PsLookupThreadByThreadId((HANDLE
)(DWORD_PTR
)dwThreadId
, &Thread
);
2204 if (!NT_SUCCESS(Status
))
2206 EngSetLastError(ERROR_INVALID_PARAMETER
);
2210 if (Thread
->ThreadsProcess
== PsGetCurrentProcess())
2212 /* Just return the handle, we queried the desktop handle of a thread running
2213 in the same context */
2214 Ret
= ((PTHREADINFO
)Thread
->Tcb
.Win32Thread
)->hdesk
;
2215 ObDereferenceObject(Thread
);
2219 /* Get the desktop handle and the desktop of the thread */
2220 if (!(hThreadDesktop
= ((PTHREADINFO
)Thread
->Tcb
.Win32Thread
)->hdesk
) ||
2221 !(DesktopObject
= ((PTHREADINFO
)Thread
->Tcb
.Win32Thread
)->rpdesk
))
2223 ObDereferenceObject(Thread
);
2224 ERR("Desktop information of thread 0x%x broken!?\n", dwThreadId
);
2228 /* We could just use DesktopObject instead of looking up the handle, but latter
2229 may be a bit safer (e.g. when the desktop is being destroyed */
2230 /* Switch into the context of the thread we're trying to get the desktop from,
2231 so we can use the handle */
2232 KeAttachProcess(&Thread
->ThreadsProcess
->Pcb
);
2233 Status
= ObReferenceObjectByHandle(hThreadDesktop
,
2235 ExDesktopObjectType
,
2237 (PVOID
*)&DesktopObject
,
2238 &HandleInformation
);
2241 /* The handle couldn't be found, there's nothing to get... */
2242 if (!NT_SUCCESS(Status
))
2244 ObDereferenceObject(Thread
);
2248 /* Lookup our handle table if we can find a handle to the desktop object,
2249 if not, create one */
2250 Ret
= IntGetDesktopObjectHandle(DesktopObject
);
2252 /* All done, we got a valid handle to the desktop */
2253 ObDereferenceObject(DesktopObject
);
2254 ObDereferenceObject(Thread
);
2258 TRACE("Leave NtUserGetThreadDesktop, ret=%p\n",_ret_
);
2264 IntUnmapDesktopView(IN PDESKTOP pdesk
)
2267 PW32HEAP_USER_MAPPING HeapMapping
, *PrevLink
;
2268 NTSTATUS Status
= STATUS_SUCCESS
;
2270 TRACE("IntUnmapDesktopView called for desktop object %p\n", pdesk
);
2272 ppi
= PsGetCurrentProcessWin32Process();
2275 * Unmap if we're the last thread using the desktop.
2276 * Start the search at the next mapping: skip the first entry
2277 * as it must be the global user heap mapping.
2279 PrevLink
= &ppi
->HeapMappings
.Next
;
2280 HeapMapping
= *PrevLink
;
2281 while (HeapMapping
!= NULL
)
2283 if (HeapMapping
->KernelMapping
== (PVOID
)pdesk
->pheapDesktop
)
2285 if (--HeapMapping
->Count
== 0)
2287 *PrevLink
= HeapMapping
->Next
;
2289 TRACE("ppi 0x%p unmapped heap of desktop 0x%p\n", ppi
, pdesk
);
2290 Status
= MmUnmapViewOfSection(PsGetCurrentProcess(),
2291 HeapMapping
->UserMapping
);
2293 ObDereferenceObject(pdesk
);
2295 UserHeapFree(HeapMapping
);
2300 PrevLink
= &HeapMapping
->Next
;
2301 HeapMapping
= HeapMapping
->Next
;
2308 IntMapDesktopView(IN PDESKTOP pdesk
)
2311 PW32HEAP_USER_MAPPING HeapMapping
, *PrevLink
;
2312 PVOID UserBase
= NULL
;
2313 SIZE_T ViewSize
= 0;
2314 LARGE_INTEGER Offset
;
2317 TRACE("IntMapDesktopView called for desktop object 0x%p\n", pdesk
);
2319 ppi
= PsGetCurrentProcessWin32Process();
2322 * Find out if another thread already mapped the desktop heap.
2323 * Start the search at the next mapping: skip the first entry
2324 * as it must be the global user heap mapping.
2326 PrevLink
= &ppi
->HeapMappings
.Next
;
2327 HeapMapping
= *PrevLink
;
2328 while (HeapMapping
!= NULL
)
2330 if (HeapMapping
->KernelMapping
== (PVOID
)pdesk
->pheapDesktop
)
2332 HeapMapping
->Count
++;
2333 return STATUS_SUCCESS
;
2336 PrevLink
= &HeapMapping
->Next
;
2337 HeapMapping
= HeapMapping
->Next
;
2340 /* We're the first, map the heap */
2341 Offset
.QuadPart
= 0;
2342 Status
= MmMapViewOfSection(pdesk
->hsectionDesktop
,
2343 PsGetCurrentProcess(),
2351 PAGE_EXECUTE_READ
); /* Would prefer PAGE_READONLY, but thanks to RTL heaps... */
2352 if (!NT_SUCCESS(Status
))
2354 ERR("Failed to map desktop\n");
2358 TRACE("ppi 0x%p mapped heap of desktop 0x%p\n", ppi
, pdesk
);
2360 /* Add the mapping */
2361 HeapMapping
= UserHeapAlloc(sizeof(*HeapMapping
));
2362 if (HeapMapping
== NULL
)
2364 MmUnmapViewOfSection(PsGetCurrentProcess(), UserBase
);
2365 ERR("UserHeapAlloc() failed!\n");
2366 return STATUS_NO_MEMORY
;
2369 HeapMapping
->Next
= NULL
;
2370 HeapMapping
->KernelMapping
= (PVOID
)pdesk
->pheapDesktop
;
2371 HeapMapping
->UserMapping
= UserBase
;
2372 HeapMapping
->Limit
= ViewSize
;
2373 HeapMapping
->Count
= 1;
2374 *PrevLink
= HeapMapping
;
2376 ObReferenceObject(pdesk
);
2378 return STATUS_SUCCESS
;
2382 IntSetThreadDesktop(IN HDESK hDesktop
,
2383 IN BOOL FreeOnFailure
)
2385 PDESKTOP pdesk
= NULL
, pdeskOld
;
2388 PCLIENTTHREADINFO pctiOld
, pctiNew
= NULL
;
2391 ASSERT(NtCurrentTeb());
2393 TRACE("IntSetThreadDesktop hDesktop:0x%p, FOF:%i\n",hDesktop
, FreeOnFailure
);
2395 pti
= PsGetCurrentThreadWin32Thread();
2396 pci
= pti
->pClientInfo
;
2398 /* If the caller gave us a desktop, ensure it is valid */
2399 if (hDesktop
!= NULL
)
2401 /* Validate the new desktop. */
2402 Status
= IntValidateDesktopHandle( hDesktop
, UserMode
, 0, &pdesk
);
2403 if (!NT_SUCCESS(Status
))
2405 ERR("Validation of desktop handle (0x%p) failed\n", hDesktop
);
2409 if (pti
->rpdesk
== pdesk
)
2412 ObDereferenceObject(pdesk
);
2417 /* Make sure that we don't own any window in the current desktop */
2418 if (!IsListEmpty(&pti
->WindowListHead
))
2421 ObDereferenceObject(pdesk
);
2422 ERR("Attempted to change thread desktop although the thread has windows!\n");
2423 EngSetLastError(ERROR_BUSY
);
2427 /* Desktop is being re-set so clear out foreground. */
2428 if (pti
->rpdesk
!= pdesk
&& pti
->MessageQueue
== gpqForeground
)
2430 // Like above, there shouldn't be any windows, hooks or anything active on this threads desktop!
2431 IntSetFocusMessageQueue(NULL
);
2434 /* Before doing the switch, map the new desktop heap and allocate the new pcti */
2437 Status
= IntMapDesktopView(pdesk
);
2438 if (!NT_SUCCESS(Status
))
2440 ERR("Failed to map desktop heap!\n");
2441 ObDereferenceObject(pdesk
);
2442 SetLastNtError(Status
);
2446 pctiNew
= DesktopHeapAlloc( pdesk
, sizeof(CLIENTTHREADINFO
));
2447 if (pctiNew
== NULL
)
2449 ERR("Failed to allocate new pcti\n");
2450 IntUnmapDesktopView(pdesk
);
2451 ObDereferenceObject(pdesk
);
2452 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY
);
2457 /* free all classes or move them to the shared heap */
2458 if (pti
->rpdesk
!= NULL
)
2460 if (!IntCheckProcessDesktopClasses(pti
->rpdesk
, FreeOnFailure
))
2462 ERR("Failed to move process classes to shared heap!\n");
2465 DesktopHeapFree(pdesk
, pctiNew
);
2466 IntUnmapDesktopView(pdesk
);
2467 ObDereferenceObject(pdesk
);
2473 pdeskOld
= pti
->rpdesk
;
2474 if (pti
->pcti
!= &pti
->cti
)
2475 pctiOld
= pti
->pcti
;
2482 pti
->rpdesk
= pdesk
;
2483 pti
->hdesk
= hDesktop
;
2484 pti
->pDeskInfo
= pti
->rpdesk
->pDeskInfo
;
2485 pti
->pcti
= pctiNew
;
2487 pci
->ulClientDelta
= DesktopHeapGetUserDelta();
2488 pci
->pDeskInfo
= (PVOID
)((ULONG_PTR
)pti
->pDeskInfo
- pci
->ulClientDelta
);
2489 pci
->pClientThreadInfo
= (PVOID
)((ULONG_PTR
)pti
->pcti
- pci
->ulClientDelta
);
2491 /* initialize the new pcti */
2492 if (pctiOld
!= NULL
)
2494 RtlCopyMemory(pctiNew
, pctiOld
, sizeof(CLIENTTHREADINFO
));
2498 RtlZeroMemory(pctiNew
, sizeof(CLIENTTHREADINFO
));
2499 pci
->fsHooks
= pti
->fsHooks
;
2500 pci
->dwTIFlags
= pti
->TIF_flags
;
2507 pti
->pDeskInfo
= NULL
;
2508 pti
->pcti
= &pti
->cti
; // Always point inside so there will be no crash when posting or sending msg's!
2509 pci
->ulClientDelta
= 0;
2510 pci
->pDeskInfo
= NULL
;
2511 pci
->pClientThreadInfo
= NULL
;
2514 /* clean up the old desktop */
2515 if (pdeskOld
!= NULL
)
2517 RemoveEntryList(&pti
->PtiLink
);
2518 if (pctiOld
) DesktopHeapFree(pdeskOld
, pctiOld
);
2519 IntUnmapDesktopView(pdeskOld
);
2520 ObDereferenceObject(pdeskOld
);
2525 InsertTailList(&pdesk
->PtiList
, &pti
->PtiLink
);
2528 TRACE("IntSetThreadDesktop: pti 0x%p ppi 0x%p switched from object 0x%p to 0x%p\n", pti
, pti
->ppi
, pdeskOld
, pdesk
);
2534 * NtUserSetThreadDesktop
2541 NtUserSetThreadDesktop(HDESK hDesktop
)
2545 UserEnterExclusive();
2547 // FIXME: IntSetThreadDesktop validates the desktop handle, it should happen
2548 // here too and set the NT error level. Q. Is it necessary to have the validation
2549 // in IntSetThreadDesktop? Is it needed there too?
2550 if (hDesktop
|| (!hDesktop
&& PsGetCurrentProcess() == gpepCSRSS
))
2551 ret
= IntSetThreadDesktop(hDesktop
, FALSE
);