如何将 StructureToPtr 与 F# 结构结合使用?麻烦类型

发布于 2024-12-02 23:00:17 字数 1671 浏览 1 评论 0原文

我有结构:

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”打包为非托管类型 结构;无法计算有意义的大小或偏移。

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

谢谢

I've got structure :

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
    }

And I need to copy some use it as byte array. In C# I can declare the size of byte array here, but for now I don't know the size of pwd.

I'm trying to use :

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()

But got error message :

Error Unable to package type "Model + OneDevice" as an unmanaged
structure; impossible to calculate the size or offset that make sense.

I think that is because I don't know the pwd size here. So how can I use it on F# Structure then ? Or maybe I can declare static-size array type somehow ?

Thank you

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

蹲墙角沉默 2024-12-09 23:00:17

您需要使用 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)

You need to ensure that the structure you're marshaling has the same layout as the native structure using the StructLayout attribute, eg.

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

In addition, you need to explicitly mark any fields with the MarshalAs attribute if they require marshaling with non-default marshaling behavior, such as arrays. The default marshaling behavior for arrays is LPArray, but by the sounds of it, your native structure is expecting a ByValArray.

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

Lastly, replace GCHandle.Alloc with Marshal.AllocHGlobal for allocating unmanaged memory, and use Marshal.FreeHGlobal to free it.

Note: I'm uncertain whether F# record types can accept these attributes, but I would expect them to work. If not, then you would need to use them in combination with jpalmer's suggestion of using a struct.

Edit:

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)

Edit:

The above is for copying a populated OneDevice structure into an empty byte array. If you want to do the reverse - converting a populated byte array into a structure, it's largely the same thing.

let size = Marshal.SizeOf(typeof<OneDevice>)
let unmanagedPtr = Marshal.AllocHGlobal(size)
Marshal.Copy(bytes, unmanagedPtr, 0, size)
Marshal.PtrToStructure(unmanagedPtr, device)
Marshal.FreeHGlobal(unmanagedPtr)
心头的小情儿 2024-12-09 23:00:17

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

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

I think what you want is to creat a struct. In F# you do this with

[<StructAttribute>]
type t = 
    ....
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文