11

私は最近、C#とC ++の相互運用機能をいじくり回しており、特にC++DLLから呼び出されるコールバック関数を設定しています。

namespace TomCSharpDLLImport
{
    class Program
    {
        public delegate void TomDelegate(int a, int b);

        [DllImport("TomDllNative.dll", CallingConvention = CallingConvention.Cdecl)]
        public static extern void GetData();

        [DllImport("TomDllNative.dll", CallingConvention = CallingConvention.Cdecl)]
        public static extern void SetCallback(TomDelegate aCallback);

        static void Main(string[] args)
        {
            TomDelegate lTD = new TomDelegate(Program.TomCallback);

            SetCallback(lTD); //Sets up the callback

            int thread = Thread.CurrentThread.ManagedThreadId;

            GetData(); //This calls the callback in unmanaged code

            while (true) ;
        }

        //Callback function which is called from the unmanaged code
        public static void TomCallback(int a, int b)
        {
            Console.WriteLine("A: {0} B: {1}", a, b);
            int thread = Thread.CurrentThread.ManagedThreadId;
        }
    }
}

私が持っている質問は、プログラムコントロールがTomCallback関数に入るとき、それがMainのwhile(true)ループにヒットすることを期待していたということです。ただし、代わりにプログラムは終了します。私はその行動に頭を悩ませることはできません。私の一部はこれが予想通りであると想像していますが、私の一部はそれが主に続くことを期待していたでしょう。

私が期待していたこと...

  1. GetData()関数が呼び出されます
  2. GetData関数はコールバックを呼び出します
  3. コールバック関数はGetDataに戻ります
  4. GetDataはmain()に戻ります

しかし、これは完全に正しくありません。

誰かが何が起こるかを説明するのに十分親切でしょうか。

スペースを節約するために、アンマネージコードは投稿していませんが、必要に応じて投稿できます。

編集: アンマネージデバッグをオンにしました(これを行うのを完全に忘れました)。クラッシュが表示されます。

実行時チェックの失敗#0-ESPの値は、関数呼び出し全体で適切に保存されませんでした。これは通常、ある呼び出し規約で宣言された関数を、別の呼び出し規約で宣言された関数ポインターで呼び出した結果です。

これはクラッシュが発生する場所であるため、ネイティブコード

#include "stdafx.h"
typedef void (*callback_function)(int, int);

extern "C" __declspec(dllexport) void SetCallback(callback_function aCallback);
extern "C" __declspec(dllexport) void GetData();

callback_function gCBF;

__declspec(dllexport) void SetCallback(callback_function aCallback)
{
    gCBF = aCallback;
}

__declspec(dllexport) void GetData()
{
    gCBF(1, 2);
}
4

2 に答える 2

15

を使用して、マネージドコールバックをネイティブ関数ポインター(C#のIntPtr)に変換する必要があります。

IntPtr Marshal.GetFunctionPointerForDelegate(Delegate d)

方法。

System.Delegateを引数として使用したSetCallback()の使用法は間違っています。

成功する

SetCallback(Marshal.GetFunctionPointerForDelegate(lTD));

SetCallbackを次のように再宣言します

/// StdCall is crucial here
[DllImport("TomDllNative.dll", CallingConvention = CallingConvention.StdCall)]
public static extern void SetCallback(IntPtr aCallback);
于 2012-05-31T21:19:20.940 に答える
10

OPとまったく同じ問題が発生しました(同じエラーメッセージ)。受け入れられた答えはまったく役に立たなかった、それは@TomP89でさえコメントで認めている。

エラーメッセージは、渡されたコールバック関数の呼び出し規約が間違っていることを示しているので、これは驚くべきことではありませんが、想定されるソリューションは、コールバックを渡す関数の呼び出し規約を変更します。これにより、ライブラリがエクスポートするものがcdeclの場合、スタックが不均衡になります。 -関数(Win32 API以外のすべての関数のデフォルトの場合)。

.Netは、デフォルトで任意のデリゲートの呼び出し規約「stdcall」を想定していることがわかりました。通常、アンマネージコードは、エクスポートされた関数(この場合は "cdecl")と同じ呼び出し規約を想定しています。

したがって、真の解決策(最終的にはうまくいきました)は、コールバックの呼び出し規約を「cdecl」に変更することです。この質問は、これがどのように達成されるかを示しています。

    [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
    public delegate void TomDelegate(int a, int b);
于 2018-03-12T17:04:41.340 に答える