[PATCH 1/6] user32/focus: Prevent a recursive activation loop with the activation messages

Gabriel Ivăncescu gabrielopcode at gmail.com
Mon Dec 17 05:49:35 CST 2018


When activating a window and sending activation messages to the window
procedure (i.e. WM_NCACTIVATE and WM_ACTIVATE), we have to avoid a recursive
loop by not sending more of these messages or hooks while we are still
activating the window. Some applications actually depend on this behavior.

Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=46274
Signed-off-by: Gabriel Ivăncescu <gabrielopcode at gmail.com>
---

It may seem a bit strange (and I thought so as well initially), but this
is exactly what's needed to match the testcase in the next patch, so this
is how Windows does it (yes, including the hooks).

Total Commander's Multi-Rename Tool requires this behavior, if you actually
click on "Start!" in it. Closing the window then ends up in the recursion,
which is only stopped by MAX_WINPROC_RECURSION after a few seconds, but
then the listbox's caret is messed up.

 dlls/user32/focus.c | 44 +++++++++++++++++++++++++++++---------------
 dlls/user32/win.h   |  1 +
 2 files changed, 30 insertions(+), 15 deletions(-)

diff --git a/dlls/user32/focus.c b/dlls/user32/focus.c
index f1c8831..edf9256 100644
--- a/dlls/user32/focus.c
+++ b/dlls/user32/focus.c
@@ -78,7 +78,7 @@ static BOOL set_active_window( HWND hwnd, HWND *prev, BOOL mouse, BOOL focus )
 {
     HWND previous = GetActiveWindow();
     BOOL ret;
-    DWORD old_thread, new_thread;
+    DWORD winflags, old_thread, new_thread;
     CBTACTIVATESTRUCT cbt;
 
     if (previous == hwnd)
@@ -87,16 +87,24 @@ static BOOL set_active_window( HWND hwnd, HWND *prev, BOOL mouse, BOOL focus )
         return TRUE;
     }
 
-    /* call CBT hook chain */
-    cbt.fMouse     = mouse;
-    cbt.hWndActive = previous;
-    if (HOOK_CallHooks( WH_CBT, HCBT_ACTIVATE, (WPARAM)hwnd, (LPARAM)&cbt, TRUE )) return FALSE;
-
-    if (IsWindow(previous))
+    /* Prevent a recursive activation loop with the app's activation messages */
+    winflags = win_set_flags(hwnd, WIN_IS_IN_ACTIVATION, 0);
+    if (!(winflags & WIN_IS_IN_ACTIVATION))
     {
-        SendMessageW( previous, WM_NCACTIVATE, FALSE, (LPARAM)hwnd );
-        SendMessageW( previous, WM_ACTIVATE,
-                      MAKEWPARAM( WA_INACTIVE, IsIconic(previous) ), (LPARAM)hwnd );
+        ret = FALSE;
+
+        /* call CBT hook chain */
+        cbt.fMouse     = mouse;
+        cbt.hWndActive = previous;
+        if (HOOK_CallHooks( WH_CBT, HCBT_ACTIVATE, (WPARAM)hwnd, (LPARAM)&cbt, TRUE ))
+            goto clear_flags;
+
+        if (IsWindow(previous))
+        {
+            SendMessageW( previous, WM_NCACTIVATE, FALSE, (LPARAM)hwnd );
+            SendMessageW( previous, WM_ACTIVATE,
+                          MAKEWPARAM( WA_INACTIVE, IsIconic(previous) ), (LPARAM)hwnd );
+        }
     }
 
     SERVER_START_REQ( set_active_window )
@@ -106,9 +114,9 @@ static BOOL set_active_window( HWND hwnd, HWND *prev, BOOL mouse, BOOL focus )
             previous = wine_server_ptr_handle( reply->previous );
     }
     SERVER_END_REQ;
-    if (!ret) return FALSE;
+    if (!ret) goto clear_flags;
     if (prev) *prev = previous;
-    if (previous == hwnd) return TRUE;
+    if (previous == hwnd) goto clear_flags;
 
     if (hwnd)
     {
@@ -116,7 +124,11 @@ static BOOL set_active_window( HWND hwnd, HWND *prev, BOOL mouse, BOOL focus )
         if (SendMessageW( hwnd, WM_QUERYNEWPALETTE, 0, 0 ))
             SendMessageTimeoutW( HWND_BROADCAST, WM_PALETTEISCHANGING, (WPARAM)hwnd, 0,
                                  SMTO_ABORTIFHUNG, 2000, NULL );
-        if (!IsWindow(hwnd)) return FALSE;
+        if (!IsWindow(hwnd))
+        {
+            ret = FALSE;
+            goto clear_flags;
+        }
     }
 
     old_thread = previous ? GetWindowThreadProcessId( previous, NULL ) : 0;
@@ -148,7 +160,7 @@ static BOOL set_active_window( HWND hwnd, HWND *prev, BOOL mouse, BOOL focus )
         }
     }
 
-    if (IsWindow(hwnd))
+    if (!(winflags & WIN_IS_IN_ACTIVATION) && IsWindow(hwnd))
     {
         SendMessageW( hwnd, WM_NCACTIVATE, (hwnd == GetForegroundWindow()), (LPARAM)previous );
         SendMessageW( hwnd, WM_ACTIVATE,
@@ -173,7 +185,9 @@ static BOOL set_active_window( HWND hwnd, HWND *prev, BOOL mouse, BOOL focus )
         }
     }
 
-    return TRUE;
+clear_flags:
+    win_set_flags(hwnd, 0, WIN_IS_IN_ACTIVATION);
+    return ret;
 }
 
 
diff --git a/dlls/user32/win.h b/dlls/user32/win.h
index f3bdfd3..fa5d1b6 100644
--- a/dlls/user32/win.h
+++ b/dlls/user32/win.h
@@ -80,6 +80,7 @@ typedef struct tagWND
 #define WIN_NEEDS_SHOW_OWNEDPOPUP 0x0020 /* WM_SHOWWINDOW:SC_SHOW must be sent in the next ShowOwnedPopup call */
 #define WIN_CHILDREN_MOVED        0x0040 /* children may have moved, ignore stored positions */
 #define WIN_HAS_IME_WIN           0x0080 /* the window has been registered with imm32 */
+#define WIN_IS_IN_ACTIVATION      0x0100 /* the window is in an activation process */
 
   /* Window functions */
 extern HWND get_hwnd_message_parent(void) DECLSPEC_HIDDEN;
-- 
2.19.1




More information about the wine-devel mailing list