Call GetMenuItemInfo returns 87

0

I have programming a new program where I can get shell context menu of the file(s).

But I have a problem when I call win32 api function GetMenuItemInfo. This method returns me false as result and when I immediately call Marshall.GetLastWin32Error then I got the error code 87.

This is my code:

int count = Win32APICaller.GetMenuItemCount(pMenu);

MENUITEMINFO mif = new MENUITEMINFO();
uint MIIM_STRING = 0x00000040;
uint MFT_STRING = 0x00000000;
mif.fMask = MIIM_STRING;
mif.fType = MFT_STRING;
mif.dwTypeData = null;
mif.cbSize = Marshal.SizeOf(mif);

bool result = Win32APICaller.GetMenuItemInfo(pMenu, 1, true, ref mif);

int errorCode = Marshal.GetLastWin32Error();

And this is my MENUITEMINFO structure:

public long cbSize;
public long fMask;
public long fType;
public uint fState;
public uint wID;
public IntPtr hSubMenu;
public IntPtr hbmpChecked;
public IntPtr hbmpUnchecked;
public IntPtr dwItemData;
public string dwTypeData;
public uint cch;
public IntPtr hbmpItem;

I don't know where I am an error.

c#
winapi
asked on Stack Overflow Aug 21, 2020 by xasd89 • edited Aug 21, 2020 by Uwe Keim

1 Answer

0

As the comment pointed out, according to the MENUITEMINFO, the struct in c++ is:

typedef struct tagMENUITEMINFOA {
  UINT      cbSize;
  UINT      fMask;
  UINT      fType;
  UINT      fState;
  UINT      wID;
  HMENU     hSubMenu;
  HBITMAP   hbmpChecked;
  HBITMAP   hbmpUnchecked;
  ULONG_PTR dwItemData;
  LPSTR     dwTypeData;
  UINT      cch;
  HBITMAP   hbmpItem;
} MENUITEMINFOA, *LPMENUITEMINFOA;

in C#, the size of uint is not equal to long: enter image description here

So you will need to replace the long with the uint. Then the size of MENUITEMINFO is 48 in 32-bit and 80 in 64-bit.

In addition, you will need to first obtain mif.cch to get the string length, and then assign mif.cch+1 size (add terminator size) to mif.dwTypeData

        bool result = GetMenuItemInfo(pMenu, 1, true, ref mif);
        if (result)
        {
            mif.cch += 1;
            //Set the length of the buffer to cch + 1
            mif.dwTypeData = new string(' ', (int)(mif.cch));
            result = GetMenuItemInfo(pMenu, 1, true, ref mif); //To fill dwTypeData
        }

To ensure uniform character, use auto charset:

    [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Auto)]
    public struct MENUITEMINFO
    {
        public uint cbSize;
        public uint fMask;
        public uint fType;
        public uint fState;
        public uint wID;
        public IntPtr hSubMenu;
        public IntPtr hbmpChecked;
        public IntPtr hbmpUnchecked;
        public IntPtr dwItemData;
        public String dwTypeData;
        public uint cch;
        public IntPtr hbmpItem;
    }

Finally, MFT_STRING is replaced by MIIM_STRING. You only need to specify MIIM_STRING. Sample:

        ContextMenu cm = new ContextMenu();
        cm.MenuItems.Add("Item 1");
        cm.MenuItems.Add("Item 2");
        IntPtr pMenu = cm.Handle;
        int count = GetMenuItemCount(pMenu);
        MENUITEMINFO mif = new MENUITEMINFO();
        uint MIIM_STRING = 0x00000040;
        //uint MFT_STRING = 0x00000000;
        mif.fMask = MIIM_STRING;
        //mif.fType = MFT_STRING;
        mif.dwTypeData = null;
        mif.cbSize = (uint)Marshal.SizeOf(mif);
        bool result = GetMenuItemInfo(pMenu, 1, true, ref mif);
        if (result)
        {
            mif.cch += 1;
            mif.dwTypeData = new string(' ', (int)(mif.cch));
            result = GetMenuItemInfo(pMenu, 1, true, ref mif); 
        }
answered on Stack Overflow Aug 24, 2020 by Drake Wu

User contributions licensed under CC BY-SA 3.0