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 if (DcType
== DC_TYPE_DIRECT
)
937 DesktopObject
= UserGetDesktopWindow();
938 DesktopHDC
= (HDC
)UserGetWindowDC(DesktopObject
);
942 PMONITOR pMonitor
= UserGetPrimaryMonitor();
943 DesktopHDC
= IntGdiCreateDisplayDC(pMonitor
->hDev
, DcType
, EmptyDC
);
950 UserRedrawDesktop(VOID
)
955 Window
= UserGetDesktopWindow();
956 Rgn
= IntSysCreateRectpRgnIndirect(&Window
->rcWindow
);
958 IntInvalidateWindows( Window
,
970 co_IntShowDesktop(PDESKTOP Desktop
, ULONG Width
, ULONG Height
, BOOL bRedraw
)
972 PWND pwnd
= Desktop
->pDeskInfo
->spwnd
;
973 UINT flags
= SWP_NOACTIVATE
|SWP_NOZORDER
|SWP_SHOWWINDOW
;
977 flags
|= SWP_NOREDRAW
;
979 co_WinPosSetWindowPos(pwnd
, NULL
, 0, 0, Width
, Height
, flags
);
982 co_UserRedrawWindow( pwnd
, NULL
, 0, RDW_UPDATENOW
| RDW_ALLCHILDREN
| RDW_INVALIDATE
);
984 return STATUS_SUCCESS
;
988 IntHideDesktop(PDESKTOP Desktop
)
992 DesktopWnd
= IntGetWindowObject(Desktop
->DesktopWindow
);
995 return ERROR_INVALID_WINDOW_HANDLE
;
997 DesktopWnd
->style
&= ~WS_VISIBLE
;
999 return STATUS_SUCCESS
;
1004 UserBuildShellHookHwndList(PDESKTOP Desktop
)
1007 PLIST_ENTRY ListEntry
;
1008 PSHELL_HOOK_WINDOW Current
;
1011 /* FIXME: If we save nb elements in desktop, we don't have to loop to find nb entries */
1012 ListEntry
= Desktop
->ShellHookWindows
.Flink
;
1013 while (ListEntry
!= &Desktop
->ShellHookWindows
)
1015 ListEntry
= ListEntry
->Flink
;
1019 if (!entries
) return NULL
;
1021 list
= ExAllocatePoolWithTag(PagedPool
, sizeof(HWND
) * (entries
+ 1), USERTAG_WINDOWLIST
); /* alloc one extra for nullterm */
1024 HWND
* cursor
= list
;
1026 ListEntry
= Desktop
->ShellHookWindows
.Flink
;
1027 while (ListEntry
!= &Desktop
->ShellHookWindows
)
1029 Current
= CONTAINING_RECORD(ListEntry
, SHELL_HOOK_WINDOW
, ListEntry
);
1030 ListEntry
= ListEntry
->Flink
;
1031 *cursor
++ = Current
->hWnd
;
1034 *cursor
= NULL
; /* Nullterm list */
1041 * Send the Message to the windows registered for ShellHook
1042 * notifications. The lParam contents depend on the Message. See
1043 * MSDN for more details (RegisterShellHookWindow)
1045 VOID
co_IntShellHookNotify(WPARAM Message
, WPARAM wParam
, LPARAM lParam
)
1047 PDESKTOP Desktop
= IntGetActiveDesktop();
1050 if (!gpsi
->uiShellMsg
)
1052 gpsi
->uiShellMsg
= IntAddAtom(L
"SHELLHOOK");
1054 TRACE("MsgType = %x\n", gpsi
->uiShellMsg
);
1055 if (!gpsi
->uiShellMsg
)
1056 ERR("LastError: %x\n", EngGetLastError());
1061 TRACE("IntShellHookNotify: No desktop!\n");
1065 // Allow other devices have a shot at foreground.
1066 if (Message
== HSHELL_APPCOMMAND
) ptiLastInput
= NULL
;
1068 // FIXME: System Tray Support.
1070 HwndList
= UserBuildShellHookHwndList(Desktop
);
1073 HWND
* cursor
= HwndList
;
1075 for (; *cursor
; cursor
++)
1077 TRACE("Sending notify\n");
1078 UserPostMessage(*cursor
,
1081 (Message
== HSHELL_LANGUAGE
? lParam
: (LPARAM
)wParam
) );
1082 /* co_IntPostOrSendMessage(*cursor,
1085 (Message == HSHELL_LANGUAGE ? lParam : (LPARAM)wParam) );*/
1088 ExFreePoolWithTag(HwndList
, USERTAG_WINDOWLIST
);
1091 if (ISITHOOKED(WH_SHELL
))
1093 co_HOOK_CallHooks(WH_SHELL
, Message
, wParam
, lParam
);
1098 * Add the window to the ShellHookWindows list. The windows
1099 * on that list get notifications that are important to shell
1100 * type applications.
1102 * TODO: Validate the window? I'm not sure if sending these messages to
1103 * an unsuspecting application that is not your own is a nice thing to do.
1105 BOOL
IntRegisterShellHookWindow(HWND hWnd
)
1107 PTHREADINFO pti
= PsGetCurrentThreadWin32Thread();
1108 PDESKTOP Desktop
= pti
->rpdesk
;
1109 PSHELL_HOOK_WINDOW Entry
;
1111 TRACE("IntRegisterShellHookWindow\n");
1113 /* First deregister the window, so we can be sure it's never twice in the
1116 IntDeRegisterShellHookWindow(hWnd
);
1118 Entry
= ExAllocatePoolWithTag(PagedPool
,
1119 sizeof(SHELL_HOOK_WINDOW
),
1127 InsertTailList(&Desktop
->ShellHookWindows
, &Entry
->ListEntry
);
1133 * Remove the window from the ShellHookWindows list. The windows
1134 * on that list get notifications that are important to shell
1135 * type applications.
1137 BOOL
IntDeRegisterShellHookWindow(HWND hWnd
)
1139 PTHREADINFO pti
= PsGetCurrentThreadWin32Thread();
1140 PDESKTOP Desktop
= pti
->rpdesk
;
1141 PLIST_ENTRY ListEntry
;
1142 PSHELL_HOOK_WINDOW Current
;
1144 ListEntry
= Desktop
->ShellHookWindows
.Flink
;
1145 while (ListEntry
!= &Desktop
->ShellHookWindows
)
1147 Current
= CONTAINING_RECORD(ListEntry
, SHELL_HOOK_WINDOW
, ListEntry
);
1148 ListEntry
= ListEntry
->Flink
;
1149 if (Current
->hWnd
== hWnd
)
1151 RemoveEntryList(&Current
->ListEntry
);
1152 ExFreePoolWithTag(Current
, TAG_WINSTA
);
1161 IntFreeDesktopHeap(IN OUT PDESKTOP Desktop
)
1163 /* FIXME: Disable until unmapping works in mm */
1165 if (Desktop
->pheapDesktop
!= NULL
)
1167 MmUnmapViewInSessionSpace(Desktop
->pheapDesktop
);
1168 Desktop
->pheapDesktop
= NULL
;
1171 if (Desktop
->hsectionDesktop
!= NULL
)
1173 ObDereferenceObject(Desktop
->hsectionDesktop
);
1174 Desktop
->hsectionDesktop
= NULL
;
1180 IntPaintDesktop(HDC hDC
)
1182 static WCHAR s_wszSafeMode
[] = L
"Safe Mode"; // FIXME: Localize!
1185 HBRUSH DesktopBrush
, PreviousBrush
;
1187 BOOL doPatBlt
= TRUE
;
1191 if (GdiGetClipBox(hDC
, &Rect
) == ERROR
)
1194 hWndDesktop
= IntGetDesktopWindow(); // rpdesk->DesktopWindow;
1196 WndDesktop
= UserGetWindowObject(hWndDesktop
); // rpdesk->pDeskInfo->spwnd;
1200 /* Retrieve the current SafeMode state */
1201 InSafeMode
= (UserGetSystemMetrics(SM_CLEANBOOT
) != 0); // gpsi->aiSysMet[SM_CLEANBOOT];
1205 DesktopBrush
= (HBRUSH
)WndDesktop
->pcls
->hbrBackground
;
1208 * Paint desktop background
1210 if (gspv
.hbmWallpaper
!= NULL
)
1216 sz
.cx
= WndDesktop
->rcWindow
.right
- WndDesktop
->rcWindow
.left
;
1217 sz
.cy
= WndDesktop
->rcWindow
.bottom
- WndDesktop
->rcWindow
.top
;
1219 if (gspv
.WallpaperMode
== wmStretch
||
1220 gspv
.WallpaperMode
== wmTile
)
1227 /* Find the upper left corner, can be negative if the bitmap is bigger than the screen */
1228 x
= (sz
.cx
/ 2) - (gspv
.cxWallpaper
/ 2);
1229 y
= (sz
.cy
/ 2) - (gspv
.cyWallpaper
/ 2);
1232 hWallpaperDC
= NtGdiCreateCompatibleDC(hDC
);
1233 if (hWallpaperDC
!= NULL
)
1237 /* Fill in the area that the bitmap is not going to cover */
1240 /* FIXME: Clip out the bitmap
1241 can be replaced with "NtGdiPatBlt(hDC, x, y, gspv.cxWallpaper, gspv.cyWallpaper, PATCOPY | DSTINVERT);"
1242 once we support DSTINVERT */
1243 PreviousBrush
= NtGdiSelectBrush(hDC
, DesktopBrush
);
1244 NtGdiPatBlt(hDC
, Rect
.left
, Rect
.top
, Rect
.right
, Rect
.bottom
, PATCOPY
);
1245 NtGdiSelectBrush(hDC
, PreviousBrush
);
1248 /* Do not fill the background after it is painted no matter the size of the picture */
1251 hOldBitmap
= NtGdiSelectBitmap(hWallpaperDC
, gspv
.hbmWallpaper
);
1253 if (gspv
.WallpaperMode
== wmStretch
)
1255 if (Rect
.right
&& Rect
.bottom
)
1256 NtGdiStretchBlt(hDC
,
1269 else if (gspv
.WallpaperMode
== wmTile
)
1271 /* Paint the bitmap across the screen then down */
1272 for (y
= 0; y
< Rect
.bottom
; y
+= gspv
.cyWallpaper
)
1274 for (x
= 0; x
< Rect
.right
; x
+= gspv
.cxWallpaper
)
1304 NtGdiSelectBitmap(hWallpaperDC
, hOldBitmap
);
1305 NtGdiDeleteObjectApp(hWallpaperDC
);
1311 /* Black desktop background in Safe Mode */
1312 DesktopBrush
= StockObjects
[BLACK_BRUSH
];
1315 /* Background is set to none, clear the screen */
1318 PreviousBrush
= NtGdiSelectBrush(hDC
, DesktopBrush
);
1319 NtGdiPatBlt(hDC
, Rect
.left
, Rect
.top
, Rect
.right
, Rect
.bottom
, PATCOPY
);
1320 NtGdiSelectBrush(hDC
, PreviousBrush
);
1324 * Display the system version on the desktop background
1326 if (InSafeMode
|| g_AlwaysDisplayVersion
|| g_PaintDesktopVersion
)
1329 static WCHAR wszzVersion
[1024] = L
"\0";
1331 /* Only used in normal mode */
1332 // We expect at most 4 strings (3 for version, 1 for optional NtSystemRoot)
1333 static POLYTEXTW VerStrs
[4] = {{0},{0},{0},{0}};
1337 HFONT hFont1
= NULL
, hFont2
= NULL
, hOldFont
= NULL
;
1338 COLORREF crText
, color_old
;
1343 if (!UserSystemParametersInfo(SPI_GETWORKAREA
, 0, &Rect
, 0))
1345 Rect
.right
= UserGetSystemMetrics(SM_CXSCREEN
);
1346 Rect
.bottom
= UserGetSystemMetrics(SM_CYSCREEN
);
1350 * Set up the fonts (otherwise use default ones)
1353 /* Font for the principal version string */
1354 hFont1
= GreCreateFontIndirectW(&gspv
.ncm
.lfCaptionFont
);
1355 /* Font for the secondary version strings */
1356 hFont2
= GreCreateFontIndirectW(&gspv
.ncm
.lfMenuFont
);
1359 hOldFont
= NtGdiSelectFont(hDC
, hFont1
);
1361 if (gspv
.hbmWallpaper
== NULL
)
1363 /* Retrieve the brush fill colour */
1364 // TODO: The following code constitutes "GreGetBrushColor".
1365 PreviousBrush
= NtGdiSelectBrush(hDC
, DesktopBrush
);
1366 pdc
= DC_LockDc(hDC
);
1369 crText
= pdc
->eboFill
.ulRGBColor
;
1374 crText
= RGB(0, 0, 0);
1376 NtGdiSelectBrush(hDC
, PreviousBrush
);
1378 /* Adjust text colour according to the brush */
1379 if (GetRValue(crText
) + GetGValue(crText
) + GetBValue(crText
) > 128 * 3)
1380 crText
= RGB(0, 0, 0);
1382 crText
= RGB(255, 255, 255);
1386 /* Always use white when the text is displayed on top of a wallpaper */
1387 crText
= RGB(255, 255, 255);
1390 color_old
= IntGdiSetTextColor(hDC
, crText
);
1391 align_old
= IntGdiSetTextAlign(hDC
, TA_RIGHT
);
1392 mode_old
= IntGdiSetBkMode(hDC
, TRANSPARENT
);
1394 /* Display the system version information */
1397 Status
= GetSystemVersionString(wszzVersion
,
1398 ARRAYSIZE(wszzVersion
),
1400 g_AlwaysDisplayVersion
);
1401 if (!InSafeMode
&& NT_SUCCESS(Status
) && *wszzVersion
)
1403 PWCHAR pstr
= wszzVersion
;
1404 for (i
= 0; (i
< ARRAYSIZE(VerStrs
)) && *pstr
; ++i
)
1406 VerStrs
[i
].n
= wcslen(pstr
);
1407 VerStrs
[i
].lpstr
= pstr
;
1408 pstr
+= (VerStrs
[i
].n
+ 1);
1414 Status
= STATUS_SUCCESS
;
1416 if (NT_SUCCESS(Status
) && *wszzVersion
)
1421 LONG TotalHeight
= 0;
1423 /* Normal Mode: multiple version information text separated by newlines */
1424 IntGdiSetTextAlign(hDC
, TA_RIGHT
| TA_BOTTOM
);
1426 /* Compute the heights of the strings */
1427 if (hFont1
) NtGdiSelectFont(hDC
, hFont1
);
1428 for (i
= 0; i
< ARRAYSIZE(VerStrs
); ++i
)
1430 if (!VerStrs
[i
].lpstr
|| !*VerStrs
[i
].lpstr
|| (VerStrs
[i
].n
== 0))
1433 GreGetTextExtentW(hDC
, VerStrs
[i
].lpstr
, VerStrs
[i
].n
, &Size
, 1);
1434 VerStrs
[i
].y
= Size
.cy
; // Store the string height
1435 TotalHeight
+= Size
.cy
;
1437 /* While the first string was using hFont1, all the others use hFont2 */
1438 if (hFont2
) NtGdiSelectFont(hDC
, hFont2
);
1440 /* The total height must not exceed the screen height */
1441 TotalHeight
= min(TotalHeight
, Rect
.bottom
);
1443 /* Display the strings */
1444 if (hFont1
) NtGdiSelectFont(hDC
, hFont1
);
1445 for (i
= 0; i
< ARRAYSIZE(VerStrs
); ++i
)
1447 if (!VerStrs
[i
].lpstr
|| !*VerStrs
[i
].lpstr
|| (VerStrs
[i
].n
== 0))
1450 TotalHeight
-= VerStrs
[i
].y
;
1453 Rect
.bottom
- TotalHeight
- 5,
1459 /* While the first string was using hFont1, all the others use hFont2 */
1460 if (hFont2
) NtGdiSelectFont(hDC
, hFont2
);
1465 if (hFont1
) NtGdiSelectFont(hDC
, hFont1
);
1467 /* Safe Mode: single version information text in top center */
1468 len
= wcslen(wszzVersion
);
1470 IntGdiSetTextAlign(hDC
, TA_CENTER
| TA_TOP
);
1471 GreExtTextOutW(hDC
, (Rect
.right
+ Rect
.left
)/2, Rect
.top
+ 3, 0, NULL
, wszzVersion
, len
, NULL
, 0);
1477 if (hFont1
) NtGdiSelectFont(hDC
, hFont1
);
1479 /* Print Safe Mode text in corners */
1480 len
= wcslen(s_wszSafeMode
);
1482 IntGdiSetTextAlign(hDC
, TA_LEFT
| TA_TOP
);
1483 GreExtTextOutW(hDC
, Rect
.left
, Rect
.top
+ 3, 0, NULL
, s_wszSafeMode
, len
, NULL
, 0);
1484 IntGdiSetTextAlign(hDC
, TA_RIGHT
| TA_TOP
);
1485 GreExtTextOutW(hDC
, Rect
.right
, Rect
.top
+ 3, 0, NULL
, s_wszSafeMode
, len
, NULL
, 0);
1486 IntGdiSetTextAlign(hDC
, TA_LEFT
| TA_BOTTOM
);
1487 GreExtTextOutW(hDC
, Rect
.left
, Rect
.bottom
- 5, 0, NULL
, s_wszSafeMode
, len
, NULL
, 0);
1488 IntGdiSetTextAlign(hDC
, TA_RIGHT
| TA_BOTTOM
);
1489 GreExtTextOutW(hDC
, Rect
.right
, Rect
.bottom
- 5, 0, NULL
, s_wszSafeMode
, len
, NULL
, 0);
1492 IntGdiSetBkMode(hDC
, mode_old
);
1493 IntGdiSetTextAlign(hDC
, align_old
);
1494 IntGdiSetTextColor(hDC
, color_old
);
1497 GreDeleteObject(hFont2
);
1501 NtGdiSelectFont(hDC
, hOldFont
);
1502 GreDeleteObject(hFont1
);
1510 UserInitializeDesktop(PDESKTOP pdesk
, PUNICODE_STRING DesktopName
, PWINSTATION_OBJECT pwinsta
)
1512 PVOID DesktopHeapSystemBase
= NULL
;
1513 ULONG_PTR HeapSize
= gdwDesktopSectionSize
* 1024;
1514 SIZE_T DesktopInfoSize
;
1517 TRACE("UserInitializeDesktop desktop 0x%p with name %wZ\n", pdesk
, DesktopName
);
1519 RtlZeroMemory(pdesk
, sizeof(DESKTOP
));
1521 /* Link the desktop with the parent window station */
1522 ObReferenceObject(pwinsta
);
1523 pdesk
->rpwinstaParent
= pwinsta
;
1524 InsertTailList(&pwinsta
->DesktopListHead
, &pdesk
->ListEntry
);
1526 /* Create the desktop heap */
1527 pdesk
->hsectionDesktop
= NULL
;
1528 pdesk
->pheapDesktop
= UserCreateHeap(&pdesk
->hsectionDesktop
,
1529 &DesktopHeapSystemBase
,
1531 if (pdesk
->pheapDesktop
== NULL
)
1533 ERR("Failed to create desktop heap!\n");
1534 return STATUS_NO_MEMORY
;
1537 /* Create DESKTOPINFO */
1538 DesktopInfoSize
= sizeof(DESKTOPINFO
) + DesktopName
->Length
+ sizeof(WCHAR
);
1539 pdesk
->pDeskInfo
= RtlAllocateHeap(pdesk
->pheapDesktop
,
1540 HEAP_NO_SERIALIZE
| HEAP_ZERO_MEMORY
,
1542 if (pdesk
->pDeskInfo
== NULL
)
1544 ERR("Failed to create the DESKTOP structure!\n");
1545 return STATUS_NO_MEMORY
;
1548 /* Initialize the DESKTOPINFO */
1549 pdesk
->pDeskInfo
->pvDesktopBase
= DesktopHeapSystemBase
;
1550 pdesk
->pDeskInfo
->pvDesktopLimit
= (PVOID
)((ULONG_PTR
)DesktopHeapSystemBase
+ HeapSize
);
1551 RtlCopyMemory(pdesk
->pDeskInfo
->szDesktopName
,
1552 DesktopName
->Buffer
,
1553 DesktopName
->Length
+ sizeof(WCHAR
));
1554 for (i
= 0; i
< NB_HOOKS
; i
++)
1556 InitializeListHead(&pdesk
->pDeskInfo
->aphkStart
[i
]);
1559 InitializeListHead(&pdesk
->ShellHookWindows
);
1560 InitializeListHead(&pdesk
->PtiList
);
1562 return STATUS_SUCCESS
;
1565 /* SYSCALLS *******************************************************************/
1568 * NtUserCreateDesktop
1570 * Creates a new desktop.
1574 * Object Attributes.
1577 * Name of the device.
1583 * Interaction flags.
1586 * Requested type of access.
1590 * If the function succeeds, the return value is a handle to the newly
1591 * created desktop. If the specified desktop already exists, the function
1592 * succeeds and returns a handle to the existing desktop. When you are
1593 * finished using the handle, call the CloseDesktop function to close it.
1594 * If the function fails, the return value is NULL.
1601 NtUserCreateDesktop(
1602 POBJECT_ATTRIBUTES ObjectAttributes
,
1603 PUNICODE_STRING lpszDesktopDevice
,
1606 ACCESS_MASK dwDesiredAccess
)
1608 PDESKTOP pdesk
= NULL
;
1609 NTSTATUS Status
= STATUS_SUCCESS
;
1611 BOOLEAN Context
= FALSE
;
1612 UNICODE_STRING ClassName
;
1613 LARGE_STRING WindowName
;
1614 BOOL NoHooks
= FALSE
;
1617 PTHREADINFO ptiCurrent
;
1620 DECLARE_RETURN(HDESK
);
1622 TRACE("Enter NtUserCreateDesktop\n");
1623 UserEnterExclusive();
1625 ptiCurrent
= PsGetCurrentThreadWin32Thread();
1627 ASSERT(gptiDesktopThread
);
1629 /* Turn off hooks when calling any CreateWindowEx from inside win32k. */
1630 NoHooks
= (ptiCurrent
->TIF_flags
& TIF_DISABLEHOOKS
);
1631 ptiCurrent
->TIF_flags
|= TIF_DISABLEHOOKS
;
1632 ptiCurrent
->pClientInfo
->dwTIFlags
= ptiCurrent
->TIF_flags
;
1635 * Try to open already existing desktop
1637 Status
= ObOpenObjectByName(
1639 ExDesktopObjectType
,
1645 if (!NT_SUCCESS(Status
))
1647 ERR("ObOpenObjectByName failed to open/create desktop\n");
1648 SetLastNtError(Status
);
1652 /* In case the object was not created (eg if it existed), return now */
1653 if (Context
== FALSE
)
1655 TRACE("NtUserCreateDesktop opened desktop %wZ\n", ObjectAttributes
->ObjectName
);
1659 /* Reference the desktop */
1660 Status
= ObReferenceObjectByHandle(hdesk
,
1662 ExDesktopObjectType
,
1666 if (!NT_SUCCESS(Status
))
1668 ERR("Failed to reference desktop object\n");
1669 SetLastNtError(Status
);
1673 /* Get the desktop window class. The thread desktop does not belong to any desktop
1674 * so the classes created there (including the desktop class) are allocated in the shared heap
1675 * It would cause problems if we used a class that belongs to the caller
1677 ClassName
.Buffer
= WC_DESKTOP
;
1678 ClassName
.Length
= 0;
1679 pcls
= IntGetAndReferenceClass(&ClassName
, 0, TRUE
);
1686 RtlZeroMemory(&WindowName
, sizeof(WindowName
));
1687 RtlZeroMemory(&Cs
, sizeof(Cs
));
1688 Cs
.x
= UserGetSystemMetrics(SM_XVIRTUALSCREEN
),
1689 Cs
.y
= UserGetSystemMetrics(SM_YVIRTUALSCREEN
),
1690 Cs
.cx
= UserGetSystemMetrics(SM_CXVIRTUALSCREEN
),
1691 Cs
.cy
= UserGetSystemMetrics(SM_CYVIRTUALSCREEN
),
1692 Cs
.style
= WS_POPUP
|WS_CLIPCHILDREN
;
1693 Cs
.hInstance
= hModClient
; // hModuleWin; // Server side winproc!
1694 Cs
.lpszName
= (LPCWSTR
) &WindowName
;
1695 Cs
.lpszClass
= (LPCWSTR
) &ClassName
;
1697 /* Use IntCreateWindow instead of co_UserCreateWindowEx cause the later expects a thread with a desktop */
1698 pWnd
= IntCreateWindow(&Cs
, &WindowName
, pcls
, NULL
, NULL
, NULL
, pdesk
);
1701 ERR("Failed to create desktop window for the new desktop\n");
1705 pdesk
->dwSessionId
= PsGetCurrentProcessSessionId();
1706 pdesk
->DesktopWindow
= pWnd
->head
.h
;
1707 pdesk
->pDeskInfo
->spwnd
= pWnd
;
1708 pWnd
->fnid
= FNID_DESKTOP
;
1710 ClassName
.Buffer
= MAKEINTATOM(gpsi
->atomSysClass
[ICLS_HWNDMESSAGE
]);
1711 ClassName
.Length
= 0;
1712 pcls
= IntGetAndReferenceClass(&ClassName
, 0, TRUE
);
1719 RtlZeroMemory(&WindowName
, sizeof(WindowName
));
1720 RtlZeroMemory(&Cs
, sizeof(Cs
));
1721 Cs
.cx
= Cs
.cy
= 100;
1722 Cs
.style
= WS_POPUP
|WS_CLIPCHILDREN
;
1723 Cs
.hInstance
= hModClient
; // hModuleWin; // Server side winproc!
1724 Cs
.lpszName
= (LPCWSTR
) &WindowName
;
1725 Cs
.lpszClass
= (LPCWSTR
) &ClassName
;
1726 pWnd
= IntCreateWindow(&Cs
, &WindowName
, pcls
, NULL
, NULL
, NULL
, pdesk
);
1729 ERR("Failed to create message window for the new desktop\n");
1733 pdesk
->spwndMessage
= pWnd
;
1734 pWnd
->fnid
= FNID_MESSAGEWND
;
1737 if !(WinStaObject->Flags & WSF_NOIO) is (not set) for desktop input output mode (see wiki)
1738 Create Tooltip. Saved in DesktopObject->spwndTooltip.
1739 Tooltip dwExStyle: WS_EX_TOOLWINDOW|WS_EX_TOPMOST
1740 hWndParent are spwndMessage. Use hModuleWin for server side winproc!
1741 The rest is same as message window.
1742 http://msdn.microsoft.com/en-us/library/bb760250(VS.85).aspx
1749 ObDereferenceObject(pdesk
);
1751 if (_ret_
== NULL
&& hdesk
!= NULL
)
1753 ObCloseHandle(hdesk
, UserMode
);
1757 ptiCurrent
->TIF_flags
&= ~TIF_DISABLEHOOKS
;
1758 ptiCurrent
->pClientInfo
->dwTIFlags
= ptiCurrent
->TIF_flags
;
1760 TRACE("Leave NtUserCreateDesktop, ret=%p\n",_ret_
);
1768 * Opens an existing desktop.
1772 * Name of the existing desktop.
1775 * Interaction flags.
1778 * Requested type of access.
1781 * Handle to the desktop or zero on failure.
1789 POBJECT_ATTRIBUTES ObjectAttributes
,
1791 ACCESS_MASK dwDesiredAccess
)
1796 Status
= ObOpenObjectByName(
1798 ExDesktopObjectType
,
1805 if (!NT_SUCCESS(Status
))
1807 ERR("Failed to open desktop\n");
1808 SetLastNtError(Status
);
1812 TRACE("Opened desktop %S with handle 0x%p\n", ObjectAttributes
->ObjectName
->Buffer
, Desktop
);
1818 * NtUserOpenInputDesktop
1820 * Opens the input (interactive) desktop.
1824 * Interaction flags.
1827 * Inheritance option.
1830 * Requested type of access.
1833 * Handle to the input desktop or zero on failure.
1840 NtUserOpenInputDesktop(
1843 ACCESS_MASK dwDesiredAccess
)
1847 ULONG HandleAttributes
= 0;
1849 UserEnterExclusive();
1850 TRACE("Enter NtUserOpenInputDesktop gpdeskInputDesktop 0x%p\n",gpdeskInputDesktop
);
1852 if (fInherit
) HandleAttributes
= OBJ_INHERIT
;
1854 /* Create a new handle to the object */
1855 Status
= ObOpenObjectByPointer(
1860 ExDesktopObjectType
,
1864 if (!NT_SUCCESS(Status
))
1866 ERR("Failed to open input desktop object\n");
1867 SetLastNtError(Status
);
1870 TRACE("NtUserOpenInputDesktop returning 0x%p\n",hdesk
);
1876 * NtUserCloseDesktop
1878 * Closes a desktop handle.
1882 * Handle to the desktop.
1888 * The desktop handle can be created with NtUserCreateDesktop or
1889 * NtUserOpenDesktop. This function will fail if any thread in the calling
1890 * process is using the specified desktop handle or if the handle refers
1891 * to the initial desktop of the calling process.
1898 NtUserCloseDesktop(HDESK hDesktop
)
1902 DECLARE_RETURN(BOOL
);
1904 TRACE("NtUserCloseDesktop called (0x%p)\n", hDesktop
);
1905 UserEnterExclusive();
1907 if (hDesktop
== gptiCurrent
->hdesk
|| hDesktop
== gptiCurrent
->ppi
->hdeskStartup
)
1909 ERR("Attempted to close thread desktop\n");
1910 EngSetLastError(ERROR_BUSY
);
1914 Status
= IntValidateDesktopHandle( hDesktop
, UserMode
, 0, &pdesk
);
1915 if (!NT_SUCCESS(Status
))
1917 ERR("Validation of desktop handle (0x%p) failed\n", hDesktop
);
1921 ObDereferenceObject(pdesk
);
1923 Status
= ZwClose(hDesktop
);
1924 if (!NT_SUCCESS(Status
))
1926 ERR("Failed to close desktop handle 0x%p\n", hDesktop
);
1927 SetLastNtError(Status
);
1934 TRACE("Leave NtUserCloseDesktop, ret=%i\n",_ret_
);
1940 * NtUserPaintDesktop
1942 * The NtUserPaintDesktop function fills the clipping region in the
1943 * specified device context with the desktop pattern or wallpaper. The
1944 * function is provided primarily for shell desktops.
1948 * Handle to the device context.
1955 NtUserPaintDesktop(HDC hDC
)
1958 UserEnterExclusive();
1959 TRACE("Enter NtUserPaintDesktop\n");
1960 Ret
= IntPaintDesktop(hDC
);
1961 TRACE("Leave NtUserPaintDesktop, ret=%i\n",Ret
);
1967 * NtUserResolveDesktop
1969 * The NtUserResolveDesktop function retrieves handles to the desktop and
1970 * the window station specified by the desktop path string.
1974 * Handle to a user process.
1977 * The desktop path string.
1980 * Handle to the desktop (direct return value) and
1981 * handle to the associated window station (by pointer).
1982 * NULL in case of failure.
1985 * Callable by CSRSS only.
1993 NtUserResolveDesktop(
1994 IN HANDLE ProcessHandle
,
1995 IN PUNICODE_STRING DesktopPath
,
1997 OUT HWINSTA
* phWinSta
)
2000 PEPROCESS Process
= NULL
;
2001 HWINSTA hWinSta
= NULL
;
2002 HDESK hDesktop
= NULL
;
2004 /* Allow only the Console Server to perform this operation (via CSRSS) */
2005 if (PsGetCurrentProcess() != gpepCSRSS
)
2008 /* Get the process object the user handle was referencing */
2009 Status
= ObReferenceObjectByHandle(ProcessHandle
,
2010 PROCESS_QUERY_INFORMATION
,
2015 if (!NT_SUCCESS(Status
)) return NULL
;
2017 // UserEnterShared();
2021 UNICODE_STRING CapturedDesktopPath
;
2023 /* Capture the user desktop path string */
2024 Status
= IntSafeCopyUnicodeStringTerminateNULL(&CapturedDesktopPath
,
2026 if (!NT_SUCCESS(Status
)) _SEH2_YIELD(goto Quit
);
2028 /* Call the internal function */
2029 Status
= IntParseDesktopPath(Process
,
2030 &CapturedDesktopPath
,
2033 if (!NT_SUCCESS(Status
))
2035 ERR("IntParseDesktopPath failed, Status = 0x%08lx\n", Status
);
2040 /* Return the window station handle */
2041 *phWinSta
= hWinSta
;
2043 /* Free the captured string */
2044 if (CapturedDesktopPath
.Buffer
)
2045 ExFreePoolWithTag(CapturedDesktopPath
.Buffer
, TAG_STRING
);
2047 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2049 Status
= _SEH2_GetExceptionCode();
2056 /* Dereference the process object */
2057 ObDereferenceObject(Process
);
2059 /* Return the desktop handle */
2064 * NtUserSwitchDesktop
2066 * Sets the current input (interactive) desktop.
2070 * Handle to desktop.
2080 NtUserSwitchDesktop(HDESK hdesk
)
2084 BOOL bRedrawDesktop
;
2085 DECLARE_RETURN(BOOL
);
2087 UserEnterExclusive();
2088 TRACE("Enter NtUserSwitchDesktop(0x%p)\n", hdesk
);
2090 Status
= IntValidateDesktopHandle( hdesk
, UserMode
, 0, &pdesk
);
2091 if (!NT_SUCCESS(Status
))
2093 ERR("Validation of desktop handle (0x%p) failed\n", hdesk
);
2097 if (PsGetCurrentProcessSessionId() != pdesk
->rpwinstaParent
->dwSessionId
)
2099 ObDereferenceObject(pdesk
);
2100 ERR("NtUserSwitchDesktop called for a desktop of a different session\n");
2104 if (pdesk
== gpdeskInputDesktop
)
2106 ObDereferenceObject(pdesk
);
2107 WARN("NtUserSwitchDesktop called for active desktop\n");
2112 * Don't allow applications switch the desktop if it's locked, unless the caller
2113 * is the logon application itself
2115 if ((pdesk
->rpwinstaParent
->Flags
& WSS_LOCKED
) &&
2116 gpidLogon
!= PsGetCurrentProcessId())
2118 ObDereferenceObject(pdesk
);
2119 ERR("Switching desktop 0x%p denied because the window station is locked!\n", hdesk
);
2123 if (pdesk
->rpwinstaParent
!= InputWindowStation
)
2125 ObDereferenceObject(pdesk
);
2126 ERR("Switching desktop 0x%p denied because desktop doesn't belong to the interactive winsta!\n", hdesk
);
2130 /* FIXME: Fail if the process is associated with a secured
2131 desktop such as Winlogon or Screen-Saver */
2132 /* FIXME: Connect to input device */
2134 TRACE("Switching from desktop 0x%p to 0x%p\n", gpdeskInputDesktop
, pdesk
);
2136 bRedrawDesktop
= FALSE
;
2138 /* The first time SwitchDesktop is called, gpdeskInputDesktop is NULL */
2139 if (gpdeskInputDesktop
!= NULL
)
2141 if ((gpdeskInputDesktop
->pDeskInfo
->spwnd
->style
& WS_VISIBLE
) == WS_VISIBLE
)
2142 bRedrawDesktop
= TRUE
;
2144 /* Hide the previous desktop window */
2145 IntHideDesktop(gpdeskInputDesktop
);
2148 /* Set the active desktop in the desktop's window station. */
2149 InputWindowStation
->ActiveDesktop
= pdesk
;
2151 /* Set the global state. */
2152 gpdeskInputDesktop
= pdesk
;
2154 /* Show the new desktop window */
2155 co_IntShowDesktop(pdesk
, UserGetSystemMetrics(SM_CXSCREEN
), UserGetSystemMetrics(SM_CYSCREEN
), bRedrawDesktop
);
2157 TRACE("SwitchDesktop gpdeskInputDesktop 0x%p\n",gpdeskInputDesktop
);
2158 ObDereferenceObject(pdesk
);
2163 TRACE("Leave NtUserSwitchDesktop, ret=%i\n",_ret_
);
2169 * NtUserGetThreadDesktop
2176 NtUserGetThreadDesktop(DWORD dwThreadId
, DWORD Unknown1
)
2180 PDESKTOP DesktopObject
;
2181 HDESK Ret
, hThreadDesktop
;
2182 OBJECT_HANDLE_INFORMATION HandleInformation
;
2183 DECLARE_RETURN(HDESK
);
2185 UserEnterExclusive();
2186 TRACE("Enter NtUserGetThreadDesktop\n");
2190 EngSetLastError(ERROR_INVALID_PARAMETER
);
2194 Status
= PsLookupThreadByThreadId((HANDLE
)(DWORD_PTR
)dwThreadId
, &Thread
);
2195 if (!NT_SUCCESS(Status
))
2197 EngSetLastError(ERROR_INVALID_PARAMETER
);
2201 if (Thread
->ThreadsProcess
== PsGetCurrentProcess())
2203 /* Just return the handle, we queried the desktop handle of a thread running
2204 in the same context */
2205 Ret
= ((PTHREADINFO
)Thread
->Tcb
.Win32Thread
)->hdesk
;
2206 ObDereferenceObject(Thread
);
2210 /* Get the desktop handle and the desktop of the thread */
2211 if (!(hThreadDesktop
= ((PTHREADINFO
)Thread
->Tcb
.Win32Thread
)->hdesk
) ||
2212 !(DesktopObject
= ((PTHREADINFO
)Thread
->Tcb
.Win32Thread
)->rpdesk
))
2214 ObDereferenceObject(Thread
);
2215 ERR("Desktop information of thread 0x%x broken!?\n", dwThreadId
);
2219 /* We could just use DesktopObject instead of looking up the handle, but latter
2220 may be a bit safer (e.g. when the desktop is being destroyed */
2221 /* Switch into the context of the thread we're trying to get the desktop from,
2222 so we can use the handle */
2223 KeAttachProcess(&Thread
->ThreadsProcess
->Pcb
);
2224 Status
= ObReferenceObjectByHandle(hThreadDesktop
,
2226 ExDesktopObjectType
,
2228 (PVOID
*)&DesktopObject
,
2229 &HandleInformation
);
2232 /* The handle couldn't be found, there's nothing to get... */
2233 if (!NT_SUCCESS(Status
))
2235 ObDereferenceObject(Thread
);
2239 /* Lookup our handle table if we can find a handle to the desktop object,
2240 if not, create one */
2241 Ret
= IntGetDesktopObjectHandle(DesktopObject
);
2243 /* All done, we got a valid handle to the desktop */
2244 ObDereferenceObject(DesktopObject
);
2245 ObDereferenceObject(Thread
);
2249 TRACE("Leave NtUserGetThreadDesktop, ret=%p\n",_ret_
);
2255 IntUnmapDesktopView(IN PDESKTOP pdesk
)
2258 PW32HEAP_USER_MAPPING HeapMapping
, *PrevLink
;
2259 NTSTATUS Status
= STATUS_SUCCESS
;
2261 TRACE("IntUnmapDesktopView called for desktop object %p\n", pdesk
);
2263 ppi
= PsGetCurrentProcessWin32Process();
2266 * Unmap if we're the last thread using the desktop.
2267 * Start the search at the next mapping: skip the first entry
2268 * as it must be the global user heap mapping.
2270 PrevLink
= &ppi
->HeapMappings
.Next
;
2271 HeapMapping
= *PrevLink
;
2272 while (HeapMapping
!= NULL
)
2274 if (HeapMapping
->KernelMapping
== (PVOID
)pdesk
->pheapDesktop
)
2276 if (--HeapMapping
->Count
== 0)
2278 *PrevLink
= HeapMapping
->Next
;
2280 TRACE("ppi 0x%p unmapped heap of desktop 0x%p\n", ppi
, pdesk
);
2281 Status
= MmUnmapViewOfSection(PsGetCurrentProcess(),
2282 HeapMapping
->UserMapping
);
2284 ObDereferenceObject(pdesk
);
2286 UserHeapFree(HeapMapping
);
2291 PrevLink
= &HeapMapping
->Next
;
2292 HeapMapping
= HeapMapping
->Next
;
2299 IntMapDesktopView(IN PDESKTOP pdesk
)
2302 PW32HEAP_USER_MAPPING HeapMapping
, *PrevLink
;
2303 PVOID UserBase
= NULL
;
2304 SIZE_T ViewSize
= 0;
2305 LARGE_INTEGER Offset
;
2308 TRACE("IntMapDesktopView called for desktop object 0x%p\n", pdesk
);
2310 ppi
= PsGetCurrentProcessWin32Process();
2313 * Find out if another thread already mapped the desktop heap.
2314 * Start the search at the next mapping: skip the first entry
2315 * as it must be the global user heap mapping.
2317 PrevLink
= &ppi
->HeapMappings
.Next
;
2318 HeapMapping
= *PrevLink
;
2319 while (HeapMapping
!= NULL
)
2321 if (HeapMapping
->KernelMapping
== (PVOID
)pdesk
->pheapDesktop
)
2323 HeapMapping
->Count
++;
2324 return STATUS_SUCCESS
;
2327 PrevLink
= &HeapMapping
->Next
;
2328 HeapMapping
= HeapMapping
->Next
;
2331 /* We're the first, map the heap */
2332 Offset
.QuadPart
= 0;
2333 Status
= MmMapViewOfSection(pdesk
->hsectionDesktop
,
2334 PsGetCurrentProcess(),
2342 PAGE_EXECUTE_READ
); /* Would prefer PAGE_READONLY, but thanks to RTL heaps... */
2343 if (!NT_SUCCESS(Status
))
2345 ERR("Failed to map desktop\n");
2349 TRACE("ppi 0x%p mapped heap of desktop 0x%p\n", ppi
, pdesk
);
2351 /* Add the mapping */
2352 HeapMapping
= UserHeapAlloc(sizeof(*HeapMapping
));
2353 if (HeapMapping
== NULL
)
2355 MmUnmapViewOfSection(PsGetCurrentProcess(), UserBase
);
2356 ERR("UserHeapAlloc() failed!\n");
2357 return STATUS_NO_MEMORY
;
2360 HeapMapping
->Next
= NULL
;
2361 HeapMapping
->KernelMapping
= (PVOID
)pdesk
->pheapDesktop
;
2362 HeapMapping
->UserMapping
= UserBase
;
2363 HeapMapping
->Limit
= ViewSize
;
2364 HeapMapping
->Count
= 1;
2365 *PrevLink
= HeapMapping
;
2367 ObReferenceObject(pdesk
);
2369 return STATUS_SUCCESS
;
2373 IntSetThreadDesktop(IN HDESK hDesktop
,
2374 IN BOOL FreeOnFailure
)
2376 PDESKTOP pdesk
= NULL
, pdeskOld
;
2380 PCLIENTTHREADINFO pctiOld
, pctiNew
= NULL
;
2383 ASSERT(NtCurrentTeb());
2385 TRACE("IntSetThreadDesktop hDesktop:0x%p, FOF:%i\n",hDesktop
, FreeOnFailure
);
2387 pti
= PsGetCurrentThreadWin32Thread();
2388 pci
= pti
->pClientInfo
;
2390 /* If the caller gave us a desktop, ensure it is valid */
2391 if (hDesktop
!= NULL
)
2393 /* Validate the new desktop. */
2394 Status
= IntValidateDesktopHandle( hDesktop
, UserMode
, 0, &pdesk
);
2395 if (!NT_SUCCESS(Status
))
2397 ERR("Validation of desktop handle (0x%p) failed\n", hDesktop
);
2401 if (pti
->rpdesk
== pdesk
)
2404 ObDereferenceObject(pdesk
);
2409 /* Make sure that we don't own any window in the current desktop */
2410 if (!IsListEmpty(&pti
->WindowListHead
))
2413 ObDereferenceObject(pdesk
);
2414 ERR("Attempted to change thread desktop although the thread has windows!\n");
2415 EngSetLastError(ERROR_BUSY
);
2419 /* Desktop is being re-set so clear out foreground. */
2420 if (pti
->rpdesk
!= pdesk
&& pti
->MessageQueue
== gpqForeground
)
2422 // Like above, there shouldn't be any windows, hooks or anything active on this threads desktop!
2423 IntSetFocusMessageQueue(NULL
);
2426 /* Before doing the switch, map the new desktop heap and allocate the new pcti */
2429 Status
= IntMapDesktopView(pdesk
);
2430 if (!NT_SUCCESS(Status
))
2432 ERR("Failed to map desktop heap!\n");
2433 ObDereferenceObject(pdesk
);
2434 SetLastNtError(Status
);
2438 pctiNew
= DesktopHeapAlloc( pdesk
, sizeof(CLIENTTHREADINFO
));
2439 if (pctiNew
== NULL
)
2441 ERR("Failed to allocate new pcti\n");
2442 IntUnmapDesktopView(pdesk
);
2443 ObDereferenceObject(pdesk
);
2444 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY
);
2449 /* free all classes or move them to the shared heap */
2450 if (pti
->rpdesk
!= NULL
)
2452 if (!IntCheckProcessDesktopClasses(pti
->rpdesk
, FreeOnFailure
))
2454 ERR("Failed to move process classes to shared heap!\n");
2457 DesktopHeapFree(pdesk
, pctiNew
);
2458 IntUnmapDesktopView(pdesk
);
2459 ObDereferenceObject(pdesk
);
2465 pdeskOld
= pti
->rpdesk
;
2466 hdeskOld
= pti
->hdesk
;
2467 if (pti
->pcti
!= &pti
->cti
)
2468 pctiOld
= pti
->pcti
;
2475 pti
->rpdesk
= pdesk
;
2476 pti
->hdesk
= hDesktop
;
2477 pti
->pDeskInfo
= pti
->rpdesk
->pDeskInfo
;
2478 pti
->pcti
= pctiNew
;
2480 pci
->ulClientDelta
= DesktopHeapGetUserDelta();
2481 pci
->pDeskInfo
= (PVOID
)((ULONG_PTR
)pti
->pDeskInfo
- pci
->ulClientDelta
);
2482 pci
->pClientThreadInfo
= (PVOID
)((ULONG_PTR
)pti
->pcti
- pci
->ulClientDelta
);
2484 /* initialize the new pcti */
2485 if (pctiOld
!= NULL
)
2487 RtlCopyMemory(pctiNew
, pctiOld
, sizeof(CLIENTTHREADINFO
));
2491 RtlZeroMemory(pctiNew
, sizeof(CLIENTTHREADINFO
));
2492 pci
->fsHooks
= pti
->fsHooks
;
2493 pci
->dwTIFlags
= pti
->TIF_flags
;
2500 pti
->pDeskInfo
= NULL
;
2501 pti
->pcti
= &pti
->cti
; // Always point inside so there will be no crash when posting or sending msg's!
2502 pci
->ulClientDelta
= 0;
2503 pci
->pDeskInfo
= NULL
;
2504 pci
->pClientThreadInfo
= NULL
;
2507 /* clean up the old desktop */
2508 if (pdeskOld
!= NULL
)
2510 RemoveEntryList(&pti
->PtiLink
);
2511 if (pctiOld
) DesktopHeapFree(pdeskOld
, pctiOld
);
2512 IntUnmapDesktopView(pdeskOld
);
2513 ObDereferenceObject(pdeskOld
);
2519 InsertTailList(&pdesk
->PtiList
, &pti
->PtiLink
);
2522 TRACE("IntSetThreadDesktop: pti 0x%p ppi 0x%p switched from object 0x%p to 0x%p\n", pti
, pti
->ppi
, pdeskOld
, pdesk
);
2528 * NtUserSetThreadDesktop
2535 NtUserSetThreadDesktop(HDESK hDesktop
)
2539 UserEnterExclusive();
2541 // FIXME: IntSetThreadDesktop validates the desktop handle, it should happen
2542 // here too and set the NT error level. Q. Is it necessary to have the validation
2543 // in IntSetThreadDesktop? Is it needed there too?
2544 if (hDesktop
|| (!hDesktop
&& PsGetCurrentProcess() == gpepCSRSS
))
2545 ret
= IntSetThreadDesktop(hDesktop
, FALSE
);