開放漏れ?未定義エラーの原因について

[戻る]
ソイレントグリーン 2007/12/23(日) 02:55:45
いつもお世話になります、長文お許しください、原因の分からない、未定義のエラーが発
生しております、ご存知の方、似たような不具合の経験がある方がおられましたら、ご教
示願います。

環境:Windows XP SP2 / Visual Studio 2005 MFC

プログラムの動作概略:
@ PreTranslateMessageを用いて、親ダイアログのキー入力を取得する
A テンキーの0〜9が押下された場合、DoModal()で子ダイアログのインスタンスを生成し
ます
B 子ダイアログの、EditBoxにコード番号を入力します
C 子ダイアログのEnterキー押下
D 子ダイアログから、EndDialog()を用いて、EditBoxに入力した、コード番号を、親ダ
イアログへ返却します
E 親ダイアログは引取った、コード番号を元に、フルパス名を生成して、インターフェ
ースクラス(CDisplayInterfaceクラス)へ
F インターフェースクラスは、与えられた引数を使い、ファイルからデータを読み込
み、vectorコンテナへデータを代入します
G @へ戻る

問題点:
大雑把ですが、プログラムの概要はこの様なものです、一連の動作は、しているのです
が、プログラム終了時、親ダイアログのOKボタン。または、Cancelボタン押下で、プログ
ラムを終了させると、下記の部分で、未定義のエラーが発生します。

原因の推測:
当初インターフェースクラスのデストラクタが呼ばれていないせいで、未定義のエラーが
発生したものと推測し、親ダイアログのOnBnClickedOk()、OnBnClickedCancel()が動いた
タイミングでインターフェースクラスのデクストラクタに、コンテナを自殺させるよう
に、コードを追加し呼び出しましたが、未定義のエラーは止まりませんでした。
Releaseモードでコンパイル、実行した場合は発生しません。
原因は、vectorコンテナのデストラクタに起因すると察するのですが、自力で解決できま
せん、この不具合の原因をご教示していただけないでしょうか、コードが大きく、断片的
な部分しか、提示できませんが宜しくお願い致します。

// 未定義のエラー(ハンドルされていない例外が発生しました:
//  0xC0000005: 場所 0xfdfdfdfd に書き込み中にアクセス違反が発生しました。)
// c:\program files\microsoft visual studio 8\vc\include\xutility
inline void __CLR_OR_THIS_CALL _Container_base::_Orphan_all() const
  {  // orphan all iterators
  _Lockit _Lock(_LOCK_DEBUG);
  if (_Myfirstiter != _IGNORE_MYITERLIST)
    {
    for (_Iterator_base **_Pnext = (_Iterator_base **)&_Myfirstiter;
      *_Pnext != 0; *_Pnext = (*_Pnext)->_Mynextiter)
      (*_Pnext)->_Mycont = 0;  //←ここで未定義のエラー発生
    *(_Iterator_base **)&_Myfirstiter = 0;
    }
  }

// 親ダイアログ、キー入力部分
BOOL C***Dlg::PreTranslateMessage(MSG* pMsg)
{
  INT_PTR nChar;
  INT_PTR result;
  if( pMsg->message == WM_KEYDOWN )
  {
    nChar = pMsg->wParam;
    switch( nChar ) {
      case VK_RETURN :
        break;
      case VK_ESCAPE :
        break;
      case VK_TAB:
        break;
      case VK_NUMPAD0 :        // 0 キー
        nChar = pMsg->wParam;
        child.SetCode( nChar );
        result = child.DoModal();
        Show( result );
        return true;
      case VK_NUMPAD1 :        // 1 キー
        nChar = pMsg->wParam;
        child.SetCode( nChar );
        result = child.DoModal();
        Show( result );
        return true;
// 以下省略(同様に、テンキー2キー〜8キーを記述)
      case VK_NUMPAD9 :        // 9 キー
        nChar = pMsg->wParam;
        child.SetCode( nChar );
        result = child.DoModal();
        Show( result );
        return true;
      default:
        break;
    }
    return CDialog::PreTranslateMessage(pMsg);
  }
  return false;
}

// インターフェースクラスのデストラクタ
CDisplayInterface::~CDisplayInterface()
  if( !containerWork.empty() )
    containerWork.clear();
  if( !containerFirst.empty() )
    containerFirst.clear();
  if( !containerSecond.empty() )
    containerSecond.clear();
// 以下省略(同様にCDisplayInterfaceクラスで、定義されている、コンテナを全て列挙
し自殺させる)

ソイレントグリーン 2007/12/23(日) 09:35:11
お世話になります、補足させていただきます、
「(*_Pnext)->_Mycont = 0; vector」で検索して調べていますと、日本語サイトは一軒も
Hitしないのですが、
http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=2019491&SiteID=1&pageid=0
このような事例に、巡りあいました、Googleの翻訳エンジンの力を借りながら、自分なり
に脳内で咀嚼していきますと、コンパイラの範囲チェック機能が働き、未定義のエラーが
発生しているのではないかと思い、インターフェースクラスのヘッダーファイルのマクロ
部分に
#define _HAS_ITERATOR_DEBUGGING 0
を追加して、範囲チェック機能を無効にしてみました、
コンパイルしますと
warning C4005: '_HAS_ITERATOR_DEBUGGING' : マクロが再定義されました。
c:\program files\microsoft visual studio 8\vc\include\yvals.h
(94) : '_HAS_ITERATOR_DEBUGGING' の前の定義を確認してください

warning C4005: '_HAS_ITERATOR_DEBUGGING' : マクロが再定義されました。
この警告を無視して、実行しますと、デッバグモードでコンパイル、実行を行っても
落ちなくなりました、やはり原因は、私の書いた、vectorコンテナの実装にありそうで
す。

// CDisplayInterfaceクラスのマクロ定義変更
#ifndef _CDISPLAYINTERFACE_H_       // 二重インクルードガード
#define _CDISPLAYINTERFACE_H_
#define _HAS_ITERATOR_DEBUGGING 0   // ここを追加しました
ソース
#endif

RAPT [HomePage] 2007/12/23(日) 11:49:54
// 未定義のエラー(ハンドルされていない例外が発生しました:
//  0xC0000005: 場所 0xfdfdfdfd に書き込み中にアクセス違反が発生しました。)
まずこれは、VC++のデバッガによる不正アクセスのエラー報告です。
よくあるのが、

char* p = new char[ 10 ];
delete[] p;
p[ 1 ] = 0; // ここでアクセス違反

おそらく、すでにポインタを削除しているのに、NULL を入れてなくて、
デストラクタで非NULLなので参照しようとして落ちているのでしょう。

肝心のコンテナの実装部分が書かれていないので憶測ですが。

RAPT [HomePage] 2007/12/23(日) 11:50:31
追記。
これは解放漏れではなく、逆の二重解放でしょう。

ソイレントグリーン 2007/12/23(日) 15:31:18
RAPTさんお世話になります、ご教示ありがとうございます
RAPTさんの、見解を参考に試行錯誤検証しています、
>>肝心のコンテナの実装部分が書かれていないので憶測ですが。
下記のような、状況故、当該不具合が、直接コンテナの実装に起因して、発生しているよ
うではないので、しばしご容赦願います。
ということで、早速ですが、検証中に、このような事実が判りました
検証A
@ プログラム起動
A 親のダイアログのOK(Cansel)ボタン押下
B 正常終了

検証B
@ プログラム起動
A テンキー操作にて、子ダイアログ起動
B 子ダイアログの、EditBoxには何も入力しない
(即ち、vectorコンテナを使う関数は、直接アクセスを行わないという事です、しかし起
動した時点で、それらのコンストラクタも動いていますから、全く無関係とは、考えてい
ません)
C 子ダイアログ、×ボタン押下
D 親のダイアログのOK(Cansel)ボタン押下
E 異常終了、未定義エラー発生

この様な状況です、故にコンテナの実装というよりも、RAPTさんに、教えて頂いた、二重
開放させるようなプログラムを組んでしまったのかも知れません、子ダイアログの起動、
終了周りの実装は下記のような感じです、何か、おかしなことをしていないか、お気付き
のことがあれば指摘願えませんでしょうか。

ダイアログ上のリソースアイテム
親ダイアログ
OKボタン    表示
Canselボタン  表示
Custom Control 表示

子ダイアログ
EditBox    入力部分はサブクラス化して、特定のキー入力しか受付けないように実装
OKボタン    非表示
Canselボタン  非表示

子ダイアログの起動、終了処理周りの実装
子ダイアログの実装
BOOL CChild::OnInitDialog()
{
  // CChild メッセージ ハンドラ
  CDialog::OnInitDialog();
  m_NEdit.SubclassDlgItem( IDC_EDIT1, this);  // サブクラスの定義
  GetDlgItem(IDOK)->ShowWindow(SW_HIDE);    // 非表示
  GetDlgItem(IDCANCEL)->ShowWindow(SW_HIDE);  // 非表示
  Proc();
  return TRUE;
}

void CChild::Proc()
{
  m_codeNum = static_cast<int>( m_code );  // EditBoxに値を設定
  SetDlgItemInt( IDC_EDIT1, m_codeNum );
  UpdateData(false);
}

void CChild::OnBnClickedOk()
{
  UpdateData();
  m_result = GetDlgItemInt( IDC_EDIT1 );  // EditBoxから、値を取得
  EndDialog( m_result );          //親ダイアログに値を返却
}

void CChild::OnBnClickedCancel()
{
  OnCancel();
}

たいちう 2007/12/23(日) 20:50:57
> >>肝心のコンテナの実装部分が書かれていないので憶測ですが。
> 下記のような、状況故、当該不具合が、直接コンテナの実装に起因して、発生しているよ
> うではないので、しばしご容赦願います。

不具合が何に起因しているか、あなたに正しい判断ができるなら
こんな投稿はしていないのでは?
メモリ関係のエラーは、原因と結果(異常の発覚)が離れているのが
大半ですよ。

「肝心のコンテナの実装部分」なしに、直接役に立つアドバイスは
付かないと思いますが。ちょっと見ただけの私の印象ですが。

ソイレントグリーン 2007/12/24(月) 15:28:16
[[解決]]
たいちうさん、お世話になります
>>不具合が何に起因しているか、あなたに正しい判断ができるなら
>>こんな投稿はしていないのでは?
ごもっともです、しかしvectorコンテナの実装部分だけでコメントも含めて、
400行強、ヘッダーファイルだけで180行もありこの様なものを、掲示しても皆さんのご迷
惑というか、読む気もならないと思い、掲示することをためらいました。
しかし、なんとか不具合の原因が判明しました。
原因は、vectorコンテナではありませんでした
子ダイアログの戻り値をEndDialog( INT_PTR )で受取り、そのデータから
親ダイアログで、フルパス名を生成してインターフェースクラスに引渡す部分で、
うっかり(上記Show( result );の部分です)
その、インターフェースクラスのファイルオープン処理を行う
ifstream ifs;
ifs.open( m_fname, ios::in );
に引き渡す、第一引数である、m_fnameをconst char*型に変換すべきものを
char buffer[40];
strcpy_s(buffer, 40, m_fname.c_str() );
勘違いから、わざわざ、このような変換を行い。且つ、
ファイル名が40バイト以上あったので
メモリーバッファーオーバーフローを誘引していました、原因はここでした
単純にm_fname.c_str()で引き渡せばいいものを、余分なことをして失敗していました。
みなさんどうも、ありがとうございました。

古いスレッドには追加発言できません。