F#使用PINVOKE使用C函数回调的Linux / ARM上的委托



您能帮助我使用f#的C函数,期望回调吗?我在基于ARM处理器的Raspberry Pi 2中使用F#,Mono和Arch Linux。

我最初的问题,了解用于回调的签名,您可以阅读以获取更多上下文。

我已经达到了僵局。使用库时,程序会以分段故障崩溃。

我相信这是因为编组不匹配。根据《专家F#3.0 》," CLR仅支持编组功能指针的STD调用惯例"。CLR假设 callee将清洁堆栈(stdcall),并且C库预计呼叫者将根据MSDN上的定义清洁堆栈(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 ?)

注意:单调项目网站上有一篇有趣的文章,标题为"与本机库的Interop"

与此同时,我写了一些循环来调查GPIO引脚。不理想但有效。

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

评论中指出的细分故障以及我原来的问题的答案中所建议的是由回调函数的垃圾收集引起的。

在我的代码中正确的位置添加 GC.StayAlive (MyFunctionDoingCallBack),当对收集的回调垃圾安全时,对审查代码的任何人都清楚了。该解决方案比弄清为什么回调要脱离范围,从而更容易,从而如何将其保持在范围内。我意识到这反映了我作为程序员的反映; - (。

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

在dllimport属性上设置CallingConvention.Cdecl。也许" CLR仅支持编组功能指针的std呼叫惯例"是我的系统(Lino上的Mono)是错误的,或者我误解了报价。

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

在MSDN

的OldNewthing博客上有一系列有关Ddlimport和Dllexport的文章

感谢@fyodorsoikin,@ildjarn,@benttranberg,@curtnichols和@vcsjones

最新更新