.net 使用来自F的指针调用C函数#

.net 使用来自F的指针调用C函数#,.net,f#,.net,F#,我是F#的新手,试图与F#的C库进行通信。我可以使用它提供的几个函数,但是Read()函数很棘手,因为它有两个参数,即指针和返回读取内容 以下是C函数定义: TPCANStatus __stdcall CAN_Read( TPCANHandle Channel, TPCANMsg* MessageBuffer, TPCANTimestamp* TimestampBuffer); [DllImport("PCANBasic.dl

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

以下是C函数定义:

TPCANStatus __stdcall CAN_Read(
        TPCANHandle Channel, 
        TPCANMsg* MessageBuffer, 
        TPCANTimestamp* TimestampBuffer);
        [DllImport("PCANBasic.dll", EntryPoint = "CAN_Read")]
        public static extern TPCANStatus Read(
            [MarshalAs(UnmanagedType.U2)]
            TPCANHandle Channel,
            out TPCANMsg MessageBuffer,
            out TPCANTimestamp TimestampBuffer);
这里是C#的定义:

TPCANStatus __stdcall CAN_Read(
        TPCANHandle Channel, 
        TPCANMsg* MessageBuffer, 
        TPCANTimestamp* TimestampBuffer);
        [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)
最后一行显示一个错误:

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
严重性代码描述项目文件行抑制状态
错误FS0001此表达式应具有类型
“拜里夫”
但这里有一种类型
“PCANBasic.TPCANMsg ref”CANTestApp C:\Users\source\repos\CANTestApp\CANTestApp\Program.fs 189处于活动状态

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

为此提供了两个选项

选项1 通过将
mbuf
tbuf
绑定为可变值,然后使用
通过引用传递它们,创建
byref
outref
而不是
ref

let main argv=
...
让可变mbuf=PCANBasic.TPCANMsg(0x010u,Array.zeroCreate 8)
设可变tbuf=PCANBasic.TPCANTimestamp(0u,0us,0us)
let status=PCANBasic.read(句柄、&mbuf、&tbuf)
选项2 将
read
定义为成员而不是函数,以允许使用
ref
参数而不是
byref
调用它。然后,您的
main
函数可以保持不变,而不是引用静态
Reader.Read
方法,而不是
Read
函数

类型读取器=
静态成员读取(chan:TPCANHandle,[]msgbuf:TPCANMsg byref,[]tsbuf:TPCANTimestamp byref)=
导入。读取(chan、msgbuf、tsbuf)
[]
属性 查看表明带有
[]
属性的
byref
参数等同于
outref
参数,因此您的
read
函数/成员定义可以(稍微)缩短为:

let read(chan:TPCANHandle,msgbuf:TPCANMsg outref,tsbuf:TPCANTimestamp outref)=
导入。读取(chan、msgbuf、tsbuf)

我尝试了选项1,这消除了我看到的错误,但mbuf不包含函数调用后预期的数据。啊,我认为
[]
外部定义中的
参数需要包含
&
,也就是说,
TPCANMsg&MessageBuffer
TPCANTimestamp&TimestampBuffer
在函数调用后仍然没有获得预期的数据。我注意到函数立即返回,而不是暂停输入。这可能是由于
mbuf
中的初始值造成的吗?在没有看到更多C#实现的情况下,我唯一能建议的是检查结构是否定义正确(字段名称、字段类型等)。您使用的
TPCANMsg
构造函数看起来也有点可疑,因为您将输入数组与另一个长度为8的数组连接起来,然后分配给一个总长度应仅为8的字段。字段类型看起来不错,但您需要更新字段名,使其与C#字段完全匹配,否则C实现将无法正确引用它们。