开发者

How to use StructureToPtr with F# Structure? Typeof trouble

开发者 https://www.devze.com 2023-04-01 14:26 出处:网络
I\'ve got structure : type OneDevice = { mutable id: System.UInt16 mutable typeDev: byte mutable portNum: byte

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       开发者_Go百科 : 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


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)


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

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

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号