Концепция
маршалинга
СОМ спроектирован
так, чтобы обеспечить прозрачную (transparent) коммуникацию клиента с сервером
независимо от того, где они находятся:
- в пространстве одного
процесса,
- на одном компьютере,
но в разных процессах,
- на разных компьютерах.
С точки зрения
клиента все СОМ-объекты управляются одинаковым способом — с помощью указателя
на интерфейс, который должен действовать в адресном пространстве клиента. Если
СОМ-объект находится в этом же пространстве, то вызов метода какого-либо из
его интерфейсов осуществляется прямо, без посредников. Если объект расположен
вне рамок клиентского процесса, то вызов осуществляется с помощью посредников,
называемых заглушками. Их либо автоматически генерирует СОМ, либо создает сам
разработчик.
С точки зрения
сервера все вызовы также осуществляются с помощью указателя на интерфейс. Но
теперь указатель должен действовать в контексте процесса серверного приложения.
Если процессы совпадают (inproc-server), то можно обойтись без заглушек, но
если нет, то нужен еще один посредник, который расположен в пространстве серверного
процесса.
Для того чтобы
клиент, написанный на любом из перечисленных (элитных) языков, мог вызвать метод
интерфейса из СОМ-объекта, расположенного в рамках другого процесса, несколько
компонентов должны объединить свои усилия. Прежде всего это две заглушки (клиентская
и серверная). В технологии RPC (Remote Procedure Call) они так и называются.
В СОМ клиентская заглушка называется proxy stub, или просто proxy (представитель
интересов сервера).
Когда клиент
вызывает метод локального или удаленного сервера (рис. 8.1), этот вызов перехватывается
представителем настоящего сервера, расположенным в адресном пространстве клиента
(proxy). Последний получает запрос на вызов метода, упаковывает параметры, которые
будут посланы серверу, и вызывает соответствующий метод при помощи RPC. Акт
передачи данных, то есть параметров функций и возвращаемых значений, за пределы
процесса называется транспортировкой. Она включает в себя упаковку, передачу
и распаковку данных по достижении ими места назначения. Отметьте, что транспортировать
надо как данные, так и интерфейсные указатели.
С другой стороны,
специальная часть кода на сервере (stub), получает от proxy запрос на вызов
метода, распаковывает параметры и вызывает нужный метод реального сервера. Сервер,
выполнив клиентский запрос, обычно возвращает какие-то данные. Посредник на
стороне сервера (stub) перехватывает эти данные, упаковывает их и направляет
соответствующему посреднику на стороне клиента (proxy). Последний получает возвращаемые
данные, распаковывает их и передает клиенту. Библиотеки СОМ автоматически обеспечивают
код функций proxy/ stub для стандартных интерфейсов. При написании же собственных
интерфейсов следует пользоваться интерфейсом, производным от iMarshal. Итак,
заместитель расположен в адресном пространстве клиента и представляет интересы
СОМ-объекта на стороне клиента, обеспечивая суррогатные точки входа для каждого
из методов, обозначенных в исходном IDL-файле. Когда клиент делает вызов удаленной
(remote) процедуры сервера, то сначала он вызывает суррогат этой процедуры в
заглушке proxy (в пространстве своего процесса). Последняя осуществляет:
- упаковку параметров
в буфер сообщения (message buffer), так чтобы они надежно могли быть доставлены
удаленному серверу;
- вызов библиотечной процедуры
передачи параметров в адресное пространство сервера;
- распаковку выходных (out)
или возвращаемых (retval) параметров и передачу их вызывающей процедуре.
Серверная заглушка,
или просто stub, распаковывает (unmarshals) параметры и передает их объекту
СОМ. Она также запаковывает ответную информацию, возвращаемые параметры, для
того чтобы передать их назад клиенту.
Описанные действия
называются маршализацией аргументов. Эта процедура сильно зависит от типа параметров.
Например, маршализация массива данных значительно сложнее маршализации переменной
целого типа или указателя на структуру. Для каждого типа данных существуют свои
отдельные функции. Proxy состоит из части, которая размещена в OLE32. DLL (proxy
manager), и частей, которые зависят от интерфейсов СОМ-объекта (interface proxies).
Для клиента proxy представляет собой реальный СОМ-объект.
Сам канал передачи
обслуживается функциями библиотеки СОМ. Канал передает буфер (с маршализованными
параметрами) во владение функциям из RPC-библиотеки, которые и занимаются его
передачей через границу между процессами. Вы можете выбирать между стандартной
маршализацией, обеспечиваемой библиотекой СОМ, и своей собственной (custom marshaling).
В последнем случае вы должны разработать интерфейс, производный от IMarshal.
Каждый отдельный интерфейс может пользоваться одним из двух способов маршализации
своих параметров. Это определяется на этапе проектирования СОМ-класса, реализующего
интерфейсы. Здесь уместно привести схему, которую вы также можете увидеть в
MSDN (Search > Marshaling Details).
Рис. 8.1.
Схема коммуникации клиент-сервер
СОМ не накладывает
ограничений на структуру компонентов, он определяет лишь порядок их взаимодействия.
В основе межпроцессной коммуникации лежит все та же косвенная адресация (таблица
виртуальных функций), которая позволяет передать управление либо прямо методу
интерфейса (inproc-server), либо его представителю (proxy) на стороне клиента,
который, в свою очередь, делает RPC (удаленный вызов) метода настоящего объекта.
Прозрачность СОМ-объекта для клиента заключается в том, что proxy-объект знает,
где расположен реальный объект (на другом компьютере — remote server, или на
том же самом — local server), а клиент об этом не знает.
Когда клиент
хочет использовать СОМ-сервер, он обращается к системной библиотеке с просьбой
найти и загрузить сервер, чей CLSID равен определенному значению. Заодно клиент
передает IID требуемого интерфейса. В ответ на это системная COM DLL вызывает
SCM (Service Control Manager) — менеджер сервисов, который запускается во время
загрузки системы. SCM является RFC-сервером, который использует системный реестр,
чтобы выполнить поиск реализации, то есть отыскать ЕХЕ- или DLL-файл, содержащий
требуемый СОМ-сервер. Чтобы найти модуль сервера, SCM ищет в реестре его CLSID.
Если он найден, то SCM возвращает связанный с ним файловый путь, а СОМ загружают
этот модуль в память. Теперь возможны два варианта действий: если сервер находится
в ЕХЕ-файле, то СОМ запускает его, устанавливает канал связи (RPC) между представителями
клиента и сервера (proxy/stub) и возвращает интерфейсный указатель клиенту.
Если СОМ-сервер находится в DLL-файле, СОМ просто передаст клиенту указатель
на фабрику классов сервера.