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 заголовочный файл:
 
   // surfboard.idl
   cpp_quote(«static void Exit(void) { ExitProcess(1); }»)
 
   Имея данный IDL-код, сгенерированный C/C++ заголовочный файл будет просто содержать следующий фрагмент:
 
   // surfboard.h static void Exit(void) { ExitProcess(1); }
 
   Ключевое слово cpp_quote может быть использовано для осуществления различных трюков в компиляторе IDL. Примером этого может служить тип данных REFIID . Фактическим определением IDL для этого типа является typedef IID *REFIID;
   В то же время тип C++ определен как typedef const IID& REFIID;
   Однако ссылки в стиле C++ не допускаются в IDL. Для решения данной проблемы системный IDL-файл использует следующий прием:
 
   // from wtypes.idl (approx.)
   // из файла wtypes.idl (приблизительно)
   cpp_quote(«#if 0») typedef IID "REFIID;
   // this is the pure IDL definition
   // это чисто IDL-определение
   cpp_quote(«#endif») cpp_quote(«#ifdef _cplusplus») cpp_quote(«#define REFIID const IID&»)
   // C++ definition
   // определение C++
   cpp_quote(«#else») cpp_quote(«#define REFIID const IID * const»)
   // С definition
   // определение С
   cpp_quote(«#endif»)
 
   Результирующий заголовочный файл C++ выглядит так:
 
   // from wtypes.h (approx.)
   // из файла wtypes.h (приблизительно)
   #if 0 typedef IID *REFIID;
   #endif
   #ifdef _cplusplus
   #define REFIID const IID&
   #else
   #define REFIID const IID * const
   #endif
   Этот несколько гротескный прием необходим, поскольку многие базовые интерфейсы СОМ были определены без учета возможного применения IDL.
 

http://www.microsoft.com/oledev). Обе эти технологии используют отложенное вычисление (lazy evaluation) для задержки выделения памяти указателям vptr.
   Невесомые и отделяемые элементы являются технологиями разработки СОМ, однако сама СОМ не дает им полномочий и не поддерживает явно. Эти технологии возникли из необходимости эффективно управлять состоянием. При использовании СОМ для разработки распределенных приложений возникают дополнительные проблемы управления состоянием, в том числе исправление распределенных ошибок, безопасность, управление параллелизмом, уравновешивание загрузки и непротиворечивость данных. К сожалению, СОМ ничего не знает о том, как объект управляет своим состоянием, так что она мало может помочь в разрешении этих проблем. Хотя разработчики могут изобретать свои собственные схемы управления состоянием, имеются явные преимущества в построении общей инфраструктуры для развития объектов со знанием своего состояния. Одной из таких инфраструктур является Microsoft Transaction Server (Сервер транзакций фирмы Microsoft – MTS).
   Модель программирования СОМ расширила традиционную модель объектно-ориентированного программирования, заставив разработчиков вникать во взаимоотношения между интерфейсом и реализацией. Модель программирования с MTS также расширяет модель СОМ, побуждая разработчиков вникать также и во взаимоотношения между состоянием и поведением. Фундаментальный принцип MTS заключается в том, что объект может быть логически смоделирован как состояние и поведение, но его физическая реализация должна явно различать эти понятия. Явно разрешив MTS управлять состоянием объекта, разработчик приложения может усилить поддержку инфраструктурой управления параллелизмом и блокировкой, локализацией ошибок, непротиворечивостью данных, а также контролем доступа на уровне мелких структурных единиц (fine-grain). Это означает, что большую часть состояния объекта можно не записывать в непрерывный блок с их указателями vptr (представляющими поведение объекта). Вместо этого в MTS предусмотрены средства для записи состояния объекта либо в длительное, либо во временное хранилище. Это хранилище находится под контролем среды MTS на этапе выполнения, и к нему обеспечен безопасный доступ для методов объекта, причем не нужно заботиться об управлении блокировкой и совместимости данных. Состояние объекта, которое должно оставаться постоянным в случае сбоя машины или нештатного прекращения работы программы, записывается в долговременное хранилище, и MTS гарантирует лишь ничтожные изменения во всей сети. Переходное состояние может быть записано в память, управляемую MTS, причем MTS гарантирует то, что обращения к памяти будут последовательными – во избежание порчи информации.
   Как в разработках на базе классов и на базе интерфейсов, модель программирования MTS, конструирующая состояние, требует дополнительного внимания и дисциплины со стороны разработчика. К счастью, как и с разработкой моделей на базе классов и на базе интерфейсов, модель MTS, конструирующая состояние, может быть принята постепенно. Конечно, пошаговое принятие означает, что преимущества MTS будут реализованы также постепенно. Это позволяет разработчикам принимать MTS со скоростью, соответствующей местной культуре программирования.
   После объединения команд разработчиков MTS и СОМ в рамках фирмы Microsoft стало ясно, что MTS являет собой следующий шаг в эволюции СОМ. Я горячо призываю всех разработчиков СОМ включиться в эту третью волну развития объектно-ориентированного программирования.
 

http://www.develop.com/essentialcom. Для удобства приложение СОМ Chat представлено здесь в отпечатанной форме.
 

СОМ Chat – программа диалогового взаимодействия на базе СОМ
 
   СОМ Chat (чат) является законченной СОМ-программой, которая реализует рассредоточенное приложение диалогового взаимодействия, состоящее из нескольких разделов. Это приложение состоит из трех двоичных компонентов:
   comchat.exe – интерактивный сервер,
   comchatps.dll – интерфейсный маршалер для всех интерфейсов СОМ Chat,
   client.exe – клиентское приложение, основанное на консоли.
   Приложение базируется на единственном классе СОМ (CLSID_ChatSession). Как показано на рис. B.1, объект класса реализует интерфейс IChatSessionManager, а каждый сеанс связи (chat session) реализует интерфейс IChatSession . Клиенты, желающие получать извещения чата, должны подсоединить интерфейс IChatSessionEvents к объекту сеанса связи.
 
 
 
COMChat.idl
   /////////////////////////////////////////////////////
   //
   // COMChat.idl
   //
   // Copyright 1997, Don Box/Addison Wesley
   //
   // This code accompanies the book "The Component
   // Object Model" from Addison Wesley. Blah blah blah
   //
   //
   interface IChatSessionEvents;
   [
   uuid(5223A050-2441-11d1-AF4F-0060976AA886),
   object
   ]
   interface IChatSession : IUnknown
   {
   import «objidl.idl»;
   [propget] HRESULT SessionName([out, string] OLECHAR **ppwsz);
   HRESULT Say([in, string] const OLECHAR *pwszStatement);
   HRESULT GetStatements([out] IEnumString **ppes);
   HRESULT Advise([in] IChatSessionEvents *pEventSink,
   [out] DWORD *pdwReg);
   HRESULT Unadvise([in] DWORD dwReg);
   }
   [
   uuid(5223A051-2441-11d1-AF4F-0060976AA886),