Литмир - Электронная Библиотека
A
A

[uuid(315BC28A-DEA7-11d0-8C5E-0080C73925BA)]

dispinterface ISurfboardUser {

methods:

[id(1)] void OnTiltingForward( [in] long nAmount);

[id(2)] void OnTiltingSideways( [in] long nAmount);

}

При программировании на Visual Basic можно объявить переменные, понимающие тип интерфейса обратного вызова, принятый по умолчанию, таким образом:

Dim WithEvents sb as Surfboard

Наличие такого описания переменной дает программистам на Visual Basic возможность писать обработчики событий. Обработчики событий – это функции или подпрограммы, использующие соглашение VariableName_EventName. Например, для обработки события обратного вызова ОпТiltingForward на определенную выше переменную sb программисту Visual Basic пришлось бы написать следующий код:

Sub sb_OnTiltingForward(ByVal nAmount as Long)

MsgBox «The surfboard just tilted forward»

End Sub

Виртуальная машина Visual Basic будет действительно на лету обрабатывать реализацию ISurfboardUser, преобразуя поступающие вызовы методов в соответствующие определенные пользователем подпрограммы.

Совмещение имен в IDL

Часто бывает необходимо объединить традиционные (старые) типы данных и идиомы программирования в одну систему на основе СОМ. В идеале существует простое и очевидное преобразование традиционного кода в его аналог, совместимый с IDL. Если у нас именно такой случай, то тогда переход к СОМ будет достаточно простым. Существуют, однако, ситуации, когда традиционные типы данных или идиомы приложения просто не могут разумным образом преобразовываться в IDL. Для решения этой проблемы в IDL предусмотрено несколько технологий замещения (aliasing techniques ), которые позволяют разработчику интерфейса составлять подпрограммы преобразования, способные переводить традиционные типы данных и идиомы в легальные, доступные для вызова представления на IDL.

Прекрасным примером ситуации, в которой данная технология полезна, является идиома IEnum . Идиома нумератора СОМ была разработана раньше, чем компилятор IDL, поддерживаемый СОМ. Это означает, что первый разработчик интерфейса IEnum не мог проверить свою разработку на соответствие известным правилам преобразования в IDL. Метод перечислителя Next не может быть чисто преобразован в IDL[1]. Рассмотрим идеальный IDL-прототип метода Next:

HRESULT Next([in] ULONG cElems, [out, size_is(cElems), length_is(*pcFetched)] double *prg, [out] ULONG *pcFetched);

К сожалению, исходное «до-IDL-овское» определение метода Next устанавливало, что вызывающие программы могут передавать в качестве третьего параметра нулевой указатель, при условии, что первый параметр показывал, что запрашивается только один элемент. Это предоставляло вызывающим программам удобную возможность извлекать по одному элементу за раз:

double dblElem;

hr = p->Next(1, &dblElem, 0);

Данное допустимое использование интерфейса противоречит приведенному выше IDL-определению, так как [out] -параметры самого верхнего уровня не имеют права быть нулевыми (нет места, куда интерфейсный заместитель мог бы сохранять результат). Для разрешения этого противоречия каждое определение метода Next должно использовать атрибут [call_as] для замены вызываемой формы (callable form) метода его отправляемой формой (remotable form).

Атрибут [call_as] позволяет разработчику интерфейса выразить один и тот же метод в двух формах. Вызываемая форма метода должна использовать атрибут [local] для подавления генерирования маршалирующего кода. В этом варианте метода согласовывается, какие клиенты будут вызывать и какие объекты – реализовать. Отправляемая форма метода должна использовать атрибут [call_as] для связывания генерируемого маршалера с соответствующим методом в интерфейсной заглушке. Этот вариант метода описывает отправляемую форму интерфейса и должен использовать стандартные структуры IDL для описания запроса и ответных сообщений, необходимых для отзыва метода. Применяя технологию [call_as] к методу Next, получим такой IDL-код:

interface IEnumDoubIe : IUnknown {

// this method is what the caller and object see

// данный метод, как его видят вызывающая программа и объект

[local] HRESULT Next([in] ULONG cElems,

[out] double *prgElems, [out] ULONG *pcFetched);

// this method is how it goes out on the wire

// данный метод, как он выходит на передачу

[call_as(Next)]

HRESULT RemoteNext([in] ULONG cElems, [out, size_is(cElems), length_is(*pcFetched)] double *prg, [out] ULONG *pcFetched);

HRESULT Skip([in] ULONG cElems);

HRESULT Reset(void); HRESULT Clone([out] IEnumDouble **ppe);

}

Результирующий заголовочный файл C/C++ будет содержать определение интерфейса, включающее в себя метод Next, но не определение метода RemoteNext. Что касается клиента и объекта, то у них нет метода RemoteNext. Он существует только для того, чтобы интерфейсный маршалер мог правильно отправить метод. Хотя у методов Next и RemoteNext списки параметров идентичны, при использовании данной технологии этого не требуется. На самом деле иногда бывает полезно включить в отправляемую форму метода добавочные параметры, чтобы дать исчерпывающее определение тому, как эта операция будет отправлена.

С добавлением в метод пары атрибутов [local]/[call_as] исходный код, сгенерированный интерфейсным маршалером, более не сможет успешно компоноваться из-за непреобразованных внешних символов. Дело в том, что в этом случае разработчик интерфейса должен предусмотреть две дополнительных подпрограммы. Одна из них будет использоваться интерфейсным заместителем для преобразования формы метода с атрибутом [local] в форму с атрибутом [call_as]. B случае приведенного выше определения интерфейса компилятор IDL будет ожидать, что разработчик интерфейса обеспечит его следующей функцией:

HRESULT STDMETHODCALLTYPE IEnumDouble_Next_Proxy(IEnumDouble *This, ULONG cElems, double *prg, ULONG *pcFetched);

Вторая необходимая подпрограмма используется интерфейсной заглушкой для преобразования формы метода с атрибутом [call_as] в форму с атрибутом [local]. В случае приведенного выше определения интерфейса компилятор IDL будет ожидать от разработчика интерфейса следующей функции:

HRESULT STDMETHODCALLTYPE IEnumDouble_Next_Stub(IEnumDouble *This, ULONG cElems, double *prg, ULONG *pcFetched);

Для удобства прототипы для этих двух подпрограмм будут приведены в сгенерированном заголовочном файле C/C++.

Как показано на рис. 7.10, определяемая пользователем подпрограмма [local]-to-[call_as] используется для заполнения таблицы vtbl интерфейсного заместителя и вызывается клиентом. Данная подпрограмма предназначена для преобразования вызова в удаленный вызов процедуры посредством вызова отправляемой версии, которая генерируется компилятором IDL. Для подпрограммы нумератора Next необходимо только убедиться, что в качестве третьего параметра передается ненулевой указатель:

Сущность технологии СОМ. Библиотека программиста - fig7_10.jpg

HRESULT STDMETHODCALLTYPE IEnumDouble_Next_Proxy( IEnumDouble *This, ULONG cElems, double *prg, ULONG *pcFetched) {

// enforce semantics on client-side

// осуществляем семантику на стороне клиента

вернуться

1 Можно утверждать, что исходное определение интерфейса было разумным, и что IDL просто недостаточно гибок для описания общих идиом программирования. Хотя это и может быть достаточным оправданием для интерфейса, определенного в 1992 году, до создания СОМ IDL, но это не может служить оправданием для современных интерфейсов. Просто примем, что всем интерфейсам следует подчиняться правилам СОМ IDL, если только не имеется достаточно обоснованной причины поступать иначе.

108
{"b":"47895","o":1}