- PR -

マネージ・アンマネージの相互コールバックに関して

1
投稿者投稿内容
NetSeed
会議室デビュー日: 2007/03/06
投稿数: 4
投稿日時: 2007-03-06 12:22
C++/CLIにて、以下のようなサンプルを使いテストしたのですが、AccesViolationが発生してしまいます。

コード:

using namespace System;
using namespace System::Runtime::InteropServices;

#pragma unmanaged
typedef int (*proc)(int hoge);

int CallbackProc(int val,proc p)
{
return p(val);
}



#pragma managed

delegate int Hoge(int val);

int clbkProc(int val)
{
return val*2;
}

int main()
{
Hoge^ h=gcnew Hoge(clbkProc);

GCHandle^ gch=GCHandle::Alloc(h);

proc pt=static_cast<proc>(Marshal::GetFunctionPointerForDelegate(h).ToPointer());

int ret=CallbackProc(100,pt);

gch->Free();

}



Stepでデバグしたところ、登録したコールバックも呼ばれてはいるのですが、
CallbackProcから、returnしたところでAccessViolationが発生します。
試しに、CallbackProcを以下のように変更したところ、
何の問題もなく処理が完了しました。

コード:

int CallbackProc(int val,proc p)
{
int ret=p(val);
return ret;
}



MSDNの”C++ Interop を使用してコールバックおよびデリゲートをマーシャリングする”
中のサンプルを参考にこのサンプルを作成したのですが、なぜ、一度ローカル変数に値を代入した上で、それを返さないと、AccessViolationが発生のか理解できず困っております。
もしご存じの方がいらっしゃいましたら、御教授の程宜しくお願いいたします。

環境:
Visual Studio 2005 Professional
/clrスイッチ付きでコンパイル。


[ メッセージ編集済み 編集者: NetSeed 編集日時 2007-03-06 13:48 ]
Blue
大ベテラン
会議室デビュー日: 2005/09/12
投稿数: 230
お住まい・勤務地: 知っている人は知っている
投稿日時: 2007-03-06 12:44
呼び出し規約が違うとか。

>typedef int (*proc)(int hoge);

typedef int (__stdcall *proc)(int); // もしくは WINAPI、CALLBACK
としてみるとどうでしょうか?


[ メッセージ編集済み 編集者: Blue 編集日時 2007-03-06 12:47 ]
NetSeed
会議室デビュー日: 2007/03/06
投稿数: 4
投稿日時: 2007-03-06 13:14
Blueさん、返信ありがとうございます。

引用:

>typedef int (*proc)(int hoge);

typedef int (__stdcall *proc)(int); // もしくは WINAPI、CALLBACK
としてみるとどうでしょうか?





これで試したところ、問題なく動きました。
呼び出し規約の差異でエラーが起きることは理解できるのですが、
ローカル変数に一度代入し、それを返すことによって、
エラーを回避できるのか、理解が出来ておりません。
もし、ご存じでしたら御教授いただけないでしょうか?
とっちゃん
大ベテラン
会議室デビュー日: 2005/07/19
投稿数: 203
投稿日時: 2007-03-06 13:26
GetFunctionPointerForDelegate の「英語の」ヘルプには、
引用:
The delegate d is converted to a function pointer that can be passed to unmanaged code using the __stdcall calling convention.

と書かれてます。
なので、Blue さんの指摘されているように、__stdcall をつけることで解決するわけですが...
ハングアップの理由は、スタックポインタがずれてしまうからです。

__stdcall(古くはPASCALコールと呼ばれていた)と、_cdecl コール(C標準形式)では、コールスタックのクリア方法がことなります。

これが、ローカル変数への代入を伴ったことにより結果的にスタックの利用状況が変わり、クラッシュしないように見えているだけにすぎません。

もし、まわりにアセンブラに詳しい人がいたらその人にレクチャーしてもらうとよいかと。


_________________
// とっちゃん(高萩 俊行)@わんくま同盟
// とっちゃん’Blog
// MS-MVP for Developer Tools - Visual C++
// WindowsInstallerの話題はhttp://www.freeml.com/msiまで
NetSeed
会議室デビュー日: 2007/03/06
投稿数: 4
投稿日時: 2007-03-06 13:48
とっちゃんさんご返信ありがとうございます。

引用:

とっちゃんさんの書き込み (2007-03-06 13:26) より:

__stdcall(古くはPASCALコールと呼ばれていた)と、_cdecl コール(C標準形式)では、コールスタックのクリア方法がことなります。

これが、ローカル変数への代入を伴ったことにより結果的にスタックの利用状況が変わり、クラッシュしないように見えているだけにすぎません。



あくまで、見かけてうまく動いてるように見えるだけカモしれないと言うことですね。
いずれにせよ、windows.hにて、CALLBACKが__stdcallとdefineされている以上、
それを使うべきなのだとおもいました。

御教授ありがとうございました。
とっちゃん
大ベテラン
会議室デビュー日: 2005/07/19
投稿数: 203
投稿日時: 2007-03-06 16:53
引用:

NetSeedさんの書き込み (2007-03-06 13:48) より:

あくまで、見かけてうまく動いてるように見えるだけカモしれないと言うことですね。


「かも」ではなく、偶発的に問題が露見していないだけです。もしかしたら別のコードを挟んだら同じように問題が発生するかもしれませんよ。
引用:

いずれにせよ、windows.hにて、CALLBACKが__stdcallとdefineされている以上、
それを使うべきなのだとおもいました。


windows.h の CALLBACK の定義が __stdcall なのは、そのほうがバイナリサイズが小さくなるからです。
#x86アーキテクチャの場合は、速度も若干早くなるとおもいますが、今時のCPUだとどうなんだろう?

なので、大半がコールバック関数に CALLBACK をつけていますが、全てがそうなっているわけではないということにも注意が必要です。
特に今回の __stdcall が必要な理由とは全然関係ない部分ですので。

今回は、日本語ドキュメント側で __stdcall 形式で返されるという部分が欠落してしまっていることが不具合発生の主な要因とおもわれます。
英語のドキュメントを読め!とはいいませんが、少しでもあやふやな点があれば、英語ドキュメントを参照する、最終的な確認は英語ドキュメントで行うというように癖をつけていけば、おそらく発生しなかったであろうとおもわれます。

とはいえ、バグはバグですので、プログラムを直すということと一緒に、今後同じ問題で後輩や新米開発者がはまらないように、MSDN フォーラムにフィードバックすることをお勧めします。

MSDNのドキュメントが修正されれば、同じ問題で悩む人は確実に減ると思いますので。
_________________
// とっちゃん(高萩 俊行)@わんくま同盟
// とっちゃん’Blog
// MS-MVP for Developer Tools - Visual C++
// WindowsInstallerの話題はhttp://www.freeml.com/msiまで
NetSeed
会議室デビュー日: 2007/03/06
投稿数: 4
投稿日時: 2007-03-06 17:18
引用:


今回は、日本語ドキュメント側で __stdcall 形式で返されるという部分が欠落してしまっていることが不具合発生の主な要因とおもわれます。
英語のドキュメントを読め!とはいいませんが、少しでもあやふやな点があれば、英語ドキュメントを参照する、最終的な確認は英語ドキュメントで行うというように癖をつけていけば、おそらく発生しなかったであろうとおもわれます。

とはいえ、バグはバグですので、プログラムを直すということと一緒に、今後同じ問題で後輩や新米開発者がはまらないように、MSDN フォーラムにフィードバックすることをお勧めします。

MSDNのドキュメントが修正されれば、同じ問題で悩む人は確実に減ると思いますので。




確認を最後まで行っていれば、自分で解決できたかもしれないと思うと、
汗顔の至りです。。。
誤訳、というわけではないですが、必要な記載事項が漏れていることが
あり得ると考えておかなければならないと思いました。

MSDNのフォーラムには、折を見てフィードバックするつもりです。
1

スキルアップ/キャリアアップ(JOB@IT)