首页 > 解决方案 > 在 F# 中通过引用编组结构

问题描述

我正在尝试从 C# 到 F#的编组类、结构和联合 MSDN 文章中重写编组 SysTime 示例。

我的实际代码现在如下所示:

module LibWrap =
    open System.Runtime.InteropServices

    [<StructLayout(LayoutKind.Sequential)>]
    type public SystemTime =
        struct
            val mutable public year:uint16
            val mutable public month:uint16
            val mutable public weekday:uint16
            val mutable public day:uint16
            val mutable public hour:uint16
            val mutable public minute:uint16
            val mutable public second:uint16
            val mutable public millisecond:uint16
        end 

    [<DllImport("Kernel32.dll")>]
    extern void GetSystemTime([<param:In>][<param: Out>]SystemTime st)


[<EntryPoint>]
let main argv =
    printfn "F# SysTime Sample using Platform Invoke";
    let st = new LibWrap.SystemTime (month = 1us, day = 2us, year = 34us)
    try
        LibWrap.GetSystemTime st
    with 
        | ex -> printfn "Failed to GetSystemTime: %O" ex
    printfn "The Date is: %d/%d/%d" st.month st.day st.year
    0

它毫无例外地编译和运行,但输出与预期不符。SystemTime 结构中的值不会被覆盖。

输出:

F# SysTime Sample using Platform Invoke
The Date is: 1/2/34

如果我在 F# 交互式控制台中运行代码,我得到System.AccessViolationException. 代码的 C# 版本在我的系统上运行良好。我尝试使用ref/byref关键字,但这没有帮助。

有什么想法有什么问题吗?任何好的信息来源如何正确使用 F# 中的 P/Invoke 和编组?我没有找到太多有用的东西。

标签: f#pinvokemarshalling

解决方案


Win32 GetSystemTime函数将结构参数定义为指针。这意味着如果您只是更改外部函数的定义方式,您的原始代码应该可以工作:

[<DllImport("Kernel32.dll")>]
extern void GetSystemTime(SystemTime& st)

然后,您将调整您的 main 方法以使其st可变并像指针一样传递它:

[<EntryPoint>]
let main argv =
    printfn "F# SysTime Sample using Platform Invoke";
    let mutable st = LibWrap.SystemTime (month = 1us, day = 2us, year = 34us)
    try
        LibWrap.GetSystemTime &st
    with 
        | ex -> printfn "Failed to GetSystemTime: %O" ex
    printfn "The Date is: %d/%d/%d" st.month st.day st.year
    0

这打印:

F# SysTime Sample using Platform Invoke
The Date is: 7/6/2018

推荐阅读