How to use StructureToPtr with F# Structure? Typeof trouble
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 =
....
精彩评论