/src/PluginPlatform.Shell/Services/PluginDispatcher.cs
C# | 295 lines | 216 code | 52 blank | 27 comment | 33 complexity | ff7b2245a84133bd2a8568d09004eeb5 MD5 | raw file
- using System;
- using System.Diagnostics;
- using System.Linq;
- using System.Runtime.InteropServices;
- using System.Threading;
- using System.Threading.Tasks;
- using PluginPlatform.Core.Util;
- using PluginPlatform.Core.WinFunctions;
- using PluginPlatform.Shell.Model;
- namespace PluginPlatform.Shell.Services
- {
- /// <summary>
- /// Диспетчер плагинов, контролирующий их работу. Позволяет отслеживать "зависшие" плагины, приостанавливать/перезапускать их.
- /// </summary>
- class PluginDispatcher
- {
- #region Variables
- private ThreadSafeCollection<PluginProcess> _listPluginProcesses;
- Timer _timer;
- private static object locker = new object();
- #endregion
- #region Конструктор Singleton PluginDispatcher
- private static volatile PluginDispatcher _instance;
- private static object _syncRoot = new Object();
- private PluginDispatcher()
- {
- _listPluginProcesses = new ThreadSafeCollection<PluginProcess>();
- _timer = new Timer(new TimerCallback(TrackPlugins), null, 0, 5000);
- }
- /// <summary>
- /// Экземпляр <see cref="PluginDispatcher"/>.
- /// </summary>
- public static PluginDispatcher Instance
- {
- get
- {
- if (_instance == null)
- {
- lock (_syncRoot)
- {
- if (_instance == null)
- _instance = new PluginDispatcher();
- }
- }
- return _instance;
- }
- }
- internal ThreadSafeCollection<PluginProcess> ListPluginProcesses
- {
- get
- {
- return _listPluginProcesses;
- }
- set
- {
- _listPluginProcesses = value;
- }
- }
- #endregion
- #region Public Methods
- public void RegisterPlugin(PluginDescription pluginDesc, Plugin plugin, int processID)
- {
- if (pluginDesc == null)
- throw new ArgumentNullException("pluginDesc");
- if (plugin == null)
- throw new ArgumentNullException("plugin");
- PluginProcess process = new PluginProcess(pluginDesc, plugin, processID);
- _listPluginProcesses.Add(process);
- }
- public void ExitPluginProcess(Guid pluginID)
- {
- PluginProcess pluginProc = _listPluginProcesses.FirstOrDefault(item => item.UID == pluginID);
- if (pluginProc == null)
- return;
- pluginProc.IsTracking = false;
- Process process = Process.GetProcessById(pluginProc.ProcessID);
- process.Kill();
- _listPluginProcesses.Remove(pluginProc);
- pluginProc = null;
- }
- #endregion
- #region Private Methods
- private void TrackPlugins(object state)
- {
- lock (_listPluginProcesses)
- {
- Parallel.ForEach(_listPluginProcesses, item =>
- {
- if (item.IsTracking)
- {
- try
- {
- Process process = Process.GetProcessById(item.ProcessID);
- bool checkResult = IsResponding(process, item.PluginViewHandle);
- PluginProcessStatuses status = checkResult ? PluginProcessStatuses.Stable : PluginProcessStatuses.NotResponding;
- if (checkResult != item.IsResponding)
- {
- OnPluginProcessStatusChanged(new PluginRespondingEventArgs(item.UID, status));
- item.IsResponding = checkResult;
- }
- }
- catch (ArgumentException)
- {
- item.IsTracking = false;
- _listPluginProcesses.Remove(item);
- OnPluginProcessStatusChanged(new PluginRespondingEventArgs(item.UID, PluginProcessStatuses.Exited));
- }
- }
- });
- }
- }
- private void TrackPlugin(Guid pluginID, bool track)
- {
- PluginProcess pluginProc = _listPluginProcesses.FirstOrDefault(item => item.UID == pluginID);
- pluginProc.IsTracking = track;
- if (pluginProc.IsTracking)
- {
- try
- {
- Process process = Process.GetProcessById(pluginProc.ProcessID);
- bool checkResponding = IsResponding(process, pluginProc.PluginViewHandle);
- PluginProcessStatuses status = checkResponding ? PluginProcessStatuses.Stable : PluginProcessStatuses.NotResponding;
- if (checkResponding != pluginProc.IsResponding)
- OnPluginProcessStatusChanged(new PluginRespondingEventArgs(pluginID, status));
- pluginProc.IsResponding = checkResponding;
- }
- catch (ArgumentException)
- {
- pluginProc.IsTracking = false;
- _listPluginProcesses.Remove(pluginProc);
- OnPluginProcessStatusChanged(new PluginRespondingEventArgs(pluginID, PluginProcessStatuses.Exited));
- }
- }
- }
- private bool IsResponding(Process process, IntPtr pluginViewHandle)
- {
- lock (locker)
- {
- if (pluginViewHandle == IntPtr.Zero)
- return true;
- HandleRef handleRef = new HandleRef(process, pluginViewHandle);
- int timeout = 5000;
- IntPtr lpdwResult;
- IntPtr lResult = WindowsFunctions.SendMessageTimeout(
- handleRef,
- 0,
- IntPtr.Zero,
- IntPtr.Zero,
- SendMessageTimeoutFlags.SMTO_ABORTIFHUNG,
- timeout,
- out lpdwResult);
- return lResult != IntPtr.Zero;
- }
- }
- private void SuspendProcess(Guid pluginID)
- {
- PluginProcess pluginProc = _listPluginProcesses.FirstOrDefault(item => item.UID == pluginID);
- Process proc = Process.GetProcessById(pluginProc.ProcessID);
- if (proc.ProcessName == string.Empty)
- return;
- foreach (ProcessThread pT in proc.Threads)
- {
- IntPtr pOpenThread = WindowsFunctions.OpenThread(ThreadAccess.SUSPEND_RESUME, false, (uint)pT.Id);
- if (pOpenThread == IntPtr.Zero)
- {
- break;
- }
- WindowsFunctions.SuspendThread(pOpenThread);
- }
- }
- private void ResumeProcess(Guid pluginID)
- {
- PluginProcess pluginProc = _listPluginProcesses.FirstOrDefault(item => item.UID == pluginID);
- Process proc = Process.GetProcessById(pluginProc.ProcessID);
- if (proc.ProcessName == string.Empty)
- return;
- foreach (ProcessThread pT in proc.Threads)
- {
- IntPtr pOpenThread = WindowsFunctions.OpenThread(ThreadAccess.SUSPEND_RESUME, false, (uint)pT.Id);
- if (pOpenThread == IntPtr.Zero)
- {
- break;
- }
- WindowsFunctions.ResumeThread(pOpenThread);
- }
- }
- #endregion
- #region Events
- /// <summary>
- /// Событие, сигнализирущее об отсутствии отклика интрефейса плагина.
- /// </summary>
- public event EventHandler<PluginRespondingEventArgs> PluginProcessStatusChanged;
- /// <summary>
- /// Вызывается при отсутствии отклика интрефейса плагина.
- /// </summary>
- /// <param _name="e">Аргументы для события.</param>
- private void OnPluginProcessStatusChanged(PluginRespondingEventArgs e)
- {
- EventHandler<PluginRespondingEventArgs> temp = Interlocked.CompareExchange(ref PluginProcessStatusChanged, null, null);
- if (temp != null)
- temp(this, e);
- }
- /// <summary>
- /// Предоставляет данные для события <see cref="PluginProcessStatusChanged"/>
- /// </summary>
- public class PluginRespondingEventArgs : EventArgs
- {
- private readonly Guid _pluginID;
- /// <summary>
- /// Guid идентификатор плагина.
- /// </summary>
- public Guid PluginID
- {
- get { return _pluginID; }
- }
- private readonly PluginProcessStatuses _pluginProcessStatus;
- /// <summary>
- /// Статус плагина.
- /// </summary>
- public PluginProcessStatuses IsResponding
- {
- get { return _pluginProcessStatus; }
- }
- /// <summary>
- /// Инициализирует новый экземпляр класса <see cref="PluginRespondingEventArgs"/>.
- /// </summary>
- /// <param _name="pluginID">Guid идентификатор плагина.</param>
- /// <param _name="checkResponding">Статус плагина.</param>
- public PluginRespondingEventArgs(Guid pluginID, PluginProcessStatuses isResponding)
- {
- _pluginID = pluginID;
- _pluginProcessStatus = isResponding;
- }
- }
- #endregion
- }
- public enum PluginProcessStatuses
- {
- Stable,
- NotResponding,
- Exited
- }
- }