我正在尝试将Marshaling Classes, Structures, and Unions MSDN文章中的编组SysTime示例从C#重写为F#。
我的实际代码现在看起来像这样:
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
关键字,但这没有帮助。
任何想法有什么问题吗? 有什么好的信息来源,如何正确使用P / Invoke和从F#编组?我找不到很多有用的东西。
答案 0 :(得分:3)
Win32 GetSystemTime函数将struct参数定义为指针。这意味着如果您仅更改外部函数的定义方式,原始代码应该可以工作:
[<DllImport("Kernel32.dll")>]
extern void GetSystemTime(SystemTime& st)
然后,您将调整主方法以使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
答案 1 :(得分:2)
根据rmunn的建议,该结构必须是普通的F#记录。
这是对我有用的代码:
module LibWrap =
open System.Runtime.InteropServices
[<CLIMutable>]
[<StructLayout(LayoutKind.Sequential)>]
type SystemTime = {
year:uint16
month:uint16
weekday:uint16
day:uint16
hour:uint16
minute:uint16
second:uint16
millisecond:uint16
}
[<DllImport("Kernel32.dll")>]
extern void GetSystemTime([<param:In>][<param: Out>]SystemTime st)
open LibWrap
[<EntryPoint>]
let main argv =
printfn "F# SysTime Sample using Platform Invoke";
let st = { year = 4us ; month = 1us ; day = 2us ; weekday = 0us ; hour = 0us ; minute = 0us ; second = 0us ; millisecond = 0us }
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
感谢您的提示,rmunn