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() するとリークが発生します。

識者の方、原因、対策など、ご教授願います。

A 回答 (2件)

//メンバ変数として


CDialog* m_pDlg;

void CTestModalDlg::OnButton1()
{
if(m_pDlg==NULL){
m_pDlg=new CDialog;
m_pDlg->Create(IDD_DIALOG1);
m_pDlg->ShowWindow(SW_SHOW);
}
}

void CTestModalDlg::OnDestroy()
{
CDialog::OnDestroy();

if(m_pDlg!=NULL){
delete m_pDlg;
}
}
こんな感じでモードレスダイアログできますよ。
じゃ。
    • good
    • 0
この回答へのお礼

アドバイスありがとうございました。
なぜか、Create()、ShowWindow()してもダイアログが表示されませんでした。
メインのダイアログはアクティブな状態だったので、仕方なくキャンセルボタンを
押したらアプリケーションエラーになってしまいました。
デバッグしようと思ってステップ実行させたら、ちゃんとダイアログが表示されました。
不思議。。。
頑張ってやってみます。
いつも適切なアドバイスありがとうございます。

お礼日時:2001/07/13 19:04

TestDlgThread関数の戻り値がないからでは、ないですか?


てゆうか、モーダルダイアログじゃなくて、モードレスダイアログを作ったらどうでしょう?

この回答への補足

すいません、記述もれです。
スレッドの関数では「return(0);」しています。
スレッドからモーダルダイアログを作成しているのは以下の理由です。
・操作できるダイアログは一番上の(DoModalで作成された)ダイアログだけ
・DoModalで作成されたダイアログが閉じられたタイミングで(Domodal関数が
戻ってきた時)、処理をしたい。
・メインのスレッドでは、他のプロセスからのメッセージを受信したい

#「スレッド+モーダル」だけNGというのが解せないです。。。

あと、追加の質問のようになってしまい申し訳ないのですが、
モードレスダイアログの作成についてご存知でしたら教えてください。

サンプルソースを以下のようにしました。
IDD_DIALOG1 は、DoModal で表示しようとしていた CTestDlg1 クラスの
ダイアログのIDです。

void CTestModalDlg::OnButton1()
{
CDialog::Create( IDD_DIALOG1, NULL );
}

しかし、実行したところ Create() で落ちてしまいました。
単純に Create() だけでは駄目でしょうか?

補足日時:2001/07/12 10:13
    • good
    • 0

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!

このQ&Aを見た人が検索しているワード

このQ&Aと関連する良く見られている質問

Qダイアログのボタンが押された時に、その「ボタン」のあるダイアログと、すでに表示されている他のダイアログを消したいです

Win2000/MFC/ダイアログベース
で作業しています。初心者です(^^;)

Aというダイアログで「あるボタンa」を押してDoModalでBというダイアログを表示させ、Bダイアログで「あるボタンb」を押してDoModalでCというダイアログを表示させ、Cダイアログで「あるボタンc」を押した時にBダイアログとCダイアログを閉じたい(消したい)のですが
EndDialogで消せません
DoModalの戻り値をret_b,ret_cとした場合
EndDialog(ret_c);
EndDialog(ret_b);
としたのですが。。。

やり方がおかしいのか、よくわかりません・・
初心者でもわかりやすいお返事お待ちしています(^^;)

Aベストアンサー

各ダイアログボックスを CDialog の派生クラスとして次のように定義したとします。

CDlgA --- A というダイアログボックス
CDlgB --- B というダイアログボックス
CDlgC --- C というダイアログボックス

CDlgA のあるメンバ関数から CDlgB::DoModal() でダイアログボックス B を表示させます。 CDlgB のあるメンバ関数で EndDialog() を実行すればダイアログボックス B を閉じることができます。ここで、 CDialog::EndDialog() は引数として INT 型の値をひとつ渡すことができますが、その値が先の CDlgB::DoModal() の戻り値となります。
戻り値がある特定の値だったとき、つまり、質問の例ではダイアログボックス B で EndDialog(ret_b) が実行されたことが特定できたときに CDlgA も EndDialog() を実行すれば連鎖してダイアログボックスを閉じることができます。
ダイアログボックス C についても同様です。

実際のコーディングでは次のようになると思います。
ただし、ダイアログボックス A もモードレスで表示されていると仮定しています。



#define ret_b (-1) // 特定の値
#define ret_c (-1) // ID_OK や ID_CANCEL と重ならないように注意

...

(CDlgA の実装)

void CDlgA::OnButtonA() // あるボタン a が押されたときの処理
{
CDlgB dlg; // ダイアログボックス B

if (dlg.DoModal() == ret_b) // モードレスで表示して、戻り値をチェック
EndDialog(-1); // ダイアログボックス A を閉じる
}

...

(CDlgB の実装)

void CDlgB::OnButtonB() // あるボタン b が押されたときの処理
{
CDlgC dlg; // ダイアログボックス C

if (dlg.DoModal() == ret_c) // モードレスで表示して、戻り値をチェック
EndDialog(ret_b); // ダイアログボックス B を閉じる
}

...

(CDlgC の実装)

void CDlgC::OnButtonC() // あるボタン c が押されたときの処理
{
EndDialog(ret_c); // ダイアログボックス C を閉じる
}

各ダイアログボックスを CDialog の派生クラスとして次のように定義したとします。

CDlgA --- A というダイアログボックス
CDlgB --- B というダイアログボックス
CDlgC --- C というダイアログボックス

CDlgA のあるメンバ関数から CDlgB::DoModal() でダイアログボックス B を表示させます。 CDlgB のあるメンバ関数で EndDialog() を実行すればダイアログボックス B を閉じることができます。ここで、 CDialog::EndDialog() は引数として INT 型の値をひとつ渡すことができますが、その値...続きを読む

Qダイアログを継承したダイアログの作成について

お世話になります。
今回お聞きしたいことは、
オリジナルに作成したダイアログを継承したオリジナルのダイアログを作成した際に、オリジナルに作成した基底ダイアログのダイアログ上のコントロールや、そのコントロールの位置を継承することはできますでしょうか?
ということです。
何がしたいかといいますと、4つのダイアログをメインダイアログから呼び出すのですが、その4つのダイアログには共通するコントロールがあり、そのコントロールの動作もその位置も全く同じなのです。
そのため、いちいち4つのダイアログにコントロールの貼り付けや、処理を記入せずに、その共通のコントロールをもった基底ダイアログを作成して、その基底ダイアログの継承としてダイアログが作れたらなと思い、質問させて頂きました。
継承をすると、基底クラスの関数等を使うことができるというのはわかるのですが、ダイアログ上のコントロールの位置までは不可能なのかなと疑問に思いました。
ご回答を宜しくお願い致します。

開発環境は
Windows CE 6.0
Visual Studio 2005
です。

お世話になります。
今回お聞きしたいことは、
オリジナルに作成したダイアログを継承したオリジナルのダイアログを作成した際に、オリジナルに作成した基底ダイアログのダイアログ上のコントロールや、そのコントロールの位置を継承することはできますでしょうか?
ということです。
何がしたいかといいますと、4つのダイアログをメインダイアログから呼び出すのですが、その4つのダイアログには共通するコントロールがあり、そのコントロールの動作もその位置も全く同じなのです。
そのため、いちいち4つの...続きを読む

Aベストアンサー

私、CEの経験がないので厳密なところはわからないのですが、
一般的なMFCであれば、可能です。
ただし、条件により困難さが変わります

1)4つのダイアログが全て同じコントロールのみからなる場合
  コントロールは同じだが、ラベルのキャプションが違うとか
  リストボックスのデータが違うとかいう場合です。

  非常に単純です。普通の派生と全く同じ感じです
  OnInitDialog等適当な関数をオーバーライドするだけです。

2)共通のコントロール以外に独自のコントロールも持つ場合
  多少困難です
  いくつかアプローチが考えられます。
  a) ダイアログ・テンプレートを個別に持つ方法
    リソース内のダイアログ・テンプレートは(共通のコントロールも含め)
    コピー&ペーストなどで独自に作成します(4つ作ります)
それぞれを制御するクラスですが、
    ウィザードのサポートを受けられない
    (ウィザードではCDialog派生にされてしまう)
    ので、既存のCDialog派生クラスを参照して
    自力で希望のCDialog派生からさらに派生させるようにします

    リソースIDとクラスの結びつけ方法などは既存のクラスを
    参考にしてください

  b)リソースも完全に共通化する
    ちょっと難しいかもしれません。
    OnInitDialog等で自力でコントロールをCreateします。
    レイアウトが視覚的にできないので非常に困難ですが、
    隣接するコントロールの位置情報をもとに計算するなどしてください。

    ダイアログの大きさに余裕があり、
    追加するコントロールも小さなもの
    (1行のみのエディット等)であれば、
    最初から場所をstaticコントロールで場所を確保しておき
    非表示にしておいて、
    派生クラスで、そのstaticの位置を取得するとかなり楽ができます。

    派生クラスで、本格的にレイアウトが変わってしまう場合は、
    レイアウトを頭に入れながら、Createし、試行錯誤で
    完成させることになるでしょう。


以上の方法がCEでも通じるのかわかりませんが、
参考になれば幸いです。    

   

私、CEの経験がないので厳密なところはわからないのですが、
一般的なMFCであれば、可能です。
ただし、条件により困難さが変わります

1)4つのダイアログが全て同じコントロールのみからなる場合
  コントロールは同じだが、ラベルのキャプションが違うとか
  リストボックスのデータが違うとかいう場合です。

  非常に単純です。普通の派生と全く同じ感じです
  OnInitDialog等適当な関数をオーバーライドするだけです。

2)共通のコントロール以外に独自のコントロールも持つ場合
 ...続きを読む

Q子ダイアログのデータを親ダイアログで取得するには

VC6.0にてMFCでダイアログベースアプリケーションを作成しています。

親ダイアログ(TestDlg.cpp)のメニューを選択すると、子ダイアログ(SetDlg.cpp)が開き、テキストボックスに文字を入力し、ボタン押下によりテキストボックスの入力数字を確定(グローバル変数(igStatu)に代入)しています。
子ウインドウで入力確定したデータを、親ダイアログにて使用したいのですが、データが受け渡されずに、親ダイアログ側にデータが入りません。
どのようにしたら、子ダイアログで設定したデータを親ダイアログで取得することができるのでしょうか?

C++、MFCともに全然わかっていませんがお願いします。

グローバル変数は、TestDlg.cpp、SetDlg.cppがインクルードしているTest.h内にstatic宣言しています。
static宣言しないとリンクエラー2005になってしまいます。

Aベストアンサー

Test.h内で
extern int igStatu;

TestDlg.cpp内で
int igStatu;

とすればいいのでは?
externについて調べてみてください。

参考URL:http://homepage3.nifty.com/mmgames/c_guide/20-02.html

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

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

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

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

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

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

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

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

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

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

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

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

Aベストアンサー

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

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

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

QワーカスレッドAfxBeginThreadのスレッド起動を遅延させたい

アラーム情報などをBASP21を用いてE-Mail送信するプログラムを
マルチスレッドにしていました。
自己メールサーバーを立てていた場合は、1秒間に何件でもセッション
張ってもよかったのですが、訳あってOCNのメールサーバーを使うように
したところ、1秒間に複数のセッションを張ると迷惑メール送信
と認識されるため、接続が拒否されていまいます。

そこで、マルチスレッド化しているアラームメールの起動間隔を伸ばす
ためにAfxBeginThreadの後に

Sleep( 5* 1000 );

を入れて1秒間に複数送信しないようにしたつもりなんです
が、何故かSleepが効かな状況です。

エラー内容としては、次のように表示されます。
Too Many Session 421
細かく見ていくと、状況によって次のエラーでした。
http://homepage1.nifty.com/yito/anhttpd/winsock_error.html
10060 WSAETIMEDOUT
10061 WSAECONNREFUSED Connection refused

ログを見てもスリープが効いておらず、間隔なしにアラームメールが
1秒間に10件近く送信されてしまいます。


一応渡されたスレッドの最後に
AfxEndThreadを書いてみたり書かないようにしてみたんですが、
状況は変わらずです。

#include "stdafx.h"
#include "windows.h"

for( i=0;i<ALARM_MAX;i++ ){
m_pThreadCL[i] = AfxBeginThread(ThreadProcCL, (LPVOID)i,THREAD_PRIORITY_NORMAL)
Sleep( 5 * 1000); //::Sleep( 5 * 1000 );の間違い?
}
念の為
スレッド起動をしなければいいのですが、単純にはいかなかった
ため、応急処置としてスレッドの起動間隔を遅延させる方法を
どうにかできないものでしょうか。あくまでも応急処置として、
お願いいたします。

アラーム情報などをBASP21を用いてE-Mail送信するプログラムを
マルチスレッドにしていました。
自己メールサーバーを立てていた場合は、1秒間に何件でもセッション
張ってもよかったのですが、訳あってOCNのメールサーバーを使うように
したところ、1秒間に複数のセッションを張ると迷惑メール送信
と認識されるため、接続が拒否されていまいます。

そこで、マルチスレッド化しているアラームメールの起動間隔を伸ばす
ためにAfxBeginThreadの後に

Sleep( 5* 1000 );

を入れて1秒間に複数送信...続きを読む

Aベストアンサー

AfxBeginThreadの 引数dwCreateにCREATE_SUSPENDEDを指定してスレッドを作成するようにしてみてはいかがでしょう

その後 ResumeThreadメソッドでスレッド起動
WaitForSingleObjectなどを使ってスレッドが終了する毎に次スレッドのResumeThreadを呼んでみてはいかがでしょう ...

for(i=0; i< ALARM_MAX;i++) {
m_pThread[i] = AfxBeginThread( ThreadProcCL,(LPVOID)i,THREAD_PRIORITY_NORMAL,0,CREATE_SUSPENDED);
}

for( i=0; i<ALAM_MAX; i++) {
m_pThread[i]->ResumeThread();
while( WaitForSingleObject( m_pThread[i]->m_hThread, 100 )!= WAIT_OBJECT_0) {
Sleep( 5 * 1000 );
}
}

AfxBeginThreadの 引数dwCreateにCREATE_SUSPENDEDを指定してスレッドを作成するようにしてみてはいかがでしょう

その後 ResumeThreadメソッドでスレッド起動
WaitForSingleObjectなどを使ってスレッドが終了する毎に次スレッドのResumeThreadを呼んでみてはいかがでしょう ...

for(i=0; i< ALARM_MAX;i++) {
m_pThread[i] = AfxBeginThread( ThreadProcCL,(LPVOID)i,THREAD_PRIORITY_NORMAL,0,CREATE_SUSPENDED);
}

for( i=0; i<ALAM_MAX; i++) {
m_pThread[i]->ResumeThread();
while( WaitForS...続きを読む


人気Q&Aランキング

おすすめ情報