你能帮我使用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引脚。不理想,但它有效。
编辑:我的内存泄漏假设错了,但是,由于评论和研究提示,我学到了很多东西并找到了答案。
答案 0 :(得分:1)
如评论中所述和我原始问题的答案中所提到的,分段错误是由回调函数的垃圾回收引起的。
在我的代码中的正确位置添加GC.StayAlive (MyFunctionDoingCallBack)
使得任何人都可以清楚地查看代码 - 我 - 在收集回调垃圾时是安全的。这个解决方案比解决回调超出范围的原因更容易,因此如何将其保持在范围内。我意识到这对我作为一名程序员反映的很糟糕; - (。
This answer解释了为什么SetLastError = false是正确的。本机代码肯定不会返回Win32错误,它无论如何都在Linux上运行,所以我只会从错误代码中获取垃圾。
在DLLImport属性上设置CallingConvention.Cdecl
有效。也许引用“ clr仅支持编组函数指针的标准调用约定”对我的系统来说是错误的(Linux上的单声道)或者我误解了引用。
在代理上设置[<UnmanagedFunctionPointer(CallingConvention.Cdecl)>]
似乎是多余的,EntryPoint = "wiringPiISR"
属性的DLLImport
部分也是如此。为清楚起见,我删除了两者
感谢@FyodorSoikin,@ ijjarn,@ BirdTranberg,@ CornNichols和@vcsjones