-
Notifications
You must be signed in to change notification settings - Fork 80
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Could not load type "that is incorrectly aligned or overlapped by a non-object field" #292
Comments
I believe you can set CsWin32/src/Microsoft.Windows.CsWin32/settings.schema.json Lines 40 to 44 in 66f4221
|
NativeMethods.SHGetPropertyStoreForWindow((HWND)form.Handle, typeof(IPropertyStore).GUID, out void* ppv).ThrowOnFailure();
return *(IPropertyStore*)ppv; Is this incorrect? Thanks! |
It looks like you're trying to return an NativeMethods.SHGetPropertyStoreForWindow((HWND)form.Handle, typeof(IPropertyStore).GUID, out void* ppv).ThrowOnFailure();
-return *(IPropertyStore*)ppv;
+return (IPropertyStore*)ppv; Then when you're done using it, you have to call |
That did it! Thanks! |
We should probably keep this active to track the bug when |
@AaronRobinsonMSFT @tannergooding I'm hoping you can help me out here. What should the C# projection do with structs with overlapping (union) fields where the fields represent a mixture of objects and values? The CLR doesn't allow that. I think my only option would be to declare all the fields as value types, including perhaps generating structs for what might otherwise be a .NET interface or object. That won't be a great experience for the user, but is there an alternative? And just as one particular case study, I was looking at public struct VARDESC
{
[StructLayout(LayoutKind.Explicit)]
public struct _Anonymous_e__Union
{
[FieldOffset(0)]
public uint oInst;
[FieldOffset(0)]
public unsafe VARIANT* lpvarValue;
}
public int memid;
public PWSTR lpstrSchema;
public _Anonymous_e__Union Anonymous;
public ELEMDESC elemdescVar;
public ushort wVarFlags;
public VARKIND varkind;
} Note how one field is exactly 4 bytes in length while the other is pointer-sized. Is that really a stable layout, considering various CPU architectures? |
@AArnott Looking around on the interweb, VC compiler & GCC appear to pick the size of the largest union field, ie the size of that union will always be pointer size (before factoring in padding). |
@davkean is correct. The union is defined to be as large as the largest field in bytes. How to interact with the union is slightly different between C and C++, but that shouldn't be an issue in this case. I've create something in godbolt so you can see the layout generated by MSVC - https://godbolt.org/z/c7M4v6EvP. Which field is an object ref in this case? I don't see it. |
@AaronRobinsonMSFT the object field is |
You cannot union a value and reference type. You really shouldn't union incompatible reference types (its akin to At a minimum you're going to need to break it apart so that the value and reference types aren't overlapping, and you'll need to handle that as part of the marshalling logic. |
Thanks, @tannergooding. The metadata doesn't give me the information necessary to know which field has the real value. The docs may, but cswin32 would need something from the metadata to automate that. I can't change the memory layout of the struct so they don't overlap either as that would break interop with the native code. So I'm not sure what you're proposing here. |
I guess having two reference typed fields would be problematic as well, right? So perhaps the logic should be if one or more fields in a union struct are reference types, generate them as value types instead. Or more simply put: "never emit ref types in union fields". We should either fail generation (since it'll fail at runtime anyway) or fallback to |
The data is already not blittable and so is already incompatible with native code. The built-in marshalling system isn't really recommended for "new code", the But in either case, the built-in marshalling system can't support such a definition anyways because the type system fundamentally doesn't support it. You will have to define and implement manual marshalling here if your type is both a union and that union contains any reference types. |
Thanks. That sounds like support for my prior comment, that I emit interop types that don't activate the .NET marshaler. Optionally I could add additional help to automate the marshaling in my own generated code, but since I lack the metadata to do so, it'll just be left as an exercise to the reader. |
Unfortunately we have these types like But maybe there's an opportunity at least for me to generate some methods on the |
This would just be defining 100% blittable data types (which is what TerraFX.Interop.Windows does). Anything else is a "convenience wrapper" on top of this that deals with things like pinning, marshalling, etc (which is what
The way I handle this in TerraFX.Interop.Windows: https://source.terrafx.dev/#TerraFX.Interop.Windows/Windows/um/oaidl/ITypeComp.cs,5a439f56db1bf1c9 There is the base struct: public unsafe partial struct ITypeComp : ITypeComp.Interface
{
public void** lpVtbl;
// ... Various helper methods exposing the VTBL members and account for ABI differences (returning // ...
public HRESULT QueryInterface(Guid* riid, void** ppvObject)
{
return ((delegate* unmanaged<ITypeComp*, Guid*, void**, int>)(lpVtbl[0]))((ITypeComp*)Unsafe.AsPointer(ref this), riid, ppvObject);
}
// ... An interface defining the required exposed members and the inheritance heirarchy: // ...
public interface Interface : IUnknown.Interface
{
HRESULT Bind(ushort* szName, uint lHashVal, ushort wFlags, ITypeInfo** ppTInfo, DESCKIND* pDescKind, BINDPTR* pBindPtr);
HRESULT BindType(ushort* szName, uint lHashVal, ITypeInfo** ppTInfo, ITypeComp** ppTComp);
}
// ... and finally a VTBL declaration for convenience: // ...
public partial struct Vtbl<TSelf>
where TSelf : unmanaged, Interface
{
public delegate* unmanaged<TSelf*, Guid*, void**, int> QueryInterface;
public delegate* unmanaged<TSelf*, uint> AddRef;
public delegate* unmanaged<TSelf*, uint> Release;
public delegate* unmanaged<TSelf*, ushort*, uint, ushort, ITypeInfo**, DESCKIND*, BINDPTR*, int> Bind;
public delegate* unmanaged<TSelf*, ushort*, uint, ITypeInfo**, ITypeComp**, int> BindType;
}
} All of these combined allow robust and convenient access/usability of the types and data, even while being |
Ah yes. I forgot this was "conveniently" converted for users. I've got nothing to add to what @tannergooding has suggested. |
CsWin32 already has the ability to emit only blittable types, and looks very similar to your structs with a vtbl field in them. But we don't do both modes at once right now. The user has to choose one or the other. Your approach of always emitting both is interesting, and something that CsWin32 has to get at least closer to. |
Actual behavior
When I import the
PROPVARIANT
type, the type is not declared property and throws aTypeLoadException
when referenced. The error message is “Could not load type __Anonymous_e__Union from assembly '…' because it contains an object field at offset 0 that is incorrectly aligned or overlapped by a non-object field.”Expected behavior
Type should work.
Repro steps
NativeMethods.txt
content:NativeMethods.json
content:This is the class that references the broken type:
FormShellProperties.cs
Context
LangVersion
: 9The text was updated successfully, but these errors were encountered: