こんにちはゲストさん。会員登録(無料)して質問・回答してみよう!

解決済みの質問

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

Windows XPとVC++ 6.0で
時間のかかる大量の計算をするプログラムを作っています。

計算部分はスレッド処理にして
進捗ダイアログ(CDialogにCProgressCtrlを貼り付けたもの)
を表示します。

・CWinAppのInitInstanceで計算処理開始(AfxBeginThread)
・計算処理内で進捗ダイアログをCreate
・計算の進み具合によって進捗ダイアログのプログレスバーを更新

上記の流れではうまくいっていたのですが
以下のように変更したところ、進捗ダイアログをCreateするところで
プログラムが応答なしになってしまうようになりました。

・メインダイアログ(モーダル)
・メインダイアログのAボタンクリックでダイアログAを開く(モーダル)
・ダイアログAの実行ボタンクリックで計算処理開始(AfxBeginThread)
・計算処理内で進捗ダイアログをCreate
・計算の進み具合によって進捗ダイアログのプログレスバーを更新

MFC Wizardでダイアログベースで作成、MFCの共有DLLを使用しています。

モーダルダイアログ→スレッド→CDialog.Createに
制限があったりするのでしょうか。
どうかご教授ください。

投稿日時 - 2008-08-10 22:10:53

QNo.4242370

困ってます

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

スレッドを作成した側の待機処理ループの中にメッセージポンプを作成してやればよさそうですよ

pThread->ResumeThread();
do {
  AfxGetApp()->PumpMessage();
} while( WaitForSingleObject( pThread->m_hThread, 0 ) != WAIT_OBJECT_0 );
といった具合で …

メインスレッド寝てしまっているので反応無しになるのかも

投稿日時 - 2008-08-13 03:20:24

お礼

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

投稿日時 - 2008-08-14 11:24:27

このQ&Aは役に立ちましたか?

25人が「このQ&Aが役に立った」と投票しています

回答(4)

ANo.3

AttachThreadInputは根本的な解決になっていないようです

新規に MFCダイアログベースで起こしたプロジェクトで
1) モーダルダイアログを追加
2) このダイアログのボタンでワーカースレッドを起動
3) スレッド制御関数の中でプログレスバーダイアログを作成

といった具合にしてみましたが すんなり動いてしまいました
CProgressBarDlg dlg;
dlg.Create( リソースID, NULL );
であればすんなり動くようです

親ウィンドウのポインタを渡してしまうとアサーションが起きます
最終的には CDialog::CreateIndirectで AfxGetMainWnd()で親を探しているので同じようにスレッド制御関数でAfxGetMainWnd()を取得して渡してもアサーションです

一番最初にコーディングした際は 確かにダンマリモードになったんですが2回目以降ダンマリモードの状態が再現できない状態です

投稿日時 - 2008-08-12 08:55:15

補足

#前回の投稿箇所を誤ってしまったようです。
↓の次の投稿になります。申し訳ありません。
> 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さん、たくさんお時間を割いていただきまして恐縮です。
ありがとうございました。

投稿日時 - 2008-08-12 21:13:09

お礼

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;
...
}

投稿日時 - 2008-08-12 20:31:02

ANo.2

おかしいですね … 当方の環境ですと動作してしまっているのですが
WinXP SP2 + VC++6.0 SP5の環境です

AfxGetThreadを AfxGetAppに変更してみてはいかがでしょう

別の手段として プログレスダイアログは ダイアログAのボタンクリックイベント内で生成して
スレッドプロークの引数に プログレスダイアログのアドレス(ポインタ)を渡すとか
他に引き渡すデータがあるなら 構造体にしてそのメンバーにプログレスダイアログポインタを持たすなどしてはいかがでしょう

投稿日時 - 2008-08-11 23:37:15

ANo.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();

といった具合で

投稿日時 - 2008-08-11 14:54:04

補足

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);
...

投稿日時 - 2008-08-11 21:46:46

あなたにオススメの質問