Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Win32 API GetMenuItemInfo returns only the first character of the item text

Tags:

c#

pinvoke

winapi

I'm trying to gather the text of a menu item using GetMenuItemInfo API.

This is the code I'm using:

    [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    public static extern bool GetMenuItemInfo(IntPtr hMenu, uint uItem, bool fByPosition, ref MENUITEMINFO lpmii);

[StructLayout(LayoutKind.Sequential)]
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;

    // Return the size of the structure
    public static uint sizeOf
    {
        get { return (uint)Marshal.SizeOf(typeof(MENUITEMINFO)); }
    }
}

MENUITEMINFO mif = new MENUITEMINFO();
mif.cbSize = MENUITEMINFO.sizeOf;
mif.fMask = MIIM_STRING; 
mif.fType = MFT_STRING;
mif.dwTypeData = null;
bool res = Win32.GetMenuItemInfo(hMenu, (uint)0, true, ref mif); //To load cch into memory

mif.cch += 1;
mif.fMask = MIIM_STRING;
mif.fType = MFT_STRING;
mif.dwTypeData = new String(' ', (Int32)(mif.cch));

res = Win32.GetMenuItemInfo(hMenu, (uint)i, true, ref mif); //To fill dwTypeData

Unfortunately after those lines dwTypeData results a string with only 1 character instead of mif.cch characters. Note: this character is the first one of the menu item!

It seems that there's some misalignment in the marshaling of data when returning MENUITEMINFO structure, isn.t it?

Please let me know how to solve such issue!

Best

cghersi

like image 751
Cristiano Ghersi Avatar asked Aug 31 '25 20:08

Cristiano Ghersi


1 Answers

Your header translation is pretty good, but you've not got dwTypeData quite right. That cannot be marshalled from caller to callee using string. You need to do that with manual marshalling like this.

[DllImport("user32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern bool GetMenuItemInfo(IntPtr hMenu, int uItem, bool fByPosition, MENUITEMINFO lpmii);

[StructLayout(LayoutKind.Sequential)]
public class MENUITEMINFO 
{
    public int 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 IntPtr dwTypeData;
    public uint cch;
    public IntPtr hbmpItem;

    public MENUITEMINFO()
    {
        cbSize = Marshal.SizeOf(typeof(MENUITEMINFO));
    }
}

....

MENUITEMINFO mif = new MENUITEMINFO();
mif.fMask = MIIM_STRING; 
mif.fType = MFT_STRING;

mif.dwTypeData = IntPtr.Zero;
bool res = GetMenuItemInfo(hMenu, 0, true, mif);
if (!res)
    throw new Win32Exception();
mif.cch++;
mif.dwTypeData = Marshal.AllocHGlobal((IntPtr)(mif.cch*2));
try
{
    res = GetMenuItemInfo(hMenu, 0, true, mif);
    if (!res)
        throw new Win32Exception();
    string caption = Marshal.PtrToStringUni(mif.dwTypeData);
}
finally
{
    Marshal.FreeHGlobal(mif.dwTypeData);
}
like image 84
David Heffernan Avatar answered Sep 03 '25 10:09

David Heffernan