2009-09-11 18 views
6

私は、最終的な使用のためにC++/CLIでラッパーを構築するために使用した(.libと.hppファイルで)作業しているネイティブのサードパーティのC++コードベースを持っていますC#で。C++コールバックからC#関数へのアクセス違反例外/クラッシュ

私は、デバッグモードからリリースモードに切り替えるときに特に問題に遭遇しました。コールバックのコードが返ってくると、アクセス違反例外が発生します。

コールバック関数形式のオリジナルHPPファイルからコード:コールバック関数形式のC++/CLIラッパーから

typedef int (*CallbackFunction) (void *inst, const void *data); 

コード: (私はその瞬間に2を宣言した理由を私は説明します)

public delegate int ManagedCallbackFunction (IntPtr oInst, const IntPtr oData); 
public delegate int UnManagedCallbackFunction (void* inst, const void* data); 

--Quickly、私は2番目「UnManagedCallbackFunctionは」私はラッパーに「仲介」コールバックを作成しようとしましたので、チェーン>はネイティブC++のバージョンにネイティブC++> C#のから変更されたことであると宣言理由C++/CLI Wrapper> C#...フル・リリースure、問題はまだ生きている、それはちょうど同じ行(戻り値)に今すぐC + +/CLIのラッパーにプッシュされています。

そして最後に、C#のからクラッシュコード:

0x04d1004cで未処理の例外 に:

public static int hReceiveLogEvent(IntPtr pInstance, IntPtr pData) 
    { 
     Console.WriteLine("in hReceiveLogEvent..."); 
     Console.WriteLine("pInstance: {0}", pInstance); 
     Console.WriteLine("pData: {0}", pData); 

     // provide object context for static member function 
     helloworld hw = (helloworld)GCHandle.FromIntPtr(pInstance).Target; 
     if (hw == null || pData == null) 
     { 
      Console.WriteLine("hReceiveLogEvent: received null instance pointer or null data\n"); 
      return 0; 
     } 

     // typecast data to DataLogger object ptr 
     IntPtr ip2 = GCHandle.ToIntPtr(GCHandle.Alloc(new DataLoggerWrap(pData))); 
     DataLoggerWrap dlw = (DataLoggerWrap)GCHandle.FromIntPtr(ip2).Target; 

     //Do Logging Stuff 

     Console.WriteLine("exiting hReceiveLogEvent..."); 
     Console.WriteLine("pInstance: {0}", pInstance); 
     Console.WriteLine("pData: {0}", pData); 
     Console.WriteLine("Setting pData to zero..."); 
     pData = IntPtr.Zero; 
     pInstance = IntPtr.Zero; 
     Console.WriteLine("pData: {0}", pData); 
     Console.WriteLine("pInstance: {0}", pInstance); 

     return 1; 
    } 

すべてが行われ、その後、我々はリターンに恐ろしいクラッシュを見ているコンソールへの書き込みhelloworld.exe:0xC0000005:アクセス 違反の読み取り場所0x04d1004c。

私はここからデバッガにステップ場合は、私が見るすべては、コールスタック上の最後のエントリがあるということである:ある80805964

:10進値に評価>「04d1004c()」あなたが示しコンソールを見ればのみ興味深い:

entering registerDataLogger 
pointer to callback handle: 790848 
fp for callback: 2631370 
pointer to inst: 790844 
in hReceiveLogEvent... 
pInstance: 790844 
pData: 80805964 
exiting hReceiveLogEvent... 
pInstance: 790844 
pData: 80805964 
Setting pData to zero... 
pData: 0 
pInstance: 0 

を、私はデバッグの間とMicrosoftの世界ではかなり異なっているいくつかのことを解放することを知っています。私はもちろん、バイトパディングと変数の初期化について心配しています。もし私がここで提供していないものがあれば、私に知らせてください。私は(すでに長い)投稿に追加します。私はまた、マネージコードがすべての所有権を解放していない可能性があり、ネイティブC + +のもの(私はコードを持っていない)削除またはpDataオブジェクトを殺すため、アプリをクラッシュしようとしている可能性があります。

さらに詳細な情報があれば、デバッグモードではうまく動作します。

実際のヘッドスクラッチ問題は、どんな助けにも感謝します!

答えて

3

私は、スタックが原因で呼び出し規約不一致の砕いてしまったと思う: コールバックデリゲートの宣言に属性

[UnmanagedFunctionPointer(CallingConvention.Cdecl)] 

を入れて試してみてください。

+0

ほとんどの場合、サポートが必要でした。サードパーティのベンダーに連絡した後、私たちはcdecl specを使用してコンパイルし、Managed Code Complianceに必要なstdcallではないことを発見しました。http://msdn.microsoft.com/en-us/library/367eeye0%28VS.80%29.aspx 。 StackOverflowの質問を追加して、これがなぜ必要なのか尋ねます。うまくいけば、誰かが参照されているMSDNの記事よりも良い説明を与えるでしょう。 – TomO

+0

プロジェクト設定では、__declspec()で指定されていない場合、使用される呼び出し規約(C/C++)の既定値があります。この呼び出し規約はコード内では見えませんでした。スタックのクリーンアップの責任が一致しない場合、スタックを破壊します(ダブルクリーンアップまたは少なすぎるためにコールの前の状態にリセットされません)。これは、スタックに渡される引数の量によって異なります。 http://en.wikipedia.org/wiki/Calling_convention – jdehaan

0

This doesn't directly answer your questionが、それは大丈夫ではないリリースモード対大丈夫デバッグモード限り正しい方向にあなたを導く可能性がありますデバッガ以来

は、一般的に、スタックに記録保持多くの情報を追加します私のプログラムのメモリサイズとレイアウトを補うために、あまり重要でない912バイトのメモリを落書きすることで、デバッグモードで「幸運にも」なっていました。しかし、デバッガがなければ、私はかなり重要なものの上に落書きしてしまい、最終的に私自身のメモリ空間の外に出て、Interopに所有していないメモリを削除させました。

DataLoggerWrapの定義は何ですか?受け取っているデータのcharフィールドが小さすぎる可能性があります。

0

私はあなたが達成しようとしていることをよく分かりません。

いくつかのポイント:あなたが記述行動は珍しいことではありません悪い所有権を持つよう

1)ガベージコレクタは、リリースモードでより積極的です。

2)私は以下のコードが何をしようとしているのか分かりません。

IntPtr ip2 = GCHandle.ToIntPtr(GCHandle.Alloc(new DataLoggerWrap(pData))); 
DataLoggerWrap dlw = (DataLoggerWrap)GCHandle.FromIntPtr(ip2).Target; 

あなたはメモリにDataLoggerWrapのインスタンスをロックするGCHandle.Allocを使用しますが、その後は管理対象外にそれを渡すことはありません - なぜあなたはそれをロックしていますか? あなたは決してそれを解放することはありませんか?

次に、2行目で参照を取得します。なぜその循環パスですか?なぜ参照 - あなたはそれを使用しない?

3)IntPtrsをnullに設定します - なぜですか? - これは関数スコープの外では効果がありません。

4)コールバックの契約内容を知る必要があります。誰がコールバック関数か呼び出し関数を所有していますか?

0

私は@ jdehaanと一緒にいますが、CallingConvetion.StdCallは、特にサードパーティのlibがBC++で書かれている場合には、答えになる可能性があります。

関連する問題