[PATCH] user32: Don't destroy in-use menus
Andrew Eikum
aeikum at codeweavers.com
Thu May 1 14:19:16 CDT 2014
---
dlls/user32/menu.c | 58 ++++++++++++++++++++++++++++++++++++++++--------
dlls/user32/tests/menu.c | 6 ++---
2 files changed, 51 insertions(+), 13 deletions(-)
diff --git a/dlls/user32/menu.c b/dlls/user32/menu.c
index ab3f73b..1966aac 100644
--- a/dlls/user32/menu.c
+++ b/dlls/user32/menu.c
@@ -102,6 +102,11 @@ typedef struct {
BOOL bScrolling; /* Scroll arrows are active */
UINT nScrollPos; /* Current scroll position */
UINT nTotalHeight; /* Total height of menu items inside menu */
+
+ /* two different kinds of "reference" counting. in_use is for internal
+ * refcounting for e.g. submenus. user_destroyed indicates whether
+ * DestroyMenu has been invoked on a handle returned by CreateMenu. */
+ BOOL in_use, user_destroyed;
/* ------------ MENUINFO members ------ */
DWORD dwStyle; /* Extended menu style */
UINT cyMax; /* max height of the whole menu, 0 is screen height */
@@ -3956,6 +3961,11 @@ BOOL WINAPI RemoveMenu( HMENU hMenu, UINT nPos, UINT wFlags )
/* Remove item */
+ if (item->fType & MF_POPUP) {
+ POPUPMENU *sub = MENU_GetMenu(item->hSubMenu);
+ InterlockedDecrement(&sub->in_use);
+ }
+
MENU_FreeItemData( item );
if (--menu->nItems == 0)
@@ -4106,16 +4116,19 @@ HMENU WINAPI CreateMenu(void)
return hMenu;
}
-
-/**********************************************************************
- * DestroyMenu (USER32.@)
- */
-BOOL WINAPI DestroyMenu( HMENU hMenu )
+static BOOL MENU_DestroyMenu( HMENU hMenu )
{
LPPOPUPMENU lppop;
TRACE("(%p)\n", hMenu);
+ if (!(lppop = MENU_GetMenu(hMenu)))
+ return FALSE;
+
+ TRACE("user_destroyed: %u, in_use: %u\n", lppop->user_destroyed, lppop->in_use);
+ if (lppop->in_use)
+ return TRUE;
+
if (!(lppop = free_user_handle( hMenu, USER_MENU ))) return FALSE;
if (lppop == OBJ_OTHER_PROCESS) return FALSE;
@@ -4132,7 +4145,11 @@ BOOL WINAPI DestroyMenu( HMENU hMenu )
MENUITEM *item = lppop->items;
for (i = lppop->nItems; i > 0; i--, item++)
{
- if (item->fType & MF_POPUP) DestroyMenu(item->hSubMenu);
+ if (item->fType & MF_POPUP) {
+ POPUPMENU *sub = MENU_GetMenu(item->hSubMenu);
+ InterlockedDecrement(&sub->in_use);
+ MENU_DestroyMenu(item->hSubMenu);
+ }
MENU_FreeItemData( item );
}
HeapFree( GetProcessHeap(), 0, lppop->items );
@@ -4141,6 +4158,20 @@ BOOL WINAPI DestroyMenu( HMENU hMenu )
return TRUE;
}
+/**********************************************************************
+ * DestroyMenu (USER32.@)
+ */
+BOOL WINAPI DestroyMenu( HMENU hMenu )
+{
+ POPUPMENU *lppop;
+
+ if (!(lppop = MENU_GetMenu(hMenu)))
+ return FALSE;
+
+ lppop->user_destroyed = TRUE;
+
+ return MENU_DestroyMenu(hMenu);
+}
/**********************************************************************
* GetSystemMenu (USER32.@)
@@ -4546,7 +4577,7 @@ BOOL WINAPI IsMenu(HMENU hmenu)
{
LPPOPUPMENU menu = MENU_GetMenu(hmenu);
- if (!menu)
+ if (!menu || menu->user_destroyed)
{
SetLastError(ERROR_INVALID_MENU_HANDLE);
return FALSE;
@@ -4787,6 +4818,13 @@ static BOOL SetMenuItemInfo_common(MENUITEM * menu,
menu->wID = lpmii->wID;
if (lpmii->fMask & MIIM_SUBMENU) {
+ if (menu->fType & MF_POPUP) {
+ /* release internal reference for replaced menu item */
+ POPUPMENU *subMenu = MENU_GetMenu(menu->hSubMenu);
+ if (subMenu)
+ InterlockedDecrement(&subMenu->in_use);
+ }
+
menu->hSubMenu = lpmii->hSubMenu;
if (menu->hSubMenu) {
POPUPMENU *subMenu = MENU_GetMenu(menu->hSubMenu);
@@ -4798,6 +4836,7 @@ static BOOL SetMenuItemInfo_common(MENUITEM * menu,
}
subMenu->wFlags |= MF_POPUP;
menu->fType |= MF_POPUP;
+ InterlockedIncrement(&subMenu->in_use);
} else {
SetLastError( ERROR_INVALID_PARAMETER);
return FALSE;
@@ -5179,11 +5218,12 @@ BOOL WINAPI SetMenuInfo (HMENU hMenu, LPCMENUINFO lpmi)
*
*/
BOOL WINAPI GetMenuInfo (HMENU hMenu, LPMENUINFO lpmi)
-{ POPUPMENU *menu;
+{
+ POPUPMENU *menu;
TRACE("(%p %p)\n", hMenu, lpmi);
- if (lpmi && (lpmi->cbSize == sizeof( MENUINFO)) && (menu = MENU_GetMenu(hMenu)))
+ if (lpmi && (lpmi->cbSize == sizeof( MENUINFO)) && (menu = MENU_GetMenu(hMenu)) && !menu->user_destroyed)
{
if (lpmi->fMask & MIM_BACKGROUND)
diff --git a/dlls/user32/tests/menu.c b/dlls/user32/tests/menu.c
index 593292d..7290fb8 100644
--- a/dlls/user32/tests/menu.c
+++ b/dlls/user32/tests/menu.c
@@ -439,7 +439,7 @@ static void test_menu_locked_by_window(void)
DestroyWindow(hwnd);
}
-/* demonstrates that subpopup's are locked
+/* demonstrates that subpopups are locked
* even after a client calls DestroyMenu on it */
static LRESULT WINAPI subpopuplocked_wnd_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
{
@@ -526,9 +526,7 @@ static void test_subpopup_locked_by_menu(void)
SetLastError( 0xdeadbeef);
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);
- }
+ ok( ret == itemid , "TrackPopupMenu returned %d error is %d\n", ret, gle);
ok( gle == 0 ||
broken(gle == 0xdeadbeef) || /* wow64 */
broken(gle == ERROR_INVALID_PARAMETER), /* win2k0 */
--
1.9.2
More information about the wine-patches
mailing list