用C#和本地Windows API操纵系统菜单
【IT168 技术文档】
注意:如果指定多个标志,应该用位操作运算符|(或)连接。例如: 四、SystemMenu 类代码分析
using System;
using System.Windows.Forms;
using System.Diagnostics;
using System.Runtime.InteropServices;

public class NoSystemMenuException : System.Exception...{}
public enum ItemFlags

...{
// The item ...
mfUnchecked = 0x00000000, // ... is not checked
mfString = 0x00000000, // ... contains a string as label
mfDisabled = 0x00000002, // ... is disabled
mfGrayed = 0x00000001, // ... is grayed
mfChecked = 0x00000008, // ... is checked
mfPopup = 0x00000010, // ... Is a popup menu. Pass the
// menu handle of the popup
// menu into the ID parameter.
mfBarBreak = 0x00000020, // ... is a bar break
mfBreak = 0x00000040, // ... is a break
mfByPosition = 0x00000400, // ... is identified by the position
mfByCommand = 0x00000000, // ... is identified by its ID
mfSeparator = 0x00000800 // ... is a seperator (String and
// ID parameters are ignored).
}
public enum WindowMessages

...{
wmSysCommand = 0x0112
}
// 帮助实现操作系统菜单的类的定义
//注意:用P/Invoke调用动态链接库中非托管函数时,应执行如下步骤:
//1,定位包含该函数的DLL。
//2,把该DLL库装载入内存。
//3,找到即将调用的函数地址,并将所有的现场压入堆栈。
//4,调用函数。
public class SystemMenu

...{
// 提示:C#把函数声明为外部的,而且使用属性DllImport来指定DLL
//和任何其他可能需要的参数。
// 首先,我们需要GetSystemMenu() 函数
// 注意这个函数没有Unicode 版本
[DllImport("USER32", EntryPoint="GetSystemMenu", SetLastError=true,
CharSet=CharSet.Unicode, ExactSpelling=true,
CallingConvention=CallingConvention.Winapi)]
private static extern IntPtr apiGetSystemMenu(IntPtr WindowHandle,
int bReset);
// 还需要AppendMenu()。 既然 .net 使用Unicode,
// 我们应该选取它的Unicode版本。
[DllImport("USER32", EntryPoint="AppendMenuW", SetLastError=true,
CharSet=CharSet.Unicode, ExactSpelling=true,
CallingConvention=CallingConvention.Winapi)]
private static extern int apiAppendMenu( IntPtr MenuHandle, int Flags,
int NewID, String Item );
//还可能需要InsertMenu()
[DllImport("USER32", EntryPoint="InsertMenuW", SetLastError=true,
CharSet=CharSet.Unicode, ExactSpelling=true,
CallingConvention=CallingConvention.Winapi)]
private static extern int apiInsertMenu ( IntPtr hMenu, int Position,int Flags,
int NewId,String Item );
private IntPtr m_SysMenu = IntPtr.Zero; // 系统菜单句柄
public SystemMenu( )

...{}
// 在给定的位置(以0为索引开始值)插入一个分隔条
public bool InsertSeparator ( int Pos )

...{
return ( InsertMenu(Pos, ItemFlags.mfSeparator |ItemFlags.mfByPosition, 0, "") );
}
// 简化的InsertMenu(),前提――Pos参数是一个0开头的相对索引位置
public bool InsertMenu ( int Pos, int ID, String Item )

...{
return ( InsertMenu(Pos, ItemFlags.mfByPosition |ItemFlags.mfString, ID, Item) );
}
// 在给定位置插入一个菜单项。具体插入的位置取决于Flags
public bool InsertMenu ( int Pos, ItemFlags Flags, int ID, String Item )

...{
return ( apiInsertMenu(m_SysMenu, Pos, (Int32)Flags, ID, Item) == 0);
}
// 添加一个分隔条
public bool AppendSeparator ( )

...{
return AppendMenu(0, "", ItemFlags.mfSeparator);
}
// 使用ItemFlags.mfString 作为缺省值
public bool AppendMenu ( int ID, String Item )

...{
return AppendMenu(ID, Item, ItemFlags.mfString);
}
// 被取代的函数
public bool AppendMenu ( int ID, String Item, ItemFlags Flags )

...{
return ( apiAppendMenu(m_SysMenu, (int)Flags, ID, Item) == 0 );
}
//从一个Form对象检索一个新对象
public static SystemMenu FromForm ( Form Frm )

...{
SystemMenu cSysMenu = new SystemMenu();
cSysMenu.m_SysMenu = apiGetSystemMenu(Frm.Handle, 0);
if ( cSysMenu.m_SysMenu == IntPtr.Zero )

...{
// 一旦失败,引发一个异常
throw new NoSystemMenuException();
}
return cSysMenu;
}
// 当前窗口菜单还原 public static void ResetSystemMenu ( Form Frm )

...{
apiGetSystemMenu(Frm.Handle, 1);
}
// 检查是否一个给定的ID在系统菜单ID范围之内
public static bool VerifyItemID ( int ID )

...{
return (bool)( ID < 0xF000 && ID > 0 );
}
}
五、使用SystemMenu类
// SystemMenu 对象 private SystemMenu m_SystemMenu = null; // ID 常数定义 private const int m_AboutID = 0x100; private const int m_ResetID = 0x101; private void frmMain_Load(object sender, System.EventArgs e) { try { m_SystemMenu = SystemMenu.FromForm(this); // 添加一个separator ... m_SystemMenu.AppendSeparator(); // 添加"关于" 菜单项 m_SystemMenu.AppendMenu(m_AboutID, "关于"); // 在菜单顶部加上"复位"菜单项 m_SystemMenu.InsertSeparator(0); m_SystemMenu.InsertMenu(0, m_ResetID, "复位系统菜单"); } catch ( NoSystemMenuException /* err */ ) { // 建立你的错误处理器 } }
protected override void WndProc ( ref Message msg ) { base.WndProc(ref msg); }
protected override void WndProc ( ref Message msg ) { // 通过截取WM_SYSCOMMAND消息并进行处理 // 注意,消息WM_SYSCOMMAND被定义在WindowMessages枚举类中 // 消息的WParam参数包含点击的项的ID // 该值与通过上面类的InsertMenu()或AppendMenu()成员函数传递的一样 if ( msg.Msg == (int)WindowMessages.wmSysCommand ) { switch ( msg.WParam.ToInt32() ) { case m_ResetID: // reset菜单项的ID { if ( MessageBox.Show(this, "\tAre you sure?","Question", MessageBoxButtons.YesNo) == DialogResult.Yes ) { // 复位系统菜单 SystemMenu.ResetSystemMenu(this); } } break; case m_AboutID: { // “关于”菜单项 MessageBox.Show(this, "作者: 朱先中 \n\n "+"e-mail: sdmyzxz@163.com", "关于"); } break; // 这里可以针对另外的菜单项设计处理过程 } } // 调用基类函数 base.WndProc(ref msg); }