[PATCH] user32: Don't depend on HMENU being valid for popup menus

Ken Thomases ken at codeweavers.com
Wed May 7 06:09:44 CDT 2014


On May 6, 2014, at 9:28 AM, Andrew Eikum wrote:

> +static HMENU MENU_GetHandle(POPUPMENU *menu)
> +{
> +    return menu ? menu->obj.handle : NULL;
> +}

Now that DestroyMenu() really destroys the handle, any use of the handle and thus this function has to be examined very carefully.  The menu pointer may be valid and menu->obj.handle may have a value, but that value may no longer be a valid handle.

> 	menuchar = SendMessageW( hwndOwner, WM_MENUCHAR,
> -                                 MAKEWPARAM( key, menu->wFlags ), (LPARAM)hmenu );
> +                                 MAKEWPARAM( key, menu->wFlags ), (LPARAM)MENU_GetHandle(menu) );

So, here we're passing the menu handle in the WM_MENUCHAR message.  If the menu has been destroyed, that's an invalid handle.  We probably need tests to verify that that's what happens on Windows.  That is, maybe it passes NULL.  Or maybe the handle is still valid somehow.  (The existing destroyed-submenu tests suggest that the handle isn't valid anymore, but maybe magic happens.)

> -                drawItem.hwndItem = (HWND)hmenu;
> +                drawItem.hwndItem = (HWND)MENU_GetHandle(menu);

> -        dis.hwndItem   = (HWND)hmenu;
> +        dis.hwndItem   = (HWND)MENU_GetHandle(menu);

Same for the WM_DRAWITEM message.

> @@ -1845,7 +1840,7 @@ static BOOL MENU_InitPopup( HWND hwndOwner, HMENU hmenu, UINT flags )
>     menu->hWnd = CreateWindowExW( ex_style, (LPCWSTR)POPUPMENU_CLASS_ATOM, NULL,
>                                 WS_POPUP, 0, 0, 0, 0,
>                                 hwndOwner, 0, (HINSTANCE)GetWindowLongPtrW(hwndOwner, GWLP_HINSTANCE),
> -                                (LPVOID)hmenu );
> +                                (LPVOID)MENU_GetHandle(menu) );

This one is interesting.  This is a value that's just forwarded through to the WM_CREATE message via the lpCreateParams field of the CREATESTRUCTW pointed to by the lparam.  It's then stored in the window extra space using SetWindowLongPtrW().  It's then looked up elsewhere in the window proc.  Some cases in the window proc try to use the handle, which will fail if it's been destroyed.

I think you can pass the menu pointer here instead of the menu handle.  Likewise, I think you can store the menu pointer into the window extra space.  You'd have to change the size of the extra space defined in the MENU_builtin_class struct.  And you'd need to change all uses of Set/GetWindowLongPtrW() on the menu's window with 0 index.

MENU_DrawPopupMenu() would need to be changed to take a menu pointer instead of a handle.

A couple of those uses are for the MM_SETMENUHANDLE and MM_GETMENUHANDLE messages.  I believe those are obsolete Wine-internal messages.  They can probably be completely removed.

For the MN_GETHMENU message, you can return MENU_GetHandle(GetWindowLongPtrW(hwnd, 0)), although, again, it would be good to test what Windows does for a destroyed-but-still-alive menu.

The stored value is also used in GetMenuBarInfo(), so you'd want to rework that.

> @@ -2023,18 +2018,17 @@ static void MENU_SelectItem( HWND hwndOwner, HMENU hmenu, UINT wIndex,

>                 SendMessageW( hwndOwner, WM_MENUSELECT, MAKEWPARAM(pos,
>                          ip->fType | ip->fState |
> -                         (ptm->wFlags & MF_SYSMENU)), (LPARAM)topmenu);
> +                         (topmenu->wFlags & MF_SYSMENU)), (LPARAM)topmenu);

Here you missed a case.  The lparam should be the menu handle, but topmenu is now a menu pointer.

It might be good to also review all remaining uses of MENU_GetMenu().  Again, I think it should be possible to confine uses of menu handles just to the interfacing with client code.  So, uses of MENU_GetMenu() at other places indicates that you're relying on the handle being valid.  For example, the call to GetSubMenu() in MENU_FindItemByKey() or the calls to EnableMenuItem() in MENU_InitSysMenuPopup().  Unfortunately, that may involve creating a lot of helper functions.

I didn't review further than this.

Sorry that my suggestions are creating so much work.  If you haven't already done so, you should probably get input from others (i.e. Alexandre) to see if my suggestions make sense.

-Ken




More information about the wine-devel mailing list