いくつかの GUI スレッドを「スピンオフ」することは可能ですか?(Application.Run 時にシステムを停止しません)
質問
私の目標
メイン処理スレッド (非 GUI) があり、必要に応じて独自のバックグラウンド スレッドで GUI をスピンオフでき、メインの非 GUI スレッドが動作し続けるようにしたいと考えています。別の言い方をすると、メインの非 GUI スレッドが GUI スレッドの所有者になり、その逆は望ましくないのです。Windows フォーム (?) でもこれが可能かどうかはわかりません。
背景
コントローラーがアセンブリを動的にロードし、共通のコンポーネントを実装するクラスをインスタンス化して実行するコンポーネントベースのシステムがあります。 IComponent
単一のメソッドを使用したインターフェース DoStuff()
.
どのコンポーネントがロードされるかは、XML 構成ファイルを介して構成され、さまざまな実装を含む新しいアセンブリを追加します。 IComponent
. 。コンポーネントは、メイン アプリケーションにユーティリティ機能を提供します。メインプログラムが本来の処理を実行している間、たとえば原子力発電所を制御している場合、コンポーネントはユーティリティ タスクを (独自のスレッドで) 実行している可能性があります。データベースをクリーニングしたり、電子メールを送信したり、面白いジョークをプリンターで印刷したり、何をしますか。私が望むのは、これらのコンポーネントの 1 つで GUI を表示できるようにすることです。当該電子メール送信コンポーネントのステータス情報を含む。
完全なシステムの寿命は次のようになります
- アプリケーションが開始されます。
- ロードするコンポーネントの構成ファイルを確認してください。ロードしてください。
- コンポーネントごとに、次を実行します。
DoStuff()
それを初期化し、独自のスレッドで独自の生活を送らせるようにします。 - アプリケーション関連の主要な仕事を永遠に続けてください。
コンポーネントが GUI を起動する場合、ポイント 3 をまだ正常に実行できません。 DoStuff()
. 。GUI が閉じるまで停止します。そして、GUI が閉じられるまで、プログラムはポイント 4 に進みません。
これらのコンポーネントが独自の Windows フォーム GUI を起動できるようになれば素晴らしいでしょう。
問題
コンポーネントが GUI を起動しようとすると、 DoStuff()
(コードの正確な行は、コンポーネントが実行されるときのものです Application.Run(theForm)
)、コンポーネント、したがってシステムが「ハング」します。 Application.Run()
GUI が閉じるまで行を続けます。さて、起動したばかりの GUI は、予想通り、正常に動作します。
コンポーネントの例。1 つは GUI とは何の関係もありませんが、2 つ目ではピンク色のふわふわのウサギが入ったかわいいウィンドウが起動します。
public class MyComponent1: IComponent
{
public string DoStuff(...) { // write something to the database }
}
public class MyComponent2: IComponent
{
public void DoStuff()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form());
// I want the thread to immediately return after the GUI
// is fired up, so that my main thread can continue to work.
}
}
私はこれを試しましたが運がありませんでした。GUI を独自のスレッドで起動しようとしても、GUI が閉じるまで実行が停止します。
public void DoStuff()
{
new Thread(ThreadedInitialize).Start()
}
private void ThreadedInitialize()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form());
}
GUI をスピンオフして、その後に戻ることは可能ですか? Application.Run()
?
解決
アプリケーション.実行 このメソッドは 1 つ (または複数) のフォームを表示し、すべてのフォームが閉じるまで実行される標準メッセージ ループを開始します。すべてのフォームを閉じるか、アプリケーションを強制的にシャットダウンする以外、そのメソッドから強制的に戻ることはできません。
ただし、 アプリケーションコンテキスト (新しい Form() の代わりに) Application.Run メソッドと ApplicationContext を使用すると、複数のフォームを一度に起動できます。これらがすべて終了した場合にのみ、アプリケーションは終了します。ここを参照してください: http://msdn.microsoft.com/en-us/library/system.windows.forms.application.run.aspx
また、非モーダルに表示するフォームは引き続きメイン フォームと並行して実行されるため、互いにブロックしない複数のウィンドウを持つことができます。これが実際にあなたが達成しようとしていることだと思います。
他のヒント
しっかりとハッキングすればこれは可能だと思いますが、それは良いアイデアではないことをお勧めします。
「Windows」(画面上に表示されるもの)はプロセスと高度に結びついています。つまり、GUI を表示する各プロセスには、ウィンドウの作成と管理に関係するすべてのメッセージ (「ボタンをクリックした」、「アプリを閉じた」、「画面を再描画した」など) を処理するメッセージ ループがあることが期待されます。 ' 等々。
このため、メッセージ ループがある場合は、プロセスの存続期間中は使用できる必要があると多かれ少なかれ想定されています。たとえば、Windows が「終了」メッセージを送信する場合、画面に何も表示されない場合でも、それを処理できるメッセージ ループを用意する必要があります。
最善の策は次のようにすることです。
「メインアプリ」のスタートアップコールapplication.runであることが示されていない偽のフォームを作成し、この偽の形式で渡します。別のスレッドで作業を行い、Gui の作業を行う必要がある場合はメイン スレッドでイベントを発生させます。
これが正しいかどうかはわかりませんが、フォームを新規作成して newForm.Show() を呼び出すだけでコンソール アプリケーションからウィンドウ フォームを実行したことを覚えています。コンポーネントが Application.Run() の代わりにそれを使用している場合は、新しいフォームはブロックすべきではありません。
もちろん、コンポーネントは、作成するフォームへの参照を維持する責任があります。