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 错误,这意味着我对委托的定义是错误的。

该函数的其余参数是字符串或整数,这意味着它们不会导致错误,如果我只传递一个 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我在调用的行上收到错误,并将函数的定义更改为,而不是然后我得到 a 。_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
[UnmanagedFunctionPointerAttribute(CallingConvention.StdCall)]
unsafe delegate int CallbackDelegate (int lCode, int lParamSize, IntPtr pParam);

如果 .NET 假定使用 cdecl 而不是 stdcall,那么您的堆栈肯定会被冲洗掉。

于 2012-11-30T13:31:00.137 回答
3

.NETlong是 64 位的。C++long可能只有 32 位。检查编译该定义的 C++ 编译器的大小long

于 2012-11-29T16:08:15.910 回答