可能的F#交互式PInvoke bug 当试图向同事证明,可以使用F类的C++类时,我提出了下面的概念证明。第一个代码段是他为挑战提供的代码,下面的代码段是我在F#中的实现 open System.Runtime.InteropServices 类型foo= 结构 值栏:int 新的(_-bar:int)={bar=_-bar} 结束 [] extern-int-triality\u-foo\u-get(triality-foo和triality-foo) [] extern void-triatile\u-foo\u集(triatile-foo&triatile\u-foo,int-bar) 使用 成员this.Get() 成员this.Set(bar)=平凡的集(&this,bar)

可能的F#交互式PInvoke bug 当试图向同事证明,可以使用F类的C++类时,我提出了下面的概念证明。第一个代码段是他为挑战提供的代码,下面的代码段是我在F#中的实现 open System.Runtime.InteropServices 类型foo= 结构 值栏:int 新的(_-bar:int)={bar=_-bar} 结束 [] extern-int-triality\u-foo\u-get(triality-foo和triality-foo) [] extern void-triatile\u-foo\u集(triatile-foo&triatile\u-foo,int-bar) 使用 成员this.Get() 成员this.Set(bar)=平凡的集(&this,bar),f#,pinvoke,f#-interactive,F#,Pinvoke,F# Interactive,当在Visual Studio中调试或作为独立程序运行时,这可以预料:trifoo.Get返回bar的值,并trifoo.Set分配给它。但是,从F#Interactive运行时,foo.Set不会设置该字段。我怀疑这可能与从非托管代码访问托管内存有关,但这并不能解释为什么只有在使用F#Interactive时才会发生这种情况。有人知道这里发生了什么吗?我不认为这个概念证明是互操作性的好证明。您最好从C++项目中创建DLL导出定义,而不是使用去修饰的名称。 作为PoC:F#创建适合CLI的MSI

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

我不认为这个概念证明是互操作性的好证明。您最好从C++项目中创建DLL导出定义,而不是使用去修饰的名称。 作为PoC:F#创建适合CLI的MSIL,因此它可以与进行互操作。如果这还不够,并且您希望使用本地的网络互操作,那么考虑使用COM,或者如上所述,DLL在C++上导出定义。我个人不会尝试用C++的类定义来与你所建议的方法进行交互,有很多方法可以做到。

或者,只需将C++项目更改为.NETC++项目,您就可以直接从F ^访问类,同时仍具有C++的能力。 当然,您可能仍然想知道为什么该示例不在FSI中运行。通过运行以下命令,可以看到答案的提示:

> System.IO.Directory.GetCurrentDirectory();;
val it : string = "R:\TMP"
要解决此问题,您有多种选择:

  • Win32Project2.dll
    复制到该目录
  • 将它所在的任何路径添加到
    path
  • 使用绝对路径
  • 使用编译时常量
  • 或使用环境变量(路径将展开)
  • 动态定位dll并动态绑定到它(复杂)
复制可能是这些解决方案中最简单的一种


由于FSI是一个应用程序,因此它可能不是为需要多个项目、库或其他复杂配置的此类任务量身定制的最佳工具。您可以考虑投票导入NuGET包,可以使用这些包来减轻这些任务。

f中的C++结构的对应项不一定是结构。在C++中,类和结构之间的唯一区别在于它们的默认访问限制。 在F#中,结构用于值类型,类用于引用类型。值类型的一个问题是,它们被用作不可变的值,临时副本通常以静默方式创建

您观察到的问题与该情景一致。出于某种原因,F#interactive会创建一个结构副本并传递对该结构的引用。然后C++代码修改副本,留下原件未被触摸。


如果您切换到使用类,请确保在让本机代码使用实例之前先锁定实例,否则最终可能会导致垃圾收集器在本机代码获得引用后将其移动。

嗯,无论如何,这不是执行互操作的方法。声明具有非+imember函数的POD结构。我猜您的代码失败了,因为结构被认为是方法的参数,但无论如何都是错误的。这不是与类进行互操作的方式。其他典型问题可能是,当使用FSI而不是.exe时,DLL不在%PATH%中。特别是由于默认情况下FSI卷影复制程序集,因此代码可能是从某个临时文件夹运行的。
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)
> System.IO.Directory.GetCurrentDirectory();;
val it : string = "R:\TMP"