简体   繁体   中英

Structure alignment in C#

I am working with SQL VDI and attempting to pass a structure from C# to C++ via a COM interface. The structure is defined in the C++ header as:

#pragma pack(8)
struct VDConfig
    {
    unsigned long deviceCount;
    unsigned long features;
    unsigned long prefixZoneSize;
    unsigned long alignment;
    unsigned long softFileMarkBlockSize;
    unsigned long EOMWarningSize;
    unsigned long serverTimeOut;
    unsigned long blockSize;
    unsigned long maxIODepth;
    unsigned long maxTransferSize;
    unsigned long bufferAreaSize;
    } ;

To emulate this, I have defined the structure in C# as:

[StructLayout(LayoutKind.Explicit)]
public struct VDConfig 
{
    [FieldOffset(0)]
    public uint deviceCount;
    [FieldOffset(4)]
    public uint features;
    [FieldOffset(8)]
    public uint prefixZoneSize;
    [FieldOffset(12)]
    public uint alignment;
    [FieldOffset(16)]
    public uint softFileMarkBlockSize;
    [FieldOffset(20)]
    public uint EOMWarningSize;
    [FieldOffset(24)]
    public uint serverTimeout;
    [FieldOffset(28)]
    public uint blockSize;
    [FieldOffset(32)]
    public uint maxIODepth;
    [FieldOffset(36)]
    public uint maxTransferSize;
    [FieldOffset(40)]
    public uint bufferAreaSize;
}

I have also tried to define the structure as LayoutKind.Sequential and tried it with Pack=8. However I define the structure, when I attempt to pass it to the function, it fails and I receive the error "Alignment must be 2**n and <= system allocation granularity." I've tried defining the function that accepts the structure as:

int CreateEx([MarshalAs(UnmanagedType.LPWStr)]string instanceName,
            [MarshalAs(UnmanagedType.LPWStr)]string name,
            IntPtr config);

and

int CreateEx([MarshalAs(UnmanagedType.LPWStr)]string instanceName,
            [MarshalAs(UnmanagedType.LPWStr)]string name,
            ref VDConfig config);

I get the same result with either definition. Can anyone tell me what I'm doing wrong here?

Edit: In looking a little closer, I'm also getting the error "Device count must be in [1..64]." I'm setting the device count to 1 and in concert with the error above, it almost looks like the function isn't getting my structure at all. Don't know if this helps or not, but maybe it'll spark something for someone.

Per request, here are the interface structures. In C++:

MIDL_INTERFACE("d0e6eb07-7a62-11d2-8573-00c04fc21759")
IClientVirtualDeviceSet2 : public IClientVirtualDeviceSet
{
public:
    virtual HRESULT STDMETHODCALLTYPE CreateEx( 
        /* [in] */ LPCWSTR lpInstanceName,
        /* [in] */ LPCWSTR lpName,
        /* [in] */ struct VDConfig *pCfg) = 0;

    virtual HRESULT STDMETHODCALLTYPE OpenInSecondaryEx( 
        /* [in] */ LPCWSTR lpInstanceName,
        /* [in] */ LPCWSTR lpSetName) = 0;

};

And my C# version:

[ComImport]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[Guid("d0e6eb07-7a62-11d2-8573-00c04fc21759")]
public interface IClientVirtualDeviceSet2
{
    void CreateEx([In, MarshalAs(UnmanagedType.LPWStr)]string instanceName,
        [In, MarshalAs(UnmanagedType.LPWStr)]string name,
        [In]ref VDConfig config);

    void OpenInSecondaryEx([MarshalAs(UnmanagedType.LPWStr)]string instanceName,
        [MarshalAs(UnmanagedType.LPWStr)]string lpSetName);
}

For anyone else who comes across this, this is the answer:

As you can see in vdi.h , IClientVirtualDeviceSet2 "inherits" from IClientVirtualDeviceSet . As far as COM is concerned, there is no such thing as interface inheritance.

Therefore, when calling CreateEx on IClientVirtualDeviceSet2 , you're actually calling Create on IClientVirtualDeviceSet (because Create is the first method in the vtable of that combined IClientVirtualDeviceSet + IClientVirtualDeviceSet2 ). That's why you end up getting invalid parameters.

The fix for this is to create a single interface ( IClientVirtualDeviceSet2 ) with all the methods, IClientVirtualDeviceSet first, then the two IClientVirtualDeviceSet2 methods (obviously in order). This ensures when CreateEx() is called, it uses the correct DispId .

I'm sure you could probably use inheritance and set the DispIdAttribute accordingly:

https://msdn.microsoft.com/en-us/library/system.runtime.interopservices.dispidattribute(v=vs.110).aspx

but there is probably little point.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM