- c - 在位数组中找到第一个零
- linux - Unix 显示有关匹配两种模式之一的文件的信息
- 正则表达式替换多个文件
- linux - 隐藏来自 xtrace 的命令
我需要从 native C/C++ .dll 调用 C# 代码 异步 .
在搜索如何做时,我发现我可以创建一个 C# 委托(delegate)并从中获取一个函数指针,我将在我的 native 代码中使用它。
问题是我的 native 代码需要运行 异步 ,即在从 native 代码创建的单独线程中,这意味着从 C# 调用的 native 代码将在调用委托(delegate)之前返回到 C#。
我在另一个 SO 问题中读到有人对此有问题,尽管 MSDN 怎么说 ,因为他的委托(delegate)在被调用之前就被垃圾收集了,因为他的任务是异步的。
我的问题是:是否真的可以使用来自在 native 代码中创建的线程中运行的 native 代码的函数指针调用 C# 委托(delegate)?谢谢你。
最佳答案
不,这是一个通用错误,并非特定于异步代码。在您的情况下,字节更有可能发生,因为您从来没有 [DllImport] 背后的机制来让您远离麻烦。我会解释为什么会出错,也许这会有所帮助。
回调方法的委托(delegate)声明总是需要的,这就是 CLR 现在知道调用方法的方式。您经常使用 delegate 关键字显式声明它,如果非托管代码是 32 位并且假定函数是用 C 或 C++ 编写的,则可能需要应用 [UnmanagedFunctionPointer] 属性。声明很重要,这就是 CLR 知道您从 native 代码传递的参数需要如何转换为其托管等效项的方式。如果您的 native 代码将字符串、数组或结构传递给回调,那么这种转换可能会很复杂。
该场景在 CLR 中进行了大量优化,这很重要,因为托管代码不可避免地在非托管操作系统上运行。有很多这样的转换,你看不到它们,因为它们大部分发生在 .NET Framework 代码中。这个优化涉及到 thunk ,一小段自动生成的机器代码,负责调用外部方法或函数。每当您进行使用委托(delegate)的互操作调用时,都会即时创建 Thunk。在 C# 代码将委托(delegate)传递给 C++ 代码的情况下。你的 C++ 代码得到一个指向 thunk 的指针,一个函数指针,你存储它并稍后进行回调。
“你存储它”是问题开始的地方。 CLR 不知道您存储了指向 thunk 的指针,垃圾收集器看不到它。 Thunk 需要内存,通常只有少数几个字节用于机器代码。它们不会永远存在,当不再需要 thunk 时,CLR 会自动释放内存。
“不再需要”是个问题,您的 C++ 代码无法神奇地告诉 CLR 它不再进行回调。所以它使用的简单而明显的规则是当委托(delegate)对象被垃圾收集时,thunk 被销毁。
程序员永远都会遇到这个规则的麻烦。他们没有意识到委托(delegate)对象的生命周期很重要。在 C# 中尤其棘手,它有很多语法糖,可以很容易地创建委托(delegate)对象。您甚至不必使用 new 关键字或命名委托(delegate)类型,只需使用目标方法名称就足够了。这种委托(delegate)对象的生命周期只是 pinvoke 调用。在调用完成并且您的 C++ 代码已存储指针后,委托(delegate)对象不再在任何地方被引用,因此有资格进行垃圾回收。
发生这种情况以及 thunk 被破坏的确切时间是不可预测的。 GC 仅在需要时运行。在您调用电话后可能是一纳秒,当然这不太可能,可能是几秒钟。最棘手的是,永远不可能。发生在不显式调用 GC.Collect() 的典型单元测试中。单元测试很少对 GC 堆施加足够的压力来引发收集。当您从另一个线程进行回调时更有可能,隐含的是其他代码正在其他线程上运行,这使得更可能触发 GC。你会更快地发现问题。尽管如此,thunk 迟早会在真正的程序中被销毁。之后在 C++ 代码中进行回调时 Kaboom。
所以,坚如磐石的规则,你必须存储对委托(delegate)的引用以避免过早收集问题。做起来很简单,只需将它存储在 C# 程序中声明为 static
的变量中。 .通常足够好,当 C# 代码告诉您的 C++ 代码停止进行回调时,您可能希望将其显式设置回 null,在您的情况下不太可能。偶尔,您会想要使用 GCHandle.Alloc()
而不是静态变量。
关于c++ - C#/C++ 异步反向 pinvoke?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28715660/
这是我尝试在 C# 中使用的头文件签名和 C 代码: __declspec(dllexport) emxArray_real_T *emxCreateWrapper_real_T(real_T *da
我的项目在 .NET Frame work 3.5 中运行成功且没有错误。但是,当我将它定位到 .NET Frame work 4 时。我得到了错误: “对 PInvoke 函数的调用使堆栈不平衡。这
我在非托管 DLL 中有一个非常简单的函数,但我没有从它获得正确的返回值。 我可以确认通用 PInvoke 机制正在使用我的 C DLL 中的一个函数: /* Return an integer */
使用 PInvoke 时可能会发生哪些异常,或者所有错误均由方法返回值处理,并且由开发人员检查并在需要时引发异常? 最佳答案 对于 P/Invoke,可以肯定地说您需要处理两种类型的错误。 P/Inv
我正在尝试为 modbusm.dll (win-tech.com/html/mbusocx.htm) 创建一个包装器 C# 文件,为此我使用 dumpbin 输出。 文件 modbusm.dll 的转
我正在尝试使用 PInvoke 绑定(bind)此 C 函数。 bool GuiTextBox(Rectangle bounds, char *text, int textSize, bool edi
我目前正在尝试通过 C# pinvoke 使用 SendMessage 从子窗口获取一些文本。但是,我之前对窗口句柄进行硬核化的尝试失败了,因为该值在应用程序启动时发生了变化。有没有办法可靠地获取这个
这个问题不太可能帮助任何 future 的访问者;它只与一个小的地理区域、一个特定的时间点或一个非常狭窄的情况有关,这些情况并不普遍适用于互联网的全局受众。为了帮助使这个问题更广泛地适用,visit
考虑以下 C 函数: void get_lib_version(const char **ver_string); 如何使用 PInvoke 正确编码它?文档说它返回一个指向静态字符串的指针。我认为这
我有个问题,很奇怪。当我在 PInvoke 中使用下一个结构时,它正在工作,但是当我将它更改为类时,它不起作用。 我的 C#: [StructLayout(LayoutKind.Sequential)
我在 C++ 原生 dll 中有以下函数,我想在 C# 应用程序中使用它。 DWORD __cdecl Foo( LPCTSTR Input,
似乎有多种方法可以做到这一点,但我尝试过的示例对我不起作用。 我在某处读到,使用不安全指针是获得需要指针的更复杂结构的方法。 (对不起,我没能再次找到来源) 我最近的尝试看起来像这样: unsafe
我正在尝试在 C++ 中创建一个简单的 dll,并使用 PInvoke 从 C# 中调用它。我想传入一个字节数组,对其进行一些操作,然后发回另一个字节数组。我想我会传入框架和大小,然后创建一个非托管的
我正在尝试使用 P/Invoke 从 C# 调用 C++ 函数。 [DllImport(PATH)] public static extern int PQunescapeByteaWrapper(
我正在尝试使用 GetFileInformationByHandle 函数获取 FILE_ID_BOTH_DIR_INFO。调用后我的所有值都设置为零。 我收到 win32 错误消息: ERROR_B
我有以下 C 代码: const BYTE* Items[3]; Items[0] = item1; Items[1] = item2; Items[2] = item3; int result =
这个问题不太可能帮助任何 future 的访问者;它只与一个小的地理区域、一个特定的时间点或一个非常狭窄的情况相关,这些情况并不普遍适用于互联网的全局受众。为了帮助使这个问题更广泛地适用,visit
我有以下 PInvoke:(C 到 C#) [DllImport("chess_api.dll", CallingConvention = CallingConvention.Cdecl)] stat
我正在尝试在 C# 项目中使用 C DLL。 我在 C 中有一个函数: extern __declspec(dllexport) void InitBoard(sPiece board[8][8]);
我如何在 C# 中使用这个 dll 函数?我尝试了以下但出现错误。“外部组件抛出异常。” 我第一次用 C# 和 Delphi 做这个 PInvoke 的东西。 function HTTPGET(loc
我是一名优秀的程序员,十分优秀!