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

Gabriel Ivăncescu gabrielopcode at gmail.com
Wed Mar 13 08:12:55 CDT 2019


When activating a window and sending activation messages to the window
procedure, 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, but this is exactly what's needed to match
the testcase in the next patch, so this is how Windows does it (including
the hooks).

 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..0d32d00 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 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 81b08fc..d53e1a5 100644
--- a/dlls/user32/win.h
+++ b/dlls/user32/win.h
@@ -79,6 +79,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.20.1




More information about the wine-devel mailing list