Программирование на Visual C++. Архив рассылки - Алекс Jenter
Шрифт:
Интервал:
Закладка:
Применяется ко всей структуре и позволяет управлять физическим расположением членов структуры в памяти. В общем случае CLR управляет расположением данных структур и классов самостоятельно, если же нужно передавать класс или структуру в неуправляемый код, используется атрибут StructLayout.
Поле Pack этого атрибута может иметь следующие значения:
• Sequential – в этом случае данные будут расположены в памяти последовательно в порядке их объявления.
• Explicit – в этом случае можно управлять точным расположением каждого члена структуры с помощью задания дополнительного атрибута FieldOffset для каждого поля.
• CharSet – задает правила маршалинга строковых данных и может принимать следующие значения:
• Ansi –строки передаются в виде 1-байтовых ANSI символов
• Auto– строки автоматически конвертируются в зависимости от системы (Unicode в WindowsNT и ANSI в Windows9x)
• None = Ansi
• Unicode – строки передаются в виде 2-байтовых символов.
Пример использования атрибутов StructLayout и MarshalAs приведен ниже:
IDL
#define CATDESC_MAX 128
typedef struct tagCATEGORYINFO {
CATID catid;
LCID lcid;
OLECHAR szDescription[CATDESC_MAX];
} CATEGORYINFO, *LPCATEGORYINFO;
C#
[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Unicode)]
public struct CATEGORYINFO {
public Guid catid;
public uint lcid;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst=128)]
public String szDescription;
};
Можно видеть, что в данном случае строка szDescription передается в виде массива фиксированной длины из Unicode-символов. Для маршалинга остальных полей применяются правила по умолчанию.
ПРИМЕЧАНИЕ
В документации Miscrosoft утверждается, что поле SizeConst атрибута MarshalAs задает размер массива в байтах, на самом деле это поле задает количество элементов в массиве.
Обработка ошибок в COM и .NETCOM методы сообщают об ошибках, возвращая соответствующий HRESULT, .NET методы – генерируя исключения. Здесь возникает одна проблема – .NET игнорирует любые положительные значения HRESULT, что приводит к неправильной работе перечислителей типа IEnumXXX, так как последние сигнализируют о достижении конца последовательности возвратом значения S_FALSE = 1. Чтобы решить эту проблему - для методов введен атрибут PreserveSig. Задание этого атрибута позволяет подавить генерацию исключений .NET, и гарантирует возврат точного значения HRESULT из COM метода, в противном случае результатом метода всегда будет S_OK = 0. Пример использования этого атрибута приведен ниже.
Описание интерфейсов – атрибуты ComImport, Guid, InterfaceTypeДля описания интерфейсов и классов применяются атрибуты ComImport и Guid. Атрибут ComImport – показывает, что тип был ранее определен в COM. CLR обращается с такими типами не так, как с , в частности – по другому создает объекты таких типов, выполняет приведение типов, удержание объектов в памяти и т.д. Этот атрибут обязательно сопровождается атрибутом Guid, название которого говорит само за себя.
Атрибут InterfaceType применяется для описания базового COM интерфейса и может принимать следующие значения: дуальный, IDispatch или IUnknown. Если этот атрибут опущен, то считается, что интерфейс дуальный. В нашем случае все интерфейсы наследуют от IUnknown.
Описания параметров методов – атрибуты In, Out, MarshalAsПараметры могут передаваться разными способами. Правильное описание параметров определяется не только атрибутами, но и модификаторами языка C#.
Для примера рассмотрим метод ICatInformation.GetCategoryDesc.
void ICatInformation.GetCategoryDesc([In] ref Guid rcatid, [In] uint lcid, [Out, MarshalAs(UnmanagedType.LPWStr)] out String pszDesc);
ПРИМЕЧАНИЕ
Данный метод можно описать в виде функции:
[return : MarshalAs(UnmanagedType.LPWStr)]
String ICatInformation.GetCategoryDesc([In] ref Guid rcatid, [In] uint lcid);
Такой синтаксис можно использовать для функций Win32API и методов COM-интерфейсов, имеющих последний параметр типа out и возвращающих HRESULT. Далее, в примерах интерфейсов и в демонстрационном приложении методы будут записываться подобным образом. Модификатор return нужен только при задании атрибута MarshalAs для методов COM-интерфейсов.
Если посмотреть на IDL-описание этого метода, видно, что передается ссылка на CLSID (GUID), по правилам языка C# структуры передаются по значению, а Guid является именно структурой. Поэтому, чтобы правильно передать параметр в COM метод, мало задать атрибут [In], нужно еще указать ключевое слово ref для параметра rcatid. Точно также, для задания выходных параметров нужно не только задавать атрибут [Out], но и ключевое слово out. При несоблюдении этих правил возможны ошибки компиляции или, что хуже, ошибки времени выполнения.
Атрибут MarshalAs задает правила передачи параметров, наиболее часто он используется в следующих видах:
• MarshalAs(UnmanagedType.LPWStr) – Unicode-строка. Память под строку распределяется и освобождается через системные функции.
• MarshalAs(UnmanagedType.LPArray, SizeParamIndex=n) – передается одномерный массив, размер массива задается параметром с номером n, нумерация параметров начинается с нуля.
• MarshalAs(UnmanagedType.Interface) – передается COM интерфейс.
Примеры интерфейсов
IDL
[object, uuid(0002E000-0000-0000-C000-000000000046), pointer_default(unique)]
interface IEnumGUID : IUnknown {
HRESULT Next([in] ULONG celt,
[out, size_is(celt), length_is(*pceltFetched)] GUID *rgelt,
[out] ULONG *pceltFetched);
HRESULT Skip([in] ULONG celt);
HRESULT Reset();
HRESULT Clone([out] IEnumGUID **ppenum);
}
[object, uuid(0002E011-0000-0000-C000-000000000046), pointer_default(unique)]
interface IEnumCATEGORYINFO : IUnknown {
HRESULT Next([in] ULONG celt,
[out, size_is(celt), length_is(*pceltFetched)] CATEGORYINFO *rgelt,
[out] ULONG *pceltFetched);
HRESULT Skip([in] ULONG celt);
HRESULT Reset();
HRESULT Clone([out] IEnumCATEGORYINFO **ppenum);
}
[object, uuid(0002E013-0000-0000-C000-000000000046), pointer_default(unique)]
interface ICatInformation : IUnknown {
HRESULT EnumCategories([in] LCID lcid,
[out] IEnumCATEGORYINFO** ppenumCategoryInfo);
HRESULT GetCategoryDesc([in] REFCATID rcatid,
[in] LCID lcid,
[out] LPWSTR* pszDesc);
[local]
HRESULT EnumClassesOfCategories([in] ULONG cImplemented,
[in,size_is(cImplemented)] CATID rgcatidImpl[],
[in] ULONG cRequired,
[in,size_is(cRequired)] CATID rgcatidReq[],
[out] IEnumCLSID** ppenumClsid);
[call_as(EnumClassesOfCategories)]
HRESULT RemoteEnumClassesOfCategories([in] ULONG cImplemented,
[in,unique,size_is(cImplemented)] CATID rgcatidImpl[],
[in] ULONG cRequired,
[in,unique,size_is(cRequired)] CATID rgcatidReq[],
[out] IEnumCLSID** ppenumClsid);
[local]
HRESULT IsClassOfCategories([in] REFCLSID rclsid,
[in] ULONG cImplemented,
[in,size_is(cImplemented)] CATID rgcatidImpl[],
[in] ULONG cRequired,
[in,size_is(cRequired)] CATID rgcatidReq[]);
[call_as(IsClassOfCategories)]
HRESULT RemoteIsClassOfCategories([in] REFCLSID rclsid,
[in] ULONG cImplemented,
[in,unique,size_is(cImplemented)] CATID rgcatidImpl[],
[in] ULONG cRequired,
[in,unique,size_is(cRequired)] CATID rgcatidReq[]);
HRESULT EnumImplCategoriesOfClass([in] REFCLSID rclsid,
[out] IEnumCATID** ppenumCatid);
HRESULT EnumReqCategoriesOfClass([in] REFCLSID rclsid,
[out] IEnumCATID** ppenumCatid);
}
C#
[ComImport, Guid("0002E000-0000-0000-C000-000000000046"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IEnumGUID {
[PreserveSig()]
int Next([In] uint celt,
[Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex=0)] Guid[] rgelt,
[Out] out uint pceltFetched);
[PreserveSig()]
int Skip([In] uint celt);
void Reset();
[return : MarshalAs(UnmanagedType.Interface)]
IEnumGUID Clone();
};
[ComImport, Guid("0002E011-0000-0000-C000-000000000046"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IEnumCATEGORYINFO {
[PreserveSig()]
int Next([In] uint celt,
[Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex=0)] CATEGORYINFO[] rgelt,
[Out] out uint pceltFetched);
[PreserveSig()]
int Skip([In] uint celt);
void Reset();
[return : MarshalAs(UnmanagedType.Interface)]
IEnumCATEGORYINFO Clone();
};
[ComImport, Guid("0002E013-0000-0000-C000-000000000046"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface ICatInformation {
[return : MarshalAs(UnmanagedType.Interface)]
IEnumCATEGORYINFO EnumCategories([In] uint lcid);
[return : MarshalAs(UnmanagedType.LPWStr)]
String GetCategoryDesc([In] ref Guid rcatid, [In] uint lcid);
[return : MarshalAs(UnmanagedType.Interface)]
IEnumGUID EnumClassesOfCategories([In] uint cImplemented,
[In, MarshalAs(UnmanagedType.LPArray, SizeParamIndex=0)] Guid[] rgcatidImpl,
[In] uint cRequired,
[In, MarshalAs(UnmanagedType.LPArray, SizeParamIndex=2)] Guid[] rgcatidReq);
[return : MarshalAs(UnmanagedType.Interface)]
IEnumGUID RemoteEnumClassesOfCategories([In] uint cImplemented,
[In, MarshalAs(UnmanagedType.LPArray, SizeParamIndex=0)] Guid[] rgcatidImpl,
[In] uint cRequired,
[In, MarshalAs(UnmanagedType.LPArray, SizeParamIndex=2)] Guid[] rgcatidReq);
[PreserveSig()]