C# 中的结构对齐
本文关键字:对齐 结构 | 更新日期: 2023-09-27 18:35:37
我正在使用SQL VDI,并尝试通过COM接口将结构从C#传递到C++。 该结构在C++标头中定义为:
#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进行了尝试。 但是我定义结构,当我尝试将其传递给函数时,它失败了,并且收到错误"对齐必须是 2**n 和 <= 系统分配粒度"。 我尝试将接受结构的函数定义为:
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,并且与上面的错误一致,看起来该功能根本没有获得我的结构。 不知道这是否有帮助,但也许它会为某人带来一些东西。
根据请求,以下是接口结构。 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 而言,没有接口继承这样的东西。
因此,当在IClientVirtualDeviceSet2
上调用CreateEx
时,您实际上是在IClientVirtualDeviceSet
上调用Create
(因为Create
是组合IClientVirtualDeviceSet
+ IClientVirtualDeviceSet2
vtable
中的第一个方法)。 这就是为什么您最终会得到无效的参数。
解决此问题的是创建一个包含所有方法的单个接口(IClientVirtualDeviceSet2
),首先IClientVirtualDeviceSet
,然后是两个IClientVirtualDeviceSet2
方法(显然是按顺序排列的)。 这可确保在调用CreateEx()
时,它使用正确的DispId
。
我相信您可能可以使用继承并相应地设置 DispIdAttribute:
https://msdn.microsoft.com/en-us/library/system.runtime.interopservices.dispidattribute(v=vs.110).aspx
但可能没有什么意义。