[PATCH] Block TrackPopupMenu if menu is already active

Nikolay Sivov nsivov at codeweavers.com
Thu Apr 8 10:47:45 CDT 2010


---
 dlls/user32/menu.c       |   23 +++++++----
 dlls/user32/tests/menu.c |  103 ++++++++++++++++++++++++++++++++++++----------
 2 files changed, 96 insertions(+), 30 deletions(-)

diff --git a/dlls/user32/menu.c b/dlls/user32/menu.c
index 1444f84..f8cc39d 100644
--- a/dlls/user32/menu.c
+++ b/dlls/user32/menu.c
@@ -3270,6 +3270,12 @@ static BOOL MENU_InitTracking(HWND hWnd, HMENU hMenu, BOOL bPopup, UINT wFlags)
 
     HideCaret(0);
 
+    /* This makes the menus of applications built with Delphi work.
+     * It also enables menus to be displayed in more than one window,
+     * but there are some bugs left that need to be fixed in this case.
+     */
+    if ((menu = MENU_GetMenu( hMenu ))) menu->hWnd = hWnd;
+
     /* Send WM_ENTERMENULOOP and WM_INITMENU message only if TPM_NONOTIFY flag is not specified */
     if (!(wFlags & TPM_NONOTIFY))
        SendMessageW( hWnd, WM_ENTERMENULOOP, bPopup, 0 );
@@ -3283,13 +3289,7 @@ static BOOL MENU_InitTracking(HWND hWnd, HMENU hMenu, BOOL bPopup, UINT wFlags)
         * menu sizes will be recalculated once the menu created/shown.
         */
     }
-    
-    /* This makes the menus of applications built with Delphi work.
-     * It also enables menus to be displayed in more than one window,
-     * but there are some bugs left that need to be fixed in this case.
-     */
-    if ((menu = MENU_GetMenu( hMenu ))) menu->hWnd = hWnd;
-    
+
     return TRUE;
 }
 /***********************************************************************
@@ -3393,6 +3393,7 @@ track_menu:
 BOOL WINAPI TrackPopupMenuEx( HMENU hMenu, UINT wFlags, INT x, INT y,
                               HWND hWnd, LPTPMPARAMS lpTpm )
 {
+    POPUPMENU *menu;
     BOOL ret = FALSE;
 
     TRACE("hmenu %p flags %04x (%d,%d) hwnd %p lpTpm %p rect %s\n",
@@ -3402,12 +3403,18 @@ BOOL WINAPI TrackPopupMenuEx( HMENU hMenu, UINT wFlags, INT x, INT y,
     /* Parameter check */
     /* FIXME: this check is performed several times, here and in the called
        functions. That could be optimized */
-    if (!MENU_GetMenu( hMenu ))
+    if (!(menu = MENU_GetMenu( hMenu )))
     {
         SetLastError( ERROR_INVALID_MENU_HANDLE );
         return FALSE;
     }
 
+    if (IsWindow(menu->hWnd))
+    {
+        SetLastError( ERROR_POPUP_ALREADY_ACTIVE );
+        return FALSE;
+    }
+
     MENU_InitTracking(hWnd, hMenu, TRUE, wFlags);
 
     /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
diff --git a/dlls/user32/tests/menu.c b/dlls/user32/tests/menu.c
index 761d70c..deecbe8 100644
--- a/dlls/user32/tests/menu.c
+++ b/dlls/user32/tests/menu.c
@@ -357,7 +357,7 @@ static void test_subpopup_locked_by_menu(void)
     ret = IsMenu( hsubmenu);
     ok( ret , "Menu handle is not valid\n");
     SetLastError( 0xdeadbeef);
-    ret = TrackPopupMenu( hmenu, 0x100, 100,100, 0, hwnd, NULL);
+    ret = TrackPopupMenu( hmenu, TPM_RETURNCMD, 100,100, 0, hwnd, NULL);
     if( ret == (itemid & 0xffff)) {
         win_skip("not on 16 bit menu subsystem\n");
         DestroyMenu( hsubmenu);
@@ -384,7 +384,7 @@ static void test_subpopup_locked_by_menu(void)
         ok( !ret , "Menu handle should be invalid\n");
         /* but TrackPopupMenu still works! */
         SetLastError( 0xdeadbeef);
-        ret = TrackPopupMenu( hmenu, 0x100, 100,100, 0, hwnd, NULL);
+        ret = TrackPopupMenu( hmenu, TPM_RETURNCMD, 100,100, 0, hwnd, NULL);
         gle = GetLastError();
         todo_wine {
             ok( ret == itemid , "TrackPopupMenu returned %d error is %d\n", ret, gle);
@@ -425,7 +425,7 @@ static void test_menu_ownerdraw(void)
     MOD_maxid = k-1;
     assert( k <= sizeof(MOD_rc)/sizeof(RECT));
     /* display the menu */
-    ret = TrackPopupMenu( hmenu, 0x100, 100,100, 0, hwnd, NULL);
+    ret = TrackPopupMenu( hmenu, TPM_RETURNCMD, 100,100, 0, hwnd, NULL);
 
     /* columns have a 4 pixel gap between them */
     ok( MOD_rc[0].right + 4 ==  MOD_rc[2].left,
@@ -449,7 +449,7 @@ static void test_menu_ownerdraw(void)
     leftcol= MOD_rc[0].left;
     ModifyMenu( hmenu, 0, MF_BYCOMMAND| MF_OWNERDRAW| MF_SEPARATOR, 0, 0); 
     /* display the menu */
-    ret = TrackPopupMenu( hmenu, 0x100, 100,100, 0, hwnd, NULL);
+    ret = TrackPopupMenu( hmenu, TPM_RETURNCMD, 100,100, 0, hwnd, NULL);
     /* left should be 4 pixels less now */
     ok( leftcol == MOD_rc[0].left + 4, 
             "columns should be 4 pixels to the left (actual %d).\n",
@@ -562,7 +562,7 @@ static void test_mbs_help( int ispop, int hassub, int mnuopt,
         ReleaseDC( hwnd, hdc);
     }
     if(ispop)
-        ret = TrackPopupMenu( hmenu, 0x100, 100,100, 0, hwnd, NULL);
+        ret = TrackPopupMenu( hmenu, TPM_RETURNCMD, 100,100, 0, hwnd, NULL);
     else {
         ret = SetMenu( hwnd, hmenu);
         ok(ret, "SetMenu failed with error %d\n", GetLastError());
@@ -2032,6 +2032,12 @@ static void test_menu_input(void) {
     HANDLE hThread, hWnd;
     DWORD tid;
 
+    if (!pSendInput)
+    {
+        win_skip("SendInput is not available\n");
+        return;
+    }
+
     wclass.lpszClassName = "MenuTestClass";
     wclass.style         = CS_HREDRAW | CS_VREDRAW;
     wclass.lpfnWndProc   = WndProc;
@@ -2670,6 +2676,12 @@ static void test_menu_getmenuinfo(void)
     BOOL ret;
     DWORD gle;
 
+    if (!pGetMenuInfo)
+    {
+        win_skip("GetMenuInfo is not available\n");
+        return;
+    }
+
     /* create a menu */
     hmenu = CreateMenu();
     assert( hmenu);
@@ -2716,6 +2728,12 @@ static void test_menu_setmenuinfo(void)
     BOOL ret;
     DWORD gle;
 
+    if (!pGetMenuInfo || !pSetMenuInfo)
+    {
+        win_skip("Get/SetMenuInfo are not available\n");
+        return;
+    }
+
     /* create a menu with a submenu */
     hmenu = CreateMenu();
     hsubmenu = CreateMenu();
@@ -2868,7 +2886,7 @@ static void test_menu_trackpopupmenu(void)
         /* start with an invalid menu handle */
         gle = 0xdeadbeef;
         gflag_initmenupopup = gflag_entermenuloop = gflag_initmenu = 0;
-        ret = MyTrackPopupMenu( Ex, NULL, 0x100, 100,100, hwnd, NULL);
+        ret = MyTrackPopupMenu( Ex, NULL, TPM_RETURNCMD, 100,100, hwnd, NULL);
         gle = GetLastError();
         ok( !ret, "TrackPopupMenu%s should have failed\n", Ex ? "Ex" : "");
         ok( gle == ERROR_INVALID_MENU_HANDLE
@@ -2884,7 +2902,7 @@ static void test_menu_trackpopupmenu(void)
         /* another one but not NULL */
         gle = 0xdeadbeef;
         gflag_initmenupopup = gflag_entermenuloop = gflag_initmenu = 0;
-        ret = MyTrackPopupMenu( Ex, (HMENU)hwnd, 0x100, 100,100, hwnd, NULL);
+        ret = MyTrackPopupMenu( Ex, (HMENU)hwnd, TPM_RETURNCMD, 100,100, hwnd, NULL);
         gle = GetLastError();
         ok( !ret, "TrackPopupMenu%s should have failed\n", Ex ? "Ex" : "");
         ok( gle == ERROR_INVALID_MENU_HANDLE
@@ -2900,7 +2918,7 @@ static void test_menu_trackpopupmenu(void)
         /* now a somewhat successful call */
         gle = 0xdeadbeef;
         gflag_initmenupopup = gflag_entermenuloop = gflag_initmenu = 0;
-        ret = MyTrackPopupMenu( Ex, hmenu, 0x100, 100,100, hwnd, NULL);
+        ret = MyTrackPopupMenu( Ex, hmenu, TPM_RETURNCMD, 100,100, hwnd, NULL);
         gle = GetLastError();
         ok( ret == 0, "TrackPopupMenu%s returned %d expected zero\n", Ex ? "Ex" : "", ret);
         ok( gle == NO_ERROR
@@ -2918,7 +2936,7 @@ static void test_menu_trackpopupmenu(void)
         ok( ret, "AppendMenA has failed!\n");
         gle = 0xdeadbeef;
         gflag_initmenupopup = gflag_entermenuloop = gflag_initmenu = 0;
-        ret = MyTrackPopupMenu( Ex, hmenu, 0x100, 100,100, hwnd, NULL);
+        ret = MyTrackPopupMenu( Ex, hmenu, TPM_RETURNCMD, 100,100, hwnd, NULL);
         gle = GetLastError();
         ok( ret == 0, "TrackPopupMenu%s returned %d expected zero\n", Ex ? "Ex" : "", ret);
         ok( gle == NO_ERROR
@@ -2937,6 +2955,53 @@ static void test_menu_trackpopupmenu(void)
     DestroyWindow(hwnd);
 }
 
+static HMENU g_hmenu;
+
+static LRESULT WINAPI menu_track_again_wnd_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
+{
+    switch (msg)
+    {
+    case WM_ENTERMENULOOP:
+    {
+        BOOL ret;
+
+        /* try a recursive call */
+        SetLastError(0xdeadbeef);
+        ret = TrackPopupMenu(g_hmenu, 0, 100, 100, 0, hwnd, NULL);
+        ok(ret == FALSE, "got %d\n", ret);
+        ok(GetLastError() == ERROR_POPUP_ALREADY_ACTIVE ||
+           broken(GetLastError() == 0xdeadbeef) /* W9x */, "got %d\n", GetLastError());
+
+        /* exit menu modal loop
+         * ( A SendMessage does not work on NT3.51 here ) */
+        return PostMessage(hwnd, WM_CANCELMODE, 0, 0);
+    }
+    }
+    return DefWindowProc(hwnd, msg, wparam, lparam);
+}
+
+static void test_menu_trackagain(void)
+{
+    HWND hwnd;
+    BOOL ret;
+
+    hwnd = CreateWindowEx(0, MAKEINTATOM(atomMenuCheckClass), NULL,
+            WS_VISIBLE, CW_USEDEFAULT, CW_USEDEFAULT, 200, 200,
+            NULL, NULL, NULL, NULL);
+    ok(hwnd != NULL, "CreateWindowEx failed with error %d\n", GetLastError());
+    if (!hwnd) return;
+    SetWindowLongPtr( hwnd, GWLP_WNDPROC, (LONG_PTR)menu_track_again_wnd_proc);
+
+    g_hmenu = CreatePopupMenu();
+    ok(g_hmenu != NULL, "CreateMenu failed with error %d\n", GetLastError());
+
+    ret = TrackPopupMenu( g_hmenu, 0, 100, 100, 0, hwnd, NULL);
+    todo_wine ok(ret == FALSE, "got %d\n", ret);
+
+    DestroyMenu(g_hmenu);
+    DestroyWindow(hwnd);
+}
+
 /* test handling of WM_CANCELMODE messages */
 static int g_got_enteridle;
 static HWND g_hwndtosend;
@@ -2997,14 +3062,14 @@ static void test_menu_cancelmode(void)
      */
     /* menu owner is top level window */
     g_hwndtosend = hwnd;
-    ret = TrackPopupMenu( menu, 0x100, 100,100, 0, hwnd, NULL);
+    ret = TrackPopupMenu( menu, TPM_RETURNCMD, 100,100, 0, hwnd, NULL);
     todo_wine {
         ok( g_got_enteridle == 0, "received %d WM_ENTERIDLE messages, none expected\n", g_got_enteridle);
     }
     ok( g_got_enteridle < 2, "received %d WM_ENTERIDLE messages, should be less than 2\n", g_got_enteridle);
     /* menu owner is child window */
     g_hwndtosend = hwndchild;
-    ret = TrackPopupMenu( menu, 0x100, 100,100, 0, hwndchild, NULL);
+    ret = TrackPopupMenu( menu, TPM_RETURNCMD, 100,100, 0, hwndchild, NULL);
     todo_wine {
         ok(g_got_enteridle == 0, "received %d WM_ENTERIDLE messages, none expected\n", g_got_enteridle);
     }
@@ -3012,7 +3077,7 @@ static void test_menu_cancelmode(void)
     /* now send the WM_CANCELMODE messages to the WRONG window */
     /* those should fail ( to have any effect) */
     g_hwndtosend = hwnd;
-    ret = TrackPopupMenu( menu, 0x100, 100,100, 0, hwndchild, NULL);
+    ret = TrackPopupMenu( menu, TPM_RETURNCMD, 100,100, 0, hwndchild, NULL);
     ok( g_got_enteridle == 2, "received %d WM_ENTERIDLE messages, should be 2\n", g_got_enteridle);
     /* cleanup */
     DestroyMenu( menu);
@@ -3204,20 +3269,14 @@ START_TEST(menu)
     test_subpopup_locked_by_menu();
     test_menu_ownerdraw();
     test_menu_bmp_and_string();
-    /* test Get/SetMenuInfo if available */
-    if( pGetMenuInfo && pSetMenuInfo) {
-        test_menu_getmenuinfo();
-        test_menu_setmenuinfo();
-    } else
-        win_skip("Get/SetMenuInfo are not available\n");
-    if( !pSendInput)
-        win_skip("SendInput is not available\n");
-    else
-        test_menu_input();
+    test_menu_getmenuinfo();
+    test_menu_setmenuinfo();
+    test_menu_input();
     test_menu_flags();
 
     test_menu_hilitemenuitem();
     test_menu_trackpopupmenu();
+    test_menu_trackagain();
     test_menu_cancelmode();
     test_menu_maxdepth();
     test_menu_circref();
-- 
1.5.6.5


--=-qlLX0VfJAiBN69lXa8hD--




More information about the wine-patches mailing list