例外で表示されるメッセージの意味について

[戻る]
マサ 2004/11/26(金) 16:37:40
こんにちは、マサと申します。

現在、ダイアログベースで開発を進めているのですが、
以下の●印の箇所で「例外処理(初回)はHogeHoge.exeにあります :0xc0000005: Access 
Violation」
と表示されます。

int CHogeDlg::OnButton1()
{
  if(a==1)
  {
    switch(b)  // ● ここで落ちます
    {
      case 1:
         return (Func1());  // 戻り値int型の関数
      case 2:
         return (Func2());  // 戻り値int型の関数
      ...(中略)
    }
  }
}

単純なswitch()文で発生しているので、この行で発生しているとは思えない為、
恐らくはここまでに通過してきたソースのどこかで、領域などを破壊しているのではないかと思
って
昨日からデバッグしているのですが、それらしき場所も見つからず、現時点で解決の目処が立ち
ません。

アウトプットウィンドゥには、
Exception 002 Thread=81b06260 Proc=81b06472
AKY=00001001 PC=00023ca7 RA=0001ef38 BVA=00000004
Process 'HogeHoge.exe'

と表示されていました。これが何かヒントになるのでしたら、とても助かるのですが、
正直、例外処理についても知識が不足しているので、何を意味しているのか分かりません。

例外処理の補足の方法(言葉の使い方が間違っていたらごめんなさい。)や、
解決への道しるべなどを助言願えないでしょうか?

よろしくご指導願います。

るーた 2004/11/26(金) 17:07:15
ちなみに、
bは、どんな値でした?(エラー時)
bの型は何?
メンバ変数?グローバル変数?

# デバッグでテストしてるのですよね?
# 例外発生時に中断するようにして、
# 呼び出し履歴などでエラー個所特定
# としてます。

Ban 2004/11/26(金) 17:11:10
> bは、どんな値でした?(エラー時)
> bの型は何?
> メンバ変数?グローバル変数?

ほぼ同じ事を聞こうとしてました。

ちなみに、例外で表示される情報は「不正アクセスの直接的原因」についての
情報(上の例ならbの近辺)ですから、もしも他で壊されている場合はあまり参考になりません。

Ban 2004/11/26(金) 17:15:12
VC には「条件付ブレイク(変数の値が変更されたらブレイク)」とかあるので、
巧く使うと壊されたタイミングで止めることができるかもしれません。

> VC6 なら Ctrl+B でダイアログがでますので[データ]タブを選ぶ。

Ban 2004/11/26(金) 17:18:01
# ぱらぱらと申し訳ない。

「a は平気で b はダメ」とかあれば比較対象になりえるので
a についても情報希望。

マサ 2004/11/26(金) 17:33:44
るーた様、Ban様、早速のアドバイスありがとうございます。

簡潔に掲載する為、省略して書いてしまいました。申し訳ありません。

この CHogeDlgは、他ダイアログから DoModal()で呼ばれる時に、
CHogeDlgのコンストラクタ内でアプリケーションへのポインタ(m_pApp)を取得しています。
問題の箇所では、アプリケーションクラスに定義しているグローバル変数を参照しています。

aは bool型、bはint型で、不正な値は入ってきません。
a は m_pApp->g_bFlg1;
b は m_pApp->g_nMode1;という具合に参照しています。
(多数のクラスで共有して使用するフラグの役割をしている為、Appに定義しています。)

強引に、if(a==1)の所を注釈にして、強制的にswitch()だけを実行させましたら、例外は発生
しませんでしたし、
aもbも使用している状態で、return (Func1());を return 1;等にしても例外は発生しませ
ん。

また、bの箇所をswitch()を辞め、if文で聞くようにしたら、例外は収まりましたが、
これで収束したとは言えないと思うのです。

それと、るーた様のご意見、
> 呼び出し履歴などでエラー個所特定
の意味が申し訳ありませんが勉強不足のため、解釈できません。
どういうものなのでしょうか?

Ban 2004/11/26(金) 18:22:52
あまり細かいところはズバリをみないと何ともいえませんが、
対象が m_pApp とかだったら、VC の条件付ブレイクで変更された(=壊された)
瞬間に止める目があると思います。

最初の時点で m_pApp のアドレスを確認して、a や b のオフセットをつけた
個所を指定するなり、他のメンバの影響を受けないようにしておいて。
一度ブレイクできてしまえばあとは「呼び出し履歴」(VC のバージョンによっては
「コールスタック」をみれば原因個所がかなり絞れそう。

 *** *** ***

m_pApp が壊れてる...としたら、a の時点で死んでますよね。
# ブレイクがかかった時点で次の命令にフォーカスがいってるだけだったりして。

int 型だと排他問題はあまり考えずらいかも知れませんが、マルチスレッドで
同時に参照している奴とかいません?
他のスレッドが走ってるなら、そいつら全部止めても現象が出るのかも(それが可能なら)
確認した方がいいかも。

> また、bの箇所をswitch()を辞め、if文で聞くようにしたら、例外は収まりましたが、
> これで収束したとは言えないと思うのです。

まず直ってないでしょうね。潜伏しただけ。
ローカルスタックの破壊の場合は、変に処理を直してしまう(ログ追加なども含む)と
現象が再現しなったりします。
問題解決まで基本的に直してはいけません。(再現性のないバグは本気で痛い)
デバッガを駆使して情報収集する方針にするべきです。

# 念のため。OnButton1 を押したら初回で必ず再現する、でいいのでしょうか。

アイススケーター 2004/11/26(金) 20:11:46
混合モードのデバッグで調べられたらいかがでしょう。
アセンブラがある程度わかれば何かつかめるかもしれません。

マサ 2004/11/26(金) 20:13:41
Ban様、ありがとうございます。

もしや、m_pAppが他の原因によって値が狂ったのでは?と思い、
コンストラクタで生成される時と、問題の発生する所で、アドレスを見てみました。
(ソース上のm_pAppの上に、マウスポインタをしばらく乗せて表示されるツールチップ内の、
アドレスで確認。)
ですが、共に同値だった為、m_pAppは壊れてはいないと思います。

> int 型だと排他問題はあまり考えずらいかも知れませんが、マルチスレッドで
同時に参照している奴とかいません?

マルチスレッド対応はしていません。
m_pAppで参照しているグローバル変数も、クラス間同士でパラメータとして共有はしています
が、
クラスのインスタンスを複数作成して、同時に参照する・・という事はしていません。
(これをマルチスレッドと呼ぶのでしょうか...?)


># 念のため。OnButton1 を押したら初回で必ず再現する、でいいのでしょうか。

はい。ただ、OnButton1 に来る前に、作成したDLLを呼んでいるなど、前処理的な処理を行なっ
ています。

2週間程度世代の古いプロジェクトでは、今回の様な致命的なエラーは発生しなかった為、
DLL関係で誤動作を起しているのではないか?、また、古い時点から修正した箇所に重点を於い
て調査しています。

コールスタック、とてもよい情報ありがとうございます。
実行される関数順に表示されているので、いちいち実行した順序をメモすることもなくなりそう
です。

あと便乗で申し訳ありませんが、例外処理についてネット等を調べていましたら、_tryなどで
キャッチするような事を多く見かけました。
この様にデバッグ中は例外処理を捕捉して、独自のエラールーチン(basicで言うところの、
「on error gosub ラベル」のようなもの?)でエラーレジュームをした方がデバッグの効率化
が望めるものなのでしょうか?

Ban 2004/11/26(金) 21:01:37
> アセンブラがある程度わかれば何かつかめるかもしれません。

例外情報の PC や RA 等に反応していなさそうだったので
ほぼ理解されていないものと思ってましたが、実は読めるんでしょうか。


>もしや、m_pAppが他の原因によって値が狂ったのでは?と思い、
> --- snip ---
>ですが、共に同値だった為、m_pAppは壊れてはいないと思います。

もちろん問題のおきたタイミングで、ですよね。
メモリウィンドウでもクイックウォッチでもいいですが、
その他のメンバの状態はどうなってました。全て正常な値ですか?
# こういうところは地道にひとつづつチェック欄を埋めるつもりで。

(これをマルチスレッドと呼ぶのでしょうか...?)

それ自体は普通呼びませんが、特に排他制御はしてないということですよね。
DLL 内は別スレッドが走るので、そいつもそのフラグなどを触ると
あまりよろしくないわけですが...今回とは直接関係ないのかな。

> はい。ただ、OnButton1 に来る前に、作成したDLLを呼んでいるなど、
> 前処理的な処理を行なっています。

スタブで動作できるなら切り分けに有効だと思うのですが、
DLL を呼ばないとどうなります?

> コールスタック、とてもよい情報ありがとうございます。

まともなデバッガなら大抵持っている基本的な機能です。非常に有用。

ちなみに、その呼び方で納得されたということは、VC6以前ですね?
# 環境に依存している問題も多いため、OS 等も含め、MFC利用の有無や
# 実行・開発環境を明記するのはこの手の掲示板の基本則です。


> あと便乗で申し訳ありませんが、例外処理についてネット等を
> -- snip --
> 率化が望めるものなのでしょうか?

あいにく VB は詳しくありませんが、多分かなり違う代物だと思います。

MSDN を読めばわかりますが、構造化例外処理(SEH) の例外はもっと
ずっと低水準ですし、普通はアプリ開発者がいじるようなものではありません。
(デバッガ等が制御するような代物で、直接触っても効率化にならない)

また、SEH と VC++6 の例外処理が混在できない点からも、
よほどSEHの構造に詳しくない限り直接は使えないと思って結構です。

mxtt 2004/11/27(土) 01:00:04
> int CHogeDlg::OnButton1()

何で戻り値が int なのかな。
OnButton1 ってボタンハンドラじゃないのかな?
void であるべき戻り値を int にしてどうなるかはよく知りませんが、
(知ってることといえば戻り値は EAX レジスタに格納されるってことくらい)
勝手にメッセージハンドラのシグネチャを変えたりするとコケるようになることはありますね。
あと、DLL 呼び出し時の呼び出し規約の不整合とかも同様。

その他、コンパイラの最適化バグってことも、たまにあります。
速度優先よりもサイズ優先にしたほうが無難という話も。
#MS のプロダクトはすべてサイズ優先だとか

で、その手の問題の場合、ジャストタイミングではなく、それよりも少し遅れて
問題が発現することが多いのでちょっと厄介です。

マサ 2004/12/01(水) 22:43:05
[[解決]]
皆様ご返答ありがとうございます。
また折角ご回答していただいたにも関わらず、返信が遅れてしまい申し訳ありません。

アイススケーター様とBan様のご指摘のありましたアセンブラについては、
当方アセンブラという単語のみ知っている程度で、
それがどの様なものなのかは知識がありません。
ですので、混合モードに関しては理解しかねます。

また、mxtt様のご指摘、ごもっともです。
掲示板という限られた情報量の中で、如何に伝えるかを自分なりに考え、
要点部分だけをピックアップしたソースを掲示いたしました。
コードをそのまま貼り付けると、無駄に長くなりますし、
何より丸投げでは読む方々にもご迷惑をおかけするかと思ったのです。

OnButton1()に関しては、ボタンハンドラですので、戻り値voidの間違いです。
大変申し訳ありませんでした。

調査した結果ですが、DLL側と呼び出す側とでやりとりを行う変数の桁数が一致していない事が
判明しました。
結局はソースのミスという事になります。

ジャストのタイミングでない分、本当にトレースが大変ですが、
これからもこの掲示板にはお世話になりたいと思います。

皆様、ありがとうございました。

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