【问题标题】:Passing Function Pointer in C#在 C# 中传递函数指针
【发布时间】:2015-03-27 12:16:25
【问题描述】:

我在将 C++ .dll 函数转换为 C# 时遇到问题。

函数是这样的:

    void funct(void*(*handler)(void*));

我认为这意味着将指针传递给采用 void 指针并返回 void 指针的函数,如下所述:

Passing Function Pointer.

我想做的是在 C# 中做同样的事情,但我知道如何做。我尝试使用委托,但我不确定如何使用以及他们是否可以做我想做的事情。

感谢您的帮助!

编辑:

以下是 register_message_handler 和 message_handler 的 C++ 函数:

void register_message_handler(void*(*handler)(void*));
void *message_handler(void *raw_message);

编辑: xanatos 在下面有准确的解释和对 C# 的转换。感谢一吨xanatos!

【问题讨论】:

  • 不,它返回 void。所以它是一个没有参数,没有返回值的方法。
  • void 函数(动作处理程序)
  • void*(handler)(void) 应该是 void (*handler)(void)
  • @Tropicana55 你能告诉我们register_message_handlermessage_handler 的C(.h 文件中的那些)签名吗?目前还不清楚它们应该如何协同工作

标签: c# function pointers


【解决方案1】:
void funct(void*(*handler)(void*));

是一个接受指针并返回指针的函数。

在 C# 中是:

IntPtr MyFunc(IntPtr ptr);

所以你需要一个像这样的代表:

public IntPtr delegate MessageHandlerDelegate(IntPtr ptr);

[DllImport("mydll.dll")]
public static extern void register_message_handler(MessageHandlerDelegate del);

请注意,P/Invoke(调用本机方法)是 Action<...>Func<...> 委托不起作用的罕见情况之一,您必须构建“特定”委托。

BUT 来调用它,它有点复杂,因为你必须保存委托的“副本”,这样如果 C 函数在任何时候调用这个方法,这个副本仍然是“活动的” " 并且还没有被 GC(例如参见 https://stackoverflow.com/a/5465074/613130)。最常见的做法是将所有内容封装在一个类中,并将副本放在该类的属性/字段中:

class MyClass
{
    public delegate IntPtr MessageHandlerDelegate(IntPtr ptr);

    [DllImport("mydll.dll")]
    public static extern void register_message_handler(MessageHandlerDelegate del);

    [DllImport("mydll.dll")] 
    public static extern IntPtr message_handler(IntPtr message);

    public MessageHandlerDelegate Del { get; set; }

    public void Register()
    {
        // Make a copy of the delegate
        Del = Handler;
        register_message_handler(Del);            
    }

    public IntPtr Handler(IntPtr ptr)
    {
        // I don't know what ptr is
        Console.WriteLine("Handled");
        return IntPtr.Zero; // Return something sensible
    }
}

请注意,如果您使用IntPtr,则不需要unsafe

如果你想将message_handler传递给register_message_handler,最安全的方法是

// Make a copy of the delegate
Del = message_handler;
register_message_handler(Del);

有可能你可以直接做 不,没有,检查 罢工>

register_message_handler(message_handler);

CLR 会解决这个问题,但我不确定……我不能轻易测试它,我也不会这样做。 (如果你想测试它,在register_message_handler 之后添加一个GC.Collect()。如果一段时间后你收到CallbackOnCollectedDelegate 错误,那么你知道你不能这样做:-))

嗯...检查。 你不能做原始的register_message_handler(message_handler),你必须使用MyClass

非常意识到一些事情:最好总是在函数指针中指定调用约定 C 端 C# 端 even。 C# 使用 stdcall,而 C 使用 cdecl。在 x86 模式下,您可能会遇到非常可怕的静默崩溃(在 x64 中只有一个调用约定)

void __stdcall register_message_handler(void* (__stdcall *handler)(void*));
void * __stdcall message_handler(void *raw_message);

和C#方面

[UnmanagedFunctionPointer(CallingConvention.StdCall)]
public delegate IntPtr MessageHandlerDelegate(IntPtr ptr);

[DllImport("Win32Project1.dll", CallingConvention = CallingConvention.StdCall)]
public static extern void register_message_handler(MessageHandlerDelegate del);

[DllImport("Win32Project1.dll", CallingConvention = CallingConvention.StdCall)]
public static extern IntPtr message_handler(IntPtr message);

(或无处不在cdecl

【讨论】:

  • 问题是关于 void *(f)(void) ,你的例子都不是完全匹配的。
  • 天哪!谢谢一吨xanatos!抱歉,返回的 void 或 void 指针混淆,我忘记将 c++ 函数转换为代码并且 * 退出了。我刚刚在 2 个 c++ 函数中进行了编辑,现在我将尝试您的建议。
  • 嘿xanatos,我实现了你的代码,我还有另一个问题,关于以后如何正确调用它。除了上面 MyClass 内部的代码之外,我还在 message_handler 函数中添加了:[DllImport("mydll.dll")] public unsafe static extern void message_handler(void *message); 然后,我稍后尝试通过以下调用调用它:MyClass.register_message_handler(MyClass.message_handler);,我收到“从 void* 转换为 IntPtr”错误。我尝试使用 IntPtr() 转换调用转换 void*,但我认为我错过了错误的重点。
  • @Tropicana55 签名错误。右一:[DllImport("mydll.dll")] public static extern IntPtr message_handler(IntPtr message)。并查看答案中添加的段落。当您看到 void* 时,请使用 IntPtr
  • 哦,对了,对不起。您确实提到了 void* 到 IntPtr 点。太感谢了!它现在完美运行。我希望我能做的不仅仅是谢谢你;见鬼,我什至不能投票给你,因为我太新了。但你提供了令人难以置信的帮助。
猜你喜欢
  • 2010-09-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-06-16
  • 2020-12-10
  • 1970-01-01
  • 2021-10-05
  • 1970-01-01
相关资源
最近更新 更多