F# Marshal.SizeOf exception involving an array of enums within a struct

Viewed 274

I am encountering a runtime exception involving Marshal.SizeOf that I do not understand. After stripping out lots of code, I have come up with a small example that demonstrates the issue:

#nowarn "9"

open System.Runtime.InteropServices

type Gray = | Black = 0 | Gray = 1 | White = 2

[<Struct; StructLayout(LayoutKind.Sequential, Pack = 1)>]
type GrayStruct =
    [<MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)>]
    val mutable Triple: Gray[]

type Color = | Red = 0 | Green = 1 | Blue = 2

[<Struct; StructLayout(LayoutKind.Sequential, Pack = 1)>]
type ColorStruct =
    [<MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)>]
    val mutable Triple: Color[]

let colorStructToUnit (colorStruct: ColorStruct) = ()

printfn "%A" (Marshal.SizeOf(typeof<GrayStruct>))
printfn "%A" (Marshal.SizeOf(typeof<ColorStruct>))

Running this sample with fsi.exe (version 4.1) results in:

12
System.ArgumentException: Type 'FSI_0001+ColorStruct' cannot be marshaled as an unmanaged structure; no meaningful size or offset can be computed.
   at System.Runtime.InteropServices.Marshal.SizeOfHelper(Type t, Boolean throwIfNotMarshalable)
   at System.Runtime.InteropServices.Marshal.SizeOf(Type t)
   at <StartupCode$FSI_0001>.$FSI_0001.main@() in 
C:\f#\Example\example.fsx:line 21
Stopped due to error

Note the similarity between GrayStruct and ColorStruct, yet Marshal.SizeOf chokes on ColorStruct. Marshal.SizeOf appears to throw an exception because of the existence of the colorStructToUnit function. If that function is removed, the output becomes:

12
12

Also, if I keep the colorStructToUnit function, but replace:

    val mutable Triple: Color[]

with:

    val mutable Triple: int[]

then the output also becomes:

12
12

Thanks for any insight you may have into this issue.


Note that I am experiencing this problem in a much larger codebase that is trying to interface with a legacy DLL via PInvoke. This problem appears to only affect arrays of enums within structs. One workaround would be to marshal the underlying types (i.e., int[] instead of Color[]) and then cast those values into enums, but that solution requires multiple definitions of almost identical structs. Is there a more elegant way to overcome this problem?

0 Answers
Related