首页 > 解决方案 > 使用 F# 中的指针调用 C 函数

问题描述

我是 F# 的新手,正在尝试与 F# 的 C 库进行通信。我可以使用它提供的几个函数,但事实证明 Read() 函数很棘手,因为它有两个参数,即指针并返回读取的内容。

这是C函数定义:

TPCANStatus __stdcall CAN_Read(
        TPCANHandle Channel, 
        TPCANMsg* MessageBuffer, 
        TPCANTimestamp* TimestampBuffer);

这是 C# 定义:

        [DllImport("PCANBasic.dll", EntryPoint = "CAN_Read")]
        public static extern TPCANStatus Read(
            [MarshalAs(UnmanagedType.U2)]
            TPCANHandle Channel,
            out TPCANMsg MessageBuffer,
            out TPCANTimestamp TimestampBuffer);

这是我正在开发的 F# 版本:

module PCANBasic =
    type TPCANHandle = uint16

    [<Struct>]
    type TPCANTimestamp =
        val millis : uint32
        val millisOverflow : uint16
        val micros : uint16
        new (_millis, _overflow, _micros) = {millis = _millis; millisOverflow = _overflow; micros = _micros}

    [<Struct>]
    type TPCANMsg =
        val mutable id : uint32
        [<MarshalAs(UnmanagedType.U1)>]
        val mutable mstype : byte
        val mutable len : byte
        [<MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)>]
        val mutable data : byte array
        new (_id, _mstype, _len, _data) = {id = _id; mstype = _mstype; len = _len; data = _data}
        new (_id, _data) = {id = _id; mstype = 0x00uy; len = byte (Array.length _data); data = Array.concat [| _data; (Array.zeroCreate 8) |]}

    module private Imported =
        [<DllImport("PCANBasic.dll", EntryPoint="CAN_Read")>]
        extern TPCANStatus Read(
                [<MarshalAs(UnmanagedType.U2)>]
                TPCANHandle Channel,
                [<Out>]
                TPCANMsg MessageBuffer,
                [<Out>]
                TPCANTimestamp TimestampBuffer)

    let read (chan : TPCANHandle, [<Out>] msgbuf : TPCANMsg byref, [<Out>] tsbuf : TPCANTimestamp byref) = 
        Imported.Read(chan, msgbuf, tsbuf)
let main argv =
    ….
     let mbuf = ref (PCANBasic.TPCANMsg(0x010u, Array.zeroCreate 8))
     let tbuf = ref (PCANBasic.TPCANTimestamp(0u,0us,0us))
     let status = PCANBasic.read(handle, mbuf, tbuf)           //Error here

最后一行显示错误:

Severity    Code    Description Project File    Line    Suppression State
Error   FS0001  This expression was expected to have type
    'byref<PCANBasic.TPCANMsg>'    
but here has type
    'PCANBasic.TPCANMsg ref'    CANTestApp  C:\Users\source\repos\CANTestApp\CANTestApp\Program.fs  189 Active

如何使用 F# 中的指针正确调用此 C 函数?使用时是否需要该[<Out>]属性byref

标签: .netf#

解决方案


有几个选择。

选项1

通过绑定和可变值创建一个byrefoutref而不是一个,然后使用“通过引用”传递它们。refmbuftbuf&

let main argv =
    ...
    let mutable mbuf = PCANBasic.TPCANMsg(0x010u, Array.zeroCreate 8)
    let mutable tbuf = PCANBasic.TPCANTimestamp(0u,0us,0us)
    let status = PCANBasic.read(handle, &mbuf, &tbuf)

选项 #2

定义read为成员而不是函数,以允许使用ref参数而不是byref. 除了引用静态方法而不是函数之外,您的main函数可以保持不变。Reader.Readread

type Reader =
    static member Read (chan : TPCANHandle, [<Out>] msgbuf : TPCANMsg byref, [<Out>] tsbuf : TPCANTimestamp byref) =
        Imported.Read(chan, msgbuf, tsbuf)

[<Out>]属性

查看F# RFC FS-1053表明byref<T>具有[<Out>]属性的参数等同于outref<T>参数,因此您的read函数/成员定义可以(稍微)缩短为:

let read (chan : TPCANHandle, msgbuf : TPCANMsg outref, tsbuf : TPCANTimestamp outref) =
    Imported.Read(chan, msgbuf, tsbuf)

推荐阅读