スレッド処理から進捗ダイアログを表示する方法は?

このQ&Aのポイント
  • Windows XPとVC++ 6.0で時間のかかる大量の計算をするプログラムを作成しています。計算部分をスレッド処理にし、進捗ダイアログを表示する方法について教えてください。
  • メインダイアログをモーダルで開き、そのダイアログ内のAボタンクリックでダイアログAを開き、ダイアログA内の実行ボタンクリックで計算処理を開始し、進捗ダイアログを表示したいです。
  • MFC Wizardでダイアログベースのプログラムを作成し、MFCの共有DLLを使用しています。モーダルダイアログからスレッドを開始し、その中で進捗ダイアログをCreateする際に、プログラムが応答なしになる問題が発生しています。解決方法を教えてください。
回答を見る
  • ベストアンサー

スレッド処理からダイアログを表示するには?

Windows XPとVC++ 6.0で 時間のかかる大量の計算をするプログラムを作っています。 計算部分はスレッド処理にして 進捗ダイアログ(CDialogにCProgressCtrlを貼り付けたもの) を表示します。 ・CWinAppのInitInstanceで計算処理開始(AfxBeginThread) ・計算処理内で進捗ダイアログをCreate ・計算の進み具合によって進捗ダイアログのプログレスバーを更新 上記の流れではうまくいっていたのですが 以下のように変更したところ、進捗ダイアログをCreateするところで プログラムが応答なしになってしまうようになりました。 ・メインダイアログ(モーダル) ・メインダイアログのAボタンクリックでダイアログAを開く(モーダル) ・ダイアログAの実行ボタンクリックで計算処理開始(AfxBeginThread) ・計算処理内で進捗ダイアログをCreate ・計算の進み具合によって進捗ダイアログのプログレスバーを更新 MFC Wizardでダイアログベースで作成、MFCの共有DLLを使用しています。 モーダルダイアログ→スレッド→CDialog.Createに 制限があったりするのでしょうか。 どうかご教授ください。

質問者が選んだベストアンサー

  • ベストアンサー
  • redfox63
  • ベストアンサー率71% (1325/1856)
回答No.4

スレッドを作成した側の待機処理ループの中にメッセージポンプを作成してやればよさそうですよ pThread->ResumeThread(); do {   AfxGetApp()->PumpMessage(); } while( WaitForSingleObject( pThread->m_hThread, 0 ) != WAIT_OBJECT_0 ); といった具合で … メインスレッド寝てしまっているので反応無しになるのかも

dahlia63
質問者

お礼

PumpMessageで解決しました。 redfox63さん、どうもありがとうございました!

その他の回答 (3)

  • redfox63
  • ベストアンサー率71% (1325/1856)
回答No.3

AttachThreadInputは根本的な解決になっていないようです 新規に MFCダイアログベースで起こしたプロジェクトで 1) モーダルダイアログを追加 2) このダイアログのボタンでワーカースレッドを起動 3) スレッド制御関数の中でプログレスバーダイアログを作成 といった具合にしてみましたが すんなり動いてしまいました CProgressBarDlg dlg; dlg.Create( リソースID, NULL ); であればすんなり動くようです 親ウィンドウのポインタを渡してしまうとアサーションが起きます 最終的には CDialog::CreateIndirectで AfxGetMainWnd()で親を探しているので同じようにスレッド制御関数でAfxGetMainWnd()を取得して渡してもアサーションです 一番最初にコーディングした際は 確かにダンマリモードになったんですが2回目以降ダンマリモードの状態が再現できない状態です

dahlia63
質問者

お礼

redfox63さん、ご回答ありがとうございます。 当方の環境はWindows XP SP2 + VC++ 6.0 SP6です。 VC++ 6.0 SP5の環境を作って試してみたのですが やはり応答なしになってしまいました。 ソースは以下のようになっています。 ※最初の質問ではモーダルダイアログが2つありましたが  単純にするため1つにしました。その他諸々簡略化してあります。 ※モーダル1つでも当方では同じ現象になります。 *MainApp.h int CalcFunc(int i1, int i2); typedef struct _CALC_PARAMDATA { int i1; int i2; CWnd* pWnd; int ret; } CALC_PARAMDATA ,*PCALC_PARAMDATA; class CMainApp : public CWinApp { ... } UINT CalcThreadProc(LPVOID pParam); *MainApp.cpp BOOL CMainApp::InitInstance() { ... CMainDlg dlg; dlg.DoModal(); ... } UINT CalcThreadProc(LPVOID pParam) { int i1 = ((PCALC_PARAMDATA)pParam)->i1; int i2 = ((PCALC_PARAMDATA)pParam)->i2; CWnd* pWnd = ((PCALC_PARAMDATA)pParam)->pWnd; // ちなみに、ここでCProgressDlgをCreateしても // 応答なしになります int ret = CalcFunc(i1, i2, pWnd); ((PCALC_PARAMDATA)pParam)->ret = ret; } int CalcFunc(DWORD dw1, DWORD dw2, CWnd* pWnd) { CProgressDlg dlg; dlg.Create(IDD_PROGRESSDLG, NULL); // またはpWnd dlg.ShowWindow(SW_SHOW); ... } *MainDlg.cpp BOOL CMainDlg::OnRun() { ... CALC_PARAMDATA ParamData; CWinThread* pThread = NULL; ParamData.i1 = n1; ParamData.i2 = n2; ParamData.pWnd = this; pThread = AfxBeginThread(::CalcThreadFunc, &Param, THREAD_PRIORITY_NORMAL, 0, CREATE_SUSPENDED); pThread->m_bAutoDelete = FALSE; pThread->ResumeThread(); do { } while(WaitForSingleObject(pThread->m_hThread, 0) !=WAIT_OBJECT_0); delete pThread; ... }

dahlia63
質問者

補足

#前回の投稿箇所を誤ってしまったようです。 ↓の次の投稿になります。申し訳ありません。 > redfox63さん、ご回答ありがとうございます。 > 当方の環境はWindows XP SP2 + VC++ 6.0 SP6です。 > VC++ 6.0 SP5の環境を作って試してみたのですが > やはり応答なしになってしまいました。 - - - - - - - - - - - - - - - - - - - - - - - - - すみません、自己解決です。(というか諦め…) スレッドの中でCProgrerss.Createを行わずに 呼び出し側のダイアログ(の実行ボタン)で 進捗ダイアログを作ることにしました。 BOOL CMainDlg::OnRun() { ... CProgressDlg dlg; dlg.Create(IDD_PROGRESSDLG, this); dlg.ShowWindow(SW_SHOW); CALC_PARAMDATA ParamData; CWinThread* pThread = NULL; ParamData.i1 = n1; ParamData.i2 = n2; ParamData.pWnd = this; pThread = AfxBeginThread(::CalcThreadFunc, &Param, THREAD_PRIORITY_NORMAL, 0, CREATE_SUSPENDED); pThread->m_bAutoDelete = FALSE; pThread->ResumeThread(); do { // 進捗の度合い(グローバル変数)によって // CProgressのプログレスバーを進める // 進捗の度合いはスレッド内で更新 } while(WaitForSingleObject(pThread->m_hThread, 0) !=WAIT_OBJECT_0); delete pThread; ... } 首記の「スレッドからダイアログを表示」については まだ腑に落ちない点がありますが、 下記のサイトで 「MFCでは、スレッドまたいでの、CWndオブジェクトのポインタによるウインドウ操作は保証されていません」 と発言している方がいらっしゃいました。 http://homepage1.nifty.com/MADIA/vc/vc_bbs/200505/200505_05050018.html ちょっとMSDNを確認しようと思います。 redfox63さん、たくさんお時間を割いていただきまして恐縮です。 ありがとうございました。

  • redfox63
  • ベストアンサー率71% (1325/1856)
回答No.2

おかしいですね … 当方の環境ですと動作してしまっているのですが WinXP SP2 + VC++6.0 SP5の環境です AfxGetThreadを AfxGetAppに変更してみてはいかがでしょう 別の手段として プログレスダイアログは ダイアログAのボタンクリックイベント内で生成して スレッドプロークの引数に プログレスダイアログのアドレス(ポインタ)を渡すとか 他に引き渡すデータがあるなら 構造体にしてそのメンバーにプログレスダイアログポインタを持たすなどしてはいかがでしょう

  • redfox63
  • ベストアンサー率71% (1325/1856)
回答No.1

親ウィンドウを探しにいって迷子になっているのでしょう ワーカースレッドの場合 AttachThreadInputなどでアプリケーションスレッドをアタッチしてやればいいようです スレッドプロークの中で CProgressDlg dlg; AttachThreadInput( ::GetCurrentThreadId(), AfxGetThread()->m_nThreadID, True); dlg.Create( IDD_PROGRESSDLG, NULL ); dlg.ShowWindow( SW_SHOW ); while( 1 ) {   if ( 終了条件 )     break;   // 何かの処理 } AttachThreadInput( ::GetCurrentThreadId(), AfxGetThread()->m_nThreadID, FALSE); dlg.DestroyWindow(); といった具合で

dahlia63
質問者

補足

redfox63さん、ご回答ありがとうございます。 しかしAttachThreadInputでも同様の結果になってしまいました。。 GetCurrentThreadIdで取得した呼び出し側スレッドのIDと AfxGetThreadで取得した現在実行中のスレッドのIDが 同じ値になっているのですが、そのことと何か関係がありますでしょうか。 また、「親ウィンドウを探しにいって迷子になっているのでしょう」をヒントに Createの第二引数に親ウィンドウのハンドルを渡してみたのですが そうするとDebugモードでは、Createの箇所でDebug Assertion Failedになってしまいます。 (wincore.cppの883行目) hWndがウィンドウかどうかの判定で「ウィンドウでない」と見なされるようです。 CProgressDlg dlg; AttachThreadInput(::GetCurrentThreadId(), AfxGetThread()->m_nThreadID, TRUE); dlg.Create(IDD_PROGRESSDLG, hWnd); // ここで応答なし またはDebug Assertion Failed dlg.ShowWindow(SW_SHOW); ...

関連するQ&A

  • ダイアログを終了させてもハンドルが1つ増加したまま。。。

    VC++のMFCを使ってダイアログベースのEXEを造っています。 ハンドルのリークが発生し、困っています。 <動作内容> メインのダイアログで、ボタン押下により別のダイアログを表示します。 DoModal()でモーダルダイアログを作成します。 別のプロセスからブロードキャストされるメッセージを処理したいため、 ボタン押下のルーチンで DoModal() せずに、スレッドを作成し、 そのスレッドで DoModal() を実行しています。 スレッド作成は AfxBeginThread() を使用しています。 <サンプルソース> ●メインダイアログ // ボタン押下のルーチン void CTestModalDlg::OnButton1() { AfxBeginThread( TestDlgThread, (LPVOID)this, THREAD_PRIORITY_NORMAL ); } // スレッド static UINT TestDlgThread(LPVOID pThis) { CTestDlg1 Dlg; Dlg.DoModal(); } ●DoModal() で表示されるダイアログ 何も手を加えてない、デフォルトのまま。 <結果> DoModal() で表示されたダイアログを CDialog::OnCancel() で終了させ、 メインのスレッドが終了しても、ハンドルカウントが1つ増加しています。 Sleep() を入れて値をみてみると、 ・スレッド作成:2増加 ・DoModal() でダイアログ表示:1増加 ・OnCancel() で終了:増減なし ・スレッド終了:2減少 =>結果、1増加となっていました。 以下のパターンでは問題ありませんでした。 ・スレッドを作成+終了(ダイアログ表示しない) ・スレッドを作成せずに、ボタン押下ルーチンから DoModal() でダイアログ表示+終了 ということで、AfxBeginThread()、DoModal() 自体は問題ないのですが、 スレッドを作成して DoModal() するとリークが発生します。 識者の方、原因、対策など、ご教授願います。

  • メインダイアログが最背面に表示されてしまいます。

    お世話になります、fujitomoです。 C++のダイアログベースのプログラムを作成しているのですが、ご質問 させていただきたい件がありまして、こちらへ書かせて頂きました。 質問させて頂きたい内容は、メインダイアログにて、他ダイアログをAfxBeginThread()を用いて別スレッドでモーダル表示させた時、その他ダイアログを他ダイアログ内でEndDialog(IDOK)で閉じた際に、メインダイアログが最背面になってしまうのですが、この原因として考えられることはありませんでしょうか? ちなみに、他ダイアログを別スレッドではなく、メインダイアログ内にてモーダル表示をし、EndDialog(IDOK)で閉じた際には、メインダイアログが最背面になることはありませんでした。 参考となるご意見がありましたら、よろしくお願い致します。 開発環境は Visual Studio 2005です。

  • 別スレッドからメインダイアログのコントロールにアクセスする方法

    VC++2005 MFC WindowsXPを使用しています。 メインダイアログに作成したエディットボックスに フォーカスをセットしたいのですが、 別スレッドから行うにはどのようにすればよいのでしょうか? メインダイアログにあるボタンを押すと別スレッドの処理に入り、 その中でメインダイアログのコントロールを操作しようとしています。 //フォーカスをセット CWnd* MyEdit = ( CWnd* )this->GetDlgItem( IDC_EDIT1 ); MyEdit->SetFocus(); 宜しくお願い致します。

  • スレッド処理内の変数の参照について

    大変困ってます。 今、親プロセスからスレッド内の変数に設定したいのですが方法がわかりません。 スレッドからの場合、普通行えばいいのでわかるのですが ・・・。 (詳細) メインプロセス(A) クリエートスレッド後のスレッド(B)  1.(A)で(B)をCreateThread  2.(A)の本処理で(B)の構造体にmemcpyの方法が    わかりません。  3.知っている方がいましたら教えていただけませんか。  4.読みずらい文章ですみません。宜しくお願いします。

  • メインスレッドのPostMessageとマルチスレッドでの処理

    お世話になります、fujicafeと申します。 現在Visual Studio 2005にてVC++のプログラムの作成をしており、 お聞きしたいことがありまして、こちらにて投稿させていただきました。 質問したい内容はPostMessageによる非同期呼び出しによるメインスレッド関数の処理と、別スレッドからのメインスレッド関数呼び出しによる処理の違いについてです。 現在、メインスレッドにマルチメディアタイマーを使って、ある時間間隔毎にメインスレッドの関数を呼び出しています。その関数の呼び出しにはPostMessageを使用しています。 その関数の呼び出しをAfxBeginThreadを使用して、メインスレッドから別スレッドを作成し、その別スレッド内にてメインスレッドのオブジェクトを作成して、実行したいメインスレッドの関数を呼び出すと、もともとのPostMessageを使用して関数を呼び出すのとでは処理としてどのような違いがあるのでしょうか? スレッドを別にするということで、メインスレッド側に負荷をかけないといったことが違うのでしょうか? だいぶ検討はずれの質問かとは思いますが、ご教授よろしくお願いいたします。

  • 閉じてしまったダイアログのエディットボックスへの書き込み禁止方法

    VC++ 6.0 MFC ダイアログベースです。 メインダイアログ画面で、ボタンを押下した時に、別ダイアログ(子ダイアログ)が呼び出されて、 その子ダイアログ内では、ワーカースレッドを使用して処理を行い、 処理結果をエディットボックスに表示させています。 子ダイアログを閉じる時に、WaitForSingleObject を使って、スレッドを完全に終了させてからダイアログを閉じるように しているのですが、スレッドの処理が、エディットボックスに書き込む時に閉じてしまうと、 スレッドがそこで止まってしまいスレッドが終了しません。 このような場合どうしたら良いのでしょうか? ・閉じるボタンが押されたら、エディットボックスに書き込まないようにすれば良いと思い、 (フラグを立てて試したのですが、フラグチェック後に閉じるが押されることがあり失敗) // スレッドの開始 m_Thread = AfxBeginThread(ThreadFunc, this);    // この時、子ダイアログを閉じてしまうと // INFINITEなので、ずっと止まってしまいます。 CString data = "処理結果"; case 0: m_M0edit.SetWindowText(data); break; case 1: m_M1edit.SetWindowText(data); break; case 2: m_M2edit.SetWindowText(data); break; ・ ・ ・ ・ // 子ダイアログを閉じる時に呼び出されます。 // スレッドでの処理が完全に終わるのを待ちます。 afx_msg void CSelecting_Dlg::OnClose() {   DWORD dwRet   dwRet = WaitForSingleObject(m_Thread->m_hThread, INFINITE);   EndDialog(0); } /***************/ 失敗談 /***************/ // スレッドの開始 m_Thread = AfxBeginThread(ThreadFunc, this);    // この時、子ダイアログを閉じてしまうと // INFINITEなので、ずっと止まってしまいます。 CString data = "処理結果"; if (m_openFlag == 1){ //フラグを見て書き込みするかチェックする。   switch (t){      case 0: m_M0edit.SetWindowText(data); break;      case 1: m_M1edit.SetWindowText(data); break;      case 2: m_M2edit.SetWindowText(data); break; ・ ・ ・ ・   } } // 子ダイアログを閉じる時に呼び出されます。 // スレッドでの処理が完全に終わるのを待ちます。 afx_msg void CSelecting_Dlg::OnClose() {   DWORD dwRet   m_openFlag = false; //閉じるボタンが押されたら フラグを降ろす。   dwRet = WaitForSingleObject(m_Thread->m_hThread, INFINITE);   EndDialog(0); }

  • マルチスレッドのスレッド数を増やしたい

    <プログラム環境> Windows XP VC++6.0 MFC AppWizard(exe) ダイアログベース <質問概略> CWinThread*を使って無限ループのスレッドを作ったのですが、 無限ループのスレッドをもう一つ作り、同時に実行しようとするとアクセスバイオレーションのエラーでます。 複数スレッドの作り方を教えていただけますと幸いです。 <質問詳細> 現状の正常に実行できるソースの必要最小限を書きます。 <.h> class CMyDlg : public CDialog{ public:   static UINT ThreadFunc( LPVOID pParam );   void Thread(); // スレッドの処理 protected:   CWinThread* m_pThread;//スレッドのアドレス }; <.cpp> void CMyDlg::OnButton(){   m_pThread = AfxBeginThread( ThreadFunc, this );   for(;;) /*無限ループ処理1*/ ; } UINT CMyDlg::ThreadFunc( LPVOID pParam ){   ((CMyDlg*)pParam)->Thread();   return 0; } void CMyDlg::Thread(){   for(;;) /*無限ループ処理2*/ ; } これに、 void CMyDlg::Thread2(){   for(;;) /*無限ループ処理3*/ ; } のようなスレッドを追加したいのですが全然出来ません。 宜しければご指摘お願い致します。

  • java のスレッドについて

    java のスレッドについて javaでスレッドを作って処理をさせたいと思います。 メインのクラスでスレッドAを呼び出し、スレッドAで計算をしてその結果をメインのクラスで利用したい場合、一番簡単な方法は何でしょうか? なお、メインのクラスは、 while (Thread.isAlive()); な感じでスレッドが終わるまで処理を止めておきたいと思います。 簡単なコードを書いていただけないでしょうか?

    • ベストアンサー
    • Java
  • MFC モーダルダイアログ表示中に

    MFCでプログラムを組んでいます。 以下のような状態なのですが、解決できる方法はありますか? (1) メインアプリケーションクラス(CWinAppクラスの派生クラス)から、フレームウィンドウを表示。 表示方法:CreateNewFrame()実行 → InitialUpdateFrame()実行 (2) (1)で表示されたウィンドウからモーダルダイアログを表示。 表示方法:モーダルダイアログクラスのDoModal()実行。 (3) メインアプリケーションクラスから(1)で表示されたウィンドウに対して、WM_CLOSEをSendMessage()。 (3)の実行後 (1)、(2)で表示された画面は両方とも閉じますが、モーダルダイアログでアサートします。 目的は、両方の画面が表示されている状態でメインアプリケーションを終了するときに、両方の画面をアサートすることなく閉じることです。 そもそもモーダルダイアログを外部から閉じること自体できるのかどうかわからないのですが、 もしできないとしたら、目的を達成するためにはどうすれば良いのでしょうか? よろしくお願いします。

  • ダイアログのOKボタンを押してウィンドウを生成する方法は?

    ダイアログのOKボタンを押すとウィンドウが生成する方法を教えてください。 MFCでプログラミングしてメインウィンドウをダイアロボックスにしています //アプリケーション class CMyApp : public CWinApp { public: virtual BOOL InitInstance(); }; //メインダイアログ class CMyDlg : public CDialog { public: CMyDlg(CWnd *pParentWnd=NULL); protected: virtual void OnOK(); virtual void OnCancel(); }; #include <afxwin.h> #include <afxdlgs.h> #include <string.h> #include "dlg.h" #include "resource.h" CMyApp myApp; BOOL CMyApp::InitInstance(){ m_pMainWnd = new CMyDlg; m_pMainWnd ->ShowWindow(m_nCmdShow); m_pMainWnd ->UpdateWindow(); return TRUE; } CMyDlg::CMyDlg(CWnd* pParent /*=NULL*/) { Create("MYDLG"); } としています。 ダイアログはできるのですが、ここからウィンドウを生成する方法を教えてください