デバッグ中、アセンブラ画面になり、止ってしまう。

[戻る]
yuki 2004/12/17(金) 16:50:12
VC++ .NETの初心者です。
単にスレッドを起動する関数を作成しました。
CreateThreadを呼んで、ResumeThreadを呼んでいる関数です。

この関数を呼び出し、returnすると、いきなり、アセンブラのソース画面
に切り替わって、以下のところでとまります。

『77E3D28E  push        eax』

このアドレスは、呼び出し履歴をみると『Kernel32.dll』の中みたいです。
これは、どのようなことなのか、分かる人、教えてください。

wood 2004/12/18(土) 13:55:55
デバックオプションで混合モードを選択しているからアセンブラ表示画面が出るんだと思います
VC6はそうでした(インストール直後はデフォルトで選択されてました)

「push        eax」は「eax」を退避しているだけです
実行アドレスはそれぞれのPCの状態で変わりますので解説できません

「CreateThreadを呼んで、ResumeThreadを呼んでいる関数です。」
の部分を詳しく提示したほうが早期解決になると思いますよ

yuki 2004/12/20(月) 11:17:03
woodさん、回答及び忠告ありがとうございます。
hjgxfmさん、どういう意味でしょうか?

デバックオプションでは、自動と設定されていたので、混合モードなのかも
しれません。
CreateThreadを行っている関数は、次のとおりです。

xxxx::Execute (
    void        (*ThreadProc)(void *lpThreadParameter),
    void       *lpThreadParameter,
)
{
    DWORD  dwThreadID;

    hThread = CreateThread(NULL, 0,
                           (LPTHREAD_START_ROUTINE)ThreadProc,
                          (LPVOID)lpThreadParameter,
                           CREATE_SUSPENDED, 
                           &dwThreadID
                          );

    if ( hThread == NULL ) {
        iError_ = GetLastError() ;
        return false; 
    }

    ResumeThread(hThread);

    return true;    <---- ここのreturnを行うと現象がおきます。
}

のような感じです。

それで、よく、調べてみました。
CreateThread関数を呼んだ後に、GetLastErrorで調べると5で返ってきて
いました。アクセスエラーだと思いますが、どうしてか、わかりません。
ThreadProc関数のアドレスは、正しいみたいです。
何が考えられるのか、教えて頂けると、ありがたいです。
申し訳ありませんが、教えて頂けませんでしょうか?
お願いします。

tetrapod 2004/12/20(月) 11:31:29
>ThreadProc関数のアドレスは、正しいみたいです。
それをどうやって確認しましたか?
提示ソースを見る限りにおいては明らかに間違っています。

int x;
printf("%d\n", x);
と同じことで、提示ソースは「絶対に動かない」代物です。

yuki 2004/12/20(月) 11:48:00
tetrapodさん、早速の回答ありがとうございます。
関数アドレスの確認は、次のように確認しました。

void *pVoid = Proc;

xxxx.Execute(Proc, this);

Procは、static関数です。

このときに、pVoidに入っているアドレスとExecute関数内で渡されたアドレス
が同一でした。

他のプログラムで、この関数を呼び出したら動いていたので、正しいと
思っていました。
初心者で、すいませんが、何が間違っているのか、教えてください。

tetrapod 2004/12/20(月) 12:29:22
あ、ごめん。前回の発言は忘れてください。
ThreadProc は関数の引数なのですね。インデントの都合で自動変数に見えてしまいました。
正しく引数に __cdecl な関数ポインタが渡されていれば問題ありません。
# non-static メンバ関数ではダメです。

MFC を使っている場合 CreateThread でスレッドを作ってはいけません。
C/C++ ランタイムライブラリを使っている場合も同様です。
MFC を使っているなら AfxBeginThread を CRT を使っているなら _beginthreadex を
使う必要があります。
その辺の確認を行ってください。

yuki 2004/12/20(月) 13:06:57
tetrapodさん、早速の回答ありがとうございます。
今回、MFCは、使っていません。
関数ポインタは、正しく、渡されていました。

先程のProc関数は、グローバル関数で、メンバ関数でも、ありません。
一応、クラス内のstaticメンバ関数で行ってみましたが、同じ、現象
でした。

non-staticメンバ関数というのは、どういうことですか?
クラス内のstaticメンバ関数でないと、ダメなのでしょうか?

すいませんが、何故なのかというのも、教えてください。

tetrapod 2004/12/20(月) 13:39:41
printf とか iostream とかまったく一切使っていないのであれば CreateThread で
問題ないはずです。当該スレッド関数が正しく起動しているかどうか確認。
# 何か使っていれば _beginthreadex でないと正しく動作しません。

クラスのnon-staticメンバ関数はその構造上 CreateThread の入り口関数にできないのです。
例えば
class hoge {
  int non_static_mem_func(void*p);
  static int static_mem_func(void*p);
};
int non_member_global_func(void*p);
とある場合 hoge::non_static_mem_func は
スレッドの開始関数(=CreateThread に渡せる関数) にはなれません。
理由は簡単、non-static メンバ関数の呼び出しの際には暗黙の引数 this が必要であるのに
対し
スレッド開始関数に渡される引数は1つだけ (this が渡せない) からです。
# 実装上 non_static_mem_func は void* と this の2つの引数を持つわけです。
hoge::static_mem_func や non_member_global_func は this が不要なので大丈夫。

>void *pVoid = Proc;
このコードはあまり感心しません。理由は型情報が失われているからです。
>    hThread = CreateThread(NULL, 0,
>                           (LPTHREAD_START_ROUTINE)ThreadProc,
>                          (LPVOID)lpThreadParameter,
この辺も同様。必要ないキャストは書いてはいけません。
プログラムの誤りをコンパイラが自動検出してくれる(かもしれない)のに、
キャストを使うと検出がなされなくなってしまいます。

yuki 2004/12/20(月) 13:58:43
tetrapodさん、早速の回答ありがとうございます。
やってみた内容は、static_mem_func関数とstatic_mem_func関数のパターン
でやってみました。
結局、現象は、同じでした。

ひとつ、疑問がわきましたが、printf とか iostream とか使っていると、
CreateThreadは、何故、ダメなのでしょうか?
理由を知りたくて、質問させて頂きました。

もし、printf とか iostreamを使ってなくても、_beginthreadex関数で、
OKならば、こちらの関数に切り替えたいと思っています。
申し訳ありませんが、理由だけ、教えて頂けませんでしょうか?

宜しくお願いします。

PATIO 2004/12/20(月) 14:04:29
私自身の理解としては、staticでないメンバー関数は呼び出しにクラスのインスタンスが
必要になるから駄目なんだと理解していました。
CreateThreadはC++ではなくてCの関数ですから、クラスのインスタンスは理解できない。
理解できない物は渡せないからstaticでないメンバー変数は渡せない。
staticメンバー関数はクラスのインスタンスがなくても呼び出せるから
CreateThreadにも渡せる。
細かい点では違いはあるけど、意味合い的には同じかもなぁ。
渡せないという理由付けが構造的な話からきているのか、
概念的に話からきているのかの違いなのかも。

PATIO 2004/12/20(月) 14:22:47
AfxBeginThreadには標準関数に関連するマルチスレッド用の初期化処理が
入っていないからです。
これに対して_beginthreadexはそもそもこれ自体が標準関数ですし、
当然の事ながら標準関数のマルチスレッド用の初期化処理が入っています。
AfxBeginThreadを使用する場合は、標準関数にあたるWin32API関数が用意されているので
そちらを使えば良いわけです。Windowsのプログラムの場合、標準ストリームへの出力は
あまりないと思うのでそれを考えるとAfxBeginThreadでも良いと思います。
ファイルの出力周りにしてもMFCをシリアライズのメカニズムを使用すれば、
解決できない問題ではないですから。
思うに最初の設計段階でその辺まで考慮に入れてデザインすべきだと思います。
最初の段階でWin32APIで行くと決めれば、AfxBeginThreadで良い訳だし、
標準関数を使うと決めるのであれば、_beginthreadexにすれば良いと。

確か、その辺の対応表がどこかのホームページにあったと思います。
標準関数に対応するWin32APIを紹介した物です。

yuki 2004/12/20(月) 14:23:36
PATIOさん、コメント、ありがとうございました。
私の認識も、同じようなものです。
staticメンバー関数は、コンパイル時に実態が既に存在しているから
CreateThreadにも渡せると思っていました。
通常のクラスのメンバー関数は、コンパイラが理解できないので、エラー
になるものと理解していました。
もし、分かるようでしたら、tetrapodさんが言っていた、printfとかiostreamとか
使っていた場合、CreateThreadは、何故、ダメなのか、教えてください。

お願いします。

tetrapod 2004/12/20(月) 14:30:27
>申し訳ありませんが、理由だけ、教えて頂けませんでしょうか?
MSDN にそう書いてあるから。
http://msdn.microsoft.com/library/default.asp?url=/library/en-
us/vclib/html/_crt__beginthread.2c_._beginthreadex.asp

PATIO 2004/12/20(月) 14:39:29
私が参考にしている情報ソースです。

http://www.cisnet.or.jp/home/tsuneoka/win32tech/19.html

yuki 2004/12/20(月) 14:40:27
tetrapodさん、早速の回答ありがとうございます。
結局、CreateThread関数は、Cランタイムライブラリを初期化して
くれないからなのですね!
よく、わかりました。ありがとうございます。

とりあえず、_beginthreadex関数でやってみます。

PATIO 2004/12/20(月) 14:50:19
因みに_beginthread、_beginthreadexでスレッドを作成するとMFCのAPIは使えないそうです。
その辺も考慮に入れて決められた方がよろしいと思います。

yuki 2004/12/20(月) 15:09:13
PATIOさん、コメント、ありがとうございました。
今回、MFCは、一切、使用していないので、大丈夫だと思います。
ありがとうございました。

nemu 2004/12/20(月) 16:34:32
> 因みに _beginthread、_beginthreadex でスレッドを作成すると MFC の API は使え
ないそうです。
↑これは、「スレッドプロシージャ内部では MFC の API ( メソッド? ) が利用できな
い。」という意味でよいのですよね?
私は、そう思ってたのですが・・・。
もしかして、どこであれ、一切使ってはいけないということなのでしょうか?

ku 2004/12/20(月) 17:23:20
VC6のMFCのソースを見ると2つあるAfxBeginThread()はどちらも
CWinThread::CreateThread()を呼んでいて、その中で_beginthreadex()を呼んでいます

上記のことから考えると下記のように考えられますがどうでしょうか
CreateThread   -> ×標準関数,×MFC
_beginthreadex -> ○標準関数,×MFC
AfxBeginThread -> ○標準関数,○MFC

> もしかして、どこであれ、一切使ってはいけないということなのでしょうか?
私はnemuさんと同じように考えますが、よくはわからないです

仕事ではマルチスレッドを使った事がないので詳しくないのですが...

nemu 2004/12/20(月) 18:47:55
> CreateThread   -> ×標準関数,×MFC
> _beginthreadex -> ○標準関数,×MFC
> AfxBeginThread -> ○標準関数,○MFC
とすると、CreateThread() は純粋に SDK な場合にしか使用できないということです
ね。

あと、少し話題がそれますが、
VC7 からは CString は MFC の枠組を抜けて、どっか ( ATL? ) に移動したと聞いたの
ですが、
それは、_beginthreadex() で作られたスレッド内でも CString が使えるということな
のでしょうか?

yuki 2004/12/21(火) 11:19:19
kuさん、nemuさん、コメントありがとうございます。
皆さんのコメントで、スレッドについて、良く、わかりました。
今回、_beginthreadex() にしたら、動作しました。
どこかで、1ヶ所だけ、標準関数を使用していました。
お騒がせしました。

ただ、
>それは、_beginthreadex() で作られたスレッド内でも CString が使えるということな
>のでしょうか?
という件ですが、誰か分かる人がいたら、教えてください。
私も、知りたいです。

PATIO 2004/12/21(火) 14:16:45
すいません、舌足らずで。
私が提示したHPを見ていただければ解ると思いますが、
作成したスレッドのほうで使用できないという話です。
このホームページを読み返していて気が付いたんですが、
kuさんが言われているようにCreateThreadでは駄目と書いてありますけれど、
AfxBeginThreadに関してはそういう記述はないみたいです。
私の勘違いですね。
AfxBeginThreadは両方とも良いという話になるとMFCでコードを書く時は
とにかく、AfxBeginThreadという事になりますね。
ところで、ATLってSTLとは別物だと思うんですが、
ATLは標準の方に入ると考えていいんですかねぇ?
私はむしろMFCよりだと思っていたんですけれど。
ATLが内部の実装部分にMFCを使っているのであれば、
ATLを使う場合はAfxBeginThreadを使わないといけないという事になると思うんですけれど。

MFCイラネ 2004/12/21(火) 15:25:23
> ところで、ATLってSTLとは別物だと思うんですが、

勿論。


> ATLは標準の方に入ると考えていいんですかねぇ?

少なくとも C/C++ 標準ではない。
単にどちらも Template Library の頭文字をとっているというだけ。

ATL 自体は(ちゃんと全部を確認したわけじゃないが) 基本的に native Win32 実装だから
標準 C/C++ ライブラリも使ってないと思う。


> ATLが内部の実装部分にMFCを使っているのであれば、
> ATLを使う場合はAfxBeginThreadを使わないといけないという事になると思うんですけれど。

少なくともわざわざ MFC なんてダメなものを使ったりはしない。
CString にしても、MFC が ATL のものを使うようになったという方が現実に近いから、
少なくとも AfxBeginThread なんてしろものはイラナイのでは?

nemu 2004/12/21(火) 15:31:20
>ATLが内部の実装部分にMFCを使っているのであれば、
>ATLを使う場合はAfxBeginThreadを使わないといけないという事になると思う
↓このページによると、ATL は非 MFC のようです。
ttp://home.att.ne.jp/banana/akatsuki/doc/atlwtl/atlwtl01-01/

PATIO 2004/12/21(火) 16:35:04
なるほど、ATLのみを使う分にはAfxBeginThreadは必要なさそうですね。

MFCの実装の全てが正しいとは言いませんけれど、
駄目な物と言うのは言いすぎだと思いますよ。
少なくともフレームワークとしては良くできていると思いますし。
まあ、ピューリファイなんかで調べるとMFCの内部でメモリーリークが
ずんどこ検出されたりするので実装そのものに問題がある部分はあると
思いますけどね。この辺はピューリファイの誤検出なのか、
MFC側の不具合なのかいまひとつ解ってないんですけれど。
CString自体は、STLとMFCがうまくなじまないからMSが新たに起こした
クラスだというのは見え見えですが、VC7ではMFCは全てATLに移行したんですか?
CStringを使うだけの話なら、AfxBeginThreadはいらないでしょうけれど、
まだ、MFCに機能が残っているのであれば、その部分を使う時は必要だと思います。

MFCイラネ 2004/12/21(火) 17:22:13
> MFC が ATL のものを使うようになったという方が現実に近いから

表現が悪かった。中身が template ベースになったという程度の意味な。
もともと CString は MFC 発祥 -> WTL に展開したもんだし。
WTL::CString 対応の延長先に今があるんだろうと思う。

> VC7ではMFCは全てATLに移行したんですか?

別にどちらも移行などしてない。
native Win32 を、片や template でジェネリックに、
片や class でオブジェクト指向風味にラップしてるはずで、
本来は直交するべきはずのもん。

MFC はそのわりにクラス構成が設計的にボテッとしてて美しくないから
個人的に嫌いなだけでそれ以上の他意はない。

MFC 使うといらだつし、漏れは ATL/WTL が好きだが、
用途次第で役には立つのだろう。
"俺的に大粒な構成が今ひとつだと思うフレームワーク"くらいに訂正。
実装の不具合とは別次元の話。

人によっては ATL の多重継承が理解できずに嫌うんだろうが、
それも C++ におけるインターフェイスは多重継承で実装されるが故。
所詮は好みの問題か。

RAPT 2004/12/21(火) 23:34:01
個人的には、WTLが好きなので、そちらを使いますが、ちょっとした
作りこまなくても良い程度のツールであれば、MFCを使う事もあります。

WTLはWinMainを隠蔽しないので、C言語に慣れた自分としては、
そちらの方が分かりやすい、というのもあります。

# ATL7のCStringは、CStringT<char> とCStringT<wchar_t>でしったっけ?
# WTL7のCString は、TCHAR ベース。

PATIO 2004/12/22(水) 11:10:20
個人的にはATL/WTL路線も良いと考えています。
ある程度の基本構造を残しつつ、コーディングの手間も減らせますし、
実行ファイルもコンパクトに出来るみたいですね。

MFCの場合は、昔の実装を引きずっているためにどうしてもスマートでない
部分があるのは仕方ないかもしれません。
あれはあれとして適材適所に使えばいいわけですし、
フレームワークを理解して利用する分にはそれなりに使えるものだと
私は思います。
まあ、状況によってはMFCでやってくれと言う場合もなきにしもあらず。
受注元の条件なら四の五の言ってられませんしね。

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