c# - C# 中的结构对齐

标签 c# c++ struct

我正在使用 SQL VDI 并尝试通过 COM 接口(interface)将结构从 C# 传递到 C++。该结构在 C++ header 中定义为:

#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;
    } ;

为了模拟这一点,我在 C# 中将结构定义为:

[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;
}

我还尝试将结构定义为 LayoutKind.Sequential 并尝试使用 Pack=8。但是我定义了结构,当我试图将它传递给函数时,它失败了并且我收到错误“Alignment must be 2**n and <= system allocation granularity.”我尝试将接受结构的函数定义为:

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

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

无论哪种定义,我都得到了相同的结果。谁能告诉我我在这里做错了什么?

编辑: 仔细观察,我还收到错误“设备计数必须在 [1..64] 中。”我将设备计数设置为 1 并且与上面的错误一致,看起来该函数根本没有得到我的结构。不知道这是否有帮助,但也许它会激发某些人的灵感。

根据请求,这里是接口(interface)结构。在 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;

};

还有我的 C# 版本:

[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);
}

最佳答案

对于遇到此问题的任何其他人,这就是答案:

正如您在 vdi.h 中看到的那样, IClientVirtualDeviceSet2 “继承”自 IClientVirtualDeviceSet .就 COM 而言,没有接口(interface)继承这回事。

因此,当调用 CreateExIClientVirtualDeviceSet2 ,你实际上是在调用 CreateIClientVirtualDeviceSet (因为 Create 是组合 vtable + IClientVirtualDeviceSetIClientVirtualDeviceSet2 中的第一个方法)。这就是您最终得到无效参数的原因。

解决这个问题的方法是创建一个接口(interface)(IClientVirtualDeviceSet2),其中包含所有方法,IClientVirtualDeviceSet首先,然后是两个IClientVirtualDeviceSet2方法(显然是按顺序)。这确保了何时 CreateEx()被调用,它使用正确的 DispId .

我确定您可以使用继承并相应地设置 DispIdAttribute:

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

但可能没什么意义。

关于c# - C# 中的结构对齐,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27705778/

相关文章:

c# - 使用 HtmlAgilitypack 问题解析表

c# - Windows 窗体应用程序中未处理的异常

c# - 用于函数的 Azure SQL 触发器在空闲后不会触发

c++ - 构造函数返回值中的异常

c++ - 比较C中的两个int数组

c - 将结构数组从用户空间传递到内核空间然后再返回时遇到问题

c++ - 静态结构链接器错误

c# - 在 .NET Core 中使用字符串(配置文件)配置 ServiceCollection

c++ - 移动构造函数调用

c++ - 寻找一种重新初始化结构的简单方法