タスクトレイにアイコンを表示してみる

タスクバーのステータス領域(タスクトレイ)

デスクトップの下端(場合によっては上や左や右のときもありますが)に表示されているタスクバーの端に、ステータス領域(status area)という領域があります。この領域は一般にはタスクトレイとも呼ばれていて、時計と一緒にアプリケーションのアイコンが表示されています。

アプリケーションは、このタスクトレイにアプリケーションの状態などを示すためのアイコンを表示することができます。また、表示したアイコン上で発生したマウスイベントはメッセージとして送られてくるので、クリックしたときにポップアップメニューを表示させるなどの処理を行なうことができます。

今回は、タスクトレイにアイコンを表示して、アプリケーションを操作してみましょう。

トレイアイコンの操作

タスクトレイへのアイコンの追加や変更、削除はすべて Shell_NotifyIcon 関数で行ないます。

BOOL Shell_NotifyIconA(
    DWORD   dwMessage,     // 送信するメッセージ
    PNOTIFYICONDATA  pnid  // NOTIFYICONDATA構造体
);

dwMessage パラメータには、タスクトレイに新しくアイコンを追加する場合には 0 (NIM_ADD) を、すでにあるアイコンの設定を変更する場合には 1 (NIM_MODIFY) を、アイコンを削除する場合には 2 (NUM_DELETE) を指定します。

pnid パラメータには、アイコンの情報などを格納した NOTIFYICONDATA 構造体のアドレスを指定します。 NOTIFYICONDATA 構造体は以下のように定義されています。

typedef struct _NOTIFYICONDATA {
    DWORD  cbSize;           // 構造体のサイズ(=88)
    HWND   hWnd;             // ウィンドウハンドル
    UINT   uID;              // トレイアイコンID
    UINT   uFlags;           // 有効メンバを示すフラグ
    UINT   uCallbackMessage; // メッセージコード
    HICON  hIcon;            // アイコンハンドル
    CHAR   szTip[64];        // ツールチップ文字列
} NOTIFYICONDATA, *PNOTIFYICONDATA;

cbSize メンバには、構造体サイズである 88 を指定します。

hWnd メンバには、トレイアイコンを所有するウィンドウのハンドルを指定します。アイコン上でマウスイベントが発生したときには、ここで指定されたウィンドウに対してウィンドウメッセージが送信されます。

uID にはタスクトレイのアイコンIDを指定します。1つのプロセスで複数のアイコンをタスクトレイに表示したい場合には、それぞれ異なるIDを指定することで操作することができます。このIDはプロセスごとに独立しているので、他のプロセスが同じIDを指定していたとしてもそれらが衝突することはありません。

uFlags メンバには、この構造体のメンバの中でどのメンバが有効であるのかを指定します。 hIcon メンバが有効であるなら 1 (NIF_MESSAGE) を、 uCallbackMessage メンバが有効であるなら 2 (NIF_ICON) を、 szTip メンバが有効であるなら 4 (NIF_TIP) を、それぞれ組み合わせて指定します。

uCallbackMessage には、アイコン上でマウスイベントが起こった場合に、 hWnd メンバで指定したウィンドウに送信するメッセージのコードを指定します。

hIcon メンバには、追加または変更するアイコンのハンドルを指定します。

szTip メンバにはアイコン上でマウスポインタが停止したときに表示されるツールチップ上に表示する文字列を格納します。 szTip メンバのデータを格納するには

(w)poke nid, 24, "(ツールチップ文字列)"

というようにしなければなりません。

自身の実行ファイルのアイコンの取得

今回はタスクトレイに表示するアイコンとして、自身の実行ファイルのアイコンを表示してみましょう。

タスクトレイに表示するアイコンには、前回やったように ExtractIconEx 関数を使って取得したものを使うことができますが、そのためにはファイル名を取得する必要があります。自分自身の実行ファイル名はシステム変数 exedir にファイル名を加えたものを指定すればよいのですが、製作の段階から実行ファイル名が決まっているならともかく、後で実行ファイル名を変えるかもしれない場合や、汎用モジュールを作成する場合には直接指定するわけには行きません。

そこで、自分自身の実行可能ファイル名を取得する必要があります。自分自身の実行ファイル名をフルパスで取得するために、 GetModuleFileName 関数を使うことができます。

DWORD GetModuleFileNameA(
    HMODULE  hModule,    // モジュールハンドル
    PTSTR    pFilename,  // バッファアドレス
    DWORD    nSize       // バッファのサイズ
);

自身のファイル名を得るには、 hModule パラメータに 0 (NULL) を指定し、 pFilename パラメータにはファイル名を格納するバッファのアドレスを、 nSize パラメータにはそのバッファのサイズをそれぞれ指定します。

このようにして取得されたファイル名を ExtractIconEx 関数に渡してやれば、アイコンを取得することができます。ここでは、インデックス 0 の小さいアイコンを取得します。

タスクバーから送られてくるメッセージ

タスクトレイのアイコンでマウスイベントが発生すると、タスクバーはそのアイコンを所有するウィンドウに NOTIFYICONDATA 構造体の uCallbackMessage メンバで指定されたメッセージを送信します。このとき、 wParam パラメータには NOTIFYICONDATA 構造体の uID メンバで指定されたタスクトレイのアイコンIDが、 lParam パラメータには以下に示すマウスイベントのメッセージコードがそれぞれ格納されます。

サンプルスクリプト

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

メインスクリプト main.as

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

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

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

    mref bmscr, 67                  ; ウィンドウのBMSCR構造体
    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 = bmscr.13            ; ウィンドウハンドル
    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

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

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

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

    mes "トレイアイコン上で右クリック時\n  -->ウィンドウ消去"
    mes "トレイアイコン上で左ダブルクリック時\n  -->ウィンドウ表示"

*mainloop
    get_message msgval
    if msgval {
        if msg == MYWM_TRAYICON : gosub *onTrayEvent
    } else {
        wait 10
    }
    goto *mainloop


*onTrayEvent
    ; タスクトレイからメッセージが送られた場合の処理
    ;   wParam パラメータはタスクトレイのアイコンID
    ;   lParam パラメータはマウスメッセージ(WM_LBUTTONDOWNなど)
    if lprm == 0x0203 {         ; WM_LBUTTONDBLCLK
        gsel 0, 1
    }
    if lprm == 0x0205 {         ; WM_RBUTTONUP
        gsel 0, -1
    }
    return

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

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

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