
- •Добавление к com-серверу поддержки событий
- •Создание модели взаимодействия приложения хоста с плагинами
- •Описание com-интерфейсов, связанных с поддержкой плагинов
- •Создание com-интерфейсов, связанных с поддержкой плагинов
- •Создание com-классов, связанных с поддержкой плагинов
- •Создание класса, отвечающего за взаимодействие с плагинами
- •Код класса Plugin.Cs
- •Внесение косметических изменений в серверную часть
- •Строим своё меню с плагинами и идентификаторами
- •Вносим изменения в главную форму сервера
- •Создание первого плагина
- •Импорт типов с сервера
- •Создание класса для хранения внутреннего представления точек
- •Создание форм редактирования данных о точках
- •Реализация форм редактирования данных о точках
- •Реализация класса первого плагина
- •Создание третьего плагина
- •Делаем наш плагин com-видимым
- •Создание главной формы плагина
- •Реализация класса третьего плагина
- •Создание инсталлятора для плагина
- •Создание второго плагина
- •Создание главной формы плагина
- •Создание библиотеки типов
- •Реализация формы второго плагина
- •Реализация класса второго плагина
- •Добавление метода GetComClassName
- •Data Execution Prevention и его отключение
- •Тестирование совместной работы клиента и плагинов
- •Исходный код
Создание com-классов, связанных с поддержкой плагинов
Теперь реализуем парочку COM-классов, которые будут отвечать за хранение информации о плагине и его действиях. Добавлять их регистрацию в проект-регистратор (ZigzagClassRegistrator) не надо, так как в этом случае он зарегистрирует их как LocalServer32. Если мы их не добавим туда, то он их зарегистрирует как InprocServer32 (при условии, что классы будут объявлены как public, а наша сборка будет COM-видимой, а она уже, см. параметры проекта ZigzagServer). Мы просто добавляем два класса в проект, указываем их guid-ы и пишем для них код. Притом даже не обязательно помечать классы как ComVisible, так как сборка всё равно COM-видима, а стало быть видимы все её классы и интерфейсы. Эти классы зарегистрируются вместе со сборкой на этапе построения.
Начнём с добавления класса PluginAction.
Напишем код класса. Он предельно прост и я думаю, что достаточно будет пояснений в комментариях.
Единственное, на что я хотел бы обратить внимание, это на то, что класс должен содержать конструктор без параметров, чтобы его можно было создать из COM-клиента просто указав его ProgID, строковое имя.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
namespace ZigzagServer
{
/// <summary>
/// Действие, доступное из плагина.
/// </summary>
[Guid("9665CD7C-2CF6-4D73-B6C9-1EC865BE5934")]
public class PluginAction : IPluginAction
{
int actionId;
string actionName;
string actionDescription;
bool isInited = false;
public PluginAction(int actionId, string actionName, string actionDescription)
{
Init(actionId, actionName, actionDescription);
}
public PluginAction()
{
}
#region Члены IPluginAction
/// <summary>
/// Получает идентификатор действия.
/// </summary>
public int ActionId
{
get { return actionId; }
}
/// <summary>
/// Получает строку помощи по данному действию.
/// </summary>
public string ActionDescription
{
get { return actionDescription; }
}
/// <summary>
/// Получает имя действия. Это значение будет отображено в меню.
/// </summary>
public string ActionName
{
get { return actionName; }
}
/// <summary>
/// Получает признак того, что экземпляр действия плагина инициализирован.
/// </summary>
public bool IsInitialized
{
get { return isInited; }
}
/// <summary>
/// Инициализирует экземпляр действия плагина.
/// </summary>
/// <param name="actionId">Идентификатор действия.</param>
/// <param name="actionName">Имя действия. Это значение будет отображено в меню.</param>
/// <param name="actionDescription">Описание действия.</param>
public void Init(int actionId, string actionName, string actionDescription)
{
this.actionId = actionId;
this.actionName = actionName;
this.actionDescription = actionDescription;
isInited = true;
}
#endregion
}
}
Теперь таким же образом добавьте в проект файл для класса PluginConnectionInfo.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
namespace ZigzagServer
{
/// <summary>
/// Информация о плагине
/// </summary>
[Guid("9665CD7C-2CF6-4D73-B6C9-1EC865BE5935")]
public class PluginConnectionInfo : IPluginConnectionInfo
{
string pluginName, pluginDescription;
IPluginAction[] actions = new IPluginAction[0];
bool isInited = false;
#region Члены IPluginConnectionInfo
/// <summary>
/// Получает имя плагина.
/// </summary>
public string PluginName
{
get { return pluginName; }
}
/// <summary>
/// Получает описание плагина.
/// </summary>
public string PluginDescription
{
get { return pluginDescription; }
}
/// <summary>
/// Получает массив возможных в плагине действий.
/// </summary>
public IPluginAction[] AvailableActions
{
get { return actions; }
}
/// <summary>
/// Инициализирует экземпляр информации подключения плагина.
/// </summary>
/// <param name="pluginName">Имя плагина.</param>
/// <param name="pluginDescription">Описание плагина.</param>
/// <param name="pluginActions">Список возможных действий, которые предоставляет плагин.</param>
public void Init(string pluginName, string pluginDescription, IPluginAction[] pluginActions)
{
try
{
this.pluginName = pluginName;
this.pluginDescription = pluginDescription;
actions = (IPluginAction[])pluginActions.Clone();
}
catch { }
isInited = true;
}
/// <summary>
/// Получает признак того, что экземпляр информации подключения плагина инициализирован.
/// </summary>
public bool IsInitialized
{
get { return isInited; }
}
#endregion
/// <summary>
/// Инициализирует экземпляр информации подключения плагина.
/// </summary>
/// <param name="pluginName">Имя плагина.</param>
/// <param name="pluginDescription">Описание плагина.</param>
/// <param name="pluginActions">Список возможных действий, которые предоставляет плагин. Сюда следует передавать массив IPluginAction-ов</param>
public PluginConnectionInfo(string pluginName, string pluginDescription, object[] pluginActions)
{
try
{
Init(pluginName, pluginDescription, pluginActions.Cast<IPluginAction>().ToArray());
}
catch { }
isInited = true;
}
public PluginConnectionInfo()
{
}
}
}
Так, эти два класса есть. В принципе, мы могли бы их и не создавать, но тогда каждый плагин сам должен был бы объявлять свои классы, реализующие интерфейсы IPluginAction и IPluginConnectionInfo соответственно. Теперь создателям плагинов под наше приложение-хост не обязательно делать это, они могут использовать наши готовые классы, хотя могут и не использовать, это зависит от их воли. Им осталось только реализовать IPlugin.