可能的F#交互式PInvoke错误

时间:2014-12-03 01:11:43

标签: f# pinvoke

在尝试向同事证明可以使用F#的C ++类时,我提出了以下概念证明。第一个片段是他为挑战提供的代码,下面的代码片段是我在F#中的实现。


namespace testapp {
    struct trivial_foo {
        int bar;
        __declspec(dllexport) void set(int n) { bar = n; }
        __declspec(dllexport) int get() { return bar; }
    }
}

open System.Runtime.InteropServices

type TrivialFoo =
    struct
        val bar: int
        new(_bar: int) = { bar = _bar }
    end

[<DllImport("Win32Project2.dll", EntryPoint="?get@trivial_foo@testapp@@QAEHXZ", CallingConvention = CallingConvention.ThisCall)>]
extern int trivial_foo_get(TrivialFoo& trivial_foo)

[<DllImport("Win32Project2.dll", EntryPoint="?set@trivial_foo@testapp@@QAEXH@Z", CallingConvention = CallingConvention.ThisCall)>]
extern void trivial_foo_set(TrivialFoo& trivial_foo, int bar)

type TrivialFoo with
    member this.Get() = trivial_foo_get(&this)
    member this.Set(bar) = trivial_foo_set(&this, bar)

在Visual Studio中调试或作为独立程序运行时,这可以预测:TrivialFoo.Get返回值barTrivialFoo.Set分配给它。但是,从F#Interactive运行时,TrivialFoo.Set将不会设置该字段。我怀疑它可能与从非托管代码访问托管内存有关,但这并不能解释为什么它只在使用F#Interactive时才会发生。有谁知道这里发生了什么?

2 个答案:

答案 0 :(得分:1)

我认为概念验证是互操作性的良好证明。您可能最好从C ++项目创建DLL导出定义,并使用去装饰名称。

作为PoC:F#创建适合CLI的MSIL,因此它可以与any other CLI language out there互操作。如果这还不够,并且您需要本机到网络互操作,请考虑使用COM,或者如上所述,在C ++上使用DLL导出定义。我个人不会像你在这里建议的那样尝试与C ++类定义互操作,有更简单的方法可以做到这一点。

或者,只需将您的C ++项目更改为.NET C ++项目,您就可以直接从F#访问这些类,同时仍具有C ++的强大功能。

当然,你可能仍然想知道为什么这个例子不能在FSI中运行。您可以通过运行以下内容来查看答案提示:

> System.IO.Directory.GetCurrentDirectory();;
val it : string = "R:\TMP"

要解决此问题,您有多种选择:

  • Win32Project2.dll复制到该目录
  • 将其所在的路径添加到PATH
  • 使用绝对路径
  • 使用编译时常量
  • 或使用环境变量(路径将被扩展)
  • 动态定位dll并动态绑定到它(复杂)

复制可能是这些解决方案中最简单的。

由于FSI意味着REPL,因此它可能不适合需要多个项目,库或其他复杂配置的此类任务。您可以考虑在this FSI request for support for #package上投票以导入NuGet包,这可用于简化此类任务。

答案 1 :(得分:0)

F#中C ++结构的对应部分不一定是结构。在C ++中,类和结构之间的唯一区别在于它们的默认访问限制。

在F#中,结构用于值类型,类用于引用类型。值类型的一个问题是它们应该用作不可变值,而临时副本通常是静默创建的。

您观察到的问题与该情况一致。出于某种原因,F#interactive会创建结构的副本并传递对该结构的引用。然后,C ++代码修改副本,保持原始状态不变。

如果切换到使用类,请确保在让本机代码使用它之前固定实例,否则最终可能会在本机代码引用它之后垃圾收集器移动它。