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

Gabriel Ivăncescu gabrielopcode at gmail.com
Mon Jan 28 06:34:14 CST 2019


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).

 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 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