.net - 使用 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
?
解决方案
有几个选择。
选项1
通过绑定和可变值创建一个byref
或outref
而不是一个,然后使用“通过引用”传递它们。ref
mbuf
tbuf
&
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.Read
read
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)
推荐阅读
- jquery - 有一种方法可以将数据表/CSV 索引到 Elasticsearch
- hyperledger-fabric - Hyperledger Fabric 如何确保 couchdb 中状态的完整性?
- linux - GNU Parallel - 如何使用输入参数捕获具有名称的文件中的输出
- python - 如何通过正则表达式过滤 numpy 数组?
- eclipse - 在 Windows 10 中的 Eclipse Oxygen 上创建 MPI 项目
- python - html 到文本:将制表符后的所有内容移至新行
- r - 根据其他 3 列的结果在 R 数据框中创建新列
- facebook - facebook 聊天进入网站时出现内容安全策略错误?
- oracle - PL/SQL - 特定于一个数据库的条件编译
- neo4j - 带有 lambda 表达式和消费者断言的 TestMethods 的 JQassistant 规则