トレイアイコンにショートカットメニューを表示

タスクトレイでのメニュー表示における問題

今回は、タスクトレイに表示されたアイコン上でマウスクリックされたときに、ショートカットメニューを表示するようにしてみましょう。

これまでの知識から、 Shell_NotifyIcon 関数と TrackPopupMenuEx 関数を使ってできるはずです。ところが、実際にやってみるとメニューを表示したときに、動作がおかしくなることがあります。

いったいどのようなことが起こるのかというと、トレイアイコンからショートカットメニューを表示したときに、表示されたメニューまたはそれを作成したウィンドウ以外の部分をクリックすると、メニューが表示されたまま残ってしまうことがある、というものです。

この問題を解決するには、まず、 SetForegroundWindow 関数を呼び出してウィンドウをフォアグラウンドに持ってきてから、 TrackPopupMenuEx 関数を呼び出して、ポップアップメニューを表示する、という手順をとります。

ウィンドウのフォアグラウンド化

まずは SetForegroundWindow 関数を呼び出します。この関数は、指定されたウィンドウをフォアグラウンド(最前面)に持ってきます。

BOOL SetForegroundWindow(
    HWND  hWnd      // ウィンドウハンドル
);

hWnd パラメータには、最前面に持ってくるウィンドウのハンドルを指定します。HSPウィンドウのハンドルを指定すればよいです。

ショートカットメニューの表示

次に、ショートカットメニューを表示します。ショートカットメニューの表示には TrackPopupMenuEx 関数を呼び出します。表示するメニューについては、あらかじめ作成しておきましょう。(表示する直前に作成してもいいですが。)

ショートカットメニューの表示については以前にもやったことがあるので、詳しい説明は省きます。

ショートカットメニューについては、『ショートカットメニューを表示してみる』を参照してください。

サンプルスクリプト

さて、実際にスクリプトを書いてみます。

サンプルスクリプト main.as

    #include "llmod.as"
    #include "hsgetmsg.as"

    ; メニューアイテムIDを定義
    #define CMD_SHOWWINDOW  1   ;「ウィンドウを表示」アイテムのID
    #define CMD_HIDEWINDOW  2   ;「ウィンドウを隠す」アイテムのID
    #define CMD_VERSION     3   ;「バージョン情報」アイテムのID
    #define CMD_QUIT        4   ;「終了」アイテムのID

    ; トレイアイコン用のアイコンID
    #define ID_TRAYICON         1

    ; トレイアイコン用のメッセージコード
    #define MYWM_TRAYICON       0x1000

    ;--------------- ショートカットメニューの準備 ------------------
    ; ショートカットメニューの作成
    dllproc "CreatePopupMenu", pm, 0, D_USER
    hpopup = stat                 ; ショートカットメニューのハンドル

    ; メニューアイテムの追加
    mesbuf = "ウィンドウを表示(&S)"
    pm = hpopup, 0, CMD_SHOWWINDOW
    getptr pm.3, mesbuf
    dllproc "AppendMenuA", pm, 4, D_USER

    mesbuf = "ウィンドウを隠す(&H)"
    pm = hpopup, 0, CMD_HIDEWINDOW
    getptr pm.3, mesbuf
    dllproc "AppendMenuA", pm, 4, D_USER

    pm = hpopup, $800, 0, 0       ; 区切り線を指定
    dllproc "AppendMenuA", pm, 4, D_USER

    mesbuf = "バージョン情報(&V)"
    pm = hpopup, 0, CMD_VERSION
    getptr pm.3, mesbuf
    dllproc "AppendMenuA", pm, 4, D_USER

    pm = hpopup, $800, 0, 0       ; 区切り線を指定
    dllproc "AppendMenuA", pm, 4, D_USER

    mesbuf = "終了(&Q)"
    pm = hpopup, 0, CMD_QUIT
    getptr pm.3, mesbuf
    dllproc "AppendMenuA", pm, 4, D_USER

    ;------------------- メッセージ取得設定 ------------------------
    ;----- (ウィンドウハンドルを取得できるので先にサブクラス化) ----
    ; トレイアイコン用メッセージを取得するようにセット
    set_subclass                ; サブクラス化
    hwnd = stat                 ; メインウィンドウのハンドル
    set_message MYWM_TRAYICON   ; MYWM_TRAYICON を取得するように設定

    ; メッセージパラメータ用変数
    dup msg,  msgval.1          ; メッセージが格納される変数
    dup wprm, msgval.2          ; wParamパラメータが格納される変数
    dup lprm, msgval.3          ; lParamパラメータが格納される変数

    ;--------------- タスクトレイアイコンの表示 --------------------
    tooltext = "HSP Tray-Icon Test"  ; ツールチップテキスト

    ; 自身の実行ファイル名取得
    sdim exefile, 260           ; ファイル名を格納する変数
    pm = 0                      ; NULL
    getptr pm.1, exefile        ; バッファアドレス取得
    pm.2 = 260                  ; バッファサイズ
    dllproc "GetModuleFileNameA", pm, 3, D_KERNEL

    ; 実行ファイルからアイコンを取得
    getptr pm.0, exefile        ; ファイル名
    pm.1 = 0                    ; アイコンインデックス
    pm.2 = 0                    ; 大きいアイコンは取得しない
    getptr pm.3, hicon          ; 小さいアイコンのハンドルを格納する変数
    pm.4 = 1                    ; 取得する数
    dllproc "ExtractIconExA", pm, 5, D_SHELL

    ; NOTIFYICONDATA 構造体
    dim  nid, 22
    nid.0 = 88                  ; 構造体サイズ
    nid.1 = hwnd                ; ウィンドウハンドル
    nid.2 = ID_TRAYICON         ; アイコンID
    nid.3 = 1 | 2 | 4           ; NIF_MESSAGE | NIF_ICON | NIF_TIP
    nid.4 = MYWM_TRAYICON       ; メッセージコード
    nid.5 = hicon               ; アイコンハンドル
    poke nid, 24, tooltext      ; ツールチップ文字列

    ; タスクトレイにアイコンを追加
    pm.0 = 0                    ; NIM_ADD
    getptr pm.1, nid            ; NOTIFYICONDATA 構造体アドレス
    dllproc "Shell_NotifyIconA", pm, 2, D_SHELL

    ; 終了時にジャンプ
    onexit *onQuit

    ;----------------------- メインループ --------------------------
*mainloop
    get_message msgval
    if msgval {
        if msg == MYWM_TRAYICON : gosub *onTrayEvent
    } else {
        wait 10
    }
    goto *mainloop

*onTrayEvent
    ;--------------- トレイアイコンがクリックされたとき -------------
    if lprm == 0x0205 {         ; WM_RBUTTONUP
        ; ウィンドウをフォアグラウンドに
        pm = hwnd               ; ウィンドウハンドル
        dllproc "SetForegroundWindow", pm, 1, D_USER

        ; ショートカットメニュー表示
        ginfo 0                 ; マウスカーソル座標取得
        pm.0 = hpopup           ; ショートカットメニューのハンドル
        pm.1 = $182             ; TPM_RIGHTBUTTON | TPM_RETURNCMD
                                ;  | TPM_NONOTIFY
        pm.2 = prmx             ; スクリーンx座標
        pm.3 = prmy             ; スクリーンy座標
        pm.4 = hwnd             ; ウィンドウハンドル
        pm.5 = 0                ; NULL
        dllproc "TrackPopupMenuEx", pm, 6, D_USER
        if stat {
            itemid = stat       ; 選択されたメニューアイテムID
            if itemid == CMD_SHOWWINDOW {
                gsel 0, 1 
            }
            if itemid == CMD_HIDEWINDOW {
                gsel 0, -1
            }
            if itemid == CMD_VERSION {
                s  = "タスクトレイのショートカットメニュー\n\n"
                s += "サンプルスクリプト\n"
                s += "Presented by ちょくと"
                dialog s, 0, "バージョン情報"
            }
            if itemid == CMD_QUIT {
                dialog "終了しますか", 2, "確認"
                if stat == 6 : goto *onQuit
            }
        }
    }
    return

*onQuit
    ;----------------------- 終了時の処理 --------------------------
    ; NOTIFYICONDATA 構造体(cbSize, uID のみセット)
    dim  nid, 22
    nid.0 = 88                  ; 構造体サイズ(=88)
    nid.1 = hwnd                ; ウィンドウハンドル
    nid.2 = ID_TRAYICON         ; アイコンID

    ; タスクトレイからアイコンを削除
    pm.0 = 2                    ; NIM_DELETE
    getptr pm.1, nid            ; NOTIFYICONDATA 構造体アドレス
    dllproc "Shell_NotifyIconA", pm, 2, D_SHELL

    ; アイコンの破棄
    dllproc "DestroyIcon", hicon, 1, D_USER

    ; メニューの破棄
    dllproc "DestroyMenu", hpopup, 1, D_USER
    end