2012-11-29 15 views
9

私は基本的にコールバックのためにC#代理人を書く方法を理解していると思いますが、これは私を混乱させます。C#コールバックの代理人

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

と私のC#のアプローチは、次のようになります: C++の定義は以下のとおりである

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

これは正しくないと思われるが、私は、デリゲートの私の定義を意味PInvokeStackInbalanceエラーを取得しますので、間違っている。

関数の残りのパラメータは文字列またはintです。つまり、エラーを引き起こすことはありません。また、デリゲートの代わりにIntPtr.Zeroを渡すだけであれば(つまり、既存のコールバック関数)AccessViolationエラーが発生します。

私は間違っていますか?

EDIT:

完全な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(); 
} 
その後、私が代わりにデリゲートの IntPtr.Zeroを渡した場合、私は、私は _Initializeを呼び出す行に PInvokeStackInbalanceエラーを取得します

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

、およびIntPtrの代わりCallbackDelegateに関数の定義を変更:onCallbackはこれです0

私はAccessViolationExceptionを得る。

+0

'関数のパラメータの残りの部分をしています文字列またはints'です。情報を隠さないでください。 –

+1

安全でないことを試しましたか?すなわち、デリゲートint CallbackDelegate(int lCode、int lParamSize、IntPtr pParam); – DougEC

+0

@HansPassant私は情報を隠そうとしていない、私は関連性のないものを捨てようとしていた。質問を編集して情報を追加します。 – Valandur

答えて

3

A .NET longは64ビットです。 C++ longはちょうど32ビットかもしれません。その定義をコンパイルしたC++コンパイラで、どのサイズのサイズがlongであるかを確認してください。

+0

良い入力、残念ながら、これは問題を解決していないようです。私はC++コンパイラをチェックすることはできません。私は.dllと.hファイルしか持っていませんが、longをInt32に変更することは役に立ちません。 – Valandur

3
[UnmanagedFunctionPointerAttribute(CallingConvention.StdCall)] 
unsafe delegate int CallbackDelegate (int lCode, int lParamSize, IntPtr pParam); 

.NETはstdcallのの代わりにCDECLを前提とした場合、あなたのスタックが最も確実にまずいことになります。

4

あなたのコードを私の現在のプロジェクトに追加しました。ここで私はVS2012のC#/ C++ interopをたくさん使っています。そして、私は "それは私のマシンで働いた"を使用することを嫌うが、それは私のためにうまくいった。私が実行したコードは、私が作ったものではなく、根本的な変更をしたものであることを示すために、以下にリストされています。

私は、以下のような_Initialize用のスタブ関数で新しいネイティブdllをビルドし、インターフェイスの両側を制御できるときに動作するかどうかを確認することをお勧めします。それは動作しますが、実際のDLLはそうでない場合は、ネイティブ側のコンパイラ設定か、ネイティブ側のバグです。スタック上で踏み込んでいます。C#の側で

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; 
    } 
} 

、私は私のインターフェイスクラスに宣言を追加しました:私のメインで

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)); 
+0

これをチェックしていただきありがとうございました。私もそれを試して、それは動作するようです。 デバッガなしでexeファイルを起動するだけでアプリケーションが動作するように見えることもあります。 – Valandur

+0

これは奇妙です。スタックに影響を与えるC++側のdllにバグがある可能性が高いようです。ネイティブdllのソースを入手して、デバッガでそれらを組み合わせて実行する方法はありますか?私はそれにアプローチするための他の良い方法は知らないし、ソースとそれを固定する方法はほとんどありません。 –

+0

いいえ、悲しいことに私はソースコードを手に入れることができません。そうでなければ、これはもっと簡単になると思います。私はC++プログラムからC++関数を呼び出そうとしましたが、これは問題なく動作するようです。そのため、DLLは問題ないと思いますが、私はコールバック関数で間違いを犯しています。しかし、助けてくれてありがとう。 – Valandur