简体   繁体   English

C#C ++ Interop回调

[英]C# C++ Interop callback

I have recently been tinkering around with C# to C++ interop, in particularly setting up a callback function which is called from the C++ DLL. 我最近一直在修补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;
        }
    }
}

The question I have is that, when the program control comes into the TomCallback function, I was expecting it to then hit the while(true) loop in Main. 我的问题是,当程序控件进入TomCallback函数时,我期待它然后在Main中点击while(true)循环。 However instead the program just exits. 然而,程序只是退出。 I can't quite get my head round the behaviour, part of me imagines this is as expected but part of me would have expected it to continue on in main. 我不能完全理解这种行为,我想象这是预期的,但我的一部分会期望它继续在主要方面。

What I was expecting... 我所期待的......

  1. The GetData() function is called 调用GetData()函数
  2. The GetData function calls the callback GetData函数调用回调
  3. The callback function returns back to GetData 回调函数返回GetData
  4. GetData returns back to main() GetData返回main()

However this is not quite right. 然而,这不太对。

Would someone be kind enough to explain what happens. 有人会善意地解释会发生什么。

In order to save space I haven't posted the unmanaged code, however if it is needed i'm happy to post 为了节省空间,我没有发布非托管代码,但如果需要,我很乐意发布

Edit: I turned on Unmanaged debugging (totally forgot to do this) and now I see the crash.. 编辑:我打开非托管调试(完全忘了这样做),现在我看到了崩溃..

Run-Time Check Failure #0 - The value of ESP was not properly saved across a function call. 运行时检查失败#0 - ESP的值未在函数调用中正确保存。 This is usually a result of calling a function declared with one calling convention with a function pointer declared with a different calling convention. 这通常是调用使用一个调用约定声明的函数和使用不同调用约定声明的函数指针的结果。

Native code as this is where crash is 本机代码就是崩溃的地方

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

You must convert your managed callback to the Native Function Pointer (IntPtr in C#) by using the 您必须使用以下命令将托管回调转换为本机函数指针(C#中的IntPtr)

IntPtr Marshal.GetFunctionPointerForDelegate(Delegate d)

method. 方法。

Your usage of SetCallback() with System.Delegate as an argument is wrong. 您使用System.Delegate作为参数的SetCallback()是错误的。

Make it 做了

SetCallback(Marshal.GetFunctionPointerForDelegate(lTD));

and redeclare SetCallback as 并重新声明SetCallback为

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

I had the exact same problem as the OP (same error message). 我遇到了与OP完全相同的问题(相同的错误消息)。 The accepted answer did not help at all, which even @TomP89 admits in the comments. 接受的答案根本没有帮助,甚至@ TomP89也在评论中承认。

This is not surprising because the error message indicates that the calling convention of the passed callback function is wrong, whereas the supposed solution changes the calling convention of the function that passes the callback , which results in a stack imbalance if what the library exports are cdecl-functions (as is the default case for all functions outside the Win32 API). 这并不奇怪,因为错误消息表明传递的回调函数的调用约定是错误的,而假设的解决方案更改了传递回调的函数的调用约定,如果库导出的是cdecl,则会导致堆栈不平衡-functions(这是Win32 API之外的所有函数的默认情况)。

It turns out that .Net assumes by default the calling convention "stdcall" for any delegate. 事实证明,.Net默认采用任何委托的调用约定“stdcall”。 Typically the unmanaged code will assume the same calling convention as its exported functions (in this case and mine: "cdecl"). 通常,非托管代码将采用与其导出函数相同的调用约定(在本例中为mine:“cdecl”)。

So the true solution (which finally worked for me) is changing the calling convention of the callback to "cdecl". 所以真正的解决方案(最终对我有用)是将回调的调用约定改为“cdecl”。 This question shows how this is accomplished, in short: 这个问题说明了这是如何实现的,简而言之:

    [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
    public delegate void TomDelegate(int a, int b);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM