> 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
* Handle WS_EX_TOPMOST in conjunction with shell windows
--
Martin Fuchs
martin-fuchs@xxxxxxx
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())