Attempting to call Marshal.SizeOf for an enum type currently results in the following:
Type '*' cannot be marshaled as an unmanaged structure; no meaningful size or offset can be computed.
+ System.Runtime.InteropServices.Marshal.SizeOfHelper(System.Type, bool)
As far as I am aware, enums are considered to be blittable as they are internally just a value type that contains a single field of a blittable type (generally this is int32). However, there isn't anything explicit about enum marshalling in https://docs.microsoft.com/en-us/dotnet/framework/interop/blittable-and-non-blittable-types.
I would expect the above call to succeed and for there to be documentation on the blittability of enum types.
CC. @jkoritzinsky
Same happens with delegate types, which are also supposed to be marshallable as function pointers.
> delegate void SomeProc();
> System.Runtime.InteropServices.Marshal.SizeOf<SomeProc>()
Type 'Submission#0+SomeProc' cannot be marshaled as an unmanaged structure; no meaningful size or offset can be computed.
+ System.Runtime.InteropServices.Marshal.SizeOfHelper(System.Type, bool)
This might be failing because it looks the C# compiler is emitting enums as auto layout, rather than sequential as it does for other value types.
CC. @jaredpar
This might be failing because it looks the C# compiler is emitting enums as auto layout, rather than sequential as it does for other value types.
"14.3 Enums [...] they shall have auto field layout (搂10.1.2)"
@tannergooding Given that ECMA-335 indicates they will be auto, what would you like to see here? Is your base assumption the size should by default be sizeof(int)? Basically, what would you intuitively expect the size to be?
what would you intuitively expect the size to be?
The size of the underlying type, i.e. the following should hold:
sizeof(MyEnum) == Marshal.SizeOf<MyEnum>()
If we do something here, we should make all the Marshal APIs consistent. E.g. Marshal.PtrToStructure should handle enums too.
what would you intuitively expect the size to be?
Exactly what @jkotas said.
Enums are blittable value-types and the only reason they don't work here is because of a runtime rule that says they should have auto layout. They always have a single field of type Enum.GetUnderlyingType and so I don't believe that the auto rule actually matters.
I think they should likely be special-cased here and should be fully supported by the Marshal class.
I think they should likely be special-cased here and should be fully supported by the Marshal class.
I am not entirely convinced of this yet. There are cases involving DISPARAMs that only support VT_I4 or VT_U4 which means they aren't marshalable in all cases if users change the underlying type.
Enums are blittable value-types and the only reason they don't work here is because of a runtime rule that says they should have auto layout.
I don't know why the decision was made, but this isn't entirely fair because it could have been specifically for the DISPARAM scenario. We should see if we can determine the history of that prior to changing this _or_ we need to validate all existing marshaling scenarios with work here.
Note that the struct-marshaling methods on Marshal are super slow. We do recommend using them for anything that matters. Fixing this for consistency is ok, but it won't make these APIs something you actually want to use.
DISPARAMscenario
I do not think these APIs have anything to do with DISPARAMs.
As @0xd4d noted the c# compiler does this because they spec tells us to. As to why the spec tells us to do this ... I have no idea. Can't even really think of a good reason. But given we use sequential everywhere else I'm guessing there was a deliberate decision here for enum.
After some investigation and conversations offline, we've decided to update Marshal.SizeOf to support enums.
Technically enum's aren't blittable in the IDispatch case when they are marshalled to a VARIANT in a DISPPARAMS structure since they will always be truncated or expanded to a 4-byte width. However, since a conversion to VARIANT isn't blittable anyway, we feel that it's fine to consider enums as blittable.
cc: @AaronRobinsonMSFT @davidwrighton
I do not think these APIs have anything to do with DISPARAMs.
They do in the sense they indicate/imply what will happen. In the DISPARAM scenario it isn't fully supported so rather than indicate a value it fails. I have no idea if this is true, but the fact that it is marked Auto is either an artifact of some logical case, in this case DISPARAM seems to support that, or it was a mistake and just never supported. I am fine with special casing Enums, but I want to understand the historical scope as well as if there are any areas where this function indicates a size but a marshaler says it is something else - I believe in DISPARAM this is the case. @jkoritzinsky is verifying.
The runtime implements multiple difference set of marshaling rules: Each of PInvoke, COM, IDispatch or WinRT have different set of rules.
These methods implement PInvoke marshaling rules. DISPPARAM rules are generally pretty different from PInvoke marshaling rules. This change should not change anything about the DISPPPARAM rules. If we want to look at changing DISPPARAM rules, it should be a separate issues.
According to ECMA 335, enums can actually have an underlying type of char or bool, so they're not always blittable. This'll take some more investigation to make sure we handle this correctly.
Yep, the builtin runtime interop is full of worms. I would treat this issue with very low priority since we discourage use of these APIs anyway. There is a lot more other interesting interop work we can be spending time on...
Most helpful comment
The size of the underlying type, i.e. the following should hold:
sizeof(MyEnum) == Marshal.SizeOf<MyEnum>()If we do something here, we should make all the Marshal APIs consistent. E.g.
Marshal.PtrToStructureshould handle enums too.