我正在使用 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)继承这回事。
因此,当调用 CreateEx
在 IClientVirtualDeviceSet2
,你实际上是在调用 Create
在 IClientVirtualDeviceSet
(因为 Create
是组合 vtable
+ IClientVirtualDeviceSet
的 IClientVirtualDeviceSet2
中的第一个方法)。这就是您最终得到无效参数的原因。
解决这个问题的方法是创建一个接口(interface)(IClientVirtualDeviceSet2
),其中包含所有方法,IClientVirtualDeviceSet
首先,然后是两个IClientVirtualDeviceSet2
方法(显然是按顺序)。这确保了何时 CreateEx()
被调用,它使用正确的 DispId
.
我确定您可以使用继承并相应地设置 DispIdAttribute:
但可能没什么意义。
关于c# - C# 中的结构对齐,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27705778/