[PATCH 05/13] win32u: Move menu item info management from user32.

Jacek Caban wine at gitlab.winehq.org
Mon May 2 05:44:38 CDT 2022


From: Jacek Caban <jacek at codeweavers.com>

Signed-off-by: Jacek Caban <jacek at codeweavers.com>
---
 dlls/user32/controls.h       |   1 -
 dlls/user32/mdi.c            |   4 +-
 dlls/user32/menu.c           | 324 ++------------------------------
 dlls/user32/user32.spec      |   2 +-
 dlls/user32/user_main.c      |   1 -
 dlls/win32u/menu.c           | 346 ++++++++++++++++++++++++++++++++++-
 dlls/win32u/ntuser_private.h |   1 -
 dlls/win32u/syscall.c        |   2 +
 dlls/win32u/win32u.spec      |   4 +-
 dlls/wow64win/syscall.h      |   2 +
 dlls/wow64win/user.c         |  63 +++++++
 include/ntuser.h             |  10 +
 12 files changed, 444 insertions(+), 316 deletions(-)

diff --git a/dlls/user32/controls.h b/dlls/user32/controls.h
index 5dcdd9bb935..6e6d7b7d214 100644
--- a/dlls/user32/controls.h
+++ b/dlls/user32/controls.h
@@ -120,7 +120,6 @@ extern void MENU_TrackMouseMenuBar( HWND hwnd, INT ht, POINT pt ) DECLSPEC_HIDDE
 extern void MENU_TrackKbdMenuBar( HWND hwnd, UINT wParam, WCHAR wChar ) DECLSPEC_HIDDEN;
 extern UINT MENU_DrawMenuBar( HDC hDC, LPRECT lprect, HWND hwnd ) DECLSPEC_HIDDEN;
 extern void MENU_EndMenu(HWND) DECLSPEC_HIDDEN;
-extern void free_menu_items( void *ptr ) DECLSPEC_HIDDEN;
 
 /* nonclient area */
 extern LRESULT NC_HandleNCPaint( HWND hwnd , HRGN clip) DECLSPEC_HIDDEN;
diff --git a/dlls/user32/mdi.c b/dlls/user32/mdi.c
index df470f74762..b856f054247 100644
--- a/dlls/user32/mdi.c
+++ b/dlls/user32/mdi.c
@@ -417,7 +417,7 @@ static LRESULT MDI_RefreshMenu(MDICLIENTINFO *ci)
                     if (mii.wID == ci->idFirstChild)
                     {
                         TRACE("removing %u items including separator\n", count - i);
-                        while (RemoveMenu(ci->hWindowMenu, i, MF_BYPOSITION))
+                        while (NtUserRemoveMenu( ci->hWindowMenu, i, MF_BYPOSITION ))
                             /* nothing */;
 
                         break;
@@ -935,7 +935,7 @@ static BOOL MDI_RestoreFrameMenu( HWND frame, HWND hChild )
 		     TRUE,
 		     &menuInfo);
 
-    RemoveMenu(menu,0,MF_BYPOSITION);
+    NtUserRemoveMenu( menu, 0, MF_BYPOSITION );
 
     if ( (menuInfo.fType & MFT_BITMAP)           &&
 	 (LOWORD(menuInfo.dwTypeData)!=0)        &&
diff --git a/dlls/user32/menu.c b/dlls/user32/menu.c
index 5e9d8df17f5..5101a095966 100644
--- a/dlls/user32/menu.c
+++ b/dlls/user32/menu.c
@@ -88,11 +88,6 @@ typedef struct
   /* Margins for popup menus */
 #define MENU_MARGIN 3
 
-/* maximum allowed depth of any branch in the menu tree.
- * This value is slightly larger than in windows (25) to
- * stay on the safe side. */
-#define MAXMENUDEPTH 30
-
   /* (other menu->FocusedItem values give the position of the focused item) */
 #define NO_SELECTED_ITEM  0xffff
 
@@ -127,8 +122,6 @@ static BOOL fEndMenu = FALSE;
 
 DWORD WINAPI DrawMenuBarTemp(HWND hwnd, HDC hDC, LPRECT lprect, HMENU hMenu, HFONT hFont);
 
-static BOOL SetMenuItemInfo_common( MENUITEM *, const MENUITEMINFOW * );
-
 static BOOL is_win_menu_disallowed(HWND hwnd)
 {
     return (GetWindowLongW(hwnd, GWL_STYLE) & (WS_CHILD | WS_POPUP)) == WS_CHILD;
@@ -623,15 +616,6 @@ static UINT MENU_FindSubMenu( HMENU *hmenu, HMENU hSubTarget )
     return NO_SELECTED_ITEM;
 }
 
-/***********************************************************************
- *           MENU_FreeItemData
- */
-static void MENU_FreeItemData( MENUITEM* item )
-{
-    /* delete text */
-    HeapFree( GetProcessHeap(), 0, item->text );
-}
-
 /***********************************************************************
  *           MENU_AdjustMenuItemRect
  *
@@ -2078,62 +2062,6 @@ static void MENU_MoveSelection( HWND hwndOwner, HMENU hmenu, INT offset )
 }
 
 
-/**********************************************************************
- *         insert_menu_item
- *
- * Insert (allocate) a new item into a menu.
- */
-static POPUPMENU *insert_menu_item(HMENU hMenu, UINT id, UINT flags, UINT *ret_pos)
-{
-    MENUITEM *newItems;
-    POPUPMENU *menu;
-    UINT pos = id;
-
-    /* Find where to insert new item */
-    if (!(menu = find_menu_item(hMenu, id, flags, &pos)))
-    {
-        if (!(menu = grab_menu_ptr(hMenu)))
-            return NULL;
-        pos = menu->nItems;
-    }
-
-    /* Make sure that MDI system buttons stay on the right side.
-     * Note: XP treats only bitmap handles 1 - 6 as "magic" ones
-     * regardless of their id.
-     */
-    while (pos > 0 && (INT_PTR)menu->items[pos - 1].hbmpItem >= (INT_PTR)HBMMENU_SYSTEM &&
-           (INT_PTR)menu->items[pos - 1].hbmpItem <= (INT_PTR)HBMMENU_MBAR_CLOSE_D)
-        pos--;
-
-    TRACE("inserting at %u flags %x\n", pos, flags);
-
-    /* Create new items array */
-
-    newItems = HeapAlloc( GetProcessHeap(), 0, sizeof(MENUITEM) * (menu->nItems+1) );
-    if (!newItems)
-    {
-        release_menu_ptr(menu);
-        WARN("allocation failed\n" );
-        return NULL;
-    }
-    if (menu->nItems > 0)
-    {
-	  /* Copy the old array into the new one */
-	if (pos > 0) memcpy( newItems, menu->items, pos * sizeof(MENUITEM) );
-	if (pos < menu->nItems) memcpy( &newItems[pos+1], &menu->items[pos],
-					(menu->nItems-pos)*sizeof(MENUITEM) );
-        HeapFree( GetProcessHeap(), 0, menu->items );
-    }
-    menu->items = newItems;
-    menu->nItems++;
-    memset( &newItems[pos], 0, sizeof(*newItems) );
-    menu->Height = 0; /* force size recalculate */
-
-    *ret_pos = pos;
-    return menu;
-}
-
-
 /**********************************************************************
  *         MENU_ParseResource
  *
@@ -3616,9 +3544,9 @@ BOOL WINAPI ChangeMenuA( HMENU hMenu, UINT pos, LPCSTR data,
     if (flags & MF_DELETE) return DeleteMenu(hMenu, pos, flags & ~MF_DELETE);
     if (flags & MF_CHANGE) return ModifyMenuA(hMenu, pos, flags & ~MF_CHANGE,
                                                 id, data );
-    if (flags & MF_REMOVE) return RemoveMenu( hMenu,
-                                              flags & MF_BYPOSITION ? pos : id,
-                                              flags & ~MF_REMOVE );
+    if (flags & MF_REMOVE) return NtUserRemoveMenu( hMenu,
+                                                    flags & MF_BYPOSITION ? pos : id,
+                                                    flags & ~MF_REMOVE );
     /* Default: MF_INSERT */
     return InsertMenuA( hMenu, pos, flags, id, data );
 }
@@ -3636,9 +3564,9 @@ BOOL WINAPI ChangeMenuW( HMENU hMenu, UINT pos, LPCWSTR data,
     if (flags & MF_DELETE) return DeleteMenu(hMenu, pos, flags & ~MF_DELETE);
     if (flags & MF_CHANGE) return ModifyMenuW(hMenu, pos, flags & ~MF_CHANGE,
                                                 id, data );
-    if (flags & MF_REMOVE) return RemoveMenu( hMenu,
-                                              flags & MF_BYPOSITION ? pos : id,
-                                              flags & ~MF_REMOVE );
+    if (flags & MF_REMOVE) return NtUserRemoveMenu( hMenu,
+                                                    flags & MF_BYPOSITION ? pos : id,
+                                                    flags & ~MF_REMOVE );
     /* Default: MF_INSERT */
     return InsertMenuW( hMenu, pos, flags, id, data );
 }
@@ -3877,10 +3805,6 @@ BOOL WINAPI InsertMenuW( HMENU hMenu, UINT pos, UINT flags,
                          UINT_PTR id, LPCWSTR str )
 {
     MENUITEMINFOW mii;
-    POPUPMENU *menu;
-    MENUITEM *item;
-    UINT newpos;
-    BOOL ret;
 
     if (IS_STRING_ITEM(flags) && str)
         TRACE("hMenu %p, pos %d, flags %08x, id %04lx, str %s\n",
@@ -3888,19 +3812,9 @@ BOOL WINAPI InsertMenuW( HMENU hMenu, UINT pos, UINT flags,
     else TRACE("hMenu %p, pos %d, flags %08x, id %04lx, str %p (not a string)\n",
                hMenu, pos, flags, id, str );
 
-    if (!(menu = insert_menu_item(hMenu, pos, flags, &newpos)))
-        return FALSE;
-
     MENU_mnu2mnuii( flags, id, str, &mii);
     mii.fMask |= MIIM_CHECKMARKS;
-
-    item = &menu->items[newpos];
-    ret = SetMenuItemInfo_common( item, &mii );
-    if (!ret)
-        RemoveMenu( hMenu, pos, flags );
-    release_menu_ptr(menu);
-
-    return ret;
+    return NtUserThunkedMenuItemInfo( hMenu, pos, flags, NtUserInsertMenuItem, &mii, NULL );
 }
 
 
@@ -3948,46 +3862,6 @@ BOOL WINAPI AppendMenuW( HMENU hMenu, UINT flags,
 }
 
 
-/**********************************************************************
- *         RemoveMenu    (USER32.@)
- */
-BOOL WINAPI RemoveMenu( HMENU hMenu, UINT id, UINT flags )
-{
-    POPUPMENU *menu;
-    UINT pos;
-
-    TRACE("(menu=%p id=%#x flags=%04x)\n", hMenu, id, flags);
-
-    if (!(menu = find_menu_item(hMenu, id, flags, &pos)))
-        return FALSE;
-
-    /* Remove item */
-    MENU_FreeItemData( &menu->items[pos] );
-
-    if (--menu->nItems == 0)
-    {
-        HeapFree( GetProcessHeap(), 0, menu->items );
-        menu->items = NULL;
-    }
-    else
-    {
-        MENUITEM *new_items, *item = &menu->items[pos];
-
-	while (pos < menu->nItems)
-	{
-	    *item = *(item+1);
-	    item++;
-	    pos++;
-	}
-        new_items = HeapReAlloc( GetProcessHeap(), 0, menu->items, menu->nItems * sizeof(MENUITEM) );
-        if (new_items) menu->items = new_items;
-    }
-    release_menu_ptr(menu);
-
-    return TRUE;
-}
-
-
 /**********************************************************************
  *         DeleteMenu    (USER32.@)
  */
@@ -4002,7 +3876,7 @@ BOOL WINAPI DeleteMenu( HMENU hMenu, UINT id, UINT flags )
     if (menu->items[pos].fType & MF_POPUP)
         NtUserDestroyMenu( menu->items[pos].hSubMenu );
 
-    RemoveMenu(menu->obj.handle, pos, flags | MF_BYPOSITION);
+    NtUserRemoveMenu( menu->obj.handle, pos, flags | MF_BYPOSITION );
     release_menu_ptr(menu);
     return TRUE;
 }
@@ -4015,26 +3889,14 @@ BOOL WINAPI ModifyMenuW( HMENU hMenu, UINT pos, UINT flags,
                          UINT_PTR id, LPCWSTR str )
 {
     MENUITEMINFOW mii;
-    POPUPMENU *menu;
-    UINT item_pos;
-    BOOL ret;
 
     if (IS_STRING_ITEM(flags))
         TRACE("%p %d %04x %04lx %s\n", hMenu, pos, flags, id, debugstr_w(str) );
     else
         TRACE("%p %d %04x %04lx %p\n", hMenu, pos, flags, id, str );
 
-    if (!(menu = find_menu_item(hMenu, pos, flags, &item_pos)))
-    {
-        /* workaround for Word 95: pretend that SC_TASKLIST item exists */
-        if (pos == SC_TASKLIST && !(flags & MF_BYPOSITION)) return TRUE;
-        return FALSE;
-    }
-    menu->Height = 0; /* force size recalculate */
     MENU_mnu2mnuii( flags, id, str, &mii);
-    ret = SetMenuItemInfo_common( &menu->items[item_pos], &mii );
-    release_menu_ptr(menu);
-    return ret;
+    return NtUserThunkedMenuItemInfo( hMenu, pos, flags, NtUserSetMenuItemInfo, &mii, NULL );
 }
 
 
@@ -4120,21 +3982,6 @@ HMENU WINAPI CreateMenu(void)
 }
 
 
-void free_menu_items( void *ptr )
-{
-    POPUPMENU *menu = ptr;
-    MENUITEM *item = menu->items;
-    int i;
-
-    for (i = menu->nItems; i > 0; i--, item++)
-    {
-        if (item->fType & MF_POPUP) NtUserDestroyMenu( item->hSubMenu );
-        MENU_FreeItemData( item );
-    }
-    HeapFree( GetProcessHeap(), 0, menu->items );
-}
-
-
 /**********************************************************************
  *         GetSystemMenu    (USER32.@)
  */
@@ -4654,108 +4501,6 @@ BOOL WINAPI GetMenuItemInfoW( HMENU hmenu, UINT item, BOOL bypos,
 }
 
 
-/**********************************************************************
- *		MENU_depth
- *
- * detect if there are loops in the menu tree (or the depth is too large)
- */
-static int MENU_depth( POPUPMENU *pmenu, int depth)
-{
-    UINT i;
-    MENUITEM *item;
-    int subdepth;
-
-    depth++;
-    if( depth > MAXMENUDEPTH) return depth;
-    item = pmenu->items;
-    subdepth = depth;
-    for( i = 0; i < pmenu->nItems && subdepth <= MAXMENUDEPTH; i++, item++){
-        POPUPMENU *psubmenu =  item->hSubMenu ? MENU_GetMenu( item->hSubMenu) : NULL;
-        if( psubmenu){
-            int bdepth = MENU_depth( psubmenu, depth);
-            if( bdepth > subdepth) subdepth = bdepth;
-        }
-        if( subdepth > MAXMENUDEPTH)
-            TRACE("<- hmenu %p\n", item->hSubMenu);
-    }
-    return subdepth;
-}
-
-
-/**********************************************************************
- *		SetMenuItemInfo_common
- *
- * Note: does not support the MIIM_TYPE flag. Use the MIIM_FTYPE,
- * MIIM_BITMAP and MIIM_STRING flags instead.
- */
-
-static BOOL SetMenuItemInfo_common( MENUITEM *menu, const MENUITEMINFOW *lpmii )
-{
-    if (!menu) return FALSE;
-
-    debug_print_menuitem("SetMenuItemInfo_common from: ", menu, "");
-
-    if (lpmii->fMask & MIIM_FTYPE ) {
-        menu->fType &= ~MENUITEMINFO_TYPE_MASK;
-        menu->fType |= lpmii->fType & MENUITEMINFO_TYPE_MASK;
-    }
-    if (lpmii->fMask & MIIM_STRING ) {
-        const WCHAR *text = lpmii->dwTypeData;
-        /* free the string when used */
-        HeapFree(GetProcessHeap(), 0, menu->text);
-        if (!text)
-            menu->text = NULL;
-        else if ((menu->text = HeapAlloc( GetProcessHeap(), 0, (lstrlenW(text)+1) * sizeof(WCHAR) )))
-            lstrcpyW( menu->text, text );
-    }
-
-    if (lpmii->fMask & MIIM_STATE)
-         /* Other menu items having MFS_DEFAULT are not converted
-           to normal items */
-         menu->fState = lpmii->fState & MENUITEMINFO_STATE_MASK;
-
-    if (lpmii->fMask & MIIM_ID)
-	menu->wID = lpmii->wID;
-
-    if (lpmii->fMask & MIIM_SUBMENU) {
-	menu->hSubMenu = lpmii->hSubMenu;
-	if (menu->hSubMenu) {
-	    POPUPMENU *subMenu = MENU_GetMenu(menu->hSubMenu);
-	    if (subMenu) {
-                if( MENU_depth( subMenu, 0) > MAXMENUDEPTH) {
-                    ERR( "Loop detected in menu hierarchy or maximum menu depth exceeded!\n");
-                    menu->hSubMenu = 0;
-                    return FALSE;
-                }
-		subMenu->wFlags |= MF_POPUP;
-		menu->fType |= MF_POPUP;
-	    } else {
-                SetLastError( ERROR_INVALID_PARAMETER);
-                return FALSE;
-            }
-	}
-	else
-	    menu->fType &= ~MF_POPUP;
-    }
-
-    if (lpmii->fMask & MIIM_CHECKMARKS)
-    {
-	menu->hCheckBit = lpmii->hbmpChecked;
-	menu->hUnCheckBit = lpmii->hbmpUnchecked;
-    }
-    if (lpmii->fMask & MIIM_DATA)
-	menu->dwItemData = lpmii->dwItemData;
-
-    if (lpmii->fMask & MIIM_BITMAP)
-	menu->hbmpItem = lpmii->hbmpItem;
-
-    if( !menu->text && !(menu->fType & MFT_OWNERDRAW) && !menu->hbmpItem)
-        menu->fType |= MFT_SEPARATOR;
-
-    debug_print_menuitem("SetMenuItemInfo_common to : ", menu, "");
-    return TRUE;
-}
-
 /**********************************************************************
  *		MENU_NormalizeMenuItemInfoStruct
  *
@@ -4808,8 +4553,6 @@ BOOL WINAPI SetMenuItemInfoA(HMENU hmenu, UINT item, BOOL bypos,
 {
     WCHAR *strW = NULL;
     MENUITEMINFOW mii;
-    POPUPMENU *menu;
-    UINT pos;
     BOOL ret;
 
     TRACE("hmenu %p, item %u, by pos %d, info %p\n", hmenu, item, bypos, lpmii);
@@ -4825,15 +4568,9 @@ BOOL WINAPI SetMenuItemInfoA(HMENU hmenu, UINT item, BOOL bypos,
         mii.dwTypeData = strW;
     }
 
-    if (!(menu = find_menu_item(hmenu, item, bypos ? MF_BYPOSITION : 0, &pos)))
-    {
-        /* workaround for Word 95: pretend that SC_TASKLIST item exists */
-        HeapFree( GetProcessHeap(), 0, strW );
-        if (item == SC_TASKLIST && !bypos) return TRUE;
-        return FALSE;
-    }
-    ret = SetMenuItemInfo_common( &menu->items[pos], &mii );
-    release_menu_ptr(menu);
+    ret = NtUserThunkedMenuItemInfo( hmenu, item, bypos ? MF_BYPOSITION : 0,
+                                     NtUserSetMenuItemInfo, &mii, NULL );
+
     HeapFree( GetProcessHeap(), 0, strW );
     return ret;
 }
@@ -4845,24 +4582,13 @@ BOOL WINAPI SetMenuItemInfoW(HMENU hmenu, UINT item, BOOL bypos,
                                  const MENUITEMINFOW *lpmii)
 {
     MENUITEMINFOW mii;
-    POPUPMENU *menu;
-    BOOL ret;
-    UINT pos;
 
     TRACE("hmenu %p, item %u, by pos %d, info %p\n", hmenu, item, bypos, lpmii);
 
     if (!MENU_NormalizeMenuItemInfoStruct( lpmii, &mii )) return FALSE;
 
-    if (!(menu = find_menu_item(hmenu, item, bypos ? MF_BYPOSITION : 0, &pos)))
-    {
-        /* workaround for Word 95: pretend that SC_TASKLIST item exists */
-        if (item == SC_TASKLIST && !bypos) return TRUE;
-        return FALSE;
-    }
-
-    ret = SetMenuItemInfo_common(&menu->items[pos], &mii);
-    release_menu_ptr(menu);
-    return ret;
+    return NtUserThunkedMenuItemInfo( hmenu, item, bypos ? MF_BYPOSITION : 0,
+                                      NtUserSetMenuItemInfo, &mii, NULL );
 }
 
 /**********************************************************************
@@ -4915,8 +4641,6 @@ BOOL WINAPI InsertMenuItemA(HMENU hMenu, UINT uItem, BOOL bypos,
 {
     WCHAR *strW = NULL;
     MENUITEMINFOW mii;
-    POPUPMENU *menu;
-    UINT pos;
     BOOL ret;
 
     TRACE("hmenu %p, item %04x, by pos %d, info %p\n", hMenu, uItem, bypos, lpmii);
@@ -4932,14 +4656,9 @@ BOOL WINAPI InsertMenuItemA(HMENU hMenu, UINT uItem, BOOL bypos,
         mii.dwTypeData = strW;
     }
 
-    if (!(menu = insert_menu_item(hMenu, uItem, bypos ? MF_BYPOSITION : 0, &pos)))
-    {
-        HeapFree( GetProcessHeap(), 0, strW );
-        return FALSE;
-    }
+    ret = NtUserThunkedMenuItemInfo( hMenu, uItem, bypos ? MF_BYPOSITION : 0,
+                                     NtUserInsertMenuItem, &mii, NULL );
 
-    ret = SetMenuItemInfo_common( &menu->items[pos], &mii );
-    release_menu_ptr(menu);
     HeapFree( GetProcessHeap(), 0, strW );
     return ret;
 }
@@ -4952,20 +4671,13 @@ BOOL WINAPI InsertMenuItemW(HMENU hMenu, UINT uItem, BOOL bypos,
                                 const MENUITEMINFOW *lpmii)
 {
     MENUITEMINFOW mii;
-    POPUPMENU *menu;
-    UINT pos;
-    BOOL ret;
 
     TRACE("hmenu %p, item %04x, by pos %d, info %p\n", hMenu, uItem, bypos, lpmii);
 
     if (!MENU_NormalizeMenuItemInfoStruct( lpmii, &mii )) return FALSE;
 
-    if (!(menu = insert_menu_item(hMenu, uItem, bypos ? MF_BYPOSITION : 0, &pos)))
-        return FALSE;
-
-    ret = SetMenuItemInfo_common( &menu->items[pos], &mii );
-    release_menu_ptr(menu);
-    return ret;
+    return NtUserThunkedMenuItemInfo( hMenu, uItem, bypos ? MF_BYPOSITION : 0,
+                                      NtUserInsertMenuItem, &mii, NULL );
 }
 
 /**********************************************************************
diff --git a/dlls/user32/user32.spec b/dlls/user32/user32.spec
index da0b7c5a1ab..298a0fa3150 100644
--- a/dlls/user32/user32.spec
+++ b/dlls/user32/user32.spec
@@ -619,7 +619,7 @@
 @ stdcall ReleaseCapture()
 @ stdcall ReleaseDC(long long) NtUserReleaseDC
 @ stdcall RemoveClipboardFormatListener(long) NtUserRemoveClipboardFormatListener
-@ stdcall RemoveMenu(long long long)
+@ stdcall RemoveMenu(long long long) NtUserRemoveMenu
 @ stdcall RemovePropA(long str)
 @ stdcall RemovePropW(long wstr)
 @ stdcall ReplyMessage(long)
diff --git a/dlls/user32/user_main.c b/dlls/user32/user_main.c
index 1400597eb5d..babd5f95e55 100644
--- a/dlls/user32/user_main.c
+++ b/dlls/user32/user_main.c
@@ -165,7 +165,6 @@ static const struct user_callbacks user_funcs =
     ImmProcessKey,
     ImmTranslateMessage,
     SetSystemMenu,
-    free_menu_items,
     free_win_ptr,
     MENU_IsMenuActive,
     notify_ime,
diff --git a/dlls/win32u/menu.c b/dlls/win32u/menu.c
index 5d1a5ec12d6..0d7127fb4c6 100644
--- a/dlls/win32u/menu.c
+++ b/dlls/win32u/menu.c
@@ -39,9 +39,26 @@ struct accelerator
     ACCEL              table[1];
 };
 
+/* maximum allowed depth of any branch in the menu tree.
+ * This value is slightly larger than in windows (25) to
+ * stay on the safe side. */
+#define MAXMENUDEPTH 30
+
 /* (other menu->FocusedItem values give the position of the focused item) */
 #define NO_SELECTED_ITEM  0xffff
 
+/* macro to test that flags do not indicate bitmap, ownerdraw or separator */
+#define IS_STRING_ITEM(flags) (MENU_ITEM_TYPE ((flags)) == MF_STRING)
+#define IS_MAGIC_BITMAP(id)     ((id) && ((INT_PTR)(id) < 12) && ((INT_PTR)(id) >= -1))
+
+#define MENUITEMINFO_TYPE_MASK                                          \
+    (MFT_STRING | MFT_BITMAP | MFT_OWNERDRAW | MFT_SEPARATOR |          \
+     MFT_MENUBARBREAK | MFT_MENUBREAK | MFT_RADIOCHECK |                \
+     MFT_RIGHTORDER | MFT_RIGHTJUSTIFY /* same as MF_HELP */ )
+#define TYPE_MASK  (MENUITEMINFO_TYPE_MASK | MF_POPUP | MF_SYSMENU)
+#define STATE_MASK (~TYPE_MASK)
+#define MENUITEMINFO_STATE_MASK (STATE_MASK & ~(MF_BYPOSITION | MF_MOUSESELECT))
+
 /**********************************************************************
  *           NtUserCopyAcceleratorTable   (win32u.@)
  */
@@ -111,6 +128,73 @@ BOOL WINAPI NtUserDestroyAcceleratorTable( HACCEL handle )
     return TRUE;
 }
 
+#define MENUFLAG(bit,text) \
+  do { \
+      if (flags & (bit)) { flags &= ~(bit); strcat(buf, (text)); } \
+  } while (0)
+
+static const char *debugstr_menuitem( const MENUITEM *item )
+{
+    static const char *const hbmmenus[] = { "HBMMENU_CALLBACK", "", "HBMMENU_SYSTEM",
+        "HBMMENU_MBAR_RESTORE", "HBMMENU_MBAR_MINIMIZE", "UNKNOWN BITMAP", "HBMMENU_MBAR_CLOSE",
+        "HBMMENU_MBAR_CLOSE_D", "HBMMENU_MBAR_MINIMIZE_D", "HBMMENU_POPUP_CLOSE",
+        "HBMMENU_POPUP_RESTORE", "HBMMENU_POPUP_MAXIMIZE", "HBMMENU_POPUP_MINIMIZE" };
+    char buf[256];
+    UINT flags;
+
+    if (!item) return "NULL";
+
+    sprintf( buf, "{ ID=0x%lx", item->wID );
+    if (item->hSubMenu) sprintf( buf + strlen(buf), ", Sub=%p", item->hSubMenu );
+
+    flags = item->fType;
+    if (flags)
+    {
+        strcat( buf, ", fType=" );
+        MENUFLAG( MFT_SEPARATOR, "sep" );
+        MENUFLAG( MFT_OWNERDRAW, "own" );
+        MENUFLAG( MFT_BITMAP, "bit" );
+        MENUFLAG( MF_POPUP, "pop" );
+        MENUFLAG( MFT_MENUBARBREAK, "barbrk" );
+        MENUFLAG( MFT_MENUBREAK, "brk");
+        MENUFLAG( MFT_RADIOCHECK, "radio" );
+        MENUFLAG( MFT_RIGHTORDER, "rorder" );
+        MENUFLAG( MF_SYSMENU, "sys" );
+        MENUFLAG( MFT_RIGHTJUSTIFY, "right" );  /* same as MF_HELP */
+        if (flags) sprintf( buf + strlen(buf), "+0x%x", flags );
+    }
+
+    flags = item->fState;
+    if (flags)
+    {
+        strcat( buf, ", State=" );
+        MENUFLAG( MFS_GRAYED, "grey" );
+        MENUFLAG( MFS_DEFAULT, "default" );
+        MENUFLAG( MFS_DISABLED, "dis" );
+        MENUFLAG( MFS_CHECKED, "check" );
+        MENUFLAG( MFS_HILITE, "hi" );
+        MENUFLAG( MF_USECHECKBITMAPS, "usebit" );
+        MENUFLAG( MF_MOUSESELECT, "mouse" );
+        if (flags) sprintf( buf + strlen(buf), "+0x%x", flags );
+    }
+
+    if (item->hCheckBit)   sprintf( buf + strlen(buf), ", Chk=%p", item->hCheckBit );
+    if (item->hUnCheckBit) sprintf( buf + strlen(buf), ", Unc=%p", item->hUnCheckBit );
+    if (item->text)        sprintf( buf + strlen(buf), ", Text=%s", debugstr_w(item->text) );
+    if (item->dwItemData)  sprintf( buf + strlen(buf), ", ItemData=0x%08lx", item->dwItemData );
+
+    if (item->hbmpItem)
+    {
+        if (IS_MAGIC_BITMAP( item->hbmpItem ))
+            sprintf( buf + strlen(buf), ", hbitmap=%s", hbmmenus[(INT_PTR)item->hbmpItem + 1] );
+        else
+            sprintf( buf + strlen(buf), ", hbitmap=%p", item->hbmpItem );
+    }
+    return wine_dbg_sprintf( "%s  }", buf );
+}
+
+#undef MENUFLAG
+
 static POPUPMENU *grab_menu_ptr( HMENU handle )
 {
     POPUPMENU *menu = get_user_handle_ptr( handle, NTUSER_OBJ_MENU );
@@ -210,6 +294,53 @@ static POPUPMENU *find_menu_item( HMENU handle, UINT id, UINT flags, UINT *pos )
     return menu;
 }
 
+static POPUPMENU *insert_menu_item( HMENU handle, UINT id, UINT flags, UINT *ret_pos )
+{
+    MENUITEM *new_items;
+    POPUPMENU *menu;
+    UINT pos = id;
+
+    /* Find where to insert new item */
+    if (!(menu = find_menu_item(handle, id, flags, &pos)))
+    {
+        if (!(menu = grab_menu_ptr(handle)))
+            return NULL;
+        pos = menu->nItems;
+    }
+
+    /* Make sure that MDI system buttons stay on the right side.
+     * Note: XP treats only bitmap handles 1 - 6 as "magic" ones
+     * regardless of their id.
+     */
+    while (pos > 0 && (INT_PTR)menu->items[pos - 1].hbmpItem >= (INT_PTR)HBMMENU_SYSTEM &&
+           (INT_PTR)menu->items[pos - 1].hbmpItem <= (INT_PTR)HBMMENU_MBAR_CLOSE_D)
+        pos--;
+
+    TRACE( "inserting at %u flags %x\n", pos, flags );
+
+    new_items = malloc( sizeof(MENUITEM) * (menu->nItems + 1) );
+    if (!new_items)
+    {
+        release_menu_ptr( menu );
+        return NULL;
+    }
+    if (menu->nItems > 0)
+    {
+        /* Copy the old array into the new one */
+        if (pos > 0) memcpy( new_items, menu->items, pos * sizeof(MENUITEM) );
+        if (pos < menu->nItems) memcpy( &new_items[pos + 1], &menu->items[pos],
+                                        (menu->nItems - pos) * sizeof(MENUITEM) );
+        free( menu->items );
+    }
+    menu->items = new_items;
+    menu->nItems++;
+    memset( &new_items[pos], 0, sizeof(*new_items) );
+    menu->Height = 0; /* force size recalculate */
+
+    *ret_pos = pos;
+    return menu;
+}
+
 static BOOL is_win_menu_disallowed( HWND hwnd )
 {
     return (get_window_long(hwnd, GWL_STYLE) & (WS_CHILD | WS_POPUP)) == WS_CHILD;
@@ -257,8 +388,19 @@ BOOL WINAPI NtUserDestroyMenu( HMENU handle )
         menu->hWnd = 0;
     }
 
-    if (menu->items && user_callbacks) /* recursively destroy submenus */
-        user_callbacks->free_menu_items( menu );
+    /* recursively destroy submenus */
+    if (menu->items)
+    {
+        MENUITEM *item = menu->items;
+        int i;
+
+        for (i = menu->nItems; i > 0; i--, item++)
+        {
+            if (item->fType & MF_POPUP) NtUserDestroyMenu( item->hSubMenu );
+            free( item->text );
+        }
+        free( menu->items );
+    }
 
     free( menu );
     return TRUE;
@@ -527,6 +669,206 @@ BOOL get_menu_info( HMENU handle, MENUINFO *info )
     return TRUE;
 }
 
+/**********************************************************************
+ *           menu_depth
+ *
+ * detect if there are loops in the menu tree (or the depth is too large)
+ */
+static int menu_depth( POPUPMENU *pmenu, int depth)
+{
+    int i, subdepth;
+    MENUITEM *item;
+
+    if (++depth > MAXMENUDEPTH) return depth;
+    item = pmenu->items;
+    subdepth = depth;
+    for (i = 0; i < pmenu->nItems && subdepth <= MAXMENUDEPTH; i++, item++)
+    {
+        POPUPMENU *submenu = item->hSubMenu ? grab_menu_ptr( item->hSubMenu ) : NULL;
+        if (submenu)
+        {
+            int bdepth = menu_depth( submenu, depth);
+            if (bdepth > subdepth) subdepth = bdepth;
+            release_menu_ptr( submenu );
+        }
+        if (subdepth > MAXMENUDEPTH)
+            TRACE( "<- hmenu %p\n", item->hSubMenu );
+    }
+
+    return subdepth;
+}
+
+static BOOL set_menu_item_info( MENUITEM *menu, const MENUITEMINFOW *info )
+{
+    if (!menu) return FALSE;
+
+    TRACE( "%s\n", debugstr_menuitem( menu ));
+
+    if (info->fMask & MIIM_FTYPE )
+    {
+        menu->fType &= ~MENUITEMINFO_TYPE_MASK;
+        menu->fType |= info->fType & MENUITEMINFO_TYPE_MASK;
+    }
+    if (info->fMask & MIIM_STRING )
+    {
+        const WCHAR *text = info->dwTypeData;
+        /* free the string when used */
+        free( menu->text );
+        if (!text)
+            menu->text = NULL;
+        else if ((menu->text = malloc( (lstrlenW(text) + 1) * sizeof(WCHAR) )))
+            lstrcpyW( menu->text, text );
+    }
+
+    if (info->fMask & MIIM_STATE)
+         /* Other menu items having MFS_DEFAULT are not converted
+           to normal items */
+         menu->fState = info->fState & MENUITEMINFO_STATE_MASK;
+
+    if (info->fMask & MIIM_ID)
+        menu->wID = info->wID;
+
+    if (info->fMask & MIIM_SUBMENU)
+    {
+        menu->hSubMenu = info->hSubMenu;
+        if (menu->hSubMenu)
+        {
+            POPUPMENU *submenu = grab_menu_ptr( menu->hSubMenu );
+            if (!submenu)
+            {
+                SetLastError( ERROR_INVALID_PARAMETER);
+                return FALSE;
+            }
+            if (menu_depth( submenu, 0 ) > MAXMENUDEPTH)
+            {
+                ERR( "Loop detected in menu hierarchy or maximum menu depth exceeded\n" );
+                menu->hSubMenu = 0;
+                release_menu_ptr( submenu );
+                return FALSE;
+            }
+            submenu->wFlags |= MF_POPUP;
+            menu->fType |= MF_POPUP;
+            release_menu_ptr( submenu );
+        }
+        else
+            menu->fType &= ~MF_POPUP;
+    }
+
+    if (info->fMask & MIIM_CHECKMARKS)
+    {
+        menu->hCheckBit = info->hbmpChecked;
+        menu->hUnCheckBit = info->hbmpUnchecked;
+    }
+    if (info->fMask & MIIM_DATA)
+        menu->dwItemData = info->dwItemData;
+
+    if (info->fMask & MIIM_BITMAP)
+        menu->hbmpItem = info->hbmpItem;
+
+    if (!menu->text && !(menu->fType & MFT_OWNERDRAW) && !menu->hbmpItem)
+        menu->fType |= MFT_SEPARATOR;
+
+    TRACE( "to: %s\n", debugstr_menuitem( menu ));
+    return TRUE;
+}
+
+/**********************************************************************
+ *           NtUserThunkedMenuItemInfo    (win32u.@)
+ */
+UINT WINAPI NtUserThunkedMenuItemInfo( HMENU handle, UINT pos, UINT flags, UINT method,
+                                       MENUITEMINFOW *info, UNICODE_STRING *str )
+{
+    POPUPMENU *menu;
+    UINT i;
+    BOOL ret;
+
+    switch (method)
+    {
+    case NtUserInsertMenuItem:
+        if (!info || info->cbSize != sizeof(*info))
+        {
+            SetLastError( ERROR_INVALID_PARAMETER );
+            return FALSE;
+        }
+
+        if (!(menu = insert_menu_item( handle, pos, flags, &i )))
+        {
+            /* workaround for Word 95: pretend that SC_TASKLIST item exists */
+            if (pos == SC_TASKLIST && !(flags & MF_BYPOSITION)) return TRUE;
+            return FALSE;
+        }
+
+        ret = set_menu_item_info( &menu->items[i], info );
+        if (!ret) NtUserRemoveMenu( handle, pos, flags );
+        release_menu_ptr(menu);
+        break;
+
+    case NtUserSetMenuItemInfo:
+        if (!info || info->cbSize != sizeof(*info))
+        {
+            SetLastError( ERROR_INVALID_PARAMETER );
+            return FALSE;
+        }
+
+        if (!(menu = find_menu_item( handle, pos, flags, &i )))
+        {
+            /* workaround for Word 95: pretend that SC_TASKLIST item exists */
+            if (pos == SC_TASKLIST && !(flags & MF_BYPOSITION)) return TRUE;
+            return FALSE;
+        }
+
+        ret = set_menu_item_info( &menu->items[i], info );
+        if (ret) menu->Height = 0; /* force size recalculate */
+        release_menu_ptr(menu);
+        break;
+
+    default:
+        FIXME( "unsupported method %u\n", method );
+        return FALSE;
+    }
+
+    return ret;
+}
+
+/**********************************************************************
+ *           NtUserRemoveMenu    (win32u.@)
+ */
+BOOL WINAPI NtUserRemoveMenu( HMENU handle, UINT id, UINT flags )
+{
+    POPUPMENU *menu;
+    UINT pos;
+
+    TRACE( "(menu=%p id=%#x flags=%04x)\n", handle, id, flags );
+
+    if (!(menu = find_menu_item( handle, id, flags, &pos )))
+        return FALSE;
+
+    /* Remove item */
+    free( menu->items[pos].text );
+
+    if (--menu->nItems == 0)
+    {
+        free( menu->items );
+        menu->items = NULL;
+    }
+    else
+    {
+        MENUITEM *new_items, *item = &menu->items[pos];
+
+        while (pos < menu->nItems)
+        {
+            *item = item[1];
+            item++;
+            pos++;
+        }
+        new_items = realloc( menu->items, menu->nItems * sizeof(MENUITEM) );
+        if (new_items) menu->items = new_items;
+    }
+
+    release_menu_ptr(menu);
+    return TRUE;
+}
+
 /**********************************************************************
  *           NtUserSetMenuContextHelpId    (win32u.@)
  */
diff --git a/dlls/win32u/ntuser_private.h b/dlls/win32u/ntuser_private.h
index fe9d7e18bc9..29e6b4eaf3e 100644
--- a/dlls/win32u/ntuser_private.h
+++ b/dlls/win32u/ntuser_private.h
@@ -38,7 +38,6 @@ struct user_callbacks
     BOOL (WINAPI *pImmProcessKey)(HWND, HKL, UINT, LPARAM, DWORD);
     BOOL (WINAPI *pImmTranslateMessage)(HWND, UINT, WPARAM, LPARAM);
     BOOL (WINAPI *pSetSystemMenu)( HWND hwnd, HMENU menu );
-    void (CDECL *free_menu_items)( void *ptr );
     void (CDECL *free_win_ptr)( struct tagWND *win );
     HWND (CDECL *is_menu_active)(void);
     void (CDECL *notify_ime)( HWND hwnd, UINT param );
diff --git a/dlls/win32u/syscall.c b/dlls/win32u/syscall.c
index dc58cbe1944..f585e90bd4a 100644
--- a/dlls/win32u/syscall.c
+++ b/dlls/win32u/syscall.c
@@ -156,6 +156,7 @@ static void * const syscalls[] =
     NtUserOpenInputDesktop,
     NtUserOpenWindowStation,
     NtUserRemoveClipboardFormatListener,
+    NtUserRemoveMenu,
     NtUserRemoveProp,
     NtUserSetKeyboardState,
     NtUserSetMenuContextHelpId,
@@ -170,6 +171,7 @@ static void * const syscalls[] =
     NtUserSetWinEventHook,
     NtUserSetWindowsHookEx,
     NtUserThunkedMenuInfo,
+    NtUserThunkedMenuItemInfo,
     NtUserUnhookWinEvent,
     NtUserUnhookWindowsHookEx,
     NtUserWindowFromDC,
diff --git a/dlls/win32u/win32u.spec b/dlls/win32u/win32u.spec
index 208b167229e..07da23f633a 100644
--- a/dlls/win32u/win32u.spec
+++ b/dlls/win32u/win32u.spec
@@ -1147,7 +1147,7 @@
 @ stub NtUserRemoteStopScreenUpdates
 @ stdcall -syscall NtUserRemoveClipboardFormatListener(long)
 @ stub NtUserRemoveInjectionDevice
-@ stub NtUserRemoveMenu
+@ stdcall -syscall NtUserRemoveMenu(long long long)
 @ stdcall -syscall NtUserRemoveProp(long wstr)
 @ stub NtUserRemoveVisualIdentifier
 @ stub NtUserReportInertia
@@ -1275,7 +1275,7 @@
 @ stdcall NtUserSystemParametersInfoForDpi(long long ptr long long)
 @ stub NtUserTestForInteractiveUser
 @ stdcall -syscall NtUserThunkedMenuInfo(long ptr)
-@ stub NtUserThunkedMenuItemInfo
+@ stdcall -syscall NtUserThunkedMenuItemInfo(long long long long ptr ptr)
 @ stdcall NtUserToUnicodeEx(long long ptr ptr long long long)
 @ stdcall NtUserTrackMouseEvent(ptr)
 @ stub NtUserTrackPopupMenuEx
diff --git a/dlls/wow64win/syscall.h b/dlls/wow64win/syscall.h
index 1bfc210eab0..776a7e671f6 100644
--- a/dlls/wow64win/syscall.h
+++ b/dlls/wow64win/syscall.h
@@ -143,6 +143,7 @@
     SYSCALL_ENTRY( NtUserOpenInputDesktop ) \
     SYSCALL_ENTRY( NtUserOpenWindowStation ) \
     SYSCALL_ENTRY( NtUserRemoveClipboardFormatListener ) \
+    SYSCALL_ENTRY( NtUserRemoveMenu ) \
     SYSCALL_ENTRY( NtUserRemoveProp ) \
     SYSCALL_ENTRY( NtUserSetKeyboardState ) \
     SYSCALL_ENTRY( NtUserSetMenuContextHelpId ) \
@@ -157,6 +158,7 @@
     SYSCALL_ENTRY( NtUserSetWinEventHook ) \
     SYSCALL_ENTRY( NtUserSetWindowsHookEx ) \
     SYSCALL_ENTRY( NtUserThunkedMenuInfo ) \
+    SYSCALL_ENTRY( NtUserThunkedMenuItemInfo ) \
     SYSCALL_ENTRY( NtUserUnhookWinEvent ) \
     SYSCALL_ENTRY( NtUserUnhookWindowsHookEx ) \
     SYSCALL_ENTRY( NtUserWindowFromDC )
diff --git a/dlls/wow64win/user.c b/dlls/wow64win/user.c
index 554a6634f0e..1f287c7631c 100644
--- a/dlls/wow64win/user.c
+++ b/dlls/wow64win/user.c
@@ -30,6 +30,22 @@
 
 WINE_DEFAULT_DEBUG_CHANNEL(wow);
 
+typedef struct
+{
+    UINT    cbSize;
+    UINT    fMask;
+    UINT    fType;
+    UINT    fState;
+    UINT    wID;
+    UINT32  hSubMenu;
+    UINT32  hbmpChecked;
+    UINT32  hbmpUnchecked;
+    UINT32  dwItemData;
+    UINT32  dwTypeData;
+    UINT    cch;
+    UINT32  hbmpItem;
+} MENUITEMINFOW32;
+
 NTSTATUS WINAPI wow64_NtUserInitializeClientPfnArrays( UINT *args )
 {
     FIXME( "\n" );
@@ -708,3 +724,50 @@ NTSTATUS WINAPI wow64_NtUserThunkedMenuInfo( UINT *args )
 
     return NtUserThunkedMenuInfo( menu, info32 ? &info : NULL );
 }
+
+NTSTATUS WINAPI wow64_NtUserThunkedMenuItemInfo( UINT *args )
+{
+    HMENU handle = get_handle( &args );
+    UINT pos = get_ulong( &args );
+    UINT flags = get_ulong( &args );
+    UINT method = get_ulong( &args );
+    MENUITEMINFOW32 *info32 = get_ptr( &args );
+    UNICODE_STRING32 *str32 = get_ptr( &args );
+    MENUITEMINFOW info = { sizeof(info) }, *info_ptr;
+    UNICODE_STRING str;
+
+    if (info32)
+    {
+        info.cbSize = sizeof(info);
+        info.fMask = info32->fMask;
+        switch (method)
+        {
+        case NtUserSetMenuItemInfo:
+        case NtUserInsertMenuItem:
+            info.fType = info32->fType;
+            info.fState = info32->fState;
+            info.wID = info32->wID;
+            info.hSubMenu = UlongToHandle( info32->hSubMenu );
+            info.hbmpChecked = UlongToHandle( info32->hbmpUnchecked );
+            info.dwItemData = info32->dwItemData;
+            info.dwTypeData = UlongToPtr( info32->dwTypeData );
+            info.cch = info32->cch;
+            info.hbmpItem = UlongToHandle( info32->hbmpItem );
+            break;
+        }
+        info_ptr = &info;
+    }
+    else info_ptr = NULL;
+
+    return NtUserThunkedMenuItemInfo( handle, pos, flags, method, info_ptr,
+                                      unicode_str_32to64( &str, str32 ));
+}
+
+NTSTATUS WINAPI wow64_NtUserRemoveMenu( UINT *args )
+{
+    HMENU handle = get_handle( &args );
+    UINT id = get_ulong( &args );
+    UINT flags = get_ulong( &args );
+
+    return NtUserRemoveMenu( handle, id, flags );
+}
diff --git a/include/ntuser.h b/include/ntuser.h
index 58486fcfcb0..75b9ba7b73c 100644
--- a/include/ntuser.h
+++ b/include/ntuser.h
@@ -178,6 +178,13 @@ enum
     NtUserSpyExit             = 0x0301,
 };
 
+/* NtUserThunkedMenuItemInfo codes */
+enum
+{
+    NtUserSetMenuItemInfo,
+    NtUserInsertMenuItem,
+};
+
 struct send_message_timeout_params
 {
     UINT flags;
@@ -581,6 +588,7 @@ ATOM    WINAPI NtUserRegisterClassExWOW( const WNDCLASSEXW *wc, UNICODE_STRING *
 BOOL    WINAPI NtUserRegisterHotKey( HWND hwnd, INT id, UINT modifiers, UINT vk );
 INT     WINAPI NtUserReleaseDC( HWND hwnd, HDC hdc );
 BOOL    WINAPI NtUserRemoveClipboardFormatListener( HWND hwnd );
+BOOL    WINAPI NtUserRemoveMenu( HMENU menu, UINT id, UINT flags );
 HANDLE  WINAPI NtUserRemoveProp( HWND hwnd, const WCHAR *str );
 BOOL    WINAPI NtUserScrollDC( HDC hdc, INT dx, INT dy, const RECT *scroll, const RECT *clip,
                                HRGN ret_update_rgn, RECT *update_rect );
@@ -629,6 +637,8 @@ BOOL    WINAPI NtUserShowWindowAsync( HWND hwnd, INT cmd );
 BOOL    WINAPI NtUserSystemParametersInfo( UINT action, UINT val, void *ptr, UINT winini );
 BOOL    WINAPI NtUserSystemParametersInfoForDpi( UINT action, UINT val, PVOID ptr, UINT winini, UINT dpi );
 BOOL    WINAPI NtUserThunkedMenuInfo( HMENU menu, const MENUINFO *info );
+UINT    WINAPI NtUserThunkedMenuItemInfo( HMENU menu, UINT pos, UINT flags, UINT method,
+                                          MENUITEMINFOW *info, UNICODE_STRING *str );
 INT     WINAPI NtUserToUnicodeEx( UINT virt, UINT scan, const BYTE *state,
                                   WCHAR *str, int size, UINT flags, HKL layout );
 BOOL    WINAPI NtUserTrackMouseEvent( TRACKMOUSEEVENT *info );
-- 
GitLab


https://gitlab.winehq.org/wine/wine/-/merge_requests/18



More information about the wine-devel mailing list