在尝试向同事证明可以使用 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
返回 bar
的值和 TrivialFoo.Set
分配给它。然而,当从 F# Interactive 运行时,TrivialFoo.Set
不会设置字段。我怀疑它可能与从非托管代码访问托管内存有关,但这并不能解释为什么它只在使用 F# Interactive 时发生。有谁知道这里发生了什么?
最佳答案
我不认为这种概念证明是互操作性的良好证明。您最好从您的 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
复制可能是这些解决方案中最简单的。
由于 FSI 是一个 REPL ,它可能不适合需要多个项目、库或其他复杂配置的此类任务。您可以考虑投票 this FSI request for support for
#package
导入 NuGet 包,可用于简化此类任务。
关于f# - 可能的 F# 交互式 PInvoke 错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27261988/