Hi Alexandre, this is a patch for a working SetShellWindow() implementation. It fullfills all my test cases. Also explorer runs fine with it. It could be a bit more clean when moving the SetWindowPos() calls from SetShellWindow() into the Wine server. But I don't know how to do this correctly. I tried it by calling link_window() but I had to see, this is not enough. If you move it into the server call, you can also remove the "if (GetShellWindow())" in SetShellWindow(). Changelog: * Move shell window into the background and leave it there forever * Add tests for Get/SetShellWindow() to the list of dlls/user test -- Martin Fuchs martin-fuchs@gmx.net
Index: dlls/user/focus.c =================================================================== RCS file: /home/wine/wine/dlls/user/focus.c,v retrieving revision 1.6 diff -u -r1.6 focus.c --- dlls/user/focus.c 7 Oct 2003 03:40:23 -0000 1.6 +++ dlls/user/focus.c 16 Oct 2003 22:46:57 -0000 @@ -361,6 +361,21 @@ { BOOL ret; + if (GetShellWindow()) + return FALSE; + + if (GetWindowLongW(hwndShell, GWL_EXSTYLE) & WS_EX_TOPMOST) + return FALSE; + + if (hwndListView != hwndShell) + if (GetWindowLongW(hwndListView, GWL_EXSTYLE) & WS_EX_TOPMOST) + return FALSE; + + if (hwndListView && hwndListView!=hwndShell) + SetWindowPos(hwndListView, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE|SWP_NOACTIVATE); + + SetWindowPos(hwndShell, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE|SWP_NOACTIVATE); + SERVER_START_REQ(set_global_windows) { req->flags = SET_GLOBAL_SHELL_WINDOWS; @@ -370,13 +385,6 @@ } SERVER_END_REQ; - if (ret) - { - if (hwndListView && hwndListView!=hwndShell) - SetWindowPos(hwndListView, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE|SWP_NOACTIVATE); - - SetWindowPos(hwndShell, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE|SWP_NOACTIVATE); - } return ret; } Index: dlls/user/tests/win.c =================================================================== RCS file: /home/wine/wine/dlls/user/tests/win.c,v retrieving revision 1.8 diff -u -r1.8 win.c --- dlls/user/tests/win.c 10 Sep 2003 03:56:47 -0000 1.8 +++ dlls/user/tests/win.c 16 Oct 2003 22:46:57 -0000 @@ -388,6 +388,122 @@ } +static void test_shell_window() +{ + BOOL ret; + DWORD error; + HMODULE hinst, hUser32; + BOOL (WINAPI*SetShellWindow)(HWND); + BOOL (WINAPI*SetShellWindowEx)(HWND, HWND); + HWND hwnd1, hwnd2, hwnd3, hwnd4, hwnd5; + HWND shellWindow, nextWnd; + + shellWindow = GetShellWindow(); + hinst = GetModuleHandle(0); + hUser32 = GetModuleHandleA("user32"); + + SetShellWindow = (BOOL(WINAPI*)(HWND)) GetProcAddress(hUser32, "SetShellWindow"); + SetShellWindowEx = (BOOL(WINAPI*)(HWND,HWND)) GetProcAddress(hUser32, "SetShellWindowEx"); + + trace("previous shell window: %p\n", shellWindow); + + if (shellWindow) { + DWORD pid; + HANDLE hProcess; + + ret = DestroyWindow(shellWindow); + error = GetLastError(); + + ok(!ret, "DestroyWindow(shellWindow)\n"); + /* passes on Win XP, but not on Win98 + ok(error==ERROR_ACCESS_DENIED, "ERROR_ACCESS_DENIED after DestroyWindow(shellWindow)\n"); */ + + /* close old shell instance */ + GetWindowThreadProcessId(shellWindow, &pid); + hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid); + ret = TerminateProcess(hProcess, 0); + ok(ret, "termination of previous shell process failed: GetLastError()=%d", GetLastError()); + WaitForSingleObject(hProcess, INFINITE); /* wait for termination */ + CloseHandle(hProcess); + } + + hwnd1 = CreateWindowEx(0, TEXT("#32770"), TEXT("TEST1"), WS_OVERLAPPEDWINDOW/*|WS_VISIBLE*/, 100, 100, 300, 200, 0, 0, hinst, 0); + trace("created window 1: %p\n", hwnd1); + + ret = SetShellWindow(hwnd1); + ok(ret, "first call to SetShellWindow(hwnd1)\n"); + shellWindow = GetShellWindow(); + ok(shellWindow==hwnd1, "wrong shell window: %p", shellWindow); + + ret = SetShellWindow(hwnd1); + ok(!ret, "second call to SetShellWindow(hwnd1)\n"); + + ret = SetShellWindow(0); + error = GetLastError(); + /* passes on Win XP, but not on Win98 + ok(!ret, "reset shell window by SetShellWindow(0)\n"); + ok(error==ERROR_INVALID_WINDOW_HANDLE, "ERROR_INVALID_WINDOW_HANDLE after SetShellWindow(0)\n"); */ + + ret = SetShellWindow(hwnd1); + /* passes on Win XP, but not on Win98 + ok(!ret, "third call to SetShellWindow(hwnd1)\n"); */ + + SetWindowLong(hwnd1, GWL_EXSTYLE, GetWindowLong(hwnd1,GWL_EXSTYLE)|WS_EX_TOPMOST); + ret = GetWindowLong(hwnd1,GWL_EXSTYLE)&WS_EX_TOPMOST? TRUE: FALSE; + ok(!ret, "SetWindowExStyle(hwnd1, WS_EX_TOPMOST)\n"); + + ret = DestroyWindow(hwnd1); + ok(ret, "DestroyWindow(hwnd1)\n"); + + hwnd2 = CreateWindowEx(WS_EX_TOPMOST, TEXT("#32770"), TEXT("TEST2"), WS_OVERLAPPEDWINDOW/*|WS_VISIBLE*/, 150, 250, 300, 200, 0, 0, hinst, 0); + trace("created window 2: %p\n", hwnd2); + ret = SetShellWindow(hwnd2); + ok(!ret, "SetShellWindow(hwnd2) with WS_EX_TOPMOST"); + + hwnd3 = CreateWindowEx(0, TEXT("#32770"), TEXT("TEST3"), WS_OVERLAPPEDWINDOW/*|WS_VISIBLE*/, 200, 400, 300, 200, 0, 0, hinst, 0); + trace("created window 3: %p\n", hwnd3); + + hwnd4 = CreateWindowEx(0, TEXT("#32770"), TEXT("TEST4"), WS_OVERLAPPEDWINDOW/*|WS_VISIBLE*/, 250, 500, 300, 200, 0, 0, hinst, 0); + trace("created window 4: %p\n", hwnd4); + + nextWnd = GetWindow(hwnd4, GW_HWNDNEXT); + ok(nextWnd==hwnd3, "wrong next window for hwnd4: %p - expected hwnd3\n", nextWnd); + + ret = SetShellWindow(hwnd4); + ok(ret, "SetShellWindow(hwnd4)\n"); + shellWindow = GetShellWindow(); + ok(shellWindow==hwnd4, "wrong shell window: %p - expected hwnd4", shellWindow); + + nextWnd = GetWindow(hwnd4, GW_HWNDNEXT); + ok(nextWnd==0, "wrong next window for hwnd4: %p - expected 0\n", nextWnd); + + ret = SetWindowPos(hwnd4, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOSIZE|SWP_NOMOVE); + ok(ret, "SetWindowPos(hwnd4, HWND_TOPMOST)\n"); + + ret = SetWindowPos(hwnd4, hwnd3, 0, 0, 0, 0, SWP_NOSIZE|SWP_NOMOVE); + ok(ret, "SetWindowPos(hwnd4, hwnd3"); + + ret = SetShellWindow(hwnd3); + ok(!ret, "SetShellWindow(hwnd3)\n"); + shellWindow = GetShellWindow(); + ok(shellWindow==hwnd4, "wrong shell window: %p %p - expected hwnd4", shellWindow); + + hwnd5 = CreateWindowEx(0, TEXT("#32770"), TEXT("TEST5"), WS_OVERLAPPEDWINDOW/*|WS_VISIBLE*/, 300, 600, 300, 200, 0, 0, hinst, 0); + trace("created window 5: %p\n", hwnd5); + ret = SetWindowPos(hwnd4, hwnd5, 0, 0, 0, 0, SWP_NOSIZE|SWP_NOMOVE); + ok(ret, "SetWindowPos(hwnd4, hwnd5)\n"); + + nextWnd = GetWindow(hwnd4, GW_HWNDNEXT); + ok(nextWnd==0, "wrong next window for hwnd4 after SetWindowPos(): %p - expected 0\n", nextWnd); + + /* destroy test windows */ + DestroyWindow(hwnd2); + DestroyWindow(hwnd3); + DestroyWindow(hwnd4); + DestroyWindow(hwnd5); +} + + START_TEST(win) { pGetAncestor = (void *)GetProcAddress( GetModuleHandleA("user32.dll"), "GetAncestor" ); @@ -408,4 +524,6 @@ assert( hwndMain2 ); test_parent_owner(); + + test_shell_window(); } Index: server/window.c =================================================================== RCS file: /home/wine/wine/server/window.c,v retrieving revision 1.24 diff -u -r1.24 window.c --- server/window.c 7 Oct 2003 03:40:23 -0000 1.24 +++ server/window.c 16 Oct 2003 22:47:00 -0000 @@ -114,6 +114,13 @@ /* link a window into the tree (or unlink it if the new parent is NULL) */ static void link_window( struct window *win, struct window *parent, struct window *previous ) { + /* don't change Z-order of shell window */ + if (win == shell_window) + { + set_error( STATUS_INVALID_PARAMETER ); + return; + } + unlink_window( win ); /* unlink it from the previous location */ if (parent) @@ -865,6 +872,13 @@ if (req->flags & SET_GLOBAL_SHELL_WINDOWS) { + /* only allow to set the shell window if there is not already one */ + if (req->shell_window && shell_window) + set_error(STATUS_ACCESS_DENIED); + + if (req->shell_listview && shell_listview) + set_error(STATUS_ACCESS_DENIED); + if (!get_new_global_window( &new_shell_window, req->shell_window )) return; if (!get_new_global_window( &new_shell_listview, req->shell_listview )) return; } @@ -876,8 +890,9 @@ { if (!get_new_global_window( &new_taskman_window, req->taskman_window )) return; } - shell_window = new_shell_window; + shell_listview = new_shell_listview; + shell_window = new_shell_window; progman_window = new_progman_window; taskman_window = new_taskman_window; } Index: windows/win.c =================================================================== RCS file: /home/wine/wine/windows/win.c,v retrieving revision 1.224 diff -u -r1.224 win.c --- windows/win.c 5 Sep 2003 23:15:39 -0000 1.224 +++ windows/win.c 16 Oct 2003 22:47:03 -0000 @@ -2028,6 +2028,9 @@ SendMessageW( hwnd, WM_STYLECHANGING, offset, (LPARAM)&style ); if (!(wndPtr = WIN_GetPtr( hwnd )) || wndPtr == WND_OTHER_PROCESS) return 0; newval = style.styleNew; + /* don`t set extended style bit WS_EX_TOPMOST for the shell window */ + if (hwnd == GetShellWindow()) + newval = newval & ~WS_EX_TOPMOST; break; case GWL_HWNDPARENT: if (wndPtr->parent == GetDesktopWindow())