2014-12-30 5 views
1

Я работаю с SQL VDI и пытаюсь передать структуру с C# на C++ через COM-интерфейс. Структура определена в 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); 
} 
+0

Вы пробовали последовательно с пакетом (8) вместе без FieldOffset? –

+0

Да. Я получаю тот же результат. –

+0

Вы фокусируетесь на неправильной проблеме. Это просто не нравится значение, указанное для VDConfig.выравнивающий элемент. Вы должны следовать его совету и использовать значение, которое имеет силу 2 и <= 65536. –

ответ

2

Для всех, кто попадается на это, это и есть ответ:

Как вы можете видеть в vdi.h, IClientVirtualDeviceSet2 "наследует" от IClientVirtualDeviceSet. Что касается COM, то наследование интерфейса не существует.

Таким образом, при вызове CreateEx на IClientVirtualDeviceSet2, вы на самом деле вызывая Create на IClientVirtualDeviceSet (потому что Create является первым методом в vtable того комбинированного IClientVirtualDeviceSet + IClientVirtualDeviceSet2). Вот почему вы получаете недопустимые параметры.

Исправление этой проблемы является создание единого интерфейса (IClientVirtualDeviceSet2) с все методы, IClientVirtualDeviceSet, а затем два IClientVirtualDeviceSet2 методы (очевидно, в порядке). Это гарантирует, что вызывается CreateEx(), он использует правильный DispId.

Я уверен, что вы могли бы использовать наследование и установить DispIdAttribute соответственно:

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

но есть, наверное, мало смысла.