[C#]タスクトレイアイコンの画像を動的に変更する

Windowsでプログラムを作成する際、通常は画面(Form)を持つプログラムを作成する事が多いですが、簡単な監視や通知プログラムの場合タスクトレイに常駐させるプログラムを作成する事が有ります。

タスクトレイアプリを作成した場合、バルーンヘルプやアイコンをクリックしたときのメニューの他に、トレイのアイコンで状態を通知したい事が有ります。例えば、タスクマネージャはCPU負荷を表示してくれますし、一般的なメール監視アプリでは新着メール着信は、メールチェック中にアイコンイメージを変更する事が多いです。


そこで、今回はC#でタスクトレイアプリを作成時に、アイコンの画像を編集する方法を説明します。

サンプルでは現在のアイコン画像を取得した上で加工を行っていますが、本サンプルを応用すれば、複数の画像をあらかじめ用意してアニメーションさせるような事も可能です。


アイコン画像の編集方法

C#(VisualStudio)でタスクトレイのアイコンを表示させるには、NotifyIconコントロールを使用します。
今回は、以下のような感じで設定しました。
NotifyIconの仕様に付いてはこちらの記事を参照してください。


次に、アイコン画像を変更処理のメソッドを作成します。

下記のサンプルプログラムでは、既存のアイコン画像データをbmp形式で取得し、アイコン左上にドットを表示した上で再登録しています。TaskTrayIcon.Iconを直接編集したいところなのですが、Iconオブジェクトは編集できない仕様なのでやむなく一旦Bitmapオブジェクトに変換しています。

また、関数の最後でDestroyIcon()をコールしていますが、これについては後述します。

// アイコンリソースの破棄API
[System.Runtime.InteropServices.DllImport( "user32.dll", CharSet = System.Runtime.InteropServices.CharSet.Auto)]
extern static bool DestroyIcon(IntPtr handle);
 
//*********************************************************************
/// <summary> タスクトレイアイコンの画像を変更する
///            (アイコン左上にドットを表示)
/// </summary>
/// <param name="mode"> 0:赤色, 1:黒色</param>
//*********************************************************************
private void changeTrayIcon( int mode ) {
	Icon   curIcon = TaskTrayIcon.Icon;
	Bitmap curBmp  = TaskTrayIcon.Icon.ToBitmap();
 
	//-----------------------
	// ドットの色を決定する
	//-----------------------
	Color notifyColor = mode == 0 ? Color.Red : Color.Black;
 
	//-----------------------------------------------
	// アイコン左上にドットを表示させる(3x3ピクセル)
	//-----------------------------------------------
	for ( int x = 0; x < 3; x++ ) { 
		for ( int y = 0; y < 3; y++ ) { 
			curBmp.SetPixel( x, y, notifyColor );
		}
	}
 
	//-----------------------
	// アイコンを変更する
	//-----------------------
	Icon newIcon = Icon.FromHandle( curBmp.GetHicon() );
	TaskTrayIcon.Icon = newIcon;
 
	//------------------------------------
	// 変更前アイコンのリソースを破棄する
	//------------------------------------
	DestroyIcon( curIcon.Handle );
}







次に上記関数の呼び出し元を作成します。
今回はお試しなので、タイマーで周期的に点滅させるようにします。


イベントハンドラの処理は,以下のような感じです。

// 周期的にアイコン画像を変更する
int tmpVal = 0;
private void timer1_Tick( object sender, EventArgs e ) {
	tmpVal = tmpVal == 0 ? 1 : 0;
 
	changeTrayIcon( tmpVal );
 
}



これで、以下のようにアイコン画像を実行時に変更させる事が可能となりました。
ちょっと分かり辛いですが、アイコン右上のドットの色が変わってます。

↑ ↓


Iconオブジェクトのリソース開放漏れに注意

説明したchangeTrayIcon()関数ですが、最後にDestroyIcon()をコールしています。

これはMSDNの関数仕様にも明記されていて、コールしないとリソースの開放漏れが発生します。

このメソッドを使用する場合は、Win32 API の DestroyIcon メソッドを使用して
結果のアイコンを廃棄して、確実にリソースが解放されるようにする必要があります。


msdnのオンラインマニュアル より


もし、DestroyIcon()をコールしなかった場合、暫くは動作するのですが徐々にリソースがリークしていくので、長時間起動させておくと以下の例外が発生してしまいます。




System.Runtime.InteropServices.ExternalException はハンドルされませんでした。
  Message=GDI+ で汎用エラーが発生しました。
  Source=System.Drawing
  ErrorCode=-2147467259
  StackTrace:
       場所 System.Drawing.Bitmap.GetHicon()
  InnerException:




画像の更新頻度が低い場合は、開発/デバッグ中に発生させづらいバグなので特に注意が必要です。

Windowsダンプの極意 エラーが発生したら、まずダンプ解析

関連記事

コメントを残す

メールアドレスが公開されることはありません。