[BlueLeaf1336]> PROGRAM>

他人のメインメニューを操作する

historyTOP

2005/03/18:作成

overviewTOP

時々、なんでこのプログラムのこのメニューにショートカットキー(Ctrl+S とか)が付いてないんだ? と思うことがあります。コレを勝手に付けられたらよいかんじだろうなぁと。

最初、DelphiのIDEのように、アクセラレータキー(ファイル(F) とか)についてなら、「&」を使用してメニューのキャプションをいじればいいんじゃないか、と思ったんですが、どうも話はそんな簡単ではなさそうで、多分駄目です。メニューのキャプションをいじったぐらいでは、ショートカットキーを追加することはおろかアクセラレータキーも。

でも、キャプションをいじることはできました。意味ありませんが。

ソースコードTOP

少し省略していますが、「Panel上でマウスボタンを押して目的のアプリケーションの上で離したとき」に、HackWindowMenu という関数を呼んでいます。実際にはアプリケーションの持ち物のひとつであるウィンドウなので、コレをさかのぼっててっぺんのウィンドウまでたどり着いています。言い換えると、メインメニューを持っているウィンドウを見つけているということです。言い換えても意味不明ですが。

//-----------------------------------------------------------------------------
//  ターゲットウィンドウ取得の旅終了
//  http://yokohama.cool.ne.jp/chokuto/urawaza/setfont.html
procedure TMainForm.Panel1MouseUp(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
begin
    //  スパイ中でなければスキップ
    if InMouseDown then
    begin
        //  フラグを倒してカーソルを元に戻す
        InMouseDown := false;
        Screen.Cursor := crDefault;
        //  マウス直下のウィンドウハンドルを取り出す
        TargetHandle := WindowFromPoint(Mouse.CursorPos);
        //  さかのぼる
        if (TargetHandle <> 0) then
        begin
            HackWindowMenu(GetMostTopLevelWindow(TargetHandle));
        end;
    end;
end;

で、コレが実際にメニューを列挙する関数です。再帰的に自分自身を呼び出しながらサブメニューを掘り進んでいきます。ついでに、メニューのキャプションもいじっていますが。

//-----------------------------------------------------------------------------
//  メニュー構造を手繰る
procedure EnumMenu(const Menu: HMENU; const Level: integer; List: TStrings);
const
    MAX_LEN = 256;
var
    SubMenu: HMENU;
    i, ItemCount: integer;
    Buf: array[0..MAX_LEN] of Char;
    Mii: TMenuItemInfo;
begin
    //-------------------------------------------
    //  メニューのアイテム数を取得する
    ItemCount := GetMenuItemCount(Menu);
    //  なければ終わる
    if ItemCount < 0 then Exit;

    //-------------------------------------------
    //  それぞれを順になめていく
    for i := 0 to ItemCount - 1 do
    begin
        //----------------------------------
        //  メニューキャプションを取得する
        GetMenuString(Menu, i, Buf, MAX_LEN, MF_BYPOSITION);
        //  表示する
        List.Add(StringOfChar(' ', Level * 4) + Buf);

        //----------------------------------
        //  サブメニューハンドルを取得する
        SubMenu := GetSubMenu(Menu, i);

        //----------------------------------
        //  再帰的に
        EnumMenu(SubMenu, Level + 1, List);

        //----------------------------------
        //  悪いことをする
        FillChar(Mii, SizeOf(Mii), 0);
        Mii.cbSize := SizeOf(Mii);
        Mii.fMask := MIIM_TYPE;
        Mii.dwTypeData := Buf;
        Mii.cch := MAX_LEN;

        //----------------------------------
        //  取り出して
        if GetMenuItemInfo(Menu, i, True, Mii) then
        begin
            //--------------------
            //  キャプションだけいじって
            Mii.dwTypeData := PChar(Buf + 'しません');
            Mii.cch := SizeOf(Mii.dwTypeData);
            //--------------------
            //  たたき返す
            SetMenuItemInfo(Menu, i, True, Mii);
        end;
    end;
end;

最後に、呼び出し例です。

//-----------------------------------------------------------------------------
//  メニューを操作してみる
procedure TMainForm.HackWindowMenu(const WH: HWND);
var
    Menu: HMENU;
begin
    //-------------------------------------------
    //  メニューを取得する
    Menu := GetMenu(WH);
    //  ないなら終わり
    if (Menu = INVALID_HANDLE_VALUE) then Exit;
    //  列挙する
    EnumMenu(Menu, 0, Memo1.Lines);

    //-------------------------------------------
    //  システムメニューを取得する
    Menu := GetSystemMenu(WH, False);
    //  ないなら終わり
    if (Menu = INVALID_HANDLE_VALUE) then Exit;
    //  列挙する
    EnumMenu(Menu, 0, Memo1.Lines);
end;

実行結果・ダウンロードTOP

「Pane1」でマウスボタンを押して(下図左)、押したまま適当なアプリケーション(ただし突然それが落ちても大丈夫なの)の上でマウスを放します。すると、そのアプリケーションの持っているメインメニューの項目が(希望は全部)表示されます。(下図右)

また、そのアプリケーションのメインメニューとシステムメニューもおまけに、全部のキャプションがかわります。この図では全メニューに「しません」を追加しています。多分。変わらないのもありましたが、全くもって気になりません。

おそらく、TApplicationがトップレベルに隠れているため、Delphi製の場合は駄目そうな気がしますが。

Hackmenu.zip(171,289bytes)

EOFTOP