none
backgroundworkerのRunWorkerCompletedで表示したモーダルウィンドウが移動不可となる場合がある RRS feed

  • 質問

  • form上のスタートボタンを押すとbackgroundworkerが動き、その完了を待ってメッセージボックスを表示するという単純なプログラムを作成しています。

    以下の操作をすると、完了時に表示したメッセージボックスが移動できなくなる(ウィンドウタイトルをつかんで移動)問題があり、対応できずに悩んでいます。

    XP,Windows7で動作確認しました。


    ①formからスタートボタンクリック

    ②backgroundworker動作中に、formのウィンドウタイトルをマウスでつかみっぱなしにする

    ③backgroundworker完了時にメッセージボックスが表示される。このとき表示されたウィンドウをマウスで

    移動しようとしても移動できない。


    以下、formのソースです。解決策があるのでしょうか?

    #pragma once
    
    
    namespace backgroundAndMsgBox {
    
    	using namespace System;
    	using namespace System::ComponentModel;
    	using namespace System::Collections;
    	using namespace System::Windows::Forms;
    	using namespace System::Data;
    	using namespace System::Drawing;
    
    	
    	public ref class Form1 : public System::Windows::Forms::Form
    	{
    	public:
    		Form1(void)
    		{
    			InitializeComponent();
    			//
    			//TODO: ここにコンストラクタ コードを追加します
    			//
    		}
    
    	protected:
    		/// <summary>
    		/// 使用中のリソースをすべてクリーンアップします。
    		/// </summary>
    		~Form1()
    		{
    			if (components)
    			{
    				delete components;
    			}
    		}
    	private: System::ComponentModel::BackgroundWorker^  backgroundWorker1;
    	protected: 
    	private: System::Windows::Forms::Button^  button1;
    
    	private:
    		/// <summary>
    		/// 必要なデザイナ変数です。
    		/// </summary>
    		System::ComponentModel::Container ^components;
    
    #pragma region Windows Form Designer generated code
    		/// <summary>
    		/// デザイナ サポートに必要なメソッドです。このメソッドの内容を
    		/// コード エディタで変更しないでください。
    		/// </summary>
    		void InitializeComponent(void)
    		{
    			this->backgroundWorker1 = (gcnew System::ComponentModel::BackgroundWorker());
    			this->button1 = (gcnew System::Windows::Forms::Button());
    			this->SuspendLayout();
    			// 
    			// backgroundWorker1
    			// 
    			this->backgroundWorker1->DoWork += gcnew System::ComponentModel::DoWorkEventHandler(this, &Form1::backgroundWorker1_DoWork);
    			this->backgroundWorker1->RunWorkerCompleted += gcnew System::ComponentModel::RunWorkerCompletedEventHandler(this, &Form1::backgroundWorker1_RunWorkerCompleted);
    			// 
    			// button1
    			// 
    			this->button1->Location = System::Drawing::Point(106, 88);
    			this->button1->Name = L"button1";
    			this->button1->Size = System::Drawing::Size(75, 23);
    			this->button1->TabIndex = 0;
    			this->button1->Text = L"START";
    			this->button1->UseVisualStyleBackColor = true;
    			this->button1->Click += gcnew System::EventHandler(this, &Form1::button1_Click);
    			// 
    			// Form1
    			// 
    			this->AutoScaleDimensions = System::Drawing::SizeF(6, 12);
    			this->AutoScaleMode = System::Windows::Forms::AutoScaleMode::Font;
    			this->ClientSize = System::Drawing::Size(292, 266);
    			this->Controls->Add(this->button1);
    			this->Name = L"Form1";
    			this->Text = L"Form1";
    			this->ResumeLayout(false);
    
    		}
    #pragma endregion
    	private: System::Void backgroundWorker1_RunWorkerCompleted(System::Object^  sender, System::ComponentModel::RunWorkerCompletedEventArgs^  e) {
    				button1->Enabled = true;
    
    				MessageBox::Show("completed");
    
    			 }
    	private: System::Void button1_Click(System::Object^  sender, System::EventArgs^  e) {
    				button1->Enabled = false;
    				backgroundWorker1->RunWorkerAsync();
    			 }
    	private: System::Void backgroundWorker1_DoWork(System::Object^  sender, System::ComponentModel::DoWorkEventArgs^  e) {
    				 Threading::Thread::Sleep(3000);
    			 }
    	};
    }
    
    

    2012年3月3日 2:00

回答

  • MFC のアプリでも再現できますし、.NET Framework 固有の問題ではないでしょう。
    回避策があるかどうかの深掘りはしていませんが、たぶん難しいか、とっても変なことをしないといけなくなるかも?

    私がその問題を突きつけられた場合、「MFC でも再現可能なので Windows の仕様、あるいは制限と思われる」、「フォーム移動中という状況下に限定され、それによる被害も軽微なので調査工数がもったいない」、「ほかの不具合をなおす、あるいは要望に対応する方が効果が高い」といったところで対応不要の判断に持ち込みたいところです。


    質問スレッドで解決した場合は、解決の参考になった投稿に対して「回答としてマーク」のボタンを押すことで、同じ問題に遭遇した別のユーザが役立つ投稿を見つけやすくなります。

    • 回答としてマーク ym_dan 2012年3月3日 11:29
    2012年3月3日 3:02
    モデレータ

すべての返信

  • MFC のアプリでも再現できますし、.NET Framework 固有の問題ではないでしょう。
    回避策があるかどうかの深掘りはしていませんが、たぶん難しいか、とっても変なことをしないといけなくなるかも?

    私がその問題を突きつけられた場合、「MFC でも再現可能なので Windows の仕様、あるいは制限と思われる」、「フォーム移動中という状況下に限定され、それによる被害も軽微なので調査工数がもったいない」、「ほかの不具合をなおす、あるいは要望に対応する方が効果が高い」といったところで対応不要の判断に持ち込みたいところです。


    質問スレッドで解決した場合は、解決の参考になった投稿に対して「回答としてマーク」のボタンを押すことで、同じ問題に遭遇した別のユーザが役立つ投稿を見つけやすくなります。

    • 回答としてマーク ym_dan 2012年3月3日 11:29
    2012年3月3日 3:02
    モデレータ
  • 自分で試したら、確かに再現しました。びっくりです。

    試しにMessageBoxではなくモードレスにしたら問題ありませんでした。このことからメッセージループ周りに課題がありそうで、したがって回避策の検討は相当な労力がかかることが推測できます(がんばっても、結局、不可能という結論に達しかねません)。

    簡単な回避策としてはモードレスかと。ただ諸事情でモードレスを使えないのであれば、Azulean氏のコメントの通り対応不要という線で進めるしかないかと。

    2012年3月3日 9:07
  • MFCでも発生するという情報ありがとうございます。

    Windowsの仕様、あるいは制限で対処しない方針に持っていこうと思います。

    良くありそうな実装なので、Web上でも情報があるかと探していたのですが探した限りでは情報見つかりませんでした。

    2012年3月3日 11:43
  • メッセージボックス表示前にApplication::DoEvents()をやってみてもダメでした。

    ウィンドウタイトル部分なのでウィンドウマネージャとしてのイベント処理に問題があるのかなと推測します(詳しくないですが…)。

    モーダル表示での解決策を探しているので対処不要方針となりそうです。

    2012年3月3日 11:53
  • Win32APIで

    case WM_COMMAND:

      if(LOWORD(wParam) == CM_TEST)

        SetTimer(hWnd, IDT_TIMER, 2000, 0);

      break;

    case WM_TIMER:

      if(wParam == IDT_TIMER)

        MessagBox(hWnd, "test", "test", MB_OK);

      break;

    これでタイトルバー上で左ボタンを押していると、MessageBoxは移動できなくなります。

    原因は、左ボタンを押していると、WM_SYSCOMMANDの中のメッセージループから

    WM_TIMER,MessageBoxが呼ばれることにあるようです。

    MessageBoxを閉じたときにWM_SYSCOMMANDの終了します。

    MessageBoxを移動しようとして2度目のWM_SYSCOMMANDを呼ぶと

    こっちはマウスを少し移動しただけで終了してしまいます。

    というわけで、最初のWM_SYSCOMMANDを終了する必要がありますが、やり方がまったくわかりませんです。


    • 編集済み snao 2012年3月4日 3:58
    2012年3月4日 3:56
  • 決着はついてるようだし snao さんの記述内容の解説みたいな書込みとなりますが

    1.タイトルバーをドラッグすることでウィンドウ移動中処理となる。

    2.移動途中にスレッドが完了しドラッグウィンドウが移動中のままダイアログがモーダル表示される。

    3.親ウィンドウが移動中であるためダイアログ側は移動中表示ができない

    という状況になっているようです。

    メッセージのネスト状況を考えると、不具合ではなく仕様と考えるべきでしょうね。

    回避方法を軽く思案してみましたが、お勧めできる単純な回避策はありません。

    他の方々の意見どおり対処不要方針が一番です。

    2012年3月4日 13:09