2012-11-29 70 views
9

我想我已经基本理解了如何编写回调函数的c#委托,但是这个让我很困惑。 C++的定义如下:用于C++回调的C#委托

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

和我的C#的做法是:

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

虽然这似乎是不正确的,因为我得到一个PInvokeStackInbalance错误,这意味着我委托的定义是错的。

该函数的其余参数是字符串或整数,这意味着它们不会导致错误,如果我只是传递一个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; 
} 

我得到了PInvokeStackInbalance错误在哪里我叫_Initialize行,如果我通过IntPtr.Zero而不是委托,函数的定义修改为IntPtr而不是CallbackDelegate然后我收到一个AccessViolationException

+0

'函数的参数的其余部分是字符串或整数“。不要隐藏信息。 –

+1

你有没有试过不使用不安全?即:代表int CallbackDelegate(int lCode,int lParamSize,IntPtr pParam); – DougEC

+0

@HansPassant我不是在试图隐藏信息,我试图离开我认为不相关的东西。我将编辑问题并添加信息。 – Valandur

回答

3

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假定cdecl而不是stdcall,那么你的堆栈肯定会被洗掉。

4

我将你的代码添加到我当前的项目中,我在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)); 
+0

谢谢你检查了这一点,应该自己做。我也试过了,它似乎工作。 还发现的是,应用程序似乎工作,如果我只是启动exe文件,没有调试... – Valandur

+0

这很奇怪。这听起来像是在C++边的dll中存在一个影响堆栈的错误。有没有什么办法让你获得本地dll的源代码,以便你可以在调试器下运行它们?我不知道有任何其他的方法来处理它,几乎可以肯定没有办法将它与源码固定在一起。 –

+0

不,可惜我无法获得源代码,否则我认为这会容易得多。我试着从C++程序中调用C++函数,这似乎工作得很好。因此,我认为DLL是好的,我只是在回调函数中犯了一个错误。感谢您的帮助。 – Valandur