This patch was updated to use gdi_handle_t for gdi handles.

server: add calls to get/set menu item info
Add server calls and modify client menu item abstraction to use these calls

Thomas Kho

---

 dlls/user/menu.c               |  347 +++++++++++++++++++---------------------
 dlls/user/tests/menu.c         |    2 
 include/wine/server_protocol.h |  111 ++++++++++++-
 server/menu.c                  |  303 ++++++++++++++++++++++++++++++++++-
 server/protocol.def            |   77 ++++++++-
 server/request.h               |    8 +
 server/trace.c                 |   91 ++++++++++
 7 files changed, 723 insertions(+), 216 deletions(-)

diff --git a/dlls/user/menu.c b/dlls/user/menu.c
index 7cfd934..6a2f7d7 100644
--- a/dlls/user/menu.c
+++ b/dlls/user/menu.c
@@ -97,7 +97,6 @@ typedef struct {
     WORD	Height;       /* Height of the whole menu */
     UINT        nItems;       /* Number of items in the menu */
     HWND        hWnd;         /* Window containing the menu */
-    MENUITEM    *items;       /* Array of menu items */
     HWND	hwndOwner;    /* window receiving the messages for ownerdraw */
     UINT        nTotalHeight; /* Total height of menu items inside menu */
     /* ------------ MENUINFO members ------ */
@@ -317,7 +316,6 @@ static NTSTATUS MENU_GetMenu(HMENU hMenu
             menu->Height = reply->height;
             menu->nItems = reply->nitems;
             menu->hWnd = reply->hwnd;
-            menu->items = reply->items;
             menu->hwndOwner = reply->hwndowner;
             menu->nTotalHeight = reply->totalheight;
 
@@ -351,9 +349,7 @@ static NTSTATUS MENU_UpdateMenu(HMENU hM
         req->flags = menu->wFlags;
         req->width = menu->Width;
         req->height = menu->Height;
-        req->nitems = menu->nItems;
         req->hwnd = menu->hWnd;
-        req->items = menu->items;
         req->hwndowner = menu->hwndOwner;
         req->totalheight = menu->nTotalHeight;
 
@@ -374,78 +370,63 @@ static NTSTATUS MENU_UpdateMenu(HMENU hM
 }
 
 /***********************************************************************
- *           MENU_GetAllMenuItems
+ *           MENU_GetMenuItem
  *
- * Get a local copy of all menu items for given hMenu
- * TODO: slow, implement reference counting
+ * Get a local copy of menu item for given hMenu
  */
-static NTSTATUS MENU_GetAllMenuItems(HMENU hMenu, MENUITEM **item)
+static NTSTATUS MENU_GetMenuItem(HMENU hMenu, unsigned int pos, MENUITEM **item)
 {
-    int i;
-    unsigned int allocsize;
-    char *tmp;
-    POPUPMENU menu;
     NTSTATUS ret;
-    if ((ret = MENU_GetMenu(hMenu, &menu)))
-        return ret;
-    if (menu.nItems == 0)
-    {
-        *item = NULL;
-        return 0;
-    }
-    allocsize = sizeof(MENUITEM) * menu.nItems;
-    for (i = 0; i < menu.nItems; i++)
-        if (menu.items[i].text)
-            allocsize += sizeof(WCHAR)*(strlenW(menu.items[i].text)+1);
-    if (!(*item = HeapAlloc(GetProcessHeap(), 0, allocsize)))
-        return STATUS_NO_MEMORY;
-    memcpy(*item, menu.items, sizeof(MENUITEM) * menu.nItems);
+    unsigned int size = sizeof(MENUITEM) + 100; /* should generally be sufficient */
+    unsigned int required_size;
 
-    tmp = (char *) *item + menu.nItems * sizeof(MENUITEM);
-    for (i = 0; i < menu.nItems; i++)
+    while (1)
     {
-        if (menu.items[i].text)
+        *item = HeapAlloc(GetProcessHeap(), 0, size);
+        SERVER_START_REQ( get_menu_item_info )
         {
-            memcpy(tmp, menu.items[i].text,
-                   sizeof(WCHAR)*(strlenW(menu.items[i].text)+1));
-            (*item)[i].text = (WCHAR *) tmp;
-            tmp += sizeof(WCHAR)*(strlenW(menu.items[i].text)+1);
+            req->handle = hMenu;
+            req->pos = pos;
+            wine_server_set_reply(req, (char *) (*item) + sizeof(MENUITEM),
+                                  size - sizeof(MENUITEM));
+            if ((ret = wine_server_call_err( req )))
+                WARN("error hMenu=0x%x, status=%ld\n", (unsigned) hMenu, ret);
+
+            required_size = reply->required_size;
+
+            (*item)->fType = reply->type;
+            (*item)->fState = reply->state;
+            (*item)->wID = reply->id;
+            (*item)->hSubMenu = reply->submenu;
+            (*item)->hCheckBit = reply->checkbit;
+            (*item)->hUnCheckBit = reply->uncheckbit;
+            (*item)->text = reply->text;
+            (*item)->dwItemData = reply->itemdata;
+            (*item)->dwTypeData = reply->typedata;
+            (*item)->hbmpItem = reply->bmpitem;
+
+            (*item)->rect.left = reply->rect.left;
+            (*item)->rect.right = reply->rect.right;
+            (*item)->rect.top = reply->rect.top;
+            (*item)->rect.bottom = reply->rect.bottom;
+            (*item)->xTab = reply->xTab;
+            (*item)->bmpsize.cx = reply->bmpsize.x;
+            (*item)->bmpsize.cy = reply->bmpsize.y;
         }
-    }
-    return 0;
-}
+        SERVER_END_REQ;
 
-/***********************************************************************
- *           MENU_GetMenuItem
- *
- * Get a local copy of menu item for given hMenu
- */
-static NTSTATUS MENU_GetMenuItem(HMENU hMenu, unsigned int pos, MENUITEM **item)
-{
-    unsigned int allocsize;
-    POPUPMENU menu;
-    NTSTATUS ret;
-    if ((ret = MENU_GetMenu(hMenu, &menu)))
-        return ret;
-    if (pos >= menu.nItems)
-    {
-        *item = NULL;
-        return 0;
-    }
-    allocsize = sizeof(MENUITEM);
-    if (menu.items[pos].text)
-        allocsize += sizeof(WCHAR)*(strlenW(menu.items[pos].text)+1);
-    if (!(*item = HeapAlloc(GetProcessHeap(), 0, allocsize)))
-        return STATUS_NO_MEMORY;
-    memcpy(*item, &menu.items[pos], sizeof(MENUITEM));
-    if (menu.items[pos].text)
-    {
-        char *dest = (char *) *item + sizeof(MENUITEM);
-        memcpy(dest, menu.items[pos].text,
-               sizeof(WCHAR)*(strlenW(menu.items[pos].text)+1));
-        (*item)->text = (WCHAR *) dest;
+        if (required_size > size - sizeof(MENUITEM))
+        {
+            HeapFree(GetProcessHeap(), 0, *item);
+            size = required_size + sizeof(MENUITEM);
+        }
+        else
+            break;
     }
-    return 0;
+
+    if ((*item)->text)
+        (*item)->text = (LPWSTR) ((char *) (*item) + sizeof(MENUITEM));
+    return ret;
 }
 
 /***********************************************************************
@@ -456,33 +437,39 @@ static NTSTATUS MENU_GetMenuItem(HMENU h
 static NTSTATUS MENU_UpdateMenuItem(HMENU hMenu, unsigned int pos,
                                     MENUITEM *item)
 {
-    POPUPMENU menu;
     NTSTATUS ret;
-    LPWSTR oldtext;
-    if ((ret = MENU_GetMenu(hMenu, &menu)))
-        return ret;
-    oldtext = menu.items[pos].text;
-    memcpy(&menu.items[pos], item, sizeof(MENUITEM));
-    if (!item->text)
-    {
-        if (oldtext)
-            HeapFree(GetProcessHeap(), 0, oldtext);
-        return 0;
-    }
-    if (oldtext && !strcmpW(oldtext, item->text))
+    SERVER_START_REQ( set_menu_item_info )
     {
-        menu.items[pos].text = oldtext;
-        return 0;
+        req->handle = hMenu;
+        req->pos = pos;
+
+        req->type = item->fType;
+        req->state = item->fState;
+        req->id = item->wID;
+        req->submenu = item->hSubMenu;
+        req->checkbit = item->hCheckBit;
+        req->uncheckbit = item->hUnCheckBit;
+        req->text = item->text;
+        req->itemdata = item->dwItemData;
+        req->typedata = item->dwTypeData;
+        req->bmpitem = item->hbmpItem;
+
+        req->rect.left = item->rect.left;
+        req->rect.right = item->rect.right;
+        req->rect.top = item->rect.top;
+        req->rect.bottom = item->rect.bottom;
+        req->xTab = item->xTab;
+        req->bmpsize.x = item->bmpsize.cx;
+        req->bmpsize.y = item->bmpsize.cy;
+
+        if (item->text)
+            wine_server_add_data(req, item->text,
+                                 (lstrlenW(item->text)+1)*sizeof(WCHAR));
+        if ((ret = wine_server_call_err( req )))
+            WARN("error hMenu=0x%x, status=%ld\n", (unsigned) hMenu, ret);
     }
-    if (oldtext)
-        HeapFree(GetProcessHeap(), 0, oldtext);
-    menu.items[pos].text = HeapAlloc(GetProcessHeap(), 0,
-                                     sizeof(WCHAR)*(strlenW(item->text)+1));
-    if (!menu.items[pos].text)
-        return STATUS_NO_MEMORY;
-    memcpy(menu.items[pos].text, item->text,
-           sizeof(WCHAR)*(strlenW(item->text)+1));
-    return 0;
+    SERVER_END_REQ;
+    return ret;
 }
 
 /***********************************************************************
@@ -496,6 +483,69 @@ static NTSTATUS MENU_ReleaseMenuItem(MEN
 }
 
 /***********************************************************************
+ *           MENU_GetAllMenuItems
+ *
+ * Get a local copy of all menu items for given hMenu
+ */
+static NTSTATUS MENU_GetAllMenuItems(HMENU hMenu, MENUITEM **item)
+{
+    char *data;
+    unsigned int i, size;
+    MENUITEM *tmp, *mi;
+    POPUPMENU menu;
+
+    MENU_GetMenu(hMenu, &menu);
+
+    /* should generally be sufficient */
+    size = (sizeof(MENUITEM) + 20 * sizeof(WCHAR)) * menu.nItems;
+    if ((mi = HeapAlloc(GetProcessHeap(), 0, size)) == NULL)
+        return STATUS_NO_MEMORY;
+    data = (char *) mi + sizeof(MENUITEM) * menu.nItems;
+    for (i = 0; i < menu.nItems; i++)
+    {
+        NTSTATUS ret;
+        if ((ret = MENU_GetMenuItem(hMenu, i, &tmp)))
+        {
+            MENU_ReleaseMenuItem(tmp);
+            return ret;
+        }
+        memcpy(&mi[i], tmp, sizeof(MENUITEM));
+        if (tmp->text)
+        {
+            unsigned int len = (lstrlenW(tmp->text) + 1) * sizeof(WCHAR);
+            if (data + len > (char *) mi + size)
+            {
+                /* copy and make larger */
+                MENUITEM *newitem;
+                newitem = HeapAlloc(GetProcessHeap(), 0, size << 1);
+                if (newitem == NULL)
+                {
+                    MENU_ReleaseMenuItem(tmp);
+                    return STATUS_NO_MEMORY;
+                }
+                memcpy(newitem, mi, data - (char *) mi);
+                data = (char *) newitem + (data - (char *) mi);
+                HeapFree(GetProcessHeap(), 0, mi);
+                mi = newitem;
+                size = size << 1;
+            }
+            /* keep text locations as offsets in case of resize */
+            mi[i].text = (LPWSTR) (data - (char *) mi);
+            memcpy(data, tmp->text, len);
+            data = (char *) data + len;
+        }
+        MENU_ReleaseMenuItem(tmp);
+    }
+
+    for (i = 0; i < menu.nItems; i++) /* convert offsets to loactions */
+        if (mi[i].text)
+            mi[i].text = (LPWSTR) ((char *) mi + (int) mi[i].text);
+
+    *item = mi;
+    return 0;
+}
+
+/***********************************************************************
  *           get_win_sys_menu
  *
  * Get the system menu of a window
@@ -898,15 +948,6 @@ UINT MENU_FindSubMenu( HMENU *hmenu, HME
 }
 
 /***********************************************************************
- *           MENU_FreeItemData
- */
-static void MENU_FreeItemData( MENUITEM* item )
-{
-    /* delete text */
-    HeapFree( GetProcessHeap(), 0, item->text );
-}
-
-/***********************************************************************
  *           MENU_AdjustMenuItemRect
  *
  * Adjust menu item rectangle according to scrolling state.
@@ -2421,49 +2462,19 @@ static BOOL MENU_SetItemData( HMENU hmen
  */
 static UINT MENU_InsertItem( HMENU hMenu, UINT pos, UINT flags )
 {
-    MENUITEM *newItems;
-    POPUPMENU tmpmenu;
-    POPUPMENU *menu = &tmpmenu;
-
-    if (MENU_GetMenu(hMenu, menu))
-        return ITEM_NOT_FOUND;
-
-    /* Find where to insert new item */
-
-    if (flags & MF_BYPOSITION) {
-        if (pos > menu->nItems)
-            pos = menu->nItems;
-    } else {
-        if (MENU_FindItem( &hMenu, &pos, flags ) == ITEM_NOT_FOUND)
-            pos = menu->nItems;
-        else {
-            if (MENU_GetMenu(hMenu, menu))
-                return ITEM_NOT_FOUND;
-        }
-    }
-
-    /* Create new items array */
-
-    newItems = HeapAlloc( GetProcessHeap(), 0, sizeof(MENUITEM) * (menu->nItems+1) );
-    if (!newItems)
-    {
-        WARN("allocation failed\n" );
-        return ITEM_NOT_FOUND;
-    }
-    if (menu->nItems > 0)
+    NTSTATUS ret;
+    UINT itempos = ITEM_NOT_FOUND;
+    SERVER_START_REQ( insert_menu_item )
     {
-	  /* 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 );
+        req->handle = hMenu;
+        req->pos = pos;
+        req->flags = flags;
+        if ((ret = wine_server_call_err( req )))
+            WARN("error hMenu=0x%x, status=%ld\n", (unsigned) hMenu, ret);
+        itempos = reply->pos;
     }
-    menu->items = newItems;
-    menu->nItems++;
-    memset( &newItems[pos], 0, sizeof(*newItems) );
-    menu->Height = 0; /* force size recalculate */
-    MENU_UpdateMenu(hMenu, menu, SET_MI_ITEMS|SET_MI_NITEMS|SET_MI_HEIGHT);
-    return pos;
+    SERVER_END_REQ;
+    return itempos;
 }
 
 
@@ -4281,39 +4292,18 @@ BOOL WINAPI AppendMenuW( HMENU hMenu, UI
  */
 BOOL WINAPI RemoveMenu( HMENU hMenu, UINT nPos, UINT wFlags )
 {
-    POPUPMENU tmpmenu;
-    LPPOPUPMENU menu = &tmpmenu;
-    MENUITEM *item;
-    UINT pos;
-
+    NTSTATUS ret;
     TRACE("(menu=%p pos=%04x flags=%04x)\n",hMenu, nPos, wFlags);
-    if ((pos = MENU_FindItem( &hMenu, &nPos, wFlags )) == ITEM_NOT_FOUND)
-        return FALSE;
-    if (MENU_GetMenu(hMenu, menu)) return FALSE;
-    item = &menu->items[pos];
-
-      /* Remove item */
-
-    MENU_FreeItemData( item );
-
-    if (--menu->nItems == 0)
-    {
-        HeapFree( GetProcessHeap(), 0, menu->items );
-        menu->items = NULL;
-    }
-    else
+    SERVER_START_REQ( remove_menu_item )
     {
-	while(nPos < menu->nItems)
-	{
-	    *item = *(item+1);
-	    item++;
-	    nPos++;
-	}
-        menu->items = HeapReAlloc( GetProcessHeap(), 0, menu->items,
-                                   menu->nItems * sizeof(MENUITEM) );
+        req->handle = hMenu;
+        req->pos = nPos;
+        req->flags = wFlags;
+        if ((ret = wine_server_call_err( req )))
+            WARN("error hMenu=0x%x, status=%ld\n", (unsigned) hMenu, ret);
     }
-    MENU_UpdateMenu(hMenu, menu, SET_MI_NITEMS|SET_MI_ITEMS);
-    return TRUE;
+    SERVER_END_REQ;
+    return ret;
 }
 
 
@@ -4491,18 +4481,6 @@ BOOL WINAPI DestroyMenu( HMENU hMenu )
         lppop->hWnd = 0;
     }
 
-    if (lppop->items) /* recursively destroy submenus */
-    {
-        int i;
-        MENUITEM *item = lppop->items;
-        for (i = lppop->nItems; i > 0; i--, item++)
-        {
-            if (item->fType & MF_POPUP) DestroyMenu(item->hSubMenu);
-            MENU_FreeItemData( item );
-        }
-        HeapFree( GetProcessHeap(), 0, lppop->items );
-    }
-
     SERVER_START_REQ( destroy_menu )
     {
         req->handle = hMenu;
@@ -4512,6 +4490,7 @@ BOOL WINAPI DestroyMenu( HMENU hMenu )
     }
     SERVER_END_REQ;
 
+    /* Free local data */
     index = USER_HANDLE_TO_INDEX(hMenu);
     HeapFree( GetProcessHeap(), 0, user_handles[index]);
     user_handles[index] = NULL;
@@ -5394,13 +5373,11 @@ UINT WINAPI GetMenuDefaultItem(HMENU hme
 BOOL WINAPI InsertMenuItemA(HMENU hMenu, UINT uItem, BOOL bypos,
                                 const MENUITEMINFOA *lpmii)
 {
-    MENUITEM *item;
     UINT pos;
     POPUPMENU menu;
     MENUITEMINFOA mii;
     pos = MENU_InsertItem(hMenu, uItem, bypos ? MF_BYPOSITION : 0 );
     MENU_GetMenu(hMenu, &menu);
-    item = &menu.items[pos];
     if( lpmii->cbSize != sizeof( mii) &&
             lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
         SetLastError( ERROR_INVALID_PARAMETER);
@@ -5422,13 +5399,11 @@ BOOL WINAPI InsertMenuItemA(HMENU hMenu,
 BOOL WINAPI InsertMenuItemW(HMENU hMenu, UINT uItem, BOOL bypos,
                                 const MENUITEMINFOW *lpmii)
 {
-    MENUITEM *item;
     UINT pos;
     POPUPMENU menu;
     MENUITEMINFOW mii;
     pos = MENU_InsertItem(hMenu, uItem, bypos ? MF_BYPOSITION : 0 );
     MENU_GetMenu(hMenu, &menu);
-    item = &menu.items[pos];
     if( lpmii->cbSize != sizeof( mii) &&
             lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
         SetLastError( ERROR_INVALID_PARAMETER);
diff --git a/dlls/user/tests/menu.c b/dlls/user/tests/menu.c
index e715ae6..e76d6ad 100644
--- a/dlls/user/tests/menu.c
+++ b/dlls/user/tests/menu.c
@@ -1721,7 +1721,7 @@ static void test_menu_remote_process()
     mii.dwTypeData = malloc(++mii.cch);
     ok (GetMenuItemInfo(hMenu, 0, TRUE, &mii),
         "error=%ld, dwTypeData='%s'\n", GetLastError(), mii.dwTypeData);
-    todo_wine ok (mii.dwTypeData && !strcmp(mii.dwTypeData, "&File"),
+    ok (mii.dwTypeData && !strcmp(mii.dwTypeData, "&File"),
         "error=%ld, dwTypeData='%s'\n", GetLastError(), mii.dwTypeData);
     free(mii.dwTypeData);
 
diff --git a/include/wine/server_protocol.h b/include/wine/server_protocol.h
index 2bf621b..ae8907f 100644
--- a/include/wine/server_protocol.h
+++ b/include/wine/server_protocol.h
@@ -32,7 +32,7 @@ struct reply_header
 
 struct request_max_size
 {
-    int pad[21];
+    int pad[22];
 };
 
 typedef void *gdi_handle_t;
@@ -3500,6 +3500,92 @@ #define SET_GLOBAL_PROGMAN_WINDOW  0x02
 #define SET_GLOBAL_TASKMAN_WINDOW  0x04
 
 
+struct get_menu_item_info_request
+{
+    struct request_header __header;
+    user_handle_t  handle;
+    unsigned int   pos;
+};
+struct get_menu_item_info_reply
+{
+    struct reply_header __header;
+    unsigned int   required_size;
+
+    unsigned int type;
+    unsigned int state;
+    unsigned int id;
+    user_handle_t submenu;
+    user_handle_t checkbit;
+    user_handle_t uncheckbit;
+    void* text;
+    unsigned long itemdata;
+    void* typedata;
+    user_handle_t bmpitem;
+
+    rectangle_t rect;
+    unsigned int xTab;
+    dimension_t bmpsize;
+
+    /* VARARG(text,unicode_str); */
+};
+
+
+struct set_menu_item_info_request
+{
+    struct request_header __header;
+    user_handle_t  handle;
+    unsigned int   pos;
+
+    unsigned int type;
+    unsigned int state;
+    unsigned int id;
+    user_handle_t submenu;
+    user_handle_t checkbit;
+    user_handle_t uncheckbit;
+    void* text;
+    unsigned long itemdata;
+    void* typedata;
+    user_handle_t bmpitem;
+
+    rectangle_t rect;
+    unsigned int xTab;
+    dimension_t bmpsize;
+
+    /* VARARG(text,unicode_str); */
+};
+struct set_menu_item_info_reply
+{
+    struct reply_header __header;
+};
+
+
+struct insert_menu_item_request
+{
+    struct request_header __header;
+    user_handle_t  handle;
+    unsigned int   pos;
+    unsigned int   flags;
+};
+struct insert_menu_item_reply
+{
+    struct reply_header __header;
+    unsigned int   pos;
+};
+
+
+struct remove_menu_item_request
+{
+    struct request_header __header;
+    user_handle_t  handle;
+    unsigned int   pos;
+    unsigned int   flags;
+};
+struct remove_menu_item_reply
+{
+    struct reply_header __header;
+};
+
+
 struct get_menu_info_request
 {
     struct request_header __header;
@@ -3508,13 +3594,13 @@ struct get_menu_info_request
 struct get_menu_info_reply
 {
     struct reply_header __header;
+    unsigned int nitems;
+
     unsigned short flags;
     unsigned short magic;
     unsigned short width;
     unsigned short height;
-    unsigned int nitems;
     user_handle_t hwnd;
-    void* items;
     user_handle_t hwndowner;
     unsigned int totalheight;
 
@@ -3538,9 +3624,7 @@ struct set_menu_info_request
     unsigned short magic;
     unsigned short width;
     unsigned short height;
-    unsigned int nitems;
     user_handle_t hwnd;
-    void* items;
     user_handle_t hwndowner;
     unsigned int totalheight;
 
@@ -3587,8 +3671,7 @@ #define SET_MI_WIDTH       0x08000000
 #define SET_MI_HEIGHT      0x04000000
 #define SET_MI_TOTALHEIGHT 0x02000000
 #define SET_MI_MAXBMPSIZE  0x01000000
-#define SET_MI_ITEMS       0x00800000
-#define SET_MI_NITEMS      0x00400000
+
 
 
 struct adjust_token_privileges_request
@@ -4026,6 +4109,10 @@ enum request
     REQ_set_clipboard_info,
     REQ_open_token,
     REQ_set_global_windows,
+    REQ_get_menu_item_info,
+    REQ_set_menu_item_info,
+    REQ_insert_menu_item,
+    REQ_remove_menu_item,
     REQ_get_menu_info,
     REQ_set_menu_info,
     REQ_create_menu,
@@ -4250,6 +4337,10 @@ union generic_request
     struct set_clipboard_info_request set_clipboard_info_request;
     struct open_token_request open_token_request;
     struct set_global_windows_request set_global_windows_request;
+    struct get_menu_item_info_request get_menu_item_info_request;
+    struct set_menu_item_info_request set_menu_item_info_request;
+    struct insert_menu_item_request insert_menu_item_request;
+    struct remove_menu_item_request remove_menu_item_request;
     struct get_menu_info_request get_menu_info_request;
     struct set_menu_info_request set_menu_info_request;
     struct create_menu_request create_menu_request;
@@ -4472,6 +4563,10 @@ union generic_reply
     struct set_clipboard_info_reply set_clipboard_info_reply;
     struct open_token_reply open_token_reply;
     struct set_global_windows_reply set_global_windows_reply;
+    struct get_menu_item_info_reply get_menu_item_info_reply;
+    struct set_menu_item_info_reply set_menu_item_info_reply;
+    struct insert_menu_item_reply insert_menu_item_reply;
+    struct remove_menu_item_reply remove_menu_item_reply;
     struct get_menu_info_reply get_menu_info_reply;
     struct set_menu_info_reply set_menu_info_reply;
     struct create_menu_reply create_menu_reply;
@@ -4493,6 +4588,6 @@ union generic_reply
     struct query_symlink_reply query_symlink_reply;
 };
 
-#define SERVER_PROTOCOL_VERSION 239
+#define SERVER_PROTOCOL_VERSION 242
 
 #endif /* __WINE_WINE_SERVER_PROTOCOL_H */
diff --git a/server/menu.c b/server/menu.c
index f4d3c26..c9d38ba 100644
--- a/server/menu.c
+++ b/server/menu.c
@@ -37,16 +37,37 @@ #include "request.h"
 #include "user.h"
 #include "unicode.h"
 
+/* Menu item structure */
+struct menu_item
+{
+    WCHAR *text;              /* Item text. */
+    unsigned int textsize;
+
+    unsigned int type;        /* Item type. */
+    unsigned int state;       /* Item state.  */
+    unsigned int id;          /* Item id.  */
+    user_handle_t submenu;    /* Pop-up menu.  */
+    gdi_handle_t checkbit;    /* Bitmap when checked.  */
+    gdi_handle_t uncheckbit;  /* Bitmap when unchecked.  */
+    unsigned long itemdata;   /* Application defined.  */
+    WCHAR *typedata;          /* depends on fMask */
+    gdi_handle_t bmpitem;     /* bitmap */
+
+    rectangle_t rect;         /* Item area (relative to menu window) */
+    unsigned int xTab;        /* X position of text after Tab */
+    dimension_t bmpsize;      /* size needed for the HBMMENU_CALLBACK bitmap */
+};
+
 /* Popup menu structure */
 struct menu {
     user_handle_t  handle;
+    struct menu_item *items;
 
     unsigned short flags;      /* Menu flags (MF_POPUP, MF_SYSMENU) */
     unsigned short width;      /* Width of the whole menu */
     unsigned short height;     /* Height of the whole menu */
     unsigned int nitems;       /* Number of items in the menu */
     user_handle_t hwnd;        /* Window containing the menu */
-    void *items;               /* Array of menu items */
     user_handle_t hwndowner;   /* window receiving the messages for ownerdraw */
     unsigned int totalheight;  /* Total height of menu items inside menu */
 
@@ -63,6 +84,7 @@ struct menu {
 
 /* (other menu->FocusedItem values give the position of the focused item) */
 #define NO_SELECTED_ITEM  0xffff
+#define ITEM_NOT_FOUND 0xffff
 
 
 /* retrive a pointer to a menu given its handle */
@@ -95,13 +117,280 @@ static struct menu *create_menu()
 
 
 /* destroy a menu */
-static void destroy_menu(struct menu *menu)
+static void destroy_menu(user_handle_t handle)
 {
+    struct menu *menu;
+
+    if (!(menu = get_menu(handle)))
+        return;
+
+    if (menu->items) /* recursively destroy submenus */
+    {
+        int i;
+        struct menu_item *item = menu->items;
+        for (i = menu->nitems; i > 0; i--, item++)
+        {
+            if (item->type & MF_POPUP)
+                destroy_menu(item->submenu);
+            free(item->text);
+        }
+        free(menu->items);
+    }
+
     free_user_handle(menu->handle);
     free(menu);
 }
 
 
+/* Find a menu item. Return a pointer on the item, and modifies *hmenu
+ * in case the item was in a sub-menu. */
+static unsigned int find_item(user_handle_t *hmenu, unsigned int *nPos,
+                              unsigned int wFlags)
+{
+    struct menu *menu;
+    struct menu_item *fallback = NULL;
+    unsigned int fallback_pos = ITEM_NOT_FOUND;
+    unsigned int i;
+
+    if ((*hmenu == (HMENU)0xffff) || !(menu = get_menu(*hmenu)))
+        return ITEM_NOT_FOUND;
+    if (wFlags & MF_BYPOSITION)
+    {
+        if (*nPos >= menu->nitems) return ITEM_NOT_FOUND;
+        return *nPos;
+    }
+    else
+    {
+        struct menu_item *items;
+        struct menu_item *item;
+        items = menu->items;
+        item = items;
+        for (i = 0; i < menu->nitems; i++, item++)
+        {
+            if (item->type & MF_POPUP)
+            {
+                user_handle_t hsubmenu = item->submenu;
+                unsigned int subitem = find_item( &hsubmenu, nPos, wFlags );
+                if (subitem != ITEM_NOT_FOUND)
+                {
+                    *hmenu = hsubmenu;
+                    return subitem;
+                }
+                else if (item->id == *nPos)
+                {
+                    /* fallback to this item if nothing else found */
+                    fallback_pos = i;
+                    fallback = item;
+                }
+            }
+            else if (item->id == *nPos)
+            {
+                *nPos = i;
+                return i;
+            }
+        }
+    }
+
+    if (fallback)
+        *nPos = fallback_pos;
+
+    return fallback_pos;
+}
+
+
+unsigned int remove_item(user_handle_t hMenu, unsigned int nPos,
+                         unsigned int wFlags)
+{
+    struct menu *menu;
+    struct menu_item *item;
+    unsigned int pos;
+
+    if (!(menu = get_menu(hMenu)))
+        return FALSE;
+
+    if ((pos = find_item( &hMenu, &nPos, wFlags )) == ITEM_NOT_FOUND)
+        return FALSE;
+
+    item = &menu->items[pos];
+
+    free(item->text);
+
+    if (--menu->nitems == 0)
+    {
+        free(menu->items);
+        menu->items = NULL;
+    }
+    else
+    {
+        struct menu_item *newitem = mem_alloc(menu->nitems
+                                              * sizeof(struct menu_item));
+        memcpy(newitem, menu->items, nPos * sizeof(struct menu_item));
+        memcpy(newitem + nPos, menu->items + nPos + 1,
+               (menu->nitems - nPos) * sizeof(struct menu_item));
+        free(menu->items);
+        menu->items = newitem;
+    }
+    return TRUE;
+}
+
+
+/* Insert (allocate) a new item into a menu. */
+static unsigned int insert_item(user_handle_t handle, unsigned int pos,
+                                unsigned int flags)
+{
+    struct menu_item *newItems;
+    struct menu *menu;
+
+    if (!(menu = get_menu(handle)))
+        return ITEM_NOT_FOUND;
+
+    /* Find where to insert new item */
+
+    if (flags & MF_BYPOSITION)
+    {
+        if (pos > menu->nitems)
+            pos = menu->nitems;
+    }
+    else
+    {
+        if (find_item( &handle, &pos, flags ) == ITEM_NOT_FOUND)
+            pos = menu->nitems;
+        else if (!(menu = get_menu(handle)))
+                return ITEM_NOT_FOUND;
+    }
+
+    /* Create new items array */
+
+    newItems = mem_alloc(sizeof(struct menu_item) * (menu->nitems+1));
+    if (!newItems)
+    {
+        set_error( STATUS_NO_MEMORY );
+        return ITEM_NOT_FOUND;
+    }
+    if (menu->nitems > 0)
+    {
+        /* Copy the old array into the new one */
+        if (pos > 0)
+            memcpy( newItems, menu->items, pos * sizeof(struct menu_item) );
+        if (pos < menu->nitems)
+            memcpy( &newItems[pos+1], &menu->items[pos],
+                    (menu->nitems-pos)*sizeof(struct menu_item) );
+        free(menu->items);
+    }
+    menu->items = newItems;
+    menu->nitems++;
+    memset( &newItems[pos], 0, sizeof(*newItems) );
+    menu->height = 0; /* force size recalculate */
+    return pos;
+}
+
+
+/* validate the given menu handle and get menu item info */
+DECL_HANDLER(get_menu_item_info)
+{
+    const unsigned int pos = req->pos;
+    struct menu *menu;
+    struct menu_item *item;
+
+    if (!(menu = get_menu(req->handle)) || pos >= menu->nitems)
+    {
+        set_error( STATUS_INVALID_HANDLE );
+        return;
+    }
+
+    item = &menu->items[pos];
+
+    reply->required_size = 0;
+    if (item->textsize > get_reply_max_size())
+    {
+        reply->required_size = item->textsize;
+        return;
+    }
+
+    reply->type = item->type;
+    reply->state = item->state;
+    reply->id = item->id;
+    reply->submenu = item->submenu;
+    reply->checkbit = item->checkbit;
+    reply->uncheckbit = item->uncheckbit;
+    reply->text = (void *) item->textsize;
+    reply->itemdata = item->itemdata;
+    reply->typedata = item->typedata;
+    reply->bmpitem = item->bmpitem;
+
+    reply->rect = item->rect;
+    reply->xTab = item->xTab;
+    reply->bmpsize = item->bmpsize;
+
+    if (item->text)
+        if (!set_reply_data(item->text, item->textsize))
+        {
+            set_error( STATUS_NO_MEMORY );
+            return;
+        }
+}
+
+
+/* validate the given menu handle and set menu item info */
+DECL_HANDLER(set_menu_item_info)
+{
+    const unsigned int pos = req->pos;
+    int text_size;
+    struct menu *menu;
+    struct menu_item *item;
+
+    if (!(menu = get_menu(req->handle)) || pos >= menu->nitems)
+    {
+        set_error( STATUS_INVALID_HANDLE );
+        return;
+    }
+
+    item = &menu->items[pos];
+
+    item->type = req->type;
+    item->state = req->state;
+    item->id = req->id;
+    item->submenu = req->submenu;
+    item->checkbit = req->checkbit;
+    item->uncheckbit = req->uncheckbit;
+    item->itemdata = req->itemdata;
+    item->typedata = req->typedata;
+    item->bmpitem = req->bmpitem;
+
+    item->rect = req->rect;
+    item->xTab = req->xTab;
+    item->bmpsize = req->bmpsize;
+
+    if (item->text)
+        free(item->text);
+
+    text_size = get_req_data_size();
+    if (!text_size)
+    {
+        item->text = NULL;
+        item->textsize = 0;
+        return;
+    }
+    item->text = mem_alloc(text_size);
+    item->textsize = text_size;
+    memcpy(item->text, get_req_data(), text_size);
+}
+
+
+/* insert (allocate) a new item into a menu */
+DECL_HANDLER(insert_menu_item)
+{
+    reply->pos = insert_item(req->handle, req->pos, req->flags);;
+}
+
+
+/* remove a menu item */
+DECL_HANDLER(remove_menu_item)
+{
+    remove_item(req->handle, req->pos, req->flags);;
+}
+
+
 /* create a menu */
 DECL_HANDLER(create_menu)
 {
@@ -117,10 +406,7 @@ DECL_HANDLER(create_menu)
 /* destroy a menu */
 DECL_HANDLER(destroy_menu)
 {
-    struct menu *menu = get_menu(req->handle);
-
-    if (menu)
-        destroy_menu(menu);
+    destroy_menu(req->handle);
 }
 
 
@@ -137,7 +423,6 @@ DECL_HANDLER(get_menu_info)
     reply->height = menu->height;
     reply->nitems = menu->nitems;
     reply->hwnd = menu->hwnd;
-    reply->items = menu->items;
     reply->hwndowner = menu->hwndowner;
     reply->totalheight = menu->totalheight;
 
@@ -157,14 +442,12 @@ DECL_HANDLER(set_menu_info)
     struct menu *menu;
 
     if (!(menu = get_menu(req->handle)))
-        set_error( STATUS_INVALID_HANDLE );
+        return;
 
     if (req->mask & SET_MI_FLAGS) menu->flags = req->flags;
     if (req->mask & SET_MI_WIDTH) menu->width = req->width;
     if (req->mask & SET_MI_HEIGHT) menu->height = req->height;
-    if (req->mask & SET_MI_NITEMS) menu->nitems = req->nitems;
     if (req->mask & SET_MI_HWND) menu->hwnd = req->hwnd;
-    if (req->mask & SET_MI_ITEMS) menu->items = req->items;
     if (req->mask & SET_MI_OWNER) menu->hwndowner = req->hwndowner;
     if (req->mask & SET_MI_TOTALHEIGHT) menu->totalheight = req->totalheight;
 
diff --git a/server/protocol.def b/server/protocol.def
index 27f27ad..a6318d2 100644
--- a/server/protocol.def
+++ b/server/protocol.def
@@ -48,7 +48,7 @@ struct reply_header
 /* this is used to construct the generic_request union */
 struct request_max_size
 {
-    int pad[21]; /* the max request size is 21 ints */
+    int pad[22]; /* the max request size is 22 ints */
 };
 
 typedef void *gdi_handle_t;
@@ -2464,17 +2464,83 @@ #define SET_GLOBAL_SHELL_WINDOWS   0x01 
 #define SET_GLOBAL_PROGMAN_WINDOW  0x02
 #define SET_GLOBAL_TASKMAN_WINDOW  0x04
 
+/* Get menu item info */
+@REQ(get_menu_item_info)
+    user_handle_t  handle;
+    unsigned int   pos;
+@REPLY
+    unsigned int   required_size;
+
+    unsigned int type;
+    unsigned int state;
+    unsigned int id;
+    user_handle_t submenu;
+    user_handle_t checkbit;
+    user_handle_t uncheckbit;
+    void* text;
+    unsigned long itemdata;
+    void* typedata;
+    user_handle_t bmpitem;
+
+    rectangle_t rect;
+    unsigned int xTab;
+    dimension_t bmpsize;
+
+    VARARG(text,unicode_str);
+@END
+
+/* Set menu item info */
+@REQ(set_menu_item_info)
+    user_handle_t  handle;
+    unsigned int   pos;
+
+    unsigned int type;
+    unsigned int state;
+    unsigned int id;
+    user_handle_t submenu;
+    user_handle_t checkbit;
+    user_handle_t uncheckbit;
+    void* text;
+    unsigned long itemdata;
+    void* typedata;
+    user_handle_t bmpitem;
+
+    rectangle_t rect;
+    unsigned int xTab;
+    dimension_t bmpsize;
+
+    VARARG(text,unicode_str);
+@REPLY
+@END
+
+/* Insert menu item */
+@REQ(insert_menu_item)
+    user_handle_t  handle;
+    unsigned int   pos;
+    unsigned int   flags;
+@REPLY
+    unsigned int   pos;
+@END
+
+/* Remove menu item */
+@REQ(remove_menu_item)
+    user_handle_t  handle;
+    unsigned int   pos;
+    unsigned int   flags;
+@REPLY
+@END
+
 /* Get menu info */
 @REQ(get_menu_info)
     user_handle_t  handle;
 @REPLY
+    unsigned int nitems;
+
     unsigned short flags;
     unsigned short magic;
     unsigned short width;
     unsigned short height;
-    unsigned int nitems;
     user_handle_t hwnd;
-    void* items;
     user_handle_t hwndowner;
     unsigned int totalheight;
 
@@ -2496,9 +2562,7 @@ #define SET_GLOBAL_TASKMAN_WINDOW  0x04
     unsigned short magic;
     unsigned short width;
     unsigned short height;
-    unsigned int nitems;
     user_handle_t hwnd;
-    void* items;
     user_handle_t hwndowner;
     unsigned int totalheight;
 
@@ -2532,8 +2596,7 @@ #define SET_MI_WIDTH       0x08000000
 #define SET_MI_HEIGHT      0x04000000
 #define SET_MI_TOTALHEIGHT 0x02000000
 #define SET_MI_MAXBMPSIZE  0x01000000
-#define SET_MI_ITEMS       0x00800000
-#define SET_MI_NITEMS      0x00400000
+/* above are in conjunction with MIIM_*, which has a maximum value of 0x100 */
 
 /* Adjust the privileges held by a token */
 @REQ(adjust_token_privileges)
diff --git a/server/request.h b/server/request.h
index 9baa1fd..9724d5a 100644
--- a/server/request.h
+++ b/server/request.h
@@ -308,6 +308,10 @@ DECL_HANDLER(set_class_info);
 DECL_HANDLER(set_clipboard_info);
 DECL_HANDLER(open_token);
 DECL_HANDLER(set_global_windows);
+DECL_HANDLER(get_menu_item_info);
+DECL_HANDLER(set_menu_item_info);
+DECL_HANDLER(insert_menu_item);
+DECL_HANDLER(remove_menu_item);
 DECL_HANDLER(get_menu_info);
 DECL_HANDLER(set_menu_info);
 DECL_HANDLER(create_menu);
@@ -531,6 +535,10 @@ static const req_handler req_handlers[RE
     (req_handler)req_set_clipboard_info,
     (req_handler)req_open_token,
     (req_handler)req_set_global_windows,
+    (req_handler)req_get_menu_item_info,
+    (req_handler)req_set_menu_item_info,
+    (req_handler)req_insert_menu_item,
+    (req_handler)req_remove_menu_item,
     (req_handler)req_get_menu_info,
     (req_handler)req_set_menu_info,
     (req_handler)req_create_menu,
diff --git a/server/trace.c b/server/trace.c
index 4a05044..7504c08 100644
--- a/server/trace.c
+++ b/server/trace.c
@@ -3049,6 +3049,80 @@ static void dump_set_global_windows_repl
     fprintf( stderr, " old_taskman_window=%p", req->old_taskman_window );
 }
 
+static void dump_get_menu_item_info_request( const struct get_menu_item_info_request *req )
+{
+    fprintf( stderr, " handle=%p,", req->handle );
+    fprintf( stderr, " pos=%08x", req->pos );
+}
+
+static void dump_get_menu_item_info_reply( const struct get_menu_item_info_reply *req )
+{
+    fprintf( stderr, " required_size=%08x,", req->required_size );
+    fprintf( stderr, " type=%08x,", req->type );
+    fprintf( stderr, " state=%08x,", req->state );
+    fprintf( stderr, " id=%08x,", req->id );
+    fprintf( stderr, " submenu=%p,", req->submenu );
+    fprintf( stderr, " checkbit=%p,", req->checkbit );
+    fprintf( stderr, " uncheckbit=%p,", req->uncheckbit );
+    fprintf( stderr, " text=%p,", req->text );
+    fprintf( stderr, " itemdata=%lx,", req->itemdata );
+    fprintf( stderr, " typedata=%p,", req->typedata );
+    fprintf( stderr, " bmpitem=%p,", req->bmpitem );
+    fprintf( stderr, " rect=" );
+    dump_rectangle( &req->rect );
+    fprintf( stderr, "," );
+    fprintf( stderr, " xTab=%08x,", req->xTab );
+    fprintf( stderr, " bmpsize=" );
+    dump_dimension( &req->bmpsize );
+    fprintf( stderr, "," );
+    fprintf( stderr, " text=" );
+    dump_varargs_unicode_str( cur_size );
+}
+
+static void dump_set_menu_item_info_request( const struct set_menu_item_info_request *req )
+{
+    fprintf( stderr, " handle=%p,", req->handle );
+    fprintf( stderr, " pos=%08x,", req->pos );
+    fprintf( stderr, " type=%08x,", req->type );
+    fprintf( stderr, " state=%08x,", req->state );
+    fprintf( stderr, " id=%08x,", req->id );
+    fprintf( stderr, " submenu=%p,", req->submenu );
+    fprintf( stderr, " checkbit=%p,", req->checkbit );
+    fprintf( stderr, " uncheckbit=%p,", req->uncheckbit );
+    fprintf( stderr, " text=%p,", req->text );
+    fprintf( stderr, " itemdata=%lx,", req->itemdata );
+    fprintf( stderr, " typedata=%p,", req->typedata );
+    fprintf( stderr, " bmpitem=%p,", req->bmpitem );
+    fprintf( stderr, " rect=" );
+    dump_rectangle( &req->rect );
+    fprintf( stderr, "," );
+    fprintf( stderr, " xTab=%08x,", req->xTab );
+    fprintf( stderr, " bmpsize=" );
+    dump_dimension( &req->bmpsize );
+    fprintf( stderr, "," );
+    fprintf( stderr, " text=" );
+    dump_varargs_unicode_str( cur_size );
+}
+
+static void dump_insert_menu_item_request( const struct insert_menu_item_request *req )
+{
+    fprintf( stderr, " handle=%p,", req->handle );
+    fprintf( stderr, " pos=%08x,", req->pos );
+    fprintf( stderr, " flags=%08x", req->flags );
+}
+
+static void dump_insert_menu_item_reply( const struct insert_menu_item_reply *req )
+{
+    fprintf( stderr, " pos=%08x", req->pos );
+}
+
+static void dump_remove_menu_item_request( const struct remove_menu_item_request *req )
+{
+    fprintf( stderr, " handle=%p,", req->handle );
+    fprintf( stderr, " pos=%08x,", req->pos );
+    fprintf( stderr, " flags=%08x", req->flags );
+}
+
 static void dump_get_menu_info_request( const struct get_menu_info_request *req )
 {
     fprintf( stderr, " handle=%p", req->handle );
@@ -3056,13 +3130,12 @@ static void dump_get_menu_info_request( 
 
 static void dump_get_menu_info_reply( const struct get_menu_info_reply *req )
 {
+    fprintf( stderr, " nitems=%08x,", req->nitems );
     fprintf( stderr, " flags=%04x,", req->flags );
     fprintf( stderr, " magic=%04x,", req->magic );
     fprintf( stderr, " width=%04x,", req->width );
     fprintf( stderr, " height=%04x,", req->height );
-    fprintf( stderr, " nitems=%08x,", req->nitems );
     fprintf( stderr, " hwnd=%p,", req->hwnd );
-    fprintf( stderr, " items=%p,", req->items );
     fprintf( stderr, " hwndowner=%p,", req->hwndowner );
     fprintf( stderr, " totalheight=%08x,", req->totalheight );
     fprintf( stderr, " style=%lx,", req->style );
@@ -3083,9 +3156,7 @@ static void dump_set_menu_info_request( 
     fprintf( stderr, " magic=%04x,", req->magic );
     fprintf( stderr, " width=%04x,", req->width );
     fprintf( stderr, " height=%04x,", req->height );
-    fprintf( stderr, " nitems=%08x,", req->nitems );
     fprintf( stderr, " hwnd=%p,", req->hwnd );
-    fprintf( stderr, " items=%p,", req->items );
     fprintf( stderr, " hwndowner=%p,", req->hwndowner );
     fprintf( stderr, " totalheight=%08x,", req->totalheight );
     fprintf( stderr, " style=%lx,", req->style );
@@ -3530,6 +3601,10 @@ static const dump_func req_dumpers[REQ_N
     (dump_func)dump_set_clipboard_info_request,
     (dump_func)dump_open_token_request,
     (dump_func)dump_set_global_windows_request,
+    (dump_func)dump_get_menu_item_info_request,
+    (dump_func)dump_set_menu_item_info_request,
+    (dump_func)dump_insert_menu_item_request,
+    (dump_func)dump_remove_menu_item_request,
     (dump_func)dump_get_menu_info_request,
     (dump_func)dump_set_menu_info_request,
     (dump_func)dump_create_menu_request,
@@ -3750,6 +3825,10 @@ static const dump_func reply_dumpers[REQ
     (dump_func)dump_set_clipboard_info_reply,
     (dump_func)dump_open_token_reply,
     (dump_func)dump_set_global_windows_reply,
+    (dump_func)dump_get_menu_item_info_reply,
+    (dump_func)0,
+    (dump_func)dump_insert_menu_item_reply,
+    (dump_func)0,
     (dump_func)dump_get_menu_info_reply,
     (dump_func)0,
     (dump_func)dump_create_menu_reply,
@@ -3970,6 +4049,10 @@ static const char * const req_names[REQ_
     "set_clipboard_info",
     "open_token",
     "set_global_windows",
+    "get_menu_item_info",
+    "set_menu_item_info",
+    "insert_menu_item",
+    "remove_menu_item",
     "get_menu_info",
     "set_menu_info",
     "create_menu",

