【问题标题】:How to use StructureToPtr with F# Structure? Typeof trouble如何将 StructureToPtr 与 F# 结构一起使用?故障类型
【发布时间】:2023-04-10 20:06:01
【问题描述】:

我有结构:

type OneDevice = {
        mutable id              : System.UInt16
        mutable typeDev         : byte
        mutable portNum         : byte
        mutable Parity          : byte
        mutable StopBits        : byte
        mutable BaudRate        : byte
        mutable addr1           : byte 
        mutable addr2           : byte 
        mutable useCanal        : byte
        mutable idGroup1        : byte
        mutable idGroup2        : byte 
        mutable idGroup3        : byte
        mutable idGroup4        : byte
        mutable idGroupSos1     : byte 
        mutable idGroupSos2     : byte 
        mutable idGroupSos3     : byte 
        mutable idGroupSos4     : byte 
        mutable idSosReserv     : byte 
        mutable addrModbus      : byte 
        mutable offsetModbus    : System.UInt16
        mutable pwd             : byte array
        mutable offsetInModbus  : System.UInt16
        mutable reserv          : System.UInt16
    }

而且我需要复制一些使用它作为字节数组。在 C# 中我可以在这里声明字节数组的大小,但现在我不知道 pwd 的大小。

我正在尝试使用:

let memcp(device : OneDevice, bytes : byte array) =
    Array.zeroCreate <| Marshal.SizeOf(typeof<OneDevice>)
        |> fun (array : byte array) ->
        GCHandle.Alloc(array, GCHandleType.Pinned) |> fun handle ->
            Marshal.StructureToPtr(device, handle.AddrOfPinnedObject(), true)
            handle.Free()

但收到错误消息:

错误无法将类型“Model + OneDevice”打包为非托管 结构体;无法计算出有意义的大小或偏移量。

我认为这是因为我不知道这里的密码大小。那么如何在 F# Structure 上使用它呢?或者我可以以某种方式声明静态大小的数组类型?

谢谢

【问题讨论】:

    标签: .net f# bytearray structure typeof


    【解决方案1】:

    您需要确保您正在编组的结构与使用 StructLayout 属性的本机结构具有相同的布局,例如。

    [<type: StructLayout(LayoutKind.Sequential, Pack=1, CharSet=CharSet.Ansi)>]
    type OneDevice = { 
        ...
    

    此外,如果需要使用非默认编组行为(例如数组)进行编组,则需要使用 MarshalAs 属性显式标记任何字段。数组的默认封送处理行为是 LPArray,但从它的声音来看,您的本机结构需要 ByValArray

    [<field: MarshalAs(UnmanagedType.ByValArray, SizeConst = 17)>]
    mutable pwd             : byte array
    

    最后,将GCHandle.Alloc 替换为Marshal.AllocHGlobal 用于分配非托管内存,并使用Marshal.FreeHGlobal 释放它。

    注意:我不确定 F# 记录类型是否可以接受这些属性,但我希望它们能够工作。如果没有,那么您需要结合 jpalmer 的使用 struct 的建议来使用它们。

    编辑:

    let size = Marshal.SizeOf(typeof<OneDevice>)
    let unmanagedPtr = Marshal.AllocHGlobal(size)
    Marshal.StructureToPtr(device, unmanagedPtr, false)
    Marshal.Copy(unmanagedPtr, bytes, 0, size)
    Marshal.FreeHGlobal(unmanagedPtr)
    

    编辑:

    以上内容用于将填充的 OneDevice 结构复制到空字节数组中。如果你想做相反的事情 - 将填充的字节数组转换为结构,这在很大程度上是相同的。

    let size = Marshal.SizeOf(typeof<OneDevice>)
    let unmanagedPtr = Marshal.AllocHGlobal(size)
    Marshal.Copy(bytes, unmanagedPtr, 0, size)
    Marshal.PtrToStructure(unmanagedPtr, device)
    Marshal.FreeHGlobal(unmanagedPtr)
    

    【讨论】:

    • ...你能告诉我如何用 Marshal.AllocHGlobal 替换 GCHandle.Alloc 吗?我不明白我该怎么做。看来我仍然需要解决它。
    • 已更新示例。使用起来很简单——Marshal.Copy 会将非托管缓冲区复制到托管字节数组中。 (对不起,命令式的风格)
    • 遗憾的是没有效果:(我的结构仍然被零值填充。
    • 您是否在编组和复制之前设置device 对象的值?还是您打算做相反的事情并将字节数组转换为 OneDevice(即,使用 Marshal.PtrToStructure)?
    • 最后编辑作品!唯一的更正: Marshal.Copy(bytes, 0, unmanagedPtr, size)
    【解决方案2】:

    我认为你想要的是创建一个结构。在 F# 中,您可以使用

    [<StructAttribute>]
    type t = 
        ....
    

    【讨论】:

    • 结构中可以有可变字段,只需要使用语法val mutable
    猜你喜欢
    • 2021-05-29
    • 2011-02-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-04-08
    • 1970-01-01
    • 1970-01-01
    • 2011-08-02
    相关资源
    最近更新 更多