12

コールバック用の C# デリゲートの書き方は基本的に理解できたと思いますが、これは私を混乱させます。C++ の定義は次のとおりです。

typedef int (__stdcall* Callback)(
long lCode,
long lParamSize,
void* pParam 
);

私のC#アプローチは次のようになります。

 unsafe delegate int CallbackDelegate (int lCode, int lParamSize, IntPtr pParam);

これは間違っているように見えますが、デリゲートの定義が間違っていることを意味する PInvokeStackInbalance エラーが発生するためです。

関数の残りのパラメーターは文字列または int です。つまり、エラーを引き起こすことはありません。また、デリゲートの代わりに IntPtr.Zero を渡すだけの場合 (存在しないコールバック関数を指していることを意味します)。 AccessViolation エラーが発生しますが、これも理にかなっています。

私は何を間違っていますか?

編集:

完全な C++ 関数は次のとおりです。

int
__stdcall
_Initialize (
const char*         FileName,
Callback            cbFunction,
int                 Code,
const char*         Name,
unsigned int        Option,
unsigned int        Option2
);

私のC#バージョンは次のとおりです。

[DllImport("MyDll.dll", CallingConvention = CallingConvention.StdCall)]
public static extern int _Initialize (string FileName, CallbackDelegate cbFunction, int Code, string Name, uint Options, uint Options2);

関数は (テスト用に) コンソール アプリケーションのメイン ルーチン内で呼び出されます。

static void Main(string[] args)
{
    CallbackDelegate del = new CallbackDelegate(onCallback);

    Console.Write(_Initialize("SomeFile.dat", del, 1000, "", 0, 4));
    Console.Read();
}

これはどこonCallbackですか:

static int onCallback(int lCode, int lParamSize, IntPtr pParam)
{
    return 0;
}

デリゲートの代わりにa を渡し、関数の定義を の代わりに に変更すると、PInvokeStackInbalanceを呼び出す行でエラーが発生します。_InitializeIntPtr.ZeroIntPtrCallbackDelegateAccessViolationException

4

3 に答える 3

5

VS2012 で多くの C#/C++ 相互運用を行っている現在のプロジェクトにコードを追加しました。そして、「自分のマシンで機能した」という言葉を使うのは嫌いですが、私にとってはうまくいきました。私が実行したコードは、基本的な変更を行っていないことを示すために以下にリストされています。

私の提案は、以下のように _Initialize のスタブ関数を使用して新しいネイティブ dll を作成し、インターフェイスの両側を制御できる場合に機能するかどうかを確認することです。それが機能しても実際の dll が機能しない場合は、ネイティブ側のコンパイラ設定に起因するか、ネイティブ側のバグであり、スタックを踏みにじっています。

extern "C" {

    typedef int (__stdcall* Callback)(long lCode,long lParamSize,void* pParam );

    TRADITIONALDLL_API int __stdcall _Initialize (const char* FileName,Callback cbFunction, int                 Code,
                                const char*         Name,unsigned int        Option,unsigned int        Option2)
    {
        cbFunction(0, 0, nullptr);
        return 0;
    }
}

C# 側では、宣言をインターフェイス クラスに追加しました。

 public delegate int CallbackDelegate(int lCode, int lParamSize, IntPtr pParam);

 [DllImport("XXX.dll", CallingConvention = CallingConvention.StdCall)]
 public static extern int _Initialize(string FileName, CallbackDelegate cbFunction, int Code, string Name, uint Options, uint Options2);

そして、私のメインでは:

private int onCallback(int lCode, int lParamSize, IntPtr pParam)
{
       return 0;
}
XXXInterface.CallbackDelegate del = new XXXInterface.CallbackDelegate(onCallback);

Console.Write(XXXInterface._Initialize("SomeFile.dat", del, 1000, "", 0, 4));
于 2012-11-30T15:05:25.980 に答える
3

.NETlongは 64 ビットです。C++longはわずか 32 ビットかもしれません。その定義をコンパイルした C++ コンパイラで、そのサイズを確認してくださいlong

于 2012-11-29T16:08:15.910 に答える
3
[UnmanagedFunctionPointerAttribute(CallingConvention.StdCall)]
unsafe delegate int CallbackDelegate (int lCode, int lParamSize, IntPtr pParam);

.NET が stdcall ではなく cdecl を想定している場合、スタックは確実に停止されます。

于 2012-11-30T13:31:00.137 に答える