[英]C# C++ Interop callback
我最近一直在修补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)循环。 然而,程序只是退出。 我不能完全理解这种行为,我想象这是预期的,但我的一部分会期望它继续在主要方面。
我所期待的......
然而,这不太对。
有人会善意地解释会发生什么。
为了节省空间,我没有发布非托管代码,但如果需要,我很乐意发布
编辑:我打开非托管调试(完全忘了这样做),现在我看到了崩溃..
运行时检查失败#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);
}
您必须使用以下命令将托管回调转换为本机函数指针(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);
我遇到了与OP完全相同的问题(相同的错误消息)。 接受的答案根本没有帮助,甚至@ TomP89也在评论中承认。
这并不奇怪,因为错误消息表明传递的回调函数的调用约定是错误的,而假设的解决方案更改了传递回调的函数的调用约定,如果库导出的是cdecl,则会导致堆栈不平衡-functions(这是Win32 API之外的所有函数的默认情况)。
事实证明,.Net默认采用任何委托的调用约定“stdcall”。 通常,非托管代码将采用与其导出函数相同的调用约定(在本例中为mine:“cdecl”)。
所以真正的解决方案(最终对我有用)是将回调的调用约定改为“cdecl”。 这个问题说明了这是如何实现的,简而言之:
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void TomDelegate(int a, int b);
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.