在Linux / ARM上使用带有C函数回调的pinvoke进行F#委托

时间:2016-06-15 18:27:08

标签: .net c f# mono pinvoke

你能帮我使用F#的C函数,它需要回调吗?我在基于ARM处理器的Raspberry Pi 2上使用F#,mono和Arch Linux。

我原来的问题是,了解用于回调的签名是answered,您可以阅读更多上下文。

我陷入了僵局。使用库时,程序会因分段错误而崩溃。

我认为这是因为编组不匹配。 " clr仅支持编组函数指针的#dd调用约定"根据书 Expert F#3.0 。 CLR假定被调用者将清理堆栈(StdCall),并且C库期望调用者根据{{3上的定义清理堆栈(Cdecl) }}。因此内存泄漏。

可悲的是,我找不到让CLR使用Cdecl代表这个委托,也不能让C在ARM处理器上使用StdCall的方法。我还没有编写C库,但我可以访问源代码。

这是C

中的函数签名
int wiringPiISR (int pin, int edgeType,  void (*function)(void)) ;

和我的F#代码的一部分:

type ISRCallback = delegate of unit -> unit

[<DllImport(wiringPiLib, EntryPoint = "wiringPiISR", CallingConvention = CallingConvention.Cdecl, SetLastError=true)>]
    extern int wiringPiISR(int pin, int mode, [<MarshalAs(UnmanagedType.FunctionPtr)>]ISRCallback callBack);

使用[<UnmanagedFunctionPointer(CallingConvention.Cdecl)>]来装饰代表并没有帮助。编译器默默地忽略它,我认为这些属性的设计是如何工作的。

还有另一种方法可以做到这一点 - 也许编辑C代码,或以其他方式包装它? (也许是C ++?)

注意:单项目网站上有一篇题为MSDN

的文章

与此同时,我已经编写了一个小循环来轮询GPIO引脚。不理想,但它有效。

编辑:我的内存泄漏假设错了,但是,由于评论和研究提示,我学到了很多东西并找到了答案。

1 个答案:

答案 0 :(得分:1)

如评论中所述和我原始问题的答案中所提到的,分段错误是由回调函数的垃圾回收引起的。

在我的代码中的正确位置添加GC.StayAlive (MyFunctionDoingCallBack)使得任何人都可以清楚地查看代码 - 我 - 在收集回调垃圾时是安全的。这个解决方案比解决回调超出范围的原因更容易,因此如何将其保持在范围内。我意识到这对我作为一名程序员反映的很糟糕; - (。

This answer解释了为什么SetLastError = false是正确的。本机代码肯定不会返回Win32错误,它无论如何都在Linux上运行,所以我只会从错误代码中获取垃圾。

在DLLImport属性上设置CallingConvention.Cdecl有效。也许引用“ clr仅支持编组函数指针的标准调用约定”对我的系统来说是错误的(Linux上的单声道)或者我误解了引用。

在代理上设置[<UnmanagedFunctionPointer(CallingConvention.Cdecl)>]似乎是多余的,EntryPoint = "wiringPiISR"属性的DLLImport部分也是如此。为清楚起见,我删除了两者

OldNewThing blog at MSDN

上有一系列关于DDLImport和DLLExport的文章

感谢@FyodorSoikin,@ ijjarn,@ BirdTranberg,@ CornNichols和@vcsjones