C#C++ Interop回调

Tom*_*P89 8 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;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

我的问题是,当程序控件进入TomCallback函数时,我期待它然后在Main中点击while(true)循环.然而,程序只是退出.我不能完全理解这种行为,我想象这是预期的,但我的一部分人会期望它继续在主要方面.

我所期待的......

  1. 调用GetData()函数
  2. GetData函数调用回调
  3. 回调函数返回GetData
  4. GetData返回main()

然而,这不太对.

有人会善意地解释会发生什么.

为了节省空间,我没有发布非托管代码,但如果需要,我很乐意发布

编辑: 我打开非托管调试(完全忘了这样做),现在我看到了崩溃..

运行时检查失败#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);
}
Run Code Online (Sandbox Code Playgroud)

Vik*_*pov 12

您必须使用以下命令将托管回调转换为本机函数指针(C#中的IntPtr)

IntPtr Marshal.GetFunctionPointerForDelegate(Delegate d)
Run Code Online (Sandbox Code Playgroud)

方法.

您使用System.Delegate作为参数的SetCallback()是错误的.

做了

SetCallback(Marshal.GetFunctionPointerForDelegate(lTD));
Run Code Online (Sandbox Code Playgroud)

并重新声明SetCallback为

/// StdCall is crucial here
[DllImport("TomDllNative.dll", CallingConvention = CallingConvention.StdCall)]
public static extern void SetCallback(IntPtr aCallback);
Run Code Online (Sandbox Code Playgroud)


oli*_*ver 5

我遇到了与OP完全相同的问题(相同的错误消息)。接受的答案根本没有帮助,甚至@ TomP89也在评论中承认了这一点。

这并不奇怪,因为错误消息表明传递的回调函数的调用约定错误,而假定的解决方案更改了传递回调的函数的调用约定,如果库导出的内容为cdecl,则会导致堆栈不平衡。 -functions(这是Win32 API之外所有函数的默认情况)。

事实证明,.Net默认情况下假定任何委托的调用约定为“ stdcall”。通常,非托管代码将采用与其导出的函数相同的调用约定(在本例中是我的:“ cdecl”)。

因此,真正的解决方案(最终对我有用)是将回调的调用约定更改为“ cdecl”。简而言之,这个问题说明了如何完成此任务:

    [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
    public delegate void TomDelegate(int a, int b);
Run Code Online (Sandbox Code Playgroud)