タスクバーのシステムトレイ(タスクトレイ)にアイコンを表示させる
|
最終更新 2003 09/17
サンプルのダウンロード → Exp_Shell_NotifyIcon.lzh(40k)
全ソースコード
このサンプルを動作させるには main.cpp と resource.rc と resource.h の
3つのファイルを新規作成して、それぞれの内容をファイルへコピーして下さい。
それと、同じフォルダに16×16ピクセルのサイズのアイコンを置いて下さい。
アイコンのファイル名は icon.ico とします。
アイコンはアイコンエディタなどを使って作成して下さい。
Vector などで探せば入手できると思います。
main.cpp
resource.rc
resource.h
Shell_NotifyIcon 関数
対応しているバージョン
95, 98, Me, NT4.0以降, CE1.0以降, 2000, XP
( Microsoft Internet Explorer 4.0以上推奨 )
使用するヘッダとライブラリ
shellapi.h
shell32.lib
|
【 タスクトレイにアイコンを登録する 】
タスクトレイにアイコンを表示させるには Shell_NotifyIcon() 関数を
使います。
NOTIFYICONDATA 構造体に表示するアイコンなどを設定したら
Shell_NotifyIcon() 関数の最初の引数に NIM_ADD を指定して
二番目の引数に先ほど設定を行った NOTIFYICONDATA 構造体の
ポインタを渡します。
NOTIFYICONDATA nid;
Shell_NotifyIcon( NIM_ADD, &nid );
タスクトレイに登録したアイコンを削除するには、最初の引数を
NIM_DELETE と変更すれば削除できます。
NOTIFYICONDATA nid;
Shell_NotifyIcon( NIM_DELETE, &nid );
以下に NOTIFYICONDATA 構造体の各メンバの説明を書きます。
NOTIFYICONDATA 構造体のメンバ
cbSize
sizeof( NOTIFYICONDATA ) を指定します。
hWnd
登録するアプリケーションのウィンドウハンドルを渡します。
ここに指定したウィンドウのコールバック関数に
タスクトレイのアイコンをクリックした時などのメッセージが
送信されます。
uID
アプリケーション側で定義されたタスクバーアイコンの識別子
とMSDNに書かれていますが、登録・削除を行うだけなら
使用しません。
uFlags
次のいずれかを指定します。
| で区切る事で複数指定することができます。
NIF_ICON hIcon メンバを使用する
NIF_MESSAGE uCallbackMessage メンバを使用する
NIF_TIP szTip メンバを使用する
uCallbackMessage
タスクトレイに登録したアイコンがクリックされたときなどに
コールバック関数に送信されるメッセージ番号を指定します。
メッセージ番号は WM_USER + 0以上の値を指定します。
#define WM_ICON_CLICK WM_USER + 1
などとデファインを使って定義しておくとよいです。
hIcon
タスクトレイに表示するアイコンを指定します。
リソースに登録したアイコンを指定する場合は
MAKEINTRESOURCE( リソースID ) を使います。
szTip
タスクトレイに表示されているアイコンに、マウスカーソルが
触った時に表示する文字を指定します。
普通はソフト名を指定します。
【 ポップアップメニューを作成する 】
タスクトレイのアイコンをクリックした時に表示させる
ポップアップメニューを作成します。
ポップアップメニューは普通のウィンドウメニューと同じように
リソーススクリプトにメニューを追加して編集します。
メニューハンドルが取得できるような状態であれば、メニューは
どのように作成しても問題はありません。
以下に説明する方法は、あくまで一例です。
( まだリソーススクリプトを作成していない状態だとします。 )
1.プロジェクト(P) - プロジェクトへ追加(A) - 新規作成(N) を選択。
2.リソーススクリプト - ファイル名(N) - OK を選択。
ここでは ファイル名(N) に resource と指定しましたが、自分の好きな名前で構いません。
3.ワークスペース にリソースビューが追加されます。
4.resource リソース と書かれているフォルダのアイコンを右クリックして 挿入(I)
を選択。
5.Menu - 新規作成(N) を選択。
IDR_MENU1 という名前でメニューが作成されました。
このIDをプログラム中に呼び出す時は MAKEINTRESOURCE( IDR_MENU1 )
というように MAKEINTRESOURCE を使って呼び出します。
(この IDR_MENU1 は便宜上つけられたものなので、後で変更できます。)
6.IDR_MENU1 をダブルクリックして、メニューエディタでメニューの項目を
編集。
メニュー項目の上で右クリックして プロパティ(P) で項目の編集を行います。
キャプション(C) と ID(I) を入力。
最終的にメニューは以下のように作成します。
//親メニュー
キャプション:ポップアップ( プロパティ の ポップアップ(O) にチェックを入れます )
ID:なし
//子メニュー1
キャプション:バージョン情報
ID:ID_POPUP_VERSION
//子メニュー2
キャプション:なし( プロパティ の セパレータ(A) にチェックを入れます )
ID:なし
//子メニュー3
キャプション:閉じる
ID:ID_POPUP_CLOSE
//子メニュー4
キャプション:終了
ID:ID_POPUP_EXIT
【 アイコンを追加する 】
アイコンはVC++に付属のエディタでも作成できるのですが
16×16サイズのアイコンを作成したはいいのですが、どのように
プログラム中で呼び出せばいいのかが分かりませんでした。
ですので、別のアイコン作成用エディタで16×16サイズの
アイコンを作成して、それをプロジェクトと同じフォルダに入れておき
リソースに追加するという方法を取ります。
1.resource リソース と書かれているフォルダのアイコンを右クリックして 挿入(I)
を選択。
2.Icon - インポート(M) を選択。
3.追加するアイコンを選択したらインポートをクリック。
IDI_ICON1 という名前でアイコンが追加されました。
このIDをプログラム中で呼び出す時は MAKEINTRESOURCE( IDI_ICON1 ) の
ようにします。(この名前は変更可能です。)
【 アイコンがクリックされたかどうかを調べる 】
アイコンとメニューをリソースに追加したら、今度はタスクトレイの
アイコンがクリックされたかどうかを調べる処理を追加します。
タスクトレイに表示したアイコンがクリックされると、メインウィンドウの
コールバック関数に Shell_NotifyIcon() 関数を呼び出すときに指定した
NOTIFYICONDATA 構造体の uCallbackMessage メンバに指定した
メッセージが送信されます。
uCallbackMessage に指定する値は WM_USER + 0以上の値です。
サンプルでは分かりやすいように
#define WM_ICON_CLICK WM_USER + 999
として WM_ICON_CLICK という名前と uCallbackMessage に指定する値を
対応させています。
こうすると、あとで WM_USER + 999 を WM_USER + 1000 に
変更しなければならなくなっても、このデファインの部分だけ
変更すれば良いので便利です。
999 を指定したのに深い意味はありません。なんとなくです。
0 でも 100 でも、他に同じ値を使用していなければ何でもかまいません。
WM_USER 以降のメッセージは、自分で定義できるメッセージです。
今回のように、自分で独自にメッセージを定義しなければならない場合に
WM_USER +0以上の値を使用します。
タスクトレイに表示されているアイコンがクリックされたかどうかを
調べるには以下のようにします。
こんな風にマウスのボタン別に処理を分けることも可能です。
サンプルでは右クリックにだけ対応させています。
このサンプルを見ると「WM_KEYDOWNを使えばキー入力も判定できるのでは?」
と考えてしまうのですが、そもそも WM_ICON_CLICK がアイコンをクリックした
時にしか呼ばれないので、キー判定はできません。
【 ポップアップメニューを表示させる 】
アイコンのクリックが判定できるようになったら、次はそれにあわせて
ポップアップメニューを表示する処理を追加します。
ポップアップメニューの表示には TrackPopupMenu 関数を使います。
この部分の処理はもう、お決まりのパターンになっているので、以下の
ソースを何も考えずに使って下さい。
(赤くなっている部分だけ、必要に応じて修正する必要があります。)
//マウスカーソルの位置を取得
POINT mouse;
if( !GetCursorPos( &mouse ) ) return FALSE;
//メニューを取得
HMENU hMenu = LoadMenu( hInstance, MAKEINTRESOURCE( IDR_MENU1 ) );
HMENU hMenuChild = GetSubMenu( hMenu, 0 );
if( hMenuChild == NULL ) return FALSE;
//選択されたメニューのIDを取得
int nCommandID = TrackPopupMenu(
hMenuChild,
TPM_RIGHTALIGN | TPM_BOTTOMALIGN | TPM_RETURNCMD,
mouse.x, mouse.y,
0,
hWnd,
NULL
);
|
上のソース中の赤い文字は、必要に応じて変更しなければならない箇所です。
IDR_MENU1 は、リソースに登録されているポップアップメニューのIDです。
メニューのIDを別の名前に変更した場合は、ここの値も変更します。
hWnd はメインウィンドウのウィンドウハンドルです。
hInstance はアプリケーションのインスタンスハンドルです。
おおまかに説明すると、画面上のマウスカーソルの位置と、IDR_MENU1 の
2番目の階層にあるメニュー項目を取得して、それを TrackPopupMenu()
関数に渡しています。
ポップアップメニューの場合、一番上の階層は必要ないので、2番目の階層を
使います。(サンプルだと、キャプションにポップアップという名前を
設定されている項目が一番上の階層です。)
そうすると、nCommandID には、ポップアップメニューのクリックされた項目の
IDが返ってきます。
ポップアップメニューを作成した時に、各メニュー項目にIDをつけたと
思いますが、nCommandID の値はそのIDと対応しているので、nCommandID の
値を switch で分岐させて、IDで捕らえれば良いということになります。
ようするにこうです。
タスクトレイに表示したアイコンがクリックされたか判定する処理から
ここまでの処理をくっつけると
LRESULT CALLBACK WinProc(
HWND hWnd,
UINT uMsg,
WPARAM wParam,
LPARAM lParam
){
switch( uMsg )
{
case WM_ICON_CLICK:
//タスクトレイのアイコンのメッセージ
switch( lParam )
{
case WM_RBUTTONDOWN:
{
//マウスカーソルの位置を取得
POINT mouse;
if( !GetCursorPos( &mouse ) ) return FALSE;
//メニューを取得
HMENU hMenu
= LoadMenu( hInstance, MAKEINTRESOURCE( IDR_MENU1 ) );
HMENU hMenuChild = GetSubMenu( hMenu, 0 );
if( hMenuChild == NULL ) return FALSE;
//選択されたメニューのIDを取得
int nCommandID
= TrackPopupMenu(
hMenuChild,
TPM_RIGHTALIGN | TPM_BOTTOMALIGN | TPM_RETURNCMD,
mouse.x, mouse.y,
0,
hWnd,
NULL
);
switch( nCommandID )
{
case ID_POPUP_VERSION://バージョン情報がクリックされた
//バージョン情報表示
break;
case ID_POPUP_CLOSE://閉じるがクリックされた
//ポップアップメニューを閉じる
break;
case ID_POPUP_EXIT://終了がクリックされた
//アプリケーション終了
break;
}
}
break;
}
break;
・
・
・
|
まー、こんな流れになるわけです・・・。
WM_RBUTTONDOWN の中の処理はけっこう長いので、サンプルでは ShowPopup()
という関数を作ってそれを呼び出し、その関数内で処理を行うようにして
います。
【 ポップアップメニューが閉じない罠 】
次にポップアップメニューの「閉じる」の処理を追加します。
よく、タスクトレイのアイコンをクリックしてポップアップメニューを
表示させた状態で、デスクトップをクリックするなどのアクションを
行うと、ポップアップメニューが開いたまま閉じなくなります。
開きっぱなしのポップアップメニューを閉じるには、選びたくもない項目を
選んで半ば強制的に閉じるしか方法がありません。
これはどうもウィンドウズのバグらしいのですが、一向に改善される気配が
ないのでこちらで対応してしまいましょう。
そのために、ポップアップメニューに「閉じる」という項目を追加しました。
ポップアップメニューもウィンドウのひとつなので、名前が分かってしまえば
FindWindow() で検索してウィンドウハンドルを取ってくる事が可能です。
で、ポップアップメニューは Esc キーを押すと閉じるようになっているので
ポップアップメニューのウィンドウに対して「エスケープキーが押されたよ」
というメッセージを送ればよいという事になります。
ポップアップメニューのウィンドウ名は、#32768 だそうです。
HWND hWndPopup = FindWindow( "#32768", NULL );
if( hWndPopup && IsWindowVisible( hWndPopup ) )
{
PostMessage( hWndPopup, WM_KEYDOWN, VK_ESCAPE, 0 );
break;
}
|
これを ID_POPUP_CLOSE の中に入れてあげればOK。
switch( nCommandID )
{
case ID_POPUP_VERSION://バージョン情報がクリックされた
//バージョン情報表示
break;
case ID_POPUP_CLOSE://閉じるがクリックされた
//ポップアップメニューを閉じる
{
HWND hWndPopup = FindWindow( "#32768", NULL );
if( hWndPopup && IsWindowVisible( hWndPopup ) )
{
PostMessage( hWndPopup, WM_KEYDOWN, VK_ESCAPE, 0 );
break;
}
}
break;
case ID_POPUP_EXIT://終了がクリックされた
//アプリケーション終了
break;
}
|
【 タスクトレイのアイコンが消える罠 】
以上の処理で十分使用する事は可能ですが、まだひとつ問題があります。
IEがフリーズして強制終了すると、タスクトレイに登録したアイコンが
全て消えてしまいます。
タスクマネージャを見ると、まだアプリケーションは動いているので
終了したい場合は強制終了しなければなりません。
「シェルが死ぬ。」などと言うそうです。
これはタスクトレイがIEと連動して動いているのが原因のようです。
IEを強制終了したなどの原因でシェルが死ぬと、自動的にタスクトレイが
再起動します。
IE4.0以上でしか利用できませんが、この再起動したかどうかを調べる
ことができます。
それには "TaskbarCreated" というメッセージをシステムに登録します。
そうするとメッセージが使用できるようなら「あいよ〜思う存分使いなされ〜」
とウィンドウズがメッセージに使用できる値を返してくれるので、この値を
コールバック関数で調べます。
もしキャッチできたら、シェルが死んでアイコンが消えてしまっているので
アイコンをもう一度タスクトレイに表示します。
メッセージの登録には RegisterWindowMessage() 関数を使います。
以下、使用例
LRESULT CALLBACK WinProc(
HWND hWnd,
UINT uMsg,
WPARAM wParam,
LPARAM lParam
){
static UINT uMsgRebootShell = 0;
switch( uMsg )
{
case WM_CREATE:
uMsgRebootShell = RegisterWindowMessage( "TaskbarCreated" );
break;
default:
if( uMsgRebootShell >= 0xC000 && uMsg == uMsgRebootShell )
{
//タスクトレイにアイコンを登録
}
}
・
・
・
|
|