Как использовать StructureToPtr с F # Structure? Тип неприятностей - PullRequest
1 голос
/ 01 сентября 2011

У меня есть структура:

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

Но получено сообщение об ошибке:

Ошибка Невозможно упаковать тип "Модель + OneDevice" как неуправляемый состав; невозможно рассчитать размер или смещение, которые имеют смысл.

Я думаю, это потому, что я не знаю размер pwd здесь. Так как же тогда использовать его в F # Structure? Или, может быть, я могу как-то объявить тип массива статического размера?

Спасибо

Ответы [ 2 ]

3 голосов
/ 01 сентября 2011

Вы должны убедиться, что структура, которую вы маршалируете, имеет ту же структуру, что и собственная структура, используя атрибут 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)
1 голос
/ 01 сентября 2011

Я думаю, что вы хотите создать структуру.В F # вы делаете это с

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