我尝试使用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
答案 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);
}
}