А если с самого начала разработчики PostAgent подошли бы немного с другой точки зрения к реализации своих интерфейсов? Что я имею ввиду? А вот что. Все вызовы COM являются по своей природе синхронными вызовами. Это означает, что COM-сервер не отдаст вам управление до тех пор, пока не выполнится функция, которую вы вызвали. Но с появлением Connection point получила право на жизнь и асинхронная модель функций в COM. То есть "солидные" приложения и их разработчики (не в пример разработчикам PostAgent) реализуют в своих серверах 2 интерфейса для одного и того же набора функций. Один из них является синхронным, а другой – асинхронным. Про некоторые прелести синхронных функций я вам только что рассказал. А как работают асинхронные функции?
Примерно так. Вы вызываете асинхронную функцию, реализация которой заключается в том, чтобы сохранить переданные ей параметры в некой глобальной структуре данных, затем запустить внутри COM-сервера поток, который будет выполнять действия по обработке переданных данных и вернуть управление клиенту. Вот и все! И никакой головной боли, что я описывал выше. Вы вызвали MessageArchiving, она запомнила переданные вами данные, вернула вам управление, а сама заставила PostAgent выполнять задачу в фоновом для вас режиме. Однако если идти дальше, то вам может понадобиться и механизм, с помощью которого вы смогли бы не только наблюдать за тем, что делает сейчас работающий COM-сервер, но так же и знать когда он закончит свою работу. Ведь так?! Понимаете, куда я клоню? Правильно! Сигнализировать о том, что работа функции, наконец, закончена, может специально предназначенный для этого Connection point. Кроме того, с его же помощью можно и прервать работу функции, если сделать соответствующий функционал в сервере. Но об этом позже. Таким образом, умелое сочетание синхронных и асинхронных функций даст возможность реализовать максимально эффективное взаимодействие через COM.
Думаю к настоящему моменту уже должно быть понятно, зачем нужны Connection points , а также хотя бы примерно, что это такое. Теперь перейдем к более детальному рассмотрению.
Connection point подразумевает под собой соединение. Соединение состоит из 2 частей: из объекта, вызывающего интерфейс и называемого источником, и из объекта, содержащего реализацию этого интерфейса и называемого приемником. Устанавливая точку соединения, источник позволяет приемнику присоединиться к себе. С помощью механизма точки соединения (интерфейс IConnectionPoint) указатель на интерфейс приемника передается объекту источника. Этот указатель обеспечивает источнику доступ к функциям-членам реализации приемника. К примеру, для возбуждения события, реализованного приемником, источник может вызвать соответствующий метод из реализации приемника.
Часть 2. Создание COM-сервера
Программа-пример Server
Итак, давайте создадим внутризадачный COM-сервер (DLL), который будет предоставлять клиенту одну-единственную функцию, результатом работы которой будет возбуждение события. Для этого запустите Visual Studio и выберите меню File->New. Затем в появившемся диалоге выберите вкладку Projects и в поле имени проекта введите ту информацию, что указана на рисунке 2, а также в качестве используемого мастера выберите MFC AppWizard (dll).
Рисунок 2
Нажмите ОК и вы увидите следующий диалог (рисунок 3). Поставьте установки, как показано на рисунке, и не забудьте отметить птичкой опцию Automation. Это важно, т.к. лишь в этом случае мастер добавит к нашему проекту код, необходимый для COM-сервера.
Рисунок 3
Нажмите Finish. Если вы сделали все правильно, то в окне ClassView вы должны увидеть ту же картину, что и на рисунке 4.
Рисунок 4
Итак, что же нам создал мастер? Он добавил минимальный джентльменский набор, который обеспечит существование скромному внутризадачному (DLL) COM-серверу. Это функции DllCanUnloadNow, DllGetClassObject и DllRegisterServer. Среди этих функций также должна находиться функция DllUnregisterServer, которая, на мой взгляд, также важна, как и DllRegisterServer, однако разработчики в Майкрософт почему-то так не считают. О том, как добавить в COM-сервер механизм unregistered, я расскажу в другой статье. Кроме того, в функцию InitInstance мастер добавил следующий код регистрации фабрики классов, за что ему и спасибо.
Читать дальше