はじめに
複数のフォームを切り替えるアプリで、処理スレッドからの処理結果をフォーム上のラベルに表示する処理がありました。
処理結果が返却されるタイミングでどの画面が表示されているかわからないため、すべての画面で同じ処理を行うようにしました。
処理スレッドからラベルを操作するため、いつものようにデリゲートでthis->InvokeRequiredを参照してラベルを操作するようにしましたが、場合によってクラッシュすることがありました。
原因
アプリの起動時に各フォームのインスタンスは生成済みでしたが、その際表示していないフォームも存在しました。
その状態で処理スレッドが動作し各画面のラベルを操作すると、表示していないフォームでクラッシュしていました。
表示していないフォームのデリゲートをデバッグしてみると、InvokeRequiredプロパティを参照してもfalseが返却されていました。
そのためそのままラベルに処理結果を反映させることになり、クラッシュしていました。
クラッシュしていたデリゲートメソッド delegate ShowResultDelegate(); /// /// 処理結果を表示する。 /// void ShowResult() { // Invokeが必要な場合 if (this->InvokeRequired) { ShowResultDelegate^ method = gcnew ShowResultDelegate(this, &SampleForm::ShowResult); this->Invoke(method); } // Invokeが不要な場合 // 表示していないフォームの場合、処理スレッドから呼び出してもこっちが実行されてしまう。 else { // ラベルに処理結果を表示する。 ResultLabel->Text = "処理結果"; } }
対応
調べたところ、表示されたかどうかはそのフォームのウィンドウハンドルが存在するかどうかをチェックすればよいようです。
ウィンドウハンドルが存在するどうかをチェックする方法は、次の2つの方法があるようです。
- FindWindowメソッドでフォーム自身のウィンドウハンドルを取得し、ウィンドウハンドルが生成されているか判断する。
- IsHandleCreatedプロパティでフォーム自身のウィンドウハンドルが生成されているか判断する。
2.のIsHandleCreatedメソッドがフォームに用意されているので、今回はこちらを使うことにしました。
対応したデリゲートメソッド delegate ShowResultDelegate(); /// /// 処理結果を表示する。 /// void ShowResult() { // このフォームが表示されていない場合 if (!this->IsHandleCreated) { // 何もせず、終了する。 return; } // Invokeが必要な場合 if (this->InvokeRequired) { ShowResultDelegate^ method = gcnew ShowResultDelegate(this, &SampleForm::ShowResult); this->Invoke(method); } // Invokeが不要な場合 else { // ラベルに処理結果を表示する。 ResultLabel->Text = "処理結果"; } }
API Reference
FindWindowメソッド
IsHandleCreatedプロパティ
おわりに
最初は定石通りデリゲートでthis->InvokeRequiredを判断してるのになぜクラッシュするかわかりませんでした。
何はともあれ、原因と対応方法がわかってよかったです。
紹介している一部の記事のコードはGitlabで公開しています。
興味のある方は覗いてみてください。
私が勤務しているニューラルでは、主に組み込み系ソフトの開発を行っております。
弊社製品のハイブリッドOS Bi-OSは高い技術力を評価されており、特に制御系や通信系を得意としています。
私自身はiOSモバイルアプリやウィンドウズアプリを得意としております。
ソフトウェア開発に関して相談などございましたら、お気軽にご連絡ください。
また一緒に働きたい技術者の方も随時募集中です。
興味がありましたらご連絡ください。
EMAIL : info-nr@newral.co.jp / m-futamata@newral.co.jp
TEL : 042-523-3663
FAX : 042-540-1688