Tests show that shell windows are per-desktop, not global. Existing tests also show that new desktops from CreateDesktop do not have a shell window set initially. The Default desktop on WinSta0 does, however.
GetShellWindow ought to return an IShellView's window, but we never actually make one of those for the desktop. However, there is a common pattern (used by e.g. GOG Galaxy) of retrieving an unelevated token by duplicating that of the owner of GetShellWindow, so having any window set at all is a step in the right direction.
From: Tim Clem tclem@codeweavers.com
--- dlls/user32/tests/win.c | 10 ++++++++++ 1 file changed, 10 insertions(+)
diff --git a/dlls/user32/tests/win.c b/dlls/user32/tests/win.c index 2fcd3a5d259..b29b1472283 100644 --- a/dlls/user32/tests/win.c +++ b/dlls/user32/tests/win.c @@ -1812,8 +1812,13 @@ static DWORD WINAPI test_shell_window_thread(LPVOID param) static void test_shell_window(void) { HDESK hdesk; + HWND orig_shell_window; HANDLE hthread;
+ orig_shell_window = GetShellWindow(); + todo_wine + ok(orig_shell_window != NULL, "default desktop doesn't have a shell window\n"); + hdesk = CreateDesktopA("winetest", NULL, NULL, 0, GENERIC_ALL, NULL);
hthread = CreateThread(NULL, 0, test_shell_window_thread, (LPVOID)hdesk, 0, NULL); @@ -1823,6 +1828,11 @@ static void test_shell_window(void) CloseHandle(hthread);
CloseDesktop(hdesk); + + if (!orig_shell_window) + skip("no shell window on default desktop\n"); + else + ok(GetShellWindow() == orig_shell_window, "changing shell window on another desktop effected the default\n"); }
/************** MDI test ****************/
From: Tim Clem tclem@codeweavers.com
--- dlls/win32u/window.c | 18 +++++------ server/protocol.def | 10 +++---- server/user.h | 4 +++ server/window.c | 71 +++++++++++++++++++++++--------------------- server/winstation.c | 4 +++ 5 files changed, 59 insertions(+), 48 deletions(-)
diff --git a/dlls/win32u/window.c b/dlls/win32u/window.c index cb7b1f6e3a6..8874bf404cf 100644 --- a/dlls/win32u/window.c +++ b/dlls/win32u/window.c @@ -5993,7 +5993,7 @@ HWND get_shell_window(void) { HWND hwnd = 0;
- SERVER_START_REQ(set_global_windows) + SERVER_START_REQ(set_desktop_shell_windows) { req->flags = 0; if (!wine_server_call_err(req)) @@ -6035,9 +6035,9 @@ BOOL WINAPI NtUserSetShellWindowEx( HWND shell, HWND list_view )
NtUserSetWindowPos( shell, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE );
- SERVER_START_REQ(set_global_windows) + SERVER_START_REQ(set_desktop_shell_windows) { - req->flags = SET_GLOBAL_SHELL_WINDOWS; + req->flags = SET_DESKTOP_SHELL_WINDOWS; req->shell_window = wine_server_user_handle( shell ); req->shell_listview = wine_server_user_handle( list_view ); ret = !wine_server_call_err(req); @@ -6050,7 +6050,7 @@ HWND get_progman_window(void) { HWND ret = 0;
- SERVER_START_REQ(set_global_windows) + SERVER_START_REQ(set_desktop_shell_windows) { req->flags = 0; if (!wine_server_call_err(req)) @@ -6062,9 +6062,9 @@ HWND get_progman_window(void)
HWND set_progman_window( HWND hwnd ) { - SERVER_START_REQ(set_global_windows) + SERVER_START_REQ(set_desktop_shell_windows) { - req->flags = SET_GLOBAL_PROGMAN_WINDOW; + req->flags = SET_DESKTOP_PROGMAN_WINDOW; req->progman_window = wine_server_user_handle( hwnd ); if (wine_server_call_err( req )) hwnd = 0; } @@ -6076,7 +6076,7 @@ HWND get_taskman_window(void) { HWND ret = 0;
- SERVER_START_REQ(set_global_windows) + SERVER_START_REQ(set_desktop_shell_windows) { req->flags = 0; if (!wine_server_call_err(req)) @@ -6091,9 +6091,9 @@ HWND set_taskman_window( HWND hwnd ) /* hwnd = MSTaskSwWClass * |-> SysTabControl32 */ - SERVER_START_REQ(set_global_windows) + SERVER_START_REQ(set_desktop_shell_windows) { - req->flags = SET_GLOBAL_TASKMAN_WINDOW; + req->flags = SET_DESKTOP_TASKMAN_WINDOW; req->taskman_window = wine_server_user_handle( hwnd ); if (wine_server_call_err( req )) hwnd = 0; } diff --git a/server/protocol.def b/server/protocol.def index f2db46bd87d..c6af7379f17 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -3292,8 +3292,8 @@ enum caret_state #define OPEN_TOKEN_AS_SELF 2
-/* Set/get the global windows */ -@REQ(set_global_windows) +/* Set/get the desktop shell windows */ +@REQ(set_desktop_shell_windows) unsigned int flags; /* flags for fields to set (see below) */ user_handle_t shell_window; /* handle to the new shell window */ user_handle_t shell_listview; /* handle to the new shell listview window */ @@ -3305,9 +3305,9 @@ enum caret_state user_handle_t old_progman_window; /* handle to the new program manager window */ user_handle_t old_taskman_window; /* handle to the new task manager window */ @END -#define SET_GLOBAL_SHELL_WINDOWS 0x01 /* set both main shell and listview windows */ -#define SET_GLOBAL_PROGMAN_WINDOW 0x02 -#define SET_GLOBAL_TASKMAN_WINDOW 0x04 +#define SET_DESKTOP_SHELL_WINDOWS 0x01 /* set both main shell and listview windows */ +#define SET_DESKTOP_PROGMAN_WINDOW 0x02 +#define SET_DESKTOP_TASKMAN_WINDOW 0x04
/* Adjust the privileges held by a token */ @REQ(adjust_token_privileges) diff --git a/server/user.h b/server/user.h index 6f10f296200..6f9612ffb93 100644 --- a/server/user.h +++ b/server/user.h @@ -73,6 +73,10 @@ struct desktop struct list threads; /* list of threads connected to this desktop */ struct window *top_window; /* desktop window for this desktop */ struct window *msg_window; /* HWND_MESSAGE top window */ + struct window *shell_window; /* shell window for this desktop */ + struct window *shell_listview; /* shell list view window for this desktop */ + struct window *progman_window; /* progman window for this desktop */ + struct window *taskman_window; /* taskman window for this desktop */ struct hook_table *global_hooks; /* table of global hooks on this desktop */ struct list hotkeys; /* list of registered hotkeys */ struct list pointers; /* list of active pointers */ diff --git a/server/window.c b/server/window.c index 3ce9643722a..a9b49d99afd 100644 --- a/server/window.c +++ b/server/window.c @@ -145,12 +145,6 @@ struct user_handle_array
static const rectangle_t empty_rect;
-/* global window pointers */ -static struct window *shell_window; -static struct window *shell_listview; -static struct window *progman_window; -static struct window *taskman_window; - /* magic HWND_TOP etc. pointers */ #define WINPTR_TOP ((struct window *)1L) #define WINPTR_BOTTOM ((struct window *)2L) @@ -2022,10 +2016,10 @@ void free_window_handle( struct window *win ) }
/* reset global window pointers, if the corresponding window is destroyed */ - if (win == shell_window) shell_window = NULL; - if (win == shell_listview) shell_listview = NULL; - if (win == progman_window) progman_window = NULL; - if (win == taskman_window) taskman_window = NULL; + if (win == win->desktop->shell_window) win->desktop->shell_window = NULL; + if (win == win->desktop->shell_listview) win->desktop->shell_listview = NULL; + if (win == win->desktop->progman_window) win->desktop->progman_window = NULL; + if (win == win->desktop->taskman_window) win->desktop->taskman_window = NULL; free_hotkeys( win->desktop, win->handle ); cleanup_clipboard_window( win->desktop, win->handle ); destroy_properties( win ); @@ -2918,9 +2912,9 @@ DECL_HANDLER(get_window_properties) }
-/* get the new window pointer for a global window, checking permissions */ -/* helper for set_global_windows request */ -static int get_new_global_window( struct window **win, user_handle_t handle ) +/* get the new window pointer for a desktop shell window, checking permissions */ +/* helper for set_desktop_shell_windows request */ +static int get_new_shell_window( struct window **win, user_handle_t handle ) { if (!handle) { @@ -2936,36 +2930,45 @@ static int get_new_global_window( struct window **win, user_handle_t handle ) return (*win != NULL); }
-/* Set/get the global windows */ -DECL_HANDLER(set_global_windows) +/* Set/get the desktop shell windows */ +DECL_HANDLER(set_desktop_shell_windows) { - struct window *new_shell_window = shell_window; - struct window *new_shell_listview = shell_listview; - struct window *new_progman_window = progman_window; - struct window *new_taskman_window = taskman_window; + struct desktop *desktop; + struct window *new_shell_window, *new_shell_listview, *new_progman_window, *new_taskman_window; + + desktop = get_desktop_obj( current->process, current->desktop, 0 ); + assert( desktop ); + + new_shell_window = desktop->shell_window; + new_shell_listview = desktop->shell_listview; + new_progman_window = desktop->progman_window; + new_taskman_window = desktop->taskman_window;
- reply->old_shell_window = shell_window ? shell_window->handle : 0; - reply->old_shell_listview = shell_listview ? shell_listview->handle : 0; - reply->old_progman_window = progman_window ? progman_window->handle : 0; - reply->old_taskman_window = taskman_window ? taskman_window->handle : 0; + reply->old_shell_window = new_shell_window ? new_shell_window->handle : 0; + reply->old_shell_listview = new_shell_listview ? new_shell_listview->handle : 0; + reply->old_progman_window = new_progman_window ? new_progman_window->handle : 0; + reply->old_taskman_window = new_taskman_window ? new_taskman_window->handle : 0;
- if (req->flags & SET_GLOBAL_SHELL_WINDOWS) + if (req->flags & SET_DESKTOP_SHELL_WINDOWS) { - if (!get_new_global_window( &new_shell_window, req->shell_window )) return; - if (!get_new_global_window( &new_shell_listview, req->shell_listview )) return; + if (!get_new_shell_window( &new_shell_window, req->shell_window )) goto done; + if (!get_new_shell_window( &new_shell_listview, req->shell_listview )) goto done; } - if (req->flags & SET_GLOBAL_PROGMAN_WINDOW) + if (req->flags & SET_DESKTOP_PROGMAN_WINDOW) { - if (!get_new_global_window( &new_progman_window, req->progman_window )) return; + if (!get_new_shell_window( &new_progman_window, req->progman_window )) goto done; } - if (req->flags & SET_GLOBAL_TASKMAN_WINDOW) + if (req->flags & SET_DESKTOP_TASKMAN_WINDOW) { - if (!get_new_global_window( &new_taskman_window, req->taskman_window )) return; + if (!get_new_shell_window( &new_taskman_window, req->taskman_window )) goto done; } - shell_window = new_shell_window; - shell_listview = new_shell_listview; - progman_window = new_progman_window; - taskman_window = new_taskman_window; + desktop->shell_window = new_shell_window; + desktop->shell_listview = new_shell_listview; + desktop->progman_window = new_progman_window; + desktop->taskman_window = new_taskman_window; + +done: + release_object( desktop ); }
/* retrieve layered info for a window */ diff --git a/server/winstation.c b/server/winstation.c index f5981721e55..e5f4bfec357 100644 --- a/server/winstation.c +++ b/server/winstation.c @@ -285,6 +285,10 @@ static struct desktop *create_desktop( const struct unicode_str *name, unsigned desktop->winstation = (struct winstation *)grab_object( winstation ); desktop->top_window = NULL; desktop->msg_window = NULL; + desktop->shell_window = NULL; + desktop->shell_listview = NULL; + desktop->progman_window = NULL; + desktop->taskman_window = NULL; desktop->global_hooks = NULL; desktop->close_timeout = NULL; desktop->foreground_input = NULL;
From: Tim Clem tclem@codeweavers.com
--- dlls/user32/tests/win.c | 1 - programs/explorer/desktop.c | 40 +++++++++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 1 deletion(-)
diff --git a/dlls/user32/tests/win.c b/dlls/user32/tests/win.c index b29b1472283..7d16f0a6dc1 100644 --- a/dlls/user32/tests/win.c +++ b/dlls/user32/tests/win.c @@ -1816,7 +1816,6 @@ static void test_shell_window(void) HANDLE hthread;
orig_shell_window = GetShellWindow(); - todo_wine ok(orig_shell_window != NULL, "default desktop doesn't have a shell window\n");
hdesk = CreateDesktopA("winetest", NULL, NULL, 0, GENERIC_ALL, NULL); diff --git a/programs/explorer/desktop.c b/programs/explorer/desktop.c index 40077f6b4a9..ddec0b6fc38 100644 --- a/programs/explorer/desktop.c +++ b/programs/explorer/desktop.c @@ -1149,6 +1149,42 @@ static inline BOOL is_whitespace(WCHAR c) return c == ' ' || c == '\t'; }
+/* Set the shell window if appropriate for the current desktop. We should set + the shell window on the "Default" desktop on a visible window station, but + not for other desktops. */ +static void set_shell_window( HWND hwnd ) +{ + HWINSTA winsta; + USEROBJECTFLAGS flags; + HDESK desk; + WCHAR desk_name[MAX_PATH]; + HMODULE user32; + BOOL (WINAPI *pSetShellWindow)( HWND ); + + if (!(winsta = GetProcessWindowStation()) || + !GetUserObjectInformationW( winsta, UOI_FLAGS, &flags, sizeof(flags), NULL ) || + !(flags.dwFlags & WSF_VISIBLE)) + { + return; + } + + if (!(desk = GetThreadDesktop( GetCurrentThreadId() )) || + !GetUserObjectInformationW( desk, UOI_NAME, desk_name, ARRAY_SIZE( desk_name ), NULL ) || + wcscmp( desk_name, L"Default" )) + { + return; + } + + if ((user32 = LoadLibraryW( L"user32.dll" )) && + (pSetShellWindow = (void *)GetProcAddress( user32, "SetShellWindow" )) && + pSetShellWindow( hwnd )) + { + TRACE( "set shell window to %p\n", hwnd ); + } + else + WARN( "failed to set shell window\n" ); +} + /* main desktop management function */ void manage_desktop( WCHAR *arg ) { @@ -1269,6 +1305,10 @@ void manage_desktop( WCHAR *arg ) desktopshellbrowserwindow_init(); shellwindows_init();
+ /* Ideally we would set the window of an IShellView here, but we never + actually create one, so the desktop window itself will have to do. */ + set_shell_window( hwnd ); + /* run the desktop message loop */ if (hwnd) {
Hi,
It looks like your patch introduced the new failures shown below. Please investigate and fix them before resubmitting your patch. If they are not new, fixing them anyway would help a lot. Otherwise please ask for the known failures list to be updated.
The tests also ran into some preexisting test failures. If you know how to fix them that would be helpful. See the TestBot job for the details:
The full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=148805
Your paranoid android.
=== w10pro64 (32 bit report) ===
user32: win.c:3851: Test failed: GetForegroundWindow returned 000102A0 win.c:3782: Test failed: SetForegroundWindow failed, error 0 win.c:3785: Test failed: GetForegroundWindow returned 000102A0 win.c:3822: Test failed: GetForegroundWindow returned 000102A0 win.c:3910: Test failed: GetActiveWindow() = 0002021C win.c:3914: Test failed: GetFocus() = 00000000 win.c:3917: Test failed: GetFocus() = 00000000
=== w10pro64 (64 bit report) ===
user32: win.c:3851: Test failed: GetForegroundWindow returned 00000000000300FA win.c:3782: Test failed: SetForegroundWindow failed, error 0 win.c:3785: Test failed: GetForegroundWindow returned 00000000000300FA win.c:3822: Test failed: GetForegroundWindow returned 00000000000300FA win.c:3910: Test failed: GetActiveWindow() = 00000000000202AC win.c:3914: Test failed: GetFocus() = 0000000000000000 win.c:3917: Test failed: GetFocus() = 0000000000000000