VC++ .NETの初心者です。
単にスレッドを起動する関数を作成しました。
CreateThreadを呼んで、ResumeThreadを呼んでいる関数です。
この関数を呼び出し、returnすると、いきなり、アセンブラのソース画面
に切り替わって、以下のところでとまります。
『77E3D28E push eax』
このアドレスは、呼び出し履歴をみると『Kernel32.dll』の中みたいです。
これは、どのようなことなのか、分かる人、教えてください。
デバックオプションで混合モードを選択しているからアセンブラ表示画面が出るんだと思います
VC6はそうでした(インストール直後はデフォルトで選択されてました)
「push eax」は「eax」を退避しているだけです
実行アドレスはそれぞれのPCの状態で変わりますので解説できません
「CreateThreadを呼んで、ResumeThreadを呼んでいる関数です。」
の部分を詳しく提示したほうが早期解決になると思いますよ
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関数のアドレスは、正しいみたいです。
何が考えられるのか、教えて頂けると、ありがたいです。
申し訳ありませんが、教えて頂けませんでしょうか?
お願いします。
>ThreadProc関数のアドレスは、正しいみたいです。
それをどうやって確認しましたか?
提示ソースを見る限りにおいては明らかに間違っています。
int x;
printf(%d\n, x);
と同じことで、提示ソースは「絶対に動かない」代物です。
tetrapodさん、早速の回答ありがとうございます。
関数アドレスの確認は、次のように確認しました。
void *pVoid = Proc;
xxxx.Execute(Proc, this);
Procは、static関数です。
このときに、pVoidに入っているアドレスとExecute関数内で渡されたアドレス
が同一でした。
他のプログラムで、この関数を呼び出したら動いていたので、正しいと
思っていました。
初心者で、すいませんが、何が間違っているのか、教えてください。
あ、ごめん。前回の発言は忘れてください。
ThreadProc は関数の引数なのですね。インデントの都合で自動変数に見えてしまいました。
正しく引数に __cdecl な関数ポインタが渡されていれば問題ありません。
# non-static メンバ関数ではダメです。
MFC を使っている場合 CreateThread でスレッドを作ってはいけません。
C/C++ ランタイムライブラリを使っている場合も同様です。
MFC を使っているなら AfxBeginThread を CRT を使っているなら _beginthreadex を
使う必要があります。
その辺の確認を行ってください。
tetrapodさん、早速の回答ありがとうございます。
今回、MFCは、使っていません。
関数ポインタは、正しく、渡されていました。
先程のProc関数は、グローバル関数で、メンバ関数でも、ありません。
一応、クラス内のstaticメンバ関数で行ってみましたが、同じ、現象
でした。
non-staticメンバ関数というのは、どういうことですか?
クラス内のstaticメンバ関数でないと、ダメなのでしょうか?
すいませんが、何故なのかというのも、教えてください。
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,
この辺も同様。必要ないキャストは書いてはいけません。
プログラムの誤りをコンパイラが自動検出してくれる(かもしれない)のに、
キャストを使うと検出がなされなくなってしまいます。
tetrapodさん、早速の回答ありがとうございます。
やってみた内容は、static_mem_func関数とstatic_mem_func関数のパターン
でやってみました。
結局、現象は、同じでした。
ひとつ、疑問がわきましたが、printf とか iostream とか使っていると、
CreateThreadは、何故、ダメなのでしょうか?
理由を知りたくて、質問させて頂きました。
もし、printf とか iostreamを使ってなくても、_beginthreadex関数で、
OKならば、こちらの関数に切り替えたいと思っています。
申し訳ありませんが、理由だけ、教えて頂けませんでしょうか?
宜しくお願いします。
私自身の理解としては、staticでないメンバー関数は呼び出しにクラスのインスタンスが
必要になるから駄目なんだと理解していました。
CreateThreadはC++ではなくてCの関数ですから、クラスのインスタンスは理解できない。
理解できない物は渡せないからstaticでないメンバー変数は渡せない。
staticメンバー関数はクラスのインスタンスがなくても呼び出せるから
CreateThreadにも渡せる。
細かい点では違いはあるけど、意味合い的には同じかもなぁ。
渡せないという理由付けが構造的な話からきているのか、
概念的に話からきているのかの違いなのかも。
AfxBeginThreadには標準関数に関連するマルチスレッド用の初期化処理が
入っていないからです。
これに対して_beginthreadexはそもそもこれ自体が標準関数ですし、
当然の事ながら標準関数のマルチスレッド用の初期化処理が入っています。
AfxBeginThreadを使用する場合は、標準関数にあたるWin32API関数が用意されているので
そちらを使えば良いわけです。Windowsのプログラムの場合、標準ストリームへの出力は
あまりないと思うのでそれを考えるとAfxBeginThreadでも良いと思います。
ファイルの出力周りにしてもMFCをシリアライズのメカニズムを使用すれば、
解決できない問題ではないですから。
思うに最初の設計段階でその辺まで考慮に入れてデザインすべきだと思います。
最初の段階でWin32APIで行くと決めれば、AfxBeginThreadで良い訳だし、
標準関数を使うと決めるのであれば、_beginthreadexにすれば良いと。
確か、その辺の対応表がどこかのホームページにあったと思います。
標準関数に対応するWin32APIを紹介した物です。
PATIOさん、コメント、ありがとうございました。
私の認識も、同じようなものです。
staticメンバー関数は、コンパイル時に実態が既に存在しているから
CreateThreadにも渡せると思っていました。
通常のクラスのメンバー関数は、コンパイラが理解できないので、エラー
になるものと理解していました。
もし、分かるようでしたら、tetrapodさんが言っていた、printfとかiostreamとか
使っていた場合、CreateThreadは、何故、ダメなのか、教えてください。
お願いします。
>申し訳ありませんが、理由だけ、教えて頂けませんでしょうか?
MSDN にそう書いてあるから。
http://msdn.microsoft.com/library/default.asp?url=/library/en-
us/vclib/html/_crt__beginthread.2c_._beginthreadex.asp
tetrapodさん、早速の回答ありがとうございます。
結局、CreateThread関数は、Cランタイムライブラリを初期化して
くれないからなのですね!
よく、わかりました。ありがとうございます。
とりあえず、_beginthreadex関数でやってみます。