ウィンドウメニューサンプル
で、ウィンドウメニューがいじれるようになったので、サンプルを作りました。
/// <summary> /// ウィンドウメニュー操作用クラス。 /// </summary> internal class RuleWindowMenu { #region MENUITEMINFO.fMask の値 /// <summary>fState</summary> const int MIIM_STATE = 0x00000001; /// <summary>wID</summary> const int MIIM_ID = 0x00000002; /// <summary>hSubMenu</summary> const int MIIM_SUBMENU = 0x00000004; /// <summary>hbmpChecked, hbmpUnchecked</summary> const int MIIM_CHECKMARKS = 0x00000008; /// <summary>fType(後方互換用)</summary> const int MIIM_TYPE = 0x00000010; /// <summary>dwItemData</summary> const int MIIM_DATA = 0x00000020; /// <summary>dwTypeData</summary> const int MIIM_STRING = 0x00000040; /// <summary>hbmpItem</summary> const int MIIM_BITMAP = 0x00000080; /// <summary>fType</summary> const int MIIM_FTYPE = 0x00000100; #endregion #region MENUITEMINFO.fType の値 /// <summary>後方互換用</summary> const int MFT_STRING = 0x00000000; /// <summary>後方互換用</summary> const int MFT_BITMAP = 0x00000004; /// <summary> /// メニューバーに対しはメニューアイテムを新しい行に配置する。ドロップダ /// ウンメニュー・サブメニュー・ショートカットメニューに対してはメニュー /// アイテムを新しい列に配置する。ドロップダウンメニュー・サブメニュー・ /// ショートカットメニューでは新しい列との間に線が引かれる。 /// </summary> const int MFT_MENUBARBREAK = 0x00000010; /// <summary> /// メニューバーに対してはメニューアイテムを新しい行に配置する。ドロップ /// ダウンメニュー・サブメニュー・ショートカットメニューに対してはメニュ /// ーアイテムを新しい列に配置する。ドロップダウンメニュー・サブメニュー /// ・ショートカットメニューでは新しい列との間には線は引かれない。 /// </summary> const int MFT_MENUBREAK = 0x00000020; /// <summary> /// オーナードロー・メニューアイテムであることを示す。メニューアイテムの /// 描画をオーナーウィンドウが行なう。オーナーウィンドウは /// WM_MEASUREITEMメッセージおよびWM_DRAWITEMメッセージを処理する必要が /// ある。この値が指定された場合は、dwTypeData はアプリケーション定義値 /// を表す。 /// </summary> const int MFT_OWNERDRAW = 0x00000100; /// <summary> // hbmpChecked メンバが 0 (NULL) のとき、チェックマークの代わりにラジオ // ボタンを表示する。 /// </summary> const int MFT_RADIOCHECK = 0x00000200; /// <summary>セパレーター(区切り線)を表示する。</summary> const int MFT_SEPARATOR = 0x00000800; /// <summary> /// アラビア語やヘブライ語などのシステムで文字列を右から左に表示する。 /// </summary> const int MFT_RIGHTORDER = 0x00002000; /// <summary> /// 以降のアイテムをメニューバーの右側に配置する。メニューバーのアイテム /// のみに有効。 /// </summary> const int MFT_RIGHTJUSTIFY = 0x00004000; #endregion /// <summary>ウィンドウメニューを操作するフォーム。</summary> private Form form; /// <summary>デリゲート・コレクション</summary> private Dictionary<int, Delegate> delegates; /// <summary> /// RuleWindowMenuの新しいインスタンスを初期化する。 /// </summary> /// <param name="form">ウィンドウメニューを操作するフォーム。</param> internal RuleWindowMenu(Form form) { this.form = form; delegates = new Dictionary<int, Delegate>(); } /// <summary> /// メニュー・アイテムを追加する。 /// </summary> /// <param name="str">メニューに表示する文字列。</param> /// <param name="id">メニューアイテムのID。</param> /// <param name="onClick"> /// メニューがクリックされたとき呼び出されるデリゲート /// </param> internal void Add(string str, int id, Delegate onClick) { IntPtr hMenu = GetSystemMenu(form.Handle, 0); MENUITEMINFO mii = new MENUITEMINFO { // 構造体のサイズ cbSize = (uint)Marshal.SizeOf(typeof(MENUITEMINFO)), fMask = MIIM_ID | MIIM_STRING, // メニューアイテムを新しい行に配置する。 fType = MFT_MENUBARBREAK, fState = 0, wID = (uint)id, hSubMenu = IntPtr.Zero, hbmpChecked = IntPtr.Zero, hbmpUnchecked = IntPtr.Zero, dwItemData = 0, dwTypeData = str, cch = 0, hbmpItem = IntPtr.Zero }; unchecked {InsertMenuItem(hMenu, (uint)-1, false, ref mii);} delegates.Add(id, onClick); } /// <summary> /// 区切り線を追加する。 /// </summary> internal void AddSeparator() { IntPtr hMenu = GetSystemMenu(form.Handle, 0); MENUITEMINFO mii = new MENUITEMINFO { // 構造体のサイズ cbSize = (uint)Marshal.SizeOf(typeof(MENUITEMINFO)), fMask = MIIM_FTYPE, fType = MFT_SEPARATOR, // セパレータ fState = 0, wID = 0, hSubMenu = IntPtr.Zero, hbmpChecked = IntPtr.Zero, hbmpUnchecked = IntPtr.Zero, dwItemData = 0, dwTypeData = string.Empty, cch = 0, hbmpItem = IntPtr.Zero }; unchecked {InsertMenuItem(hMenu, (uint)-1, false, ref mii);} } /// <summary> /// formのWndProc()メソッドから呼ぶ。 /// </summary> /// <param name="m">Windowsが送信するメッセージ。</param> /// <returns>処理された場合はtrue。それ以外の場合はfalse。</returns> internal bool WindowMenuProc(ref Message m) { // ユーザーがウィンドウメニューからコマンドを選択した、または最大表 // 示か最小化ボタンを選択したことを表すメッセージ const int WM_SYSCOMMAND = 0x112; int param = m.WParam.ToInt32(); if (m.Msg == WM_SYSCOMMAND && delegates.ContainsKey(param)) { delegates[param].Method.Invoke(form, new object[] {form, null}); return true; } return false; } /// <summary> /// ウィンドウメニュー(システムメニューまたはコントロールメニューとも呼 /// ばれる)のハンドルを取得する。このハンドルはコピーであり、ウィンドウ /// メニューの変更に利用できる。ウィンドウメニューを既定の状態へ戻すこと /// もできる。 /// </summary> /// <param name="hWnd">ウィンドウのハンドル</param> /// <param name="bRevert">元へ戻すかどうか</param> /// <returns> /// bRevert パラメータが FALSE の場合、ウィンドウメニューが内部でコピー /// され、コピー先のメニューハンドルが返る。bRevert パラメータが TRUE の /// 場合、NULL が返る。 /// </returns> [DllImport("USER32.DLL")] private static extern IntPtr GetSystemMenu(IntPtr hWnd, UInt32 bRevert); /// <summary> /// メニューの指定された位置に新しいメニュー項目を挿入する。 /// </summary> /// <param name="hMenu">メニューのハンドル</param> /// <param name="uItem">識別子または位置</param> /// <param name="fByPosition">uItem パラメータの意味</param> /// <param name="mii">メニュー項目の情報</param> /// <returns> /// 関数が成功すると、0 以外の値が返る。関数が失敗すると、0 が返る。拡張 /// エラー情報を取得するには、GetLastError() を使う。 /// </returns> [DllImport("USER32.DLL")] private static extern bool InsertMenuItem(IntPtr hMenu, UInt32 uItem, bool fByPosition, ref MENUITEMINFO mii); /// <summary> /// メニューアイテムの情報を定義する。 /// </summary> [StructLayout(LayoutKind.Sequential)] private struct MENUITEMINFO { internal UInt32 cbSize; // 構造体のサイズ internal UInt32 fMask; // 取得または設定するメンバ internal UInt32 fType; // アイテムのタイプ internal UInt32 fState; // アイテムの状態 internal UInt32 wID; // アイテム ID internal IntPtr hSubMenu; // サブメニューのハンドル internal IntPtr hbmpChecked; // チェック表示時のビットマップ internal IntPtr hbmpUnchecked; // チェック非表示時のビットマップ internal UInt32 dwItemData; // 任意の 32 ビット値 internal string dwTypeData; // アイテムの内容 internal UInt32 cch; // アイテムの文字列の長さ internal IntPtr hbmpItem; // ビットマップハンドル }; }
Formの方で以下のようにします。
public partial class Form1 : Form { private RuleWindowMenu windowMenuRuler; /// <summary> /// 構築子 /// </summary> public Form1() { windowMenuRuler = new RuleWindowMenu(this); windowMenuRuler.AddSeparator(); windowMenuRuler.Add("開く", 500, // 適当な値。 new EventHandler(ShowAbout)); } private void ShowAbout(object sernder, EventArgs e) { // 好きなことをする。 } /// <summary> /// Windows メッセージを処理する。 /// </summary> /// <param name="m">処理対象の Windows <see cref="Message"/></param> protected override void WndProc(ref Message m) { if (!windowMenuRuler.WindowMenuProc(ref m)) base.WndProc(ref m); } }
実行結果
以下のサイトを参考にしました。
http://yokohama.cool.ne.jp/chokuto/urawaza/struct/MENUITEMINFO.html
http://issun.blog.so-net.ne.jp/2009-05-11