Выделение структуры по ссылке в F # - PullRequest
0 голосов
/ 06 июля 2018

Я пытаюсь переписать пример сортировки SysTime из статьи Классы маршалинга, структуры и объединения Статья MSDN с 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 #? Я не нашел много полезных вещей.

Ответы [ 2 ]

0 голосов
/ 06 июля 2018

Функция Win32 GetSystemTime определяет параметр структуры в качестве указателя. Это означает, что ваш оригинальный код должен работать, если вы просто измените способ определения вашей внешней функции:

[<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
0 голосов
/ 06 июля 2018

Как и предполагалось 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

...