SDIプログラムからモーダルダイアログを閉じた途端エラーになります。

[戻る]
VC++6user 2004/07/30(金) 06:09:55
お世話になります。
タイトルの繰り返しになりますが
SDI(Form View)プログラムの中で、モーダルダイアログを閉じた途端

Debug Assertion Failed

File: wincore.cpp
Line: 875

などとエラーボックスが現れて、止まってしまいます。^^;

ダイアログに一つエディットボックスを貼り付けましたが
それ以外、クラスを新規作成して特に何も処理を追加していない段階で
ダイアログをDoModal()して、OKボタンでもキャンセルボタンでも
とにかく閉じた途端、エラーになります。

まだまだ初心者の段階なもので、自分では原因が思い及びません。
どうかご助言よろしくお願い致します。


CmyDialog myDLG;

if (myDLG.DoModal() == IDOK) {
    ・・・・・
    何らかの処理
}
else return FALSE;

n 2004/07/30(金) 08:39:05
すいませんが、
VCのバージョンやサービスパックの当て具合によって
>File: wincore.cpp
>Line: 875
の指す内容が違ってくるようなので、それも書いて欲しいです。

あいる 2004/07/30(金) 09:03:50
結構見かけるのは・・・

if (myDLG.DoModal() == IDOK) {
    ・・・・・
    何らかの処理
}

この「何らかの処理」のところで、消滅しているエディットボックスを操作してるとかでしょう
か?
> Debug Assertion Failed
とのことなので、ありそうです。

DoModalは、抜けるときにウィンドウを破棄してしまうので、
こういうことがよく見られます。

とりあえず、その「なんらかの処理」のところをすべてコメントアウトして
それでもエラーがでるなら他の原因だと思います。

# 何らかの処理と省略されているので推測でしかいえませんが・・・

psz 2004/07/30(金) 09:40:31
「何らかの処理」というのは
    myDlg.m_edit.GetWindowText();
とか、
    myDlg.GetDlgItem(IDC_EDIT1);
みたいなことで、そこでアサートしているのだろうと推測してみる。
(その時点ではエディットボックスは破棄されてるから、もうアクセスできないよ)

FrozenAquarium 2004/07/30(金) 13:20:29
> OKボタンでもキャンセルボタンでも

という事なのでダイアログを閉じたときに
走るダイアログクラスの中の処理が
原因じゃないかと思うんですが。

VC++6user 2004/07/30(金) 20:08:02
お世話になってます。

ダイアログに貼り付けたエディットボックスには、ビュークラスとの
文字列の受け渡しをしようと、CString型のm_textという
コントロールメンバ変数というのでしたっけ?を設けました。
ダイアログクラスには、後で徐々に処理追加するつもりで
PreTranslateMessage関数やPreCreateWindow関数,
OninitDialog関数などをオーバーライドしました。
PreCreateWindow関数の中でダイアログの初期表示位置とかを
操作できるものと勘違い?して、cs.x,cs.yなどに100とかを座標値の
つもりで入れたりしましたが、なんだかその関数は呼ばれてないみたいで?
ダイアログはセンターに現れるだけですね?
他の関数の中は今のところ、手を加えていません。

とりあえず、原因を絞り込もうと、ちょっとへんてこですが
呼ぶ側を下のようにしてみても、やはりダイアログをモーダルして
閉じただけで、同じエラーになります。
本当はm_textの値を受け渡してループ処理を続けたいのですが・・・^^;

ダイアログクラスの中に何かあるのでしょうか?
クラスウィザードでダイアログクラスを新規作成して、ほとんど手を
加えてませんので、今の自分では原因が思い当たりません。
Visual C++(Studio)6.0 Enterpriseを元のままで
使用していまして、サービスパックは当てていません。
どうか引き続きご助言よろしくお願い致します。


CString ss;
ss.Empty();
CmyDialog myDLG;

while(1) {
    myDLG.m_text = ss;
    if (myDLG.DoModal() == IDOK) return TRUE;
    else return FALSE;

    ・・・・
    ・・・・
    ・・・・
    ・・・・
    ・・・・
}  

VC++6user 2004/07/30(金) 23:11:46
例えば、DAOを使用している場合
レコードセットをAddNew()して
Update()か、CancelUpdate()待ちの間に
ダイアログをモーダルして閉じると
プログラムが落ちたりすることはあるのでしょうか?

試しに、OnDraw()関数の中に
ダイアログをモーダルして閉じる処理を入れてみると
何も起こらず、プログラムは初期起動しましたが・・・

VC++6user 2004/07/30(金) 23:28:27
ん〜DAOを使ってデータベースとレコードセットを構築して
オープン中にダイアログをモーダルして閉じると落ちたりするのでしょうか〜?
今だ、はっきりわかりません・・・^^;

VC++6user 2004/07/31(土) 00:15:41
何度もすいません。^^;閉じた途端というよりも
自分らが書くダイアログをモーダルした関数から
リターンしていって、もっとずっとコアな部分まで
戻ったところで落ちるようですが・・・
何か防ぐ方法はございませんでしょうか?

VC++6user 2004/07/31(土) 00:44:25
エラーメッセージボックスに[中止][再試行][無視]のボタンがありまして
[無視]をクリックすると、プログラムは当面そのまま走るのですが
後々まずいことになるのでしょうか?大丈夫ならば、エラーメッセージボックスなど
表示させずに、デフォルトで[無視]を選択したかのように進める方法等
あるのでしょうか?ご助言下さい。よろしくお願い致します。

wood 2004/07/31(土) 07:38:50
>if (myDLG.DoModal() == IDOK) {
>    ・・・・・
>    何らかの処理   ← 1
>}          ← 2
>else return FALSE;  ← 3
>           ← 4
1・2・3・4の内どこまで落ちないでステップ実行できる?


>File: wincore.cpp
>Line: 875
は何の関数内部ですか?

デバック実行中であれば、関数の呼び出し順序を表示しているタブが表示されて
いるはずですけど、自分のソースのどこから、その関数に到達してるか
見てみましたか

psz 2004/07/31(土) 08:52:50
wood さんの補足になりますが、

>デバック実行中であれば、関数の呼び出し順序を表示しているタブが表示されて
>いるはずですけど、自分のソースのどこから、その関数に到達してるか
>見てみましたか

エラーメッセージボックス表示されたら、[再試行]ボタンを押してください。
エラー個所で停止すると思います。
そこで、コールスタックウィンドウを見ます。
([表示]-[デバッグウィンドウ]-[コールスタック]で表示されます)
コールスタックウィンドウの一番上の行に表示されているのが現在実行中の関数で、
次の行にはその関数の呼び出し元の関数...、といった具合に続いています。

それを見ても自分ではよくわからない、ってことなら、
質問時にその内容を提示するといいでしょう。
(ウィンドウの内容を全選択して、ctrl + C で内容をクリップボードにコピーできます)

File: wincore.cpp
Line: 875
は、おそらく、CWnd::AssertValid()関数の ASSERT(::IsWindow(m_hWnd)); の行だと思われ
ます。
これは、その時点で存在すべきウィンドウが存在しない(未作成または破棄済み)という意味で
す。
CWnd::AssertValid()は各所から呼ばれるので、どこから呼ばれたかを知る必要があると思い
ます。

#私は DAO 使ったことがないので、そこが絡んでいるとお手上げですが (^^;;;

VC++6user 2004/08/01(日) 01:23:47
皆様、お世話になります。
実は、オーバーライドしたPreTranslateMessage関数から呼ばれる先で
ダイアログのインスタンスを生成して、DoModal()しているのですが
インスタンスを生成しても、DoModal()だけコメントアウトすれば落ちません。

それで下記のコードのように、DoModal()して閉じた直後を判断するフラグを
設定して、真の時には以降の処理を避けてやれば、落ちないようになりました。

psz様がご助言下さった「その時点で存在すべきウィンドウが存在しない
(未作成又は破棄済み)」という意味のメッセージが、DoModal()して
閉じてスコープも抜けて無くなったダイアログのことなのでしょうか?
きっちりはわかっていませんが・・・
PreTranslateMessage関数の中でキー検知して、飛ばした先でDoModal()した
直後は、オーバーライド元?へ通してはまずいのですね?


BOOL C????View::PreTranslateMessage(MSG* pMsg) 
{
    // TODO: この位置に固有の処理を追加するか、または基本クラスを呼び出してください

    if (pMsg->message == WM_KEYDOWN || pMsg->message == WM_SYSKEYDOWN) {
        if (m_bAddMode)
            KeyInputField(pMsg);    ←この中の方でDoModal()してるのですが
        else
            KeyChangeProcess(pMsg);
    }
    if (FLG) {            // DoModal()後のAssert防止フラグ 
        FLG = FALSE;
        return TRUE;
    }
    return CFormView::PreTranslateMessage(pMsg);
}

monkey 2004/08/01(日) 12:53:10
KeyInputField関数がどのような処理をしているのか分からないので原因は特定できませ
ん(原因が特定できるかどうかも分からない).
ただ,キーが押されたメッセージをハンドルするなら,PreTranslateMessage関数でな
く,OnKeyDown関数,OnSysKeyDown関数をオーバーライドした方が問題は少ないように思
います.

VC++6user 2004/08/02(月) 04:46:40
monkey様、ご助言ありがとうございます。

OnkeyDown関数は、フォームビューにコントロールを配置して
それらがフォーカスを持つと効かない・・・と本に書いてありまして
そういう場合は、PreTranslateMessage関数を使えばいいとの
ことでしたので、それに従えば、この様でした。^^;

OnSysKeyDown関数については、その本では詳しく載っていません
でしたが、検出できるキーが文字通りシステムキーのみ?だとか
制限あるのではないか?と思いましてパスしていました。

wood 2004/08/02(月) 05:49:36
SPY++って知ってる、あなたのやりたい行為をスパイしてみて飛んでくる
WMメッセージを処理してあげればいいんじゃないの?
クラスウィザード使えばわかると思うけど「WM_???」は関数「On???」になるはず
です

psz [HomePage] 2004/08/02(月) 10:41:50
>PreTranslateMessage関数の中でキー検知して、飛ばした先でDoModal()した
>直後は、オーバーライド元?へ通してはまずいのですね?

(1)
アサートの問題はさておき、このような処理手順であれば DoModal() 後は
return TRUE; で処理を終えるのが普通でしょう。
ダイアログを表示するきっかけになったキー入力を、DoModal() 後に継続して
デフォルト処理したい場面なんてほとんどありえないと思います。
#大昔のキー入力をいまさら思い出してどうする、ってなものです。
この処理手順では return TRUE; で抜けるのは、アサート防止のためなどではなく、
そうするのが当たり前なので、そうしてください。

#KeyChangeProcess()という関数を実行するケースもあるようですが、
#こちらは関数名から想像するに、別のキー入力に変換するのが目的ですね?
#その場合は、逆に、デフォルトの処理に通すのが当然、てことになります。

(2)
さて、上記とは別になぜアサートに至ってしまうのか?ってことですが、
どうやら PreTranslateMessage で渡される pMsg の先にあるメッセージ
データが DoModal() の前後で書き換わってしまうことが原因のようです。
たぶん、DoModal() 内でキューから取り出した最新のメッセージに
置き換わっています(このメッセージは既に DoModal() 内で処理済のはずです)。
なので、DoModal() 後に継続して処理させようとすれば、処理矛盾が生じるのは
当然のことです。
おそらく MFC の設計者は、(1)のような理由から、メッセージループを別途
回すようなことをするなら pMsg の先のデータは置き換わってしまっても構わない
と考えたのでしょう。

あと、PreTranslateMessage の使い方についてですが、
私は別の方法で可能ならば、PreTranslateMessage を使わずに済ませるように
したほうがいいと思います。
しかし、それではどうにもならない場合もあります。
例ではフォームビューでこれを使っているようなので、Tab のような特別なキーも
拾いたいなら PreTranslateMessage でないと拾えないです。
#特別なキー入力は PreTranslateMessage を抜けたらキー入力とは別の処理に
#変換されてしまいますからね

monkey 2004/08/02(月) 11:17:51
> OnkeyDown関数は、フォームビューにコントロールを配置して
> それらがフォーカスを持つと効かない

確かにフォームビューだとキーストロークのメッセージはビューのOn...ハンドラを呼び
出さないようになっていますね.
失礼しました.

VC++6user 2004/08/02(月) 20:22:51
psz様

今の自分ではまだ突き止められないような部分まで明らかにして下さいまして
本当に勉強になりました。ありがとうございました。
やはり無駄な、無意味な処理の流れを入れていると問題も起きやすいということ
なのでしょうね?^^;良い経験でもありました。
所々で「当たり前」「当然」と言われまして、ちょっぴりズキッとしましたが(笑)
これからもっと洗練されたコードを書けるようにも頑張っていきたいと思います。


wood様

いろいろ便利なツールを使いこなしてのデバッグ手順等、ご指導頂いても
戸惑ってしまう自分の苦手としている部分を改めてわからせても下さいまして
ありがとうございます。今後、いろんなデバッグ技術も習得して効率良く
作成できるよう頑張りたいと思います。


monkey様

>確かにフォームビューだとキーストロークのメッセージはOn...ハンドラを呼び
>出さないようになっていますね.
>失礼しました.

とんでもございません。自分でそういうことを調べられて、そういう表現できるまでに
なれるよう先ず頑張りたいと思います。^^;


皆様、この度はお忙しい中ご助言ご指導下さいまして、本当にありがとうございました。
また何かお気付きになられました時には宜しくお願い致します。

VC++6user 2004/08/09(月) 20:52:22
[[解決]]
うっかりしてました。^^;解決マークもチェックしておきます。

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