Reputation: 83
I'm trying to rewrite the marshalling SysTime sample from the Marshaling Classes, Structures, and Unions MSDN article from C# to F#.
My actual code now looks like this:
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
It compiles and run with no exception but the output is not as expected. The values in the SystemTime structure are not overwritten.
Output:
F# SysTime Sample using Platform Invoke
The Date is: 1/2/34
If I run the code in the F# interactive console I got System.AccessViolationException
. The C# version of the code works fine on my system. I tried to use ref
/byref
keywords but that did not help.
Any ideas what is wrong? Any good source of information how to use P/Invoke and marshalling from F# correctly? I did not find much useful stuff.
Upvotes: 3
Views: 483
Reputation: 6510
The Win32 GetSystemTime function defines the struct parameter as a pointer. That means your original code should work if you just change the way your external function is defined:
[<DllImport("Kernel32.dll")>]
extern void GetSystemTime(SystemTime& st)
Then you would tweak your main method to make st
mutable and pass it like a pointer:
[<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
This prints:
F# SysTime Sample using Platform Invoke
The Date is: 7/6/2018
Upvotes: 3
Reputation: 83
As rmunn suggested, the struct must be a plain F# record.
So this is the code that works for me:
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
The output is now as expected:
F# SysTime Sample using Platform Invoke
The Date is: 7/6/2018
Thanks for your hint, rmunn
Upvotes: 2