Сущность технологии СОМ. Библиотека программиста - Дональд Бокс
- Дата:20.06.2024
- Категория: Компьютеры и Интернет / Программирование
- Название: Сущность технологии СОМ. Библиотека программиста
- Автор: Дональд Бокс
- Просмотров:3
- Комментариев:0
Шрифт:
Интервал:
Закладка:
[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 необходимо только убедиться, что в качестве третьего параметра передается ненулевой указатель:
HRESULT STDMETHODCALLTYPE IEnumDouble_Next_Proxy( IEnumDouble *This, ULONG cElems, double *prg, ULONG *pcFetched) {
// enforce semantics on client-side
// осуществляем семантику на стороне клиента
if (pcFetched == 0 && cElems != 1) return E_INVALIDARG;
// provide a location for last [out] param
// обеспечиваем место для последнего [out]-параметра
ULONG cFetched;
if (pcFetched == 0) pcFetched = &cFetched;
// call remote method with non-null pointer as last param
// вызываем удаленный метод с ненулевым указателем
// в качестве последнего параметра
return IEnumDouble_RemoteNext_Proxy(This, cElems, prg, pcFetched);
}
Отметим, что во всех случаях отправляемая версия метода получает в качестве последнего параметра ненулевой указатель.
Определяемая пользователем подпрограмма [local]-to-[call_as] будет вызываться интерфейсной заглушкой после демаршалинга отправляемой формы метода. Эта подпрограмма предназначена для преобразования отправляемой формы вызова в локальный вызов процедуры на текущий объект. Поскольку реализации объекта иногда проявляют небрежность и не считают нужным показывать, сколько элементов возвращается при возвращении S_OK, правильность установки этого параметра обеспечивает подпрограмма преобразования со стороны объекта:
HRESULT STDMETHODCALLTYPE IEnumDouble_Next_Stub( IEnumDouble *This, ULONG cElems, double *prg, ULONG *pcFetched) {
// call method on actual object
// вызываем метод на текущий объект
HRESULT hr = This->Next(cElems, prg, pcFetched);
// enforce semantics on object-side
// проводим в жизнь семантику на стороне объекта
if (hr == S_OK)
// S_OK implies all elements sent
// S_OK означает, что все элементы посланы
*pcFetched = cElems;
// [length_is] must be explicit
// атрибут [length_is] должен быть явным
return hr;
}
Интерфейсная заглушка всегда будет вызывать данную подпрограмму с ненулевым последним параметром.
Технология с атрибутом [call_as] является полезной при организации преобразований из вызываемой формы в отправляемую по схеме «метод-за-методом». В СОМ также предусмотрена возможность специфицировать определяемые пользователем преобразования для отдельных типов данных при помощью атрибутов определения типов [transmit_as] и [wire_marshal]. Эти три технологии не следует считать основными при разработке интерфейсов; они существуют в основном для поддержки традиционных идиом и типов данных. Еще одним приемом, которым владеет компилятор IDL, является cpp_quote. Ключевое слово cpp_quote разрешает появление в IDL-файле любых операторов C/C++, даже если этот оператор не является допустимым в IDL. Рассмотрим следующее простейшее применение cpp_quote для внедрения определения встраиваемой функции в сгенерированный IDL заголовочный файл:
- Аквариум. (Новое издание, исправленное и переработанное) - Виктор Суворов (Резун) - Шпионский детектив
- Права на результаты интеллектуальной деятельности и средства индивидуализации: Комментарий к части четвертой Гражданского кодекса Российской Федерации - Вадим Погуляев - Юриспруденция
- У меня есть идея! Что дальше? - Михаил Соболев - О бизнесе популярно
- Ориентирование - К. М. Станич - Современные любовные романы
- Проект 365 - Константин Рочев - Поэзия