6

そのため、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;
    }

コンソールへのすべての書き込みが完了すると、戻ってきたときに恐ろしいクラッシュが発生します。

helloworld.exe の 0x04d1004c で未処理の例外: 0xC0000005: アクセス違反の読み取り場所 0x04d1004c.

ここからデバッガーにステップインすると、コール スタックの最後のエントリが次のようになるだけです: > "04d1004c()" これは 10 進数値として評価されます:

これは、次のようなコンソールを見る場合にのみ興味深いものです。

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

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

より完全な開示、それはすべてデバッグモードで(一見)正常に動作します!

どんな助けにも感謝する本当のヘッドスクラッチの問題!

4

4 に答える 4

4

呼び出し規約が一致しないためにスタックがクラッシュしたと思います:属性を配置してみてください

 [UnmanagedFunctionPointer(CallingConvention.Cdecl)]

コールバック デリゲート宣言で。

于 2009-09-11T20:43:18.430 に答える
0

あなたが何を達成しようとしているのかわかりません。

いくつかのポイント:

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を所有しているのは誰ですか?

于 2009-09-16T17:08:10.733 に答える
0

私は@jdehaanと一緒ですが、CallingConvetion.StdCallが答えになる可能性があることを除いて、特にサードパーティのライブラリがBC ++で書かれている場合などです。

于 2009-09-16T19:15:20.040 に答える