Win32 API GetMenuItemInfo仅返回项文本的第一个字符

时间:2014-04-04 02:32:05

标签: c# winapi pinvoke

我尝试使用GetMenuItemInfo API收集菜单项的文本。

这是我使用的代码:

    [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

不幸的是,在这些行之后,dwTypeData会产生一个只包含1个字符而不是mif.cch字符的字符串。注意:这个字符是菜单项的第一个!

当返回MENUITEMINFO结构时,似乎在数据编组中存在一些错位,不是吗?

请让我知道如何解决这个问题!

最佳

cghersi

2 个答案:

答案 0 :(得分:3)

您的标题翻译非常好,但您没有dwTypeData完全正确。无法使用string从调用者到被调用者编组。你需要这样做手动编组。

[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);
}

答案 1 :(得分:1)

更新2

使用反射在System.Windows.Forms.MenuItem中找到的代码。似乎他们也使用字符串。

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public class MENUITEMINFO_T
{
    public int cbSize = Marshal.SizeOf(typeof(NativeMethods.MENUITEMINFO_T));
    public int fMask;
    public int fType;
    public int fState;
    public int wID;
    public IntPtr hSubMenu;
    public IntPtr hbmpChecked;
    public IntPtr hbmpUnchecked;
    public IntPtr dwItemData;
    public string dwTypeData;
    public int cch;
}

[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern bool GetMenuItemInfo(HandleRef hMenu, int uItem, bool fByPosition, [In, Out] MENUITEMINFO_T lpmii);

  ................

  MENUITEMINFO_T menuiteminfo_t;
  menuiteminfo_t = new MENUITEMINFO_T();
  menuiteminfo_t.fMask = MIIM_STRING;
  menuiteminfo_t.dwTypeData = new string('\0', 256);
  menuiteminfo_t.cch = menuiteminfo_t.dwTypeData.Length - 1;
  bool result = GetMenuItemInfo(new HandleRef(null, hMenu), 0, true, menuiteminfo_t);

更新1

正如@Remy Lebeau所说,String marshaling与char []编组之间存在差异。在我将CharSet=CharSet.Auto属性添加到结构后,使用您的代码一切正常。

[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;

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


        IntPtr hWnd = FindWindow(null, "untitled - notepad");
        IntPtr hMenu = GetMenu(hWnd);


        MENUITEMINFO mif = new MENUITEMINFO();
        uint MIIM_STRING = 0x00000040;
        uint MFT_STRING = 0x00000000;
        mif.cbSize = MENUITEMINFO.sizeOf;//(uint)Marshal.SizeOf(typeof(MENUITEMINFO));
        mif.fMask = MIIM_STRING;
        mif.fType = MFT_STRING;
        mif.dwTypeData = null;
        //IntPtr hMenu = menuStrip1.Handle;



        //1st call to get the text length into (cch)
        bool res = GetMenuItemInfo(hMenu, (uint)0, true, ref mif); //To load cch into memory
        if (res)
        {
            mif.cch += 1;
            //Set the length of the buffer to cch + 1
            mif.dwTypeData = new string(' ', (int)(mif.cch));
            res = GetMenuItemInfo(hMenu, (uint)0, true, ref mif); //To fill dwTypeData

        }

查看此链接,了解有关Default Marshaling for Strings

的更多详情

OLD:

您需要初始化dwTypeData和cch数据成员。

获取菜单项字符串示例(使用新的MENUITEMINFO结构):

List<string> ls = new List<string>();
IntPtr hMenu = Win32.GetMenu(hWnd);

if (hMenu.ToInt32() != 0)
{
    for (int i = Win32.GetMenuItemCount(hMenu); i >= 0; i--)
    {
        uint MIIM_STRING = 0x00000040;
        uint MFT_STRING  = 0x00000000;
        Win32.MENUITEMINFO mif = new Win32.MENUITEMINFO();
        mif.cbSize = (uint)Marshal.SizeOf(typeof(Win32.MENUITEMINFO));
        mif.fMask = MIIM_STRING;
        mif.fType = MFT_STRING;
        mif.cch = 256;
        mif.dwTypeData = new string(' ', (int)(mif.cch));

        bool a = Win32.GetMenuItemInfo(hMenu, 0, true, ref mif);
        ls.Add(mif.dwTypeData);
    }
}