C#コールバックからC#関数へのアクセス違反例外/クラッシュ
-
05-07-2019 - |
質問
つまり、C#で最終的に使用するラッパーをC ++ / CLIで作成するために使用したネイティブのサードパーティC ++コードベース(.libおよび.hppファイル)があります。
デバッグモードからリリースモードに切り替えると、コールバックのコードが返されたときにアクセス違反例外が発生するという特定の問題が発生しました。
コールバック関数形式の元のhppファイルのコード:
typedef int (*CallbackFunction) (void *inst, const void *data);
コールバック関数形式のC ++ / CLIラッパーのコード: (すぐに2つ宣言した理由を説明します)
public delegate int ManagedCallbackFunction (IntPtr oInst, const IntPtr oData);
public delegate int UnManagedCallbackFunction (void* inst, const void* data);
-すぐに、2番目の" UnManagedCallbackFunction"を宣言した理由「中間」を作成しようとしたことです。ラッパー内のコールバックのため、チェーンはネイティブC ++から変更されました> C#からネイティブC ++のバージョンへ> C ++ / CLIラッパー> C#...完全な開示、問題はまだ存在し、同じ行でC ++ / CLIラッパーにプッシュされたばかりです(リターン)。
そして最後に、C#のクラッシュコード:
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;
}
コンソールへのすべての書き込みが完了し、戻り時に恐ろしいクラッシュが見られます:
0x04d1004cの未処理の例外 helloworld.exe:0xC0000005:アクセス 違反読み取り場所0x04d1004c。
ここからデバッガにステップインした場合、表示されるのは、コールスタックの最後のエントリが>であることだけです。 " 04d1004c()" 10進数値80805964に評価されます
以下を示すコンソールを見ると、どれが面白いですか:
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オブジェクトを削除または削除しようとして、アプリがクラッシュする可能性があると思います。
より完全な開示、デバッグモードではすべて(一見)正常に動作します!
実際のヘッドスクラッチの問題で、助けに感謝します!
解決
呼び出し規約が一致しないため、スタックがクラッシュしたと思います。 属性を入れてみてください
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
コールバックデリゲート宣言。
他のヒント
これはあなたの質問に直接答えませんが、デバッグモードが問題ない場合とリリースモードが問題ない場合は正しい方向に導くことができます。
デバッガは多くの記録保持情報をスタックに追加するため、通常はプログラムのサイズとレイアウトをメモリに追加するため、私は“ラッキーになりました”デバッグモードでは、非常に重要ではなかった912バイトを超えるメモリを落書きします。ただし、デバッガーがないと、かなり重要なものの上に落書きして、最終的に自分のメモリスペースの外に出て、Interopが所有していないメモリを削除しました。
DataLoggerWrapの定義は何ですか? charフィールドは、受信するデータに対して小さすぎる可能性があります。
何を達成しようとしているのかわかりません。
いくつかのポイント:
1)ガベージコレクターはリリースモードでより積極的であるため、所有権が悪い場合、説明する動作は珍しくありません。
2)以下のコードが何をしようとしているのか理解できませんか?
IntPtr ip2 = GCHandle.ToIntPtr(GCHandle.Alloc(new DataLoggerWrap(pData)));
DataLoggerWrap dlw = (DataLoggerWrap)GCHandle.FromIntPtr(ip2).Target;
GCHandle.Allocを使用してメモリ内のDataLoggerWrapのインスタンスをロックしますが、それをアンマネージドに渡すことはありません。なぜロックするのですか? あなたもそれを決して解放しませんか?
2行目は参照を取得します-なぜ循環経路なのですか?なぜ参照-あなたはそれを決して使用しないのですか?
3)IntPtrsをnullに設定します-なぜですか? -これは、関数スコープ外では効果がありません。
4)コールバックのコントラクトを知る必要があります。コールバックまたは呼び出し関数のpDataの所有者は誰ですか?
特にサードパーティのライブラリがBC ++で記述されている場合など、CallingConvetion.StdCallが答えになる可能性があることを除いて、@ jdehaanと一緒にいます。