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

Код класса Plugin.Cs

На случай, если я что-то забыл, привожу здесь получившийся код файла целиком.

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using System.Runtime.InteropServices;

using System.Reflection;

namespace ZigzagServer

{

#region Исключения, связанные с плагинами

class CannotLoadDllAsPluginException : Exception

{

string dllPath;

public CannotLoadDllAsPluginException(string dllPath)

{ this.dllPath = dllPath; }

public string DllPath

{ get { return dllPath; } }

public override string Message

{ get { return "Невозможно загрузить dll как плагин сервера СПО: " + dllPath; } }

}

class InternalPluginErrorException : Exception

{

int errorCode;

public InternalPluginErrorException(int errorCode)

{ this.errorCode = errorCode; }

public int ErrorCode

{ get { return errorCode; } }

}

class PluginNotConnectedYetException : Exception

{

public PluginNotConnectedYetException()

{ }

}

#endregion

class Plugin : IDisposable

{

/// <summary> Путь к плагину. </summary>

string path;

#region Набор булевых признаков класса.

/// <summary> Показывает, написан ли плагин в управляемом коде, т.е. на семействе языков платформы .NET. </summary>

bool isManaged = false;

/// <summary> Показывает, содержит ли плагин COM-сервер. </summary>

bool isCOMServer = false;

/// <summary> Показывает, подключен ли плагин. </summary>

bool isConnected = false;

#endregion

#region Импорт из kernel32.dll для взаимодействия с неуправляемыми библиотеками.

/// <summary>

/// Загружает библиотеку с заданным путём в память приложения и возвращает её хэндл.

/// </summary>

/// <param name="dllToLoad">Путь к библиотеке.</param>

/// <returns>Хэндл загруженной библиотеки.</returns>

[DllImport("kernel32.dll")]

private static extern IntPtr LoadLibrary(string dllToLoad);

/// <summary>

/// Получает указатель процедуры библиотеки по имени процедуры и хэндлу библиотеки.

/// </summary>

/// <param name="hModule">Хэндл библиотеки.</param>

/// <param name="procedureName">Имя процедуры.</param>

/// <returns>Возвращает указатель на процедуру.</returns>

[DllImport("kernel32.dll")]

private static extern IntPtr GetProcAddress(IntPtr hModule, string procedureName);

/// <summary>

/// Закрывает библиотеку и выгружает её из памяти приложения.

/// </summary>

/// <param name="hModule">Хэндл библиотеки.</param>

/// <returns>Возвращает булевый признак успешного завершения операции</returns>

[DllImport("kernel32.dll")]

private static extern bool FreeLibrary(IntPtr hModule);

#endregion

#region Делегаты для привзяки к неуправляемым процедурам.

private delegate string GetCOMClassNameDelegate();

private delegate int ConnectDelegate(object iZigzagControl, ref object iPluginConnectionInfo);

private delegate int DisconnectDelegate();

private delegate int PerformActionDelegate(int actionId);

/// <summary>

/// Вызов процедуры получения имени COM-класса плагина, содержащейся в unmanaged-библиотеке.

/// </summary>

private GetCOMClassNameDelegate GetCOMClassNameUnmanaged;

/// <summary>

/// Вызов процудуры подключение к плагину, содержащейся в unmanaged-библиотеке.

/// </summary>

private ConnectDelegate ConnectUnmanaged;

/// <summary>

/// Вызов процудуры отключения от плагина, содержащейся в unmanaged-библиотеке.

/// </summary>

private DisconnectDelegate DisconnectUnmanaged;

/// <summary>

/// Вызов процудуры выполнения действия плагина с заданным идентификатором, содержащейся в unmanaged-библиотеке.

/// </summary>

private PerformActionDelegate PerformActionUnmanaged;

#endregion

#region Переменные для вызова методов из управляемых библиотек, не содержащих COM-сервера.

//Сделаны для поддержки Framework 3.5. В Framework 4 можно использовать тип dynamic и не обламываться.

private MethodInfo connectMethod, disconnectMethod, performActionMethod, getComClassNameMethod;

#endregion

#region Информация о плагине, полученная из его IPluginConnectionInfo

/// <summary> Информация о плагине, полученная при подключении. </summary>

IPluginConnectionInfo pluginConnectionInfo;

/// <summary> Имя плагина. </summary>

private string pluginName;

/// <summary> Описание плагина. </summary>

private string pluginDescription;

/// <summary> Список действий плагина. </summary>

private List<IPluginAction> actions = new List<IPluginAction>();

#endregion

/// <summary> Содержит COM-объект с плагином. Используется как для плагинов в управляемом коде, так и для плагинов в коде неуправляемом. </summary>

private IPlugin COMPlugin;

/// <summary> Хэндл библиотеки с неуправляемым плагином. </summary>

private IntPtr unmanagedPluginDllHandle;

/// <summary> Экземлпяр класса ZigzagControl. </summary>

private IZigzagControl iZigzagControl;

#region Поля класса

/// <summary> Получает имя плагина. </summary>

public string Name

{

get

{ return pluginName; }

}

/// <summary> Получает описание плагина. </summary>

public string Description

{

get

{ return pluginDescription; }

}

/// <summary> Получает список действий, которые плагин может выполнить. </summary>

public List<IPluginAction> ActionsList

{

get

{ return new List<IPluginAction>(actions.ToArray()); }

}

/// <summary> Получает состояние подключенности плагина. </summary>

public bool IsConnected

{

get

{ return isConnected; }

}

#endregion

#region Функционал класса

/// <summary>

/// Конструктор класса Plugin.

/// </summary>

/// <param name="dllPath">Путь к библиотеке, содержащей плагин.</param>

public Plugin(string dllPath)

{

// Сохраним путь и проверим наличие файла.

path = dllPath;

System.IO.FileInfo fi = new System.IO.FileInfo(dllPath);

if (!fi.Exists)

throw new System.IO.FileNotFoundException("dll-файл плагина не найден.", dllPath);

// Попытаемся найти в библиотеке сборку. Если библиотека написана в управляемом коде, то она содержит её и всё будет ок, иначе программа выбросит исключение.

try

{

// Грузим либу.

Assembly pluginAssembly = Assembly.LoadFile(path);

isManaged = true;

// Будем считать, что управляемая библиотека должна содержать тип ZigzagPlugin

// Если библиотека содержит внутренний COM-сервер, то этот тип содержит метод string GetCOMClassName(), который возвращает имя COM-класса при его вызове.

// В противном случае он содержит все те методы, которые объявлены в IPlugin.

Type spoServerPluginType = pluginAssembly.GetType("ZigzagPlugin"); //Получим тип через reflection.

getComClassNameMethod = spoServerPluginType.GetMethod("GetCOMClassName", System.Type.EmptyTypes); //Пошмонаем его на наличие нужных нам методов.

// В GetMethod передаётся имя искомой функции и массив типов её аргументов

if (getComClassNameMethod != null)

{

// Если метод, возвращающий имя COM-класса найден, стало быть либа содержит COM-сервер и остальные

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

isCOMServer = true;

return;

}

// В противном случае -- поищем остальные методы.

connectMethod = spoServerPluginType.GetMethod("Connect", new Type[] { typeof(object), typeof(object).MakeByRefType() });

disconnectMethod = spoServerPluginType.GetMethod("Disconnect", System.Type.EmptyTypes);

performActionMethod = spoServerPluginType.GetMethod("PerformAction", new Type[] { typeof(int) });

if ((connectMethod != null) && (disconnectMethod != null) &&

(performActionMethod != null))

return; // Все необходимые методы нашлись, работа конструктора завершена.

}

catch (Exception)

{ }

// Если выполнение дошло до сюда, значит наша гипотеза о том, что плагин написан в управляемом коде, была неверна.

isManaged = false;

try

{

//Здесь логика работы аналогичная, только уже не через reflection, а через interop.

//Грузим либу.

unmanagedPluginDllHandle = LoadLibrary(path);

//Ищем функции.

IntPtr getComClassNameMethodPtr = GetProcAddress(unmanagedPluginDllHandle, "GetCOMClassName");

if (getComClassNameMethodPtr != IntPtr.Zero)

{

// Если метод, возвращающий имя COM-класса найден, стало быть либа содержит COM-сервер и остальные

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

isCOMServer = true;

GetCOMClassNameUnmanaged = (GetCOMClassNameDelegate)Marshal.GetDelegateForFunctionPointer(getComClassNameMethodPtr, typeof(GetCOMClassNameDelegate));

return;

}

// Это не COM, ищем другие методы.

IntPtr connectMethodPtr = GetProcAddress(unmanagedPluginDllHandle, "Connect");

IntPtr disconnectMethodPtr = GetProcAddress(unmanagedPluginDllHandle, "Disconnect");

IntPtr performActionPtr = GetProcAddress(unmanagedPluginDllHandle, "PerformAction");

if ((connectMethodPtr != IntPtr.Zero) && (disconnectMethodPtr != IntPtr.Zero) && (performActionPtr != IntPtr.Zero))

{

#region Работа этой секции кода не тестировалась

ConnectUnmanaged = (ConnectDelegate)Marshal.GetDelegateForFunctionPointer(connectMethodPtr, typeof(ConnectDelegate));

DisconnectUnmanaged = (DisconnectDelegate)Marshal.GetDelegateForFunctionPointer(disconnectMethodPtr, typeof(DisconnectDelegate));

PerformActionUnmanaged = (PerformActionDelegate)Marshal.GetDelegateForFunctionPointer(performActionPtr, typeof(PerformActionDelegate));

return; // Все необходимые методы нашлись, работа конструктора завершена.

#endregion

}

}

catch (Exception)

{ }

throw new CannotLoadDllAsPluginException(path); // Плагин какой-то палёный, выбрасываем исключение.

}

/// <summary>

/// Заполняет поля с информацией о плагине значениями из IPluginConnectionInfo

/// </summary>

/// <param name="info"></param>

private void FillInfo(IPluginConnectionInfo info)

{

pluginName = info.PluginName;

pluginDescription = info.PluginDescription;

IPluginAction[] pluginActions = info.AvailableActions;

actions = new List<IPluginAction>(pluginActions);

}

/// <summary>

/// Подключает плагин к приложению.

/// </summary>

/// <param name="iZigzagControl">Экземпляр класса ZigzagControl.</param>

/// <returns>Возвращает 0, если плагин подключился успешно, иначе возвращает код ошибки.</returns>

public int Connect(IZigzagControl iZigzagControl)

{

this.iZigzagControl = iZigzagControl;

if (!isConnected)

{

int result = 0;

object connectionInfo = null;

pluginConnectionInfo = null;

if (isCOMServer)

{

// Преимуществом технологии COM является то, что можно единообразно обращаться как с managed, так и c unmanaged-кодом.

// Различия будут лишь в способе вызова метода получения имени COM-класса

string typeName;

// Сначала получим имя класса.

if (isManaged)

typeName = (string)getComClassNameMethod.Invoke(null, null);

else

typeName = GetCOMClassNameUnmanaged();

if ((typeName == null) || (typeName == ""))

return -404;

Type type = Type.GetTypeFromProgID(typeName); // Получим экземпляр класса по его имени.

object plugin = Activator.CreateInstance(type); // Создадим его. Будет использован конструктор по умолчанию.

COMPlugin = plugin as IPlugin; // Прикастим к нашему интерфейсу.

result = COMPlugin.Connect(iZigzagControl, ref connectionInfo); // Подключим.

pluginConnectionInfo = (IPluginConnectionInfo)connectionInfo; // Получим информацию о нём.

if ((pluginConnectionInfo == null) || (result != 0))

throw new InternalPluginErrorException(result);

FillInfo(pluginConnectionInfo); // Заполним поля нашего класса полученной информации.

isConnected = true;

return result; // Готово.

}

if (isManaged)

{

object[] args = new object[] { iZigzagControl, connectionInfo }; // Создаем массив с аргументами.

result = (int)connectMethod.Invoke(null, args); // Вызываем исполнение метода.

pluginConnectionInfo = (IPluginConnectionInfo)args[1]; // Если метод выполнился без ошибок, то во втором аргументе будет инфа о плагине.

if ((pluginConnectionInfo == null) || (result != 0))

throw new InternalPluginErrorException(result);

FillInfo(pluginConnectionInfo); //Заполняем поля класса

isConnected = true;

return result;

}

else

{

#region Работа этой секции кода не тестировалась

result = ConnectUnmanaged(iZigzagControl, ref connectionInfo);

pluginConnectionInfo = (IPluginConnectionInfo)connectionInfo;

if ((pluginConnectionInfo == null) || (result != 0))

throw new InternalPluginErrorException(result);

FillInfo(pluginConnectionInfo);

isConnected = true;

return result;

#endregion

}

}

else

return 0;

}

/// <summary>

/// Отключает плагин от приложения.

/// </summary>

/// <returns>Возвращает 0, если плагин подключился успешно, иначе возвращает код ошибки.</returns>

public int Disconnect()

{

try

{

if (isConnected)

{

int result = 0;

if (isCOMServer)

{

result = COMPlugin.Disconnect();

if (!isManaged)

FreeLibrary(unmanagedPluginDllHandle);

if (result != 0)

throw new InternalPluginErrorException(result);

isConnected = false;

//((IDisposable)iZigzagControl).Dispose();

return result;

}

if (isManaged)

{

result = (int)disconnectMethod.Invoke(null, null);

if (result != 0)

throw new InternalPluginErrorException(result);

isConnected = false;

//((IDisposable)iZigzagControl).Dispose();

return result;

}

else

{

#region Работа этой секции кода не тестировалась

result = DisconnectUnmanaged();

if (result != 0)

throw new InternalPluginErrorException(result);

FreeLibrary(unmanagedPluginDllHandle);

isConnected = false;

//((IDisposable)iZigzagControl).Dispose();

return result;

#endregion

}

}

else

return 0;

}

catch (Exception)

{ return 1; }

}

/// <summary>

/// Выполняет действие, доступное из плагина.

/// </summary>

/// <param name="actionId">Идентификатор действия.</param>

/// <returns>Возвращает 0, если плагин подключился успешно, иначе возвращает код ошибки.</returns>

public int PerformAction(int actionId)

{

if (isConnected)

{

int result = 0;

if (isCOMServer)

{

// В случае, если плагин имеет COM-сервер, просто запрашиваем выполнение действия и контроллируем результат.

result = COMPlugin.PerformAction(actionId);

if (result != 0)

throw new InternalPluginErrorException(result);

return result;

}

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

if (isManaged)

{

try

{

result = (int)performActionMethod.Invoke(null, new object[] { actionId });

if (result != 0)

throw new InternalPluginErrorException(result);

}

catch (TargetInvocationException)

{ result = -100; }

return result;

}

else

{

#region Работа этой секции кода не тестировалась

result = PerformActionUnmanaged(actionId);

if (result != 0)

throw new InternalPluginErrorException(result);

return result;

#endregion

}

}

else

throw new PluginNotConnectedYetException();

}

#endregion

#region Члены IDisposable

/// <summary>

/// Деструктор класса, который будет автоматически освобождать объект после того, как не осталось ни одной ссылки на него.

/// </summary>

public void Dispose()

{

if (isConnected)

Disconnect();

}

#endregion

}

}

Смотрите под лупой либо копируйте куда-то.

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