トップ回答者
NotifyIconでタスクトレイに表示したアイコンがAlt+F4で消えてしまう

質問
-
いつもお世話になります。
mkmarimoです。
タスクトレイに常駐するプログラムを作成しているのですが、
件名に書かせていただいたような現象が発生し、解決方法がわからず困っています。
情報をお持ちの方がいらっしゃいましたらご教授ください。
タスクトレイへのアイコンの表示方法は以下のように実施しています。
・親となるFormにNotifyIconを配置
・親となるFormのコンストラクタでForm.Visible = falseとして親Formは表示されないようにする
・プログラムのエントリポイントで親となるFormをnewした後、Application.Run()を引数なしで実行
NotifyIconはContextMenuStripを持っており、そのメニューのうち1つをクリックすると、
親となるFormとは別のFormを表示します。
このときの表示には、ShowDialog(this)をコールしています。
このContextMenuStripのメニューから呼び出された親となるFormとは
別のFormを表示中にAlt+F4キーでFormを閉じ、
そのまま連続してAlt+F4キーを押すと、タスクトレイ上のNotifyIconが消えてしまいます。
こうなると、プロセスは残っているのに、タスクトレイ上からアイコンがない状態に
なってしまい、プロセスを終了するのにタスクマネージャからしか実施できなくなります。
2回目のAlt+F4キーでタスクトレイ上のアイコンを消せなくするにはどうすればよいでしょうか。
以上、よろしくお願いします。
回答
-
NotifyIcon は内部にウィンドウメッセージを処理するためのトップレベルウィンドウを持っていますが、これが曲者みたいですね。
コンテキストメニューを表示する際、このウィンドウをフォアグラウンドにしてからコンテキストメニューを表示しています(じゃないとほかのウィンドウがアクティブになったときコンテキストメニューの表示のキャンセルができない)。
フォアグラウンドになった結果、このウィンドウがキー入力を受け取るようになってしまいます。そして Alt+F4 に対するデフォルトの動作は「トップレベルウィンドウを閉じる」ですから、ウィンドウが閉じてしまいます。そして NotifyIcon は関連付けられたウィンドウが閉じられるとタスクトレイから自動的に削除される仕組みのようです。
どうしても今の時点で解決しなければならないのなら、リフレクションでこの内部の NativeWindow を取り出して新たな NativeWindow で WM_SYSCOMMAND / SC_CLOSE を無視するようにすればいいでしょう。
ただ、私自身 NativeWindow 周りはあまり詳しくありませんので、この処理を追加したことによって何らかの不具合が発生するかもしれません。またリフレクションを使用すると言うことは本来未公開のものを操作するわけですから、今後の WinForm の実装変更によって利用できなくなるケースもあるでしょう。
Shell_NotifyIcon を直接使用することも視野に入れてもいいかもしれません。Win32 プログラミングに慣れているなら、NotifyIcon クラスと言うブラックボックスを通さない分理解しやすいと思います。コーディングは面倒ですけど。NotifyIcon クラスの実装が参考になるでしょう(Visual Studio 2008で見る.NET Frameworkのソースコード - @IT)。
消えたら再表示、というスタンスでは多少はマシですが、消えていることを確認するのには内部の NativeWindow の Handle が IntPtr.Zero かどうかをチェックする必要があり、ここで結局リフレクションが必要になってしまいます。
この問題は、私見ですが WinForm のバグであると思います。
プロダクト フィードバック センター にポストしてみてはいかがでしょうか。
// バグと認められてもすぐ直るわけじゃないですが。
-
Hongliangさん
ご回答ありがとうございます。
mkmarimoです。
Hongliang さんからの引用 NotifyIcon は内部にウィンドウメッセージを処理するためのトップレベルウィンドウを持っていますが、これが曲者みたいですね。
コンテキストメニューを表示する際、このウィンドウをフォアグラウンドにしてからコンテキストメニューを表示しています(じゃないとほかのウィンドウがアクティブになったときコンテキストメニューの表示のキャンセルができない)。
フォアグラウンドになった結果、このウィンドウがキー入力を受け取るようになってしまいます。そして Alt+F4 に対するデフォルトの動作は「トップレベルウィンドウを閉じる」ですから、ウィンドウが閉じてしまいます。そして NotifyIcon は関連付けられたウィンドウが閉じられるとタスクトレイから自動的に削除される仕組みのようです。
上記の説明、大変参考になりました。
ご説明いただいた内容を元に自己解決できました。多分。。。
NotifyIconがフォアグラウンドになるタイミングで、プラットフォームSDKの
GetDesktopWindowとSetForegroundWindowを利用してデスクトップをフォアグラウンドに
設定することで現象は発生しなくなりました。
Hongliang さんからの引用 この問題は、私見ですが WinForm のバグであると思います。
プロダクト フィードバック センター にポストしてみてはいかがでしょうか。
// バグと認められてもすぐ直るわけじゃないですが。
私もバグと思います。
プロダクト フィードバックセンターにポストしてみようと思います。
以上、ありがとうございました。
すべての返信
-
FormClosingイベントで制御するっぽいかな?
Minimise Form to System Tray on Close (.NET 2.0)
http://www.vbforums.com/showthread.php?t=545214C# minmize/close to system tray
http://bytes.com/topic/net/answers/850984-c-minmize-close-system-tray -
trapemiyaさん
mkmarimoです。
ご回答ありがとうございます。
trapemiya さんからの引用 FormClosingイベントで制御するっぽいかな?
Minimise Form to System Tray on Close (.NET 2.0)
http://www.vbforums.com/showthread.php?t=545214C# minmize/close to system tray
http://bytes.com/topic/net/answers/850984-c-minmize-close-system-tray上記のページ参考にさせていただきましたが、残念ながら、
私の期待する動きにはなりませんでした。
上記のページは、通常のWindowsアプリケーションのFormを
最小化した際、タスクバーに格納するのではなく、
タスクトレイのアイコンに格納するという前提で書かれているようです。
私の期待する動作では、アプリ開始時からFormは一切表示せず、
タスクトレイにアイコンを表示し、常駐するアプリです。
その際、メインとなるFormをプロパティのVisible=false、もしくは、
Hide()メソッドで非表示にするのですが、こうなると、FormClosingイベントなど
のFormの表示、非表示に関するイベントが発生しなくなり、
解決には至りませんでした。
そもそも私の作成したアプリの開始時の処理なども合っているのか
自信がないんですが、また、何か方法がありましたらご教授ください。
以上、ありがとうございました。
-
NotifyIcon は内部にウィンドウメッセージを処理するためのトップレベルウィンドウを持っていますが、これが曲者みたいですね。
コンテキストメニューを表示する際、このウィンドウをフォアグラウンドにしてからコンテキストメニューを表示しています(じゃないとほかのウィンドウがアクティブになったときコンテキストメニューの表示のキャンセルができない)。
フォアグラウンドになった結果、このウィンドウがキー入力を受け取るようになってしまいます。そして Alt+F4 に対するデフォルトの動作は「トップレベルウィンドウを閉じる」ですから、ウィンドウが閉じてしまいます。そして NotifyIcon は関連付けられたウィンドウが閉じられるとタスクトレイから自動的に削除される仕組みのようです。
どうしても今の時点で解決しなければならないのなら、リフレクションでこの内部の NativeWindow を取り出して新たな NativeWindow で WM_SYSCOMMAND / SC_CLOSE を無視するようにすればいいでしょう。
ただ、私自身 NativeWindow 周りはあまり詳しくありませんので、この処理を追加したことによって何らかの不具合が発生するかもしれません。またリフレクションを使用すると言うことは本来未公開のものを操作するわけですから、今後の WinForm の実装変更によって利用できなくなるケースもあるでしょう。
Shell_NotifyIcon を直接使用することも視野に入れてもいいかもしれません。Win32 プログラミングに慣れているなら、NotifyIcon クラスと言うブラックボックスを通さない分理解しやすいと思います。コーディングは面倒ですけど。NotifyIcon クラスの実装が参考になるでしょう(Visual Studio 2008で見る.NET Frameworkのソースコード - @IT)。
消えたら再表示、というスタンスでは多少はマシですが、消えていることを確認するのには内部の NativeWindow の Handle が IntPtr.Zero かどうかをチェックする必要があり、ここで結局リフレクションが必要になってしまいます。
この問題は、私見ですが WinForm のバグであると思います。
プロダクト フィードバック センター にポストしてみてはいかがでしょうか。
// バグと認められてもすぐ直るわけじゃないですが。
-
Hongliangさん
ご回答ありがとうございます。
mkmarimoです。
Hongliang さんからの引用 NotifyIcon は内部にウィンドウメッセージを処理するためのトップレベルウィンドウを持っていますが、これが曲者みたいですね。
コンテキストメニューを表示する際、このウィンドウをフォアグラウンドにしてからコンテキストメニューを表示しています(じゃないとほかのウィンドウがアクティブになったときコンテキストメニューの表示のキャンセルができない)。
フォアグラウンドになった結果、このウィンドウがキー入力を受け取るようになってしまいます。そして Alt+F4 に対するデフォルトの動作は「トップレベルウィンドウを閉じる」ですから、ウィンドウが閉じてしまいます。そして NotifyIcon は関連付けられたウィンドウが閉じられるとタスクトレイから自動的に削除される仕組みのようです。
上記の説明、大変参考になりました。
ご説明いただいた内容を元に自己解決できました。多分。。。
NotifyIconがフォアグラウンドになるタイミングで、プラットフォームSDKの
GetDesktopWindowとSetForegroundWindowを利用してデスクトップをフォアグラウンドに
設定することで現象は発生しなくなりました。
Hongliang さんからの引用 この問題は、私見ですが WinForm のバグであると思います。
プロダクト フィードバック センター にポストしてみてはいかがでしょうか。
// バグと認められてもすぐ直るわけじゃないですが。
私もバグと思います。
プロダクト フィードバックセンターにポストしてみようと思います。
以上、ありがとうございました。