Добавить в цитаты Настройки чтения

Страница 186 из 372

Параметр CLSCTX_INPROC_SERVER задает использование именно сервера в процессе клиента.

Для загрузки сервера в суррогатный процесс достаточно заменить этот параметр на CLSCTX_LOCAL_SERVER для обоих задействованных в приложении классов: CoBook и СоJournal.

Итак, сервер будет загружаться в DLL суррогат. Выполняется это следующим образом. При построении фабрики класса (CoGetClassObject) система обнаруживает, что клиент требует локальный сервер. Однако в реестре для данного класса имеется только путь к DLL, реализующей сервер в процессе клиента — параметр InProcServer32. Однако, для данного класса имеется и параметр AppID, задающий GUID приложения, использующего данный класс. Обращаясь к разделу реестра, описывающего данное приложение, система обнаруживает, что требуется загрузка сервера в суррогатный процесс — параметр DllSurrogate.

Стандартный маршалинг

Итак, клиент будет размещен в одном процессе, а сервер в другом. Как же будут осуществляться вызовы методов, передача параметров, возвращение результатов? Очевидно, что здесь не работают механизмы, использовавшиеся ранее, когда клиент и сервер находились в одном адресном пространстве.

Идеология различных распределенных архитектур основана на использовании понятий прокси, менеджера прокси, канала и заглушки.

В СОМ прокси является объектом, который агрегирован в еще один объект — менеджер прокси. Менеджер прокси загружен в адресное пространство клиента и имитирует для него удаленный объект. Точнее, каждый используемый данным клиентом интерфейс удаленного объекта имитируется своим прокси и все эти прокси агрегированы менеджером прокси. Таким образом, клиент может напрямую вызывать методы удаленного объекта как и в случае сервера в пространстве клиента. Конечно, реализация методов удаленного объекта не полная. Реально прокси принимает вызов клиента, кодирует и пакетирует его и передает специальному объекту канала, который ответственен за физическую передачу данных по сети. На стороне сервера пакеты принимает заглушка, в чьи функции входит распаковка вызова и передача его серверу. Таким образом, и для сервера создается иллюзия того, что клиент находится в одном с ним адресном пространстве.

Процесс упаковки, передачи и распаковки при вызове метода удаленного объекта и называется маршалингом. В СОМ имеется три типа маршалинга:

• Пользовательский маршалинг

Это наиболее сложный для реализации выбор, но при этом программист получает полный контроль над процессом передачи данных. Дня реализации пользовательского маршалинга надо реализовать станартный интерфейс iMarshai, выбрать и реализовать протокол передачи данных.

• Стандартный маршалинг

Здесь используются прокси и заглушки, взаимодействующие друг с другом по протоколу ORPC (Object RPC). Причем, если для описания интерфейсов используется IDL, от программиста не требуется писать какой-либо код. Компилятор с IDL — Microsoft IDL (MIDL) генерирует несколько файлов с кодами прокси и заглушки. Используя их можно построить DLL, которая после регистрации и обеспечит маршалинг для описанных в IDL-файле интерфейсов.

• Универсальный маршалинг

В этом случае не нужно даже строить DLL заглушки/прокси. Эта DLL строится "на лету". Необходимые условия:

♦ в методах интерфейсов используются параметры только так называемых variant-совместимых типов (некоторое подмножество типов, допустимых в IDL);

♦ в IDL файле интерфейсы описаны с атрибутом oleautomation;

♦ имеется зарегистрированная библиотека типов, в которой описаны интерфейсы;

♦ информация об интерфейсах включена в реестр под ключом НКЕY_CLASSЕS_ROOT Interface.

Возможность универсального маршалинга связана с тем, что для всех стандартных интерфейсов код маршалинга уже существует. При использовании упомянутого ограниченного набора типов этот код может использоваться для автоматического формирования кода маршалинга нового интерфейса.

Далее приведен стандартный алгоритм действий для построения DLL прокси/заглушки, обеспечивающих стандартный маршалинг:

1. Открыть новый проект Win32 DLL — PSPubinProcServer и добавить в него сгенерированные компилятором с IDL файлы:

♦ PubInPrосServerTуреInfo_i.с — определения GUID, упомянутых в IDL-файле,

♦ PubInPrосServerTуреInfo_р. с, dlldata.c — код для прокси/заглушек и макросы для саморегистрации формируемой DLL.

2. Создать DEF-файл PSPubinProcServer.def

3. LIBRARY PSPubInProcServer.dll

4. EXPOTRS

5.          DllGetClassObject @1 PRIVATE





6.          DllCcanUnioadNow @2 PRIVATE

7.          DllRegisterServer @3 PRIVATE

8.          DllUnregisterServer @4 PRIVATE

9. В диалоге Project]Settings на вкладке C/C++ в поле Preprocessor definitions добавить через запятую флажки REGISTER_PROXY_DLL и _WIN32_DCOM. В результате в проект будет вставлен код саморегистрации.

10. В том же диалоге на вкладке Link в поле Object/library moduls добавить через пробел ссылки на библиотеки поддержки удаленного вызова процедур rpcndr.lib, rpcns4.lib, rpcrt4.lib.

11. Откомпилировать проект и зарегистрировать построенную DLL прокси/заглушки с помощью Tools|Register Control.

В процессе саморегистрации построенной DLL прокси/заглушки она получает CLSID равный CLSID интерфейса IPub. Под соответствующим ключом реестре имеется раздел InProcServer32 с параметром по умолчанию, задающим путь к построенной DLL. Кроме того, регистрируются все три интерфейса под ключом HKEY_CLASES_ROOT Interface. Интерфейсы регистрируются под своими собственными идентификаторами (GUID). Для каждого имеется подраздел NumMethods, в котором указывается число методов интерфейса (включая наследуемые), и подраздел ProxyStabClsid32, в котором указывается GUID для DLL прокси/заглушки.

Теперь клиент и сервер не только запускаются в различных процессах, но и могут обмениваться друг с другом необходимыми данными.

Говоря про маршалинг следует остановиться на вопросе передачи данных между клиентом и сервером. В случае, когда передаются, например, массивы, необходимо следить за тем, кто выделяет и освобождает память, сколько выделяется памяти. Приведенные ниже примеры взяты из работы Richard Grimes, "Marshaling Your Data: Efficient Data Transfer Techniques Using COM and Windows 2000", MSDN Magazine, September 2000.

Предположим, что некоторый интерфейс включает методы, описания которых на IDL приведено ниже

HRESULT PassLongs([in] ULONG ulNum,

                             [in, size_is(ulNum)] LONG* pArrln);

HRESULT GetLongs([in] ULONG ulNum,

                            [out, size_is(ulNum)] LONG* pArrOut);

Здесь от клиента к объекту и обратно передаются массивы. Проще всего случай передачи массива от клиента к объекту. В этом случае клиент сам выделяет память под массив и сам ее освобождает. При передаче массива от объекта к клиенту возможны два варианта. В первом клиент заранее, до вызова метода знает размер передаваемого массива, во втором этот размер сообщает объект уже после вызова метода. Рассмотрим обе эти возможности.

Начнем со случая, когда клиент до вызова метода имеет информацию о размере массива. Код клиента

ULONG ulNum = 10;

LONG 1 [10];

hr = pArrObj — > GetLongs(ulNum, 1);

Код сервера

STDMETHODIMP CArrays::GetLongs(ULONG ulNum, LONG* pArr)

{

    for (ULONG x = 0; x < ulNum; x++) pArr[x] = x;

    return S_OK;

}

Как все это работает. Клиент сам выделяет память под массив в виде стековой переменной. Это позволяет ему не заботится об ее освобождении. Маршалер (система объектов, обеспечивающая маршализацию данных, передаваемых между клиентом и сервером) как на стороне клиента, так и на стороне объекта имеет информацию о размере передаваемого массива еще до возврата из метода. Это позволяет ему выделить на стороне объекта память под этот массив и передать указатель на выделенную память объекту (рАгг). Объект заполняет массив данными, после чего эти данные передаются по каналу клиенту. После передачи данных маршалер сам освобождает выделенную на стороне объекта память.