Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Lab3.docx
Скачиваний:
0
Добавлен:
01.05.2025
Размер:
3.22 Mб
Скачать

Реализация класса второго плагина

Перейдём к заголовочному файлу ZigzagPluginImpl.h.

Тот код, который вы видите сейчас, создан билдером автоматически, используя макросы ATL. Он размечает структуру нашего COM-класса ZigzagPlugin. Нам предстоить изменить его так, чтобы он стал поддерживать события, предоставляемые интерфейсом IZigzagControlEvents. Обрабатывать мы будем лишь событие изменения точек на сервере.

Сперва нам нужно будет описать сигнатуру функции-обработчика. Вставим сразу после секции include-ов следующее:

static _ATL_FUNC_INFO PointListChangedInfo = {CC_STDCALL, VT_EMPTY, 0};

Тут мы создаём структуру, содержащую информацию об принимаемых функцией аргументах _ATL_FUNC_INFO. В ней CC_STDCALL - соглашения вызова, stdcall; VT_EMPTY - возвращаемое значение - Variant_Empty, т.е. void; 0 - указатель на массив типов аргументов (0 означает отсутствие принимаемых аргументов, ссылка на null). Таким образом мы дадим ATL знать, что наш метод-обработчик использует соглашения вызова stdcall, не принимает никаких аргументов и возвращает void.

Далее, глядим на объявление нашего COM-класса:

class ATL_NO_VTABLE TZigzagPluginImpl :

public CComObjectRootEx<CComSingleThreadModel>,

public CComCoClass<TZigzagPluginImpl, &CLSID_ZigzagPlugin>,

public IDispatchImpl<IPlugin, &IID_IPlugin, &LIBID_Plugin2>

Добавляем в список наследуемых сущностей ещё одну:

public IDispatchImpl<IPlugin, &IID_IPlugin, &LIBID_Plugin2>,

public IDispEventSimpleImpl</*nID =*/ 1, TZigzagPluginImpl, &DIID_IZigzagControlEvents> // Добавляется для поддержки событий сервера.

Единицей будет идентификатор интерфейса-источника событий. Её мы будем использовать чуть позже. О классе IDispEventSimple можно почитать на MSDN.

Затем переходим к телу класса. Находим код, размечающий интерфейсы класса:

BEGIN_COM_MAP(TZigzagPluginImpl)

COM_INTERFACE_ENTRY(IPlugin)

COM_INTERFACE_ENTRY2(IDispatch, IPlugin)

END_COM_MAP()

Сразу после него добавляем следующее:

BEGIN_SINK_MAP(TZigzagPluginImpl) // После этого можно перечислять обработчики событий

SINK_ENTRY_INFO(/*nID =*/ 1, DIID_IZigzagControlEvents, /*dispid =*/ 2, PointListChanged, &PointListChangedInfo)

END_SINK_MAP()

Здесь 1 - идентификатор nID интерфейса-источника событий (см. в объявлении класса), DIID_IZigzagControlEvents - GUID интерфейса-источника, DispID - идентификатор члена диспинтерфейса (это идентификатор конкретного события, можете посмотреть его через OleView.exe), PointListChangedInfo - указатель на информацию об принимаемых аргументах _ATL_FUNC_INFO, который мы объявили в начале файла.

Насчёт DispID, его можно задать и посмотреть непосредственно в объявлении интерфейса. Откройте файл сервера IZigzagControlEvents.cs и всё сразу станет ясно.

Теперь объявим и реализуем прямо тут же сам обработчик события:

// Обработчик события изменения списка точек ломаной.

void __stdcall PointListChanged()

{

RefreshFormData();

}

И в конце класса, прямо перед закрытием фигурной скобки, объявим приватный метод RefreshFormData:

private:

void __fastcall RefreshFormData();

};

#endif //ZigzagPluginImplH

Перейдём к реализации методов плагина. Переключитесь к .cpp нашего класса плагина.

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

#include "MainForm.h"

И, как и всегда, добавляем нужные переменные.

/////////////////////////////////////////////////////////////////////////////

// TZigzagPluginImpl

USEFORM("MainForm.cpp", MainForm);

IZigzagControlDisp zigzagDisp; // Врапер над экземпляром класса ZigzagControl.

bool isConnected = false; // Переменная, хранящая состояние подключенности плагина.

Начинаем реализовывать метод Connect – дальнейший код пишем в теле метода. Первым делом попытаемся привязать сгенерированный борландом враппер над ZigzagControl к COM-классу с сервера.

HRESULT hr = zigzagDisp.Bind(IZigzagControl);

// В hr получим результат действия.

if (hr != S_OK) // Сравним его с нулём.

{

*pRetVal = hr; // Если он не равен нулю, то вернём его код в качестве кода ошибки.

return Error(StringToOleStr("Ошибка при попытке привязаться к интерфейсу IZigzagControl"),

IID_IZigzagControl, hr);

}

Нужно здесь сказать вам, что в билдере вы тоже можете не стесняясь выводить на экран сообщения во время отладки. Сделать это можно через вызов функции ShowMessage, аналога MessageBox.Show() из шарпа.

Например: ShowMessage("Bind result: " + IntToHex((__int64)hr, 8));

Создадим действие плагина по имени его COM-класса.

IPluginActionDisp pluginActionDisp;

hr = pluginActionDisp.Bind(StringToOleStr("ZigzagServer.PluginAction"));

if (hr != S_OK)

{

*pRetVal = hr;

return Error(StringToOleStr("Ошибка при получении экземпляра класса ZigzagServer.PluginAction"),

CLSID_PluginAction, hr);

}

Во время вызова Bind для класса был вызван конструктор без параметров, потому он не инициализирован. Исправим это.

hr = pluginActionDisp.Init((long)1, StringToOleStr("Показать форму"), StringToOleStr("Отображает форму на экране")); // Инициализиуем экземпляр действия.

if (hr != S_OK)

{

*pRetVal = hr;

return Error(StringToOleStr("Ошибка при попытке инициализировать экземпляра класса ZigzagServer.PluginAction"),

CLSID_PluginAction, hr);

}

Ещё раз обратите внимание на то, что шарповский int здесь уже называется long. Строки же следует переводить в OLE-формат через функцию StringToOleStr. Теперь, чтобы создать информацию о плагине, нам потребуется использовать SafeArray. Безопасные массивы повсеместно используются в COM. В шарпе не было нужды создавать для них отдельный класс, ибо массивы там и так самоописываемы и безопасны. Создадим массив и поместим туда единственное наше действие.

tagSAFEARRAY* psaActions = SafeArrayCreateVector(VT_UNKNOWN, 0, 1); // Создаём массив указателей IUnknown FAR*, 0 - нижняя граница массива, 1 - верхняя.

SafeArrayAllocData(psaActions); // Выделяем под массив память.

long index = 0;

hr = SafeArrayPutElement(psaActions, &index, pluginActionDisp); // Кладём наше единственное действие по нулевому индексу массива.

if (hr != S_OK)

{

*pRetVal = hr;

return Error(StringToOleStr("Ошибка при создании массива экземпляров класса ZigzagServer.PluginAction"),

CLSID_PluginAction, hr);

}

Уже знакомым нам образом создадим экземпляр класса PluginConnectionInfo.

IPluginConnectionInfoDisp pluginConnectionInfoDisp;

hr = pluginConnectionInfoDisp.Bind(StringToOleStr("ZigzagServer.PluginConnectionInfo")); // Создаём информацию подключения плагина по имени соответствующего класса.

if (hr != S_OK)

{

*pRetVal = hr;

return Error(StringToOleStr("Ошибка при получении экземпляра класса ZigzagServer.PluginConnectionInfo"),

CLSID_PluginConnectionInfo, hr);

}

И схожим образом инициализируем его, вызвав его метод Init.

hr = pluginConnectionInfoDisp.Init(StringToOleStr("Zigzag player"),

StringToOleStr("Воспроизводит последовательность действий пользователя"),

psaActions);

if (hr != S_OK)

{

*pRetVal = hr;

return Error(StringToOleStr("Ошибка при попытке инициализировать экземпляра класса ZigzagServer.PluginConnectionInfo"),

CLSID_PluginConnectionInfo, hr);

}

Вернём через второй аргумент информацию о плагине и освободим некоторые ненужные более экземпляры-обёртки.

*IPluginConnectionInfo = pluginConnectionInfoDisp; // Второй аргумент служит для возврата информации о плагине, присваиваем ему значение.

pluginActionDisp->Release(); // Освобождаем память

pluginConnectionInfoDisp->Release();

Создадим экземпляр главного окна, установим возвращаемый результат в 0, установим флаг подключенности.

Application->CreateForm(__classid(TMainForm), &MainForm); // Создаём окно.

*pRetVal = 0; // Вернём 0, т.к. всё прошло без ошибок.

isConnected = true;

И, напоследок, начнём прослушивать события сервера, вызвав метод DispEventAdvise с экземпляром ZigzagControl в качестве аргумента, ведь именно его события мы собираемся прослушивать. Завершим метод, вернув S_OK, то есть 0.

DispEventAdvise(zigzagDisp); // Начнём прослушивать события данного экземпляра COM-класса.

return S_OK;

С методом подключения покончено, осталось совсем чуть-чуть. Теперь реализуем метод отключения.

// Метод отключения плагина от приложения-хоста.

STDMETHODIMP TZigzagPluginImpl::Disconnect(long* pRetVal)

{

// Уничтожим форму.

MainForm->Release();

// Отключимся от событий с сервера.

DispEventUnadvise(zigzagDisp);

isConnected = false;

// Уничтожим экземпляр ZigzagControl, получивши доступ к интерфейсу IDisposable.

IDisposableDisp disposable;

disposable.Bind(zigzagDisp);

disposable.Dispose();

zigzagDisp = 0;

*pRetVal = 0; // Вернём 0.

return S_OK;

}

Здесь всё так же, как и в других подобных методах, за исключением вызова DispEventUnadvise, прекращающего прослушку событий с сервера. Напишем метод выполнения действия плагина.

// Метод выполнения действия плагина. Вызывается приложением-хостом.

STDMETHODIMP TZigzagPluginImpl::PerformAction(long actionId, long* pRetVal)

{

if (isConnected) // Если плаги подключен

{

switch (actionId)

{

case 1:

MainForm->Show(); // Показываем окно.

MainForm->RefreshInfo(zigzagDisp); // Заставляем его загрузить свежие данные о точках ломаной с сервера.

*pRetVal = 0;

return S_OK;

default:

*pRetVal = -1; // Действия с запрошенным actionId не существует. Такие дела.

return S_OK;

}

}

else

{

*pRetVal = -1;

return S_OK;

}

}

Тут тоже всё ясно из комментариев.

Так, и у нас остался один лишь метод, который мы объявили в загловочном файле, но ещё не реализовали. Он вызывается из обработчика события изменения списка точек, нам в нём нужно попросить нашу форму перезагрузить данные о точках ломаной с формы. An easy objective.

// Метод, заставляющий окно запросить данные о точках ломаной с сервера.

void __fastcall TZigzagPluginImpl::RefreshFormData()

{

if (isConnected)

MainForm->PointListChanged();

}

Класс плагина завершен.

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]