質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

90.87%

  • C++

    3115questions

    C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

  • Visual Studio

    1611questions

    Microsoft Visual StudioはMicrosoftによる統合開発環境(IDE)です。多種多様なプログラミング言語に対応しています。

  • VC++

    129questions

    VC++ (Visual C++) とは、Microsoft製のC++のための統合開発環境です。

  • MFC

    109questions

    MFC (Microsoft Fouondation Class)とは、MicrosoftがVC++用に開発したWindows用アプリケーションのフレームワークです。

  • マルチスレッド

    48questions

    マルチスレッドは、どのように機能がコンピュータによって実行したのかを、(一般的にはスレッドとして参照される)実行の複合的な共同作用するストリームへ区分することが出来ます。

メインスレッドのコントロールを参照しているサブスレッドがアプリケーション終了時にコントロールの値の参照を正常に行えない[マルチスレッド][C++/MFC]

解決済

回答 3

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 606

notgoodpg

score 18

タイトルをより適切に変更しました

前提・実現したいこと

MFCアプリケーションを閉じる動作を行う際、コントロールが参照不能になる前に処理を行いたい。
 閉じる動作を行う際にコントロールが参照不能になる以前の状態で処理を行えるイベントが知りたい。
閉じる動作を行う際にコントロールが参照不能になる現象を解消、または回避したい。

  • 作成中のアプリケーションはダイアログベースのMFCアプリケーションです。
  • OnDestroy()イベント関数内で、CDialog::OnDestroy()が走る前にGetExitCodeThread()で別スレッドの終了コードを取得して別スレッドの終了を確認してからアプリケーションを終了する処理を実装しようとしています。
  • 別スレッドは1つです。
  • 別スレッド内ではループで処理を行い、その中ではメインスレッドのコントロールの値を参照しています。
  • メインスレッドではGetExitCodeThread()を実行する前に別スレッドのループを終わらせるフラグを立てて、別スレッドが終了したときにGetExitCodeThread()でexitCode = 0 となるようにしいます。
  • OnDestroy()を使用しているのはブレークポイントを設置して観察して、OnClose()や~CMyAppDlg()よりも早く実行されることを確認したためです。

実際のソースコードを提示できないため抽象的な質問になり申し訳ありませんが、アドバイスなど頂けたら幸いです。
よろしくお願いいたします。

発生している問題・エラーメッセージ

OnDestory()に処理が移って別スレッドのループ終了フラグがtrueになったあと
別スレッドが最後のループに入ったときに、スライドコントロールの位置を参照しようと( CSliderCtrl::GetPos() )すると
エラーメッセージが出るわけでも突然プロセスが終了するわけでもなく、GetExitCodeThread()の値を監視しているループから抜けられずアプリケーションが終了しなくなってしまいます。
別スレッドではCSliderCtrl::GetPos()の処理に入ってから処理が進まなくなります。

該当のソースコード

このサイトなどを参照するとOnDestroy()より先にOnClose()が走るようなので修正しました。
OnDestroy内に記述でもよいとのご指摘があったので元に戻し、ご指摘のあった部分を修正しました。

void CMyAppDlg::OnDestroy()
{
    // デストロイする前にカメラスレッドを終了させる
    WaitForSingleObject( this->m_mutex, 0);
    this->m_exitFlagAnotherThd = true;
    ReleaseMutex( this->m_mutex);

    DWORD exitCode = -1;
    do{
                // this->m_hAnotherThread は ResumeThread( this->m_hAnotherThread);によって開始済みのスレッド
        if( !GetExitCodeThread( this->m_hAnotherThread, &exitCode))
        {
            TerminateThread( this->m_hAnotherThread, E_FAIL);
            break;
        }
    }while( exitCode == STILL_ACTIVE);
    // mutex
    CloseHandle(this->m_mutex);

    CDialog::OnDestroy();

    // TODO: ここにメッセージ ハンドラ コードを追加します。
}
// 別スレッドの実行内容
DWORD WINAPI CMyAppDlg::ExecThread()
{
    WaitForSingleObject( this->m_mutex, 0);
    this->m_exitAnotherThd = false; // ループ終了フラグ
    ReleaseMutex( this->m_mutex);
        // メインループ(の代わりのWhileループ)
    int i =0;
    while( !this->m_exitAnotherThd){ // m_exitAnotherThd == falseになるまで待機 
        // ここから先に進まない
        i = ((CSliderCtrl *)FromHanlde(this->m_hwnd)->GetDlgItem(IDC_SLIDER1))->GetPos(); // ウィンドウハンドラを介して取得
    }
    i = ((CSliderCtrl *)FromHanlde(this->m_hwnd)->GetDlgItem(IDC_SLIDER1))->GetPos(); 
    // i = ((CSliderCtrl *)GetDlgItem(IDC_SLIDER1))->GetPos(); 
    return S_OK;
}
  • OnDestory()関数が呼び出されるまではiにスライダの値が正常に入ります。
    以上のコードは実際に実装しているものとは異なります。雰囲気だけでも伝われば幸いです。

補足情報(言語/FW/ツール等のバージョンなど)

VisualStudio2008 SP1
C++/MFC
Windows7/64bit SP1

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 3

checkベストアンサー

+2

OnDestroyのタイミングでコントロールにアクセスすることは問題ありません。

なお、別スレッド側のコードが不明ですが、以下が気になります。

まず、別スレッド終了待ちループにおいて、別スレッドがS_OK以外で終了、あるいはGetExitCodeThread失敗した時、無限ループに陥ります。
この状態に陥っている可能性はないでしょうか?

ちなみに私なら以下のようにSTILL_ACTIVEチェックするループを組みます。

do{
    if( !GetExitCodeThread( this->m_hAnotherThread, &exitCode)){
        TerminateThread( this->m_hAnotherThread, E_FAIL);
        break;
    }
}while( exitCode == STILL_ACTIVE);

また、別スレッドにおいて、ダイアログ内で宣言したコントロール変数を介してGetPos()しているのであれば

ウィンドウ ハンドルのマップ

通常、スレッドは自分自身が作成した MFC オブジェクトしかアクセスできません。 これは、Windows ハンドルの一時マップとパーマネント マップがスレッド ローカル ストレージに保持されており、複数のスレッドから同時にアクセスできないためです。

とあるように、その動作は保証されません。
別スレッド側ではウインドウハンドル経由でアクセスされているのであれば問題ありませんが。

別スレッド処理について追記

えーと。

  • メンバ関数をそのままAfxBeginThreadのスレッド関数として渡す
  • (したがって)別スレッド側でthisポインタでダイアログインスタンスにアクセスする

のはまずいです。詳細はAfxBeginThread member functionなどで検索してみてください。
この問題には目をつぶったうえでGetPosが無反応になる原因としてはHarahiraさんの指摘のとおりと推察します。
GetPosは、内部ではスライダーコントロールに対してのメッセージ処理が行われていますが、メイン側のダイアログでメッセージループ処理が行われていないため、処理が止まっていると思います。

とりあえず以下のような関数を定義しOnDestroy内での終了待ちループ内で呼び出せばGetPos動作すると思われます(動作未検証)

void PumpMessage( void)
{
    MSG msg;
    while(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)){
        TranslateMessage(&msg);
        DispatchMessage(&msg);  
    }
}


が、これは悪手です。

根本的には、以下のように処理を見直したほうがよいです。

  • メイン側とサブ側の両方からアクセスできるスライダー位置変数を用意する。(m_exitFlagAnotherThd のような)
  • メイン側にスライダーの位置変更ハンドラを追加し、ハンドラ関数では位置変数値を更新する。
  • サブ側では位置変数値の参照のみを行う。

基本的には、サブ側スレッドは

  • メイン側で設定された変数値の参照だけを行う。
  • メイン側への何らかの通知は、ユーザー定義メッセージをPostなりでおこなう

ほうが、今回のような問題が発生せず、構造が簡易になります。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/12/28 13:02 編集

    ご回答ありがとうございます。
    ご指摘頂いた箇所を改善しました。STILL_ALIVEチェックを知らなかったので大変助かりました。ありがとうございます。
    実装方法が間違えているのか、現象は解決しませんでした・・・。
    /*今リンク先に改善方法についての記述があるのに気づきました。現在試してみています。
    */
    /*ウィンドウハンドル経由でのアクセスの仕方について、GetDlgItem()関数を利用しての取得方法だと解釈して、質問に更新したとおりに取得するよう変更したのですが、現象自体は改善していません。
    */

    キャンセル

  • 2017/12/28 16:02

    ご回答ありがとうございます。
    1. 別スレッドのコントロールを無理に参照しようとしない。
    2. メンバ変数を介して参照する
    3. メッセージの送信でコントロールの値は変更する
    を最終的に満足するよう実装を続けます。
    お忙しいなか丁寧な解説を補足していただき大変ありがとうございます。取り急ぎお礼だけ。

    キャンセル

+2

OnDestroy内で、ループしてるため、ウィンドウのメッセージループが回ってないためではないでしょうか。

通常、サブスレッドから、直接CSliderCtrl::GetPos()などは呼べないはずなので、そこいらあたりがどうなっているのかわかりませんが、そこ近辺に問題があると思います。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/12/28 13:29

    ご回答ありがとうございます。
    値の取得方法を見直してみます。

    キャンセル

+1

「別スレッド内ではループで処理を行い、その中ではメインスレッドのコントロールの値を参照しています。」

「別スレッドではスライドコントロールの値を取得する行から処理が進まなくなっているようです。 」
なので、何か異常が発生しているのではないでしょうか。
エラーか何かの場合はループから抜ける処理などは実装されてますか?

また、GetExitCodeThread()の戻り値をチェックしてないのも気になります。
exitCodeとは別に、

BOOL b = GetExitCodeThread( this->m_hAnotherThread, &exitCode);


のbのような感じで。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/12/28 09:26

    ご回答ありがとうございます。
    参考にさせていただきまして、コードを修正しました。
    しかし、CSliderCtrl::GetPos()関数に処理が移ったらエラーも起こらず値も返らず、処理も進まない状態であることが確認できました。
    GetExitCodeThread()の返り値も常にTrueです。
    CSliderCtrl::GetPos()内で無限ループでも発生しているかのような動作で問題を引き起こしているようです・・・。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 90.87%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る

  • C++

    3115questions

    C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

  • Visual Studio

    1611questions

    Microsoft Visual StudioはMicrosoftによる統合開発環境(IDE)です。多種多様なプログラミング言語に対応しています。

  • VC++

    129questions

    VC++ (Visual C++) とは、Microsoft製のC++のための統合開発環境です。

  • MFC

    109questions

    MFC (Microsoft Fouondation Class)とは、MicrosoftがVC++用に開発したWindows用アプリケーションのフレームワークです。

  • マルチスレッド

    48questions

    マルチスレッドは、どのように機能がコンピュータによって実行したのかを、(一般的にはスレッドとして参照される)実行の複合的な共同作用するストリームへ区分することが出来ます。

  • トップ
  • C++に関する質問
  • メインスレッドのコントロールを参照しているサブスレッドがアプリケーション終了時にコントロールの値の参照を正常に行えない[マルチスレッド][C++/MFC]