private void OnPluginStopped(PluginInstance pluginInstance, bool crashed = false) { if (IsDisposed || pluginInstance.Status == PluginStatus.Stopped) { return; } if (crashed) { // Notify user about crash LogTo.Information($"{pluginInstance.Denomination.CapitalizeFirst()} {pluginInstance.Metadata.PackageName} has crashed"); } else { LogTo.Information($"{pluginInstance.Denomination.CapitalizeFirst()} {pluginInstance.Metadata.PackageName} has stopped."); } foreach (var interfaceType in pluginInstance.InterfaceChannelMap.Keys) { UnregisterChannelType(interfaceType, pluginInstance.Guid); } _runningPluginMap.TryRemove(pluginInstance.Guid, out _); pluginInstance.OnStopped(); }
private void OnPluginStarting(PluginInstance pluginInstance) { LogTo.Information( $"Starting {pluginInstance.Denomination} {pluginInstance.Package.Id}."); _runningPluginMap[pluginInstance.OnStarting()] = pluginInstance; }
private void OnPluginStopping(PluginInstance pluginInstance) { LogTo.Information( $"Stopping {pluginInstance.Denomination} {pluginInstance.Package.Id}."); pluginInstance.OnStopping(); }
private void OnPluginStopped(PluginInstance pluginInstance) { if (IsDisposed || pluginInstance.Status == PluginStatus.Stopped) { return; } bool crashed = false; try { if (pluginInstance.Process?.HasExited ?? false) { crashed = pluginInstance.Process.ExitCode != 0; } } catch { /* ignored */ } LogTo.Information($"{pluginInstance.Denomination.CapitalizeFirst()} {pluginInstance.Metadata.PackageName} " + $"has {(crashed ? "crashed" : "stopped")}"); foreach (var interfaceType in pluginInstance.InterfaceChannelMap.Keys) { UnregisterChannelType(interfaceType, pluginInstance.Guid, false); } _runningPluginMap.TryRemove(pluginInstance.Guid, out _); pluginInstance.OnStopped(); }
private void OnPluginConnected(PluginInstance pluginInstance, ISMAPlugin plugin) { LogTo.Information($"Connected {pluginInstance.Denomination} {pluginInstance.Package.Id}."); pluginInstance.OnConnected(plugin); }
public async Task StopPlugin(PluginInstance pluginInstance) { try { using (await pluginInstance.Lock.LockAsync()) { if (pluginInstance.Status == PluginStatus.Stopping) { return; } if (pluginInstance.Status == PluginStatus.Stopped) { return; } OnPluginStopping(pluginInstance); try { pluginInstance.Plugin.Dispose(); } catch (Exception ex) { LogTo.Error( ex, $"An exception occured while gracefully stopping {pluginInstance.Denomination} {pluginInstance.Metadata.PackageName}."); } } try { if (await Task.Run(() => pluginInstance.Process.WaitForExit(PluginStopTimeout))) { return; } pluginInstance.Process.Refresh(); if (pluginInstance.Process.HasExited) { return; } LogTo.Warning( $"{pluginInstance.Denomination.CapitalizeFirst()} {pluginInstance.Metadata.PackageName} didn't shut down gracefully after {PluginStopTimeout}ms. Attempting to kill process"); pluginInstance.Process.Kill(); } catch (Exception ex) { LogTo.Error(ex, $"An exception occured while killing {pluginInstance.Denomination} {pluginInstance.Metadata.PackageName}"); } } finally { OnPluginStopped(pluginInstance); } }
/// <inheritdoc /> protected override void OnPluginStartFailed( PluginInstance pluginInstance, PluginStartFailure reason, string errMsg) { base.OnPluginStartFailed(pluginInstance, reason, errMsg); errMsg.ShowDesktopNotification(); }
/// <inheritdoc /> protected override void OnPluginCrashed(PluginInstance pluginInstance) { ToastContent toastContent = new ToastContent { Visual = new ToastVisual { BindingGeneric = new ToastBindingGeneric { Children = { new AdaptiveText { Text = $"{pluginInstance.ToString().CapitalizeFirst()} has crashed." } } } }, Actions = new ToastActionsCustom { Buttons = { // Restart action new ToastButton("Restart", new QueryString { { "action", ToastActionRestartAfterCrash}, { ToastActionParameterPluginId, pluginInstance.Package.Id } }.ToString()) { ActivationType = ToastActivationType.Background }, // Open logs folder action new ToastButton("Open the logs folder",SMAFileSystem.LogDir.FullPathWin) { ActivationType = ToastActivationType.Protocol } } } }; var doc = new XmlDocument(); doc.LoadXml(toastContent.GetContent()); // And create the toast notification var toast = new ToastNotification(doc); // And then show it DesktopNotificationManager.CreateToastNotifier().Show(toast); }
public bool CanPluginStartOrPause(PluginInstance pluginInstance) { if (pluginInstance.Metadata.Enabled == false) { return(false); } switch (pluginInstance.Status) { case PluginStatus.Connected: return(true); case PluginStatus.Stopped: return(_runningPluginMap.Values.Any(p => p.Package.Id == pluginInstance.Package.Id) == false); default: return(false); } }
public async Task UninstallPlugin(PluginInstance pluginInstance) { if (pluginInstance.Metadata.IsDevelopment) { throw new ArgumentException($"Cannot uninstall a development plugin"); } await StopPlugin(pluginInstance); using (await pluginInstance.Lock.LockAsync()) { var pm = PackageManager; var success = await pm.UninstallPluginAsync(pluginInstance.Package.Id); if (success) { _allPlugins.Remove(pluginInstance); } } }
/// <inheritdoc /> protected override void OnPluginCrashed(PluginInstance pluginInstance) { $"{pluginInstance.ToString().CapitalizeFirst()} has crashed.".ShowDesktopNotification( // Restart action new ToastButton("Restart", new QueryString { { "action", ToastActionRestartAfterCrash }, { ToastActionParameterPluginId, pluginInstance.Package.Id } }.ToString()) { ActivationType = ToastActivationType.Background }, // Open logs folder action new ToastButton("Open the logs folder", SMAFileSystem.LogDir.FullPathWin) { ActivationType = ToastActivationType.Protocol } ); }
public async Task StartPlugin(PluginInstance pluginInstance) { var pluginPackage = pluginInstance.Package; var packageName = pluginPackage.Id; try { using (await pluginInstance.Lock.LockAsync()) { if (pluginInstance.Status != PluginStatus.Stopped) { return; } if (CanPluginStartOrPause(pluginInstance) == false) { throw new InvalidOperationException("A plugin with the same Package name is already running"); } OnPluginStarting(pluginInstance); } var cmdLineParams = new PluginHostParameters { PackageName = packageName, HomePath = pluginPackage.GetHomeDir().FullPath, SessionString = pluginInstance.Guid.ToString(), SMAChannelName = IpcServerChannelName, SMAProcessId = System.Diagnostics.Process.GetCurrentProcess().Id, IsDeveloment = pluginInstance.Metadata.IsDevelopment, }; var processArgs = Parser.Default.FormatCommandLine(cmdLineParams); var pluginStartInfo = new ProcessStartInfo( SMAFileSystem.PluginHostExeFile.FullPath, processArgs) { UseShellExecute = false, }; pluginInstance.Process = System.Diagnostics.Process.Start(pluginStartInfo); if (pluginInstance.Process == null) { LogTo.Warning($"Failed to start process for {pluginInstance.Denomination} {packageName}"); return; } pluginInstance.Process.EnableRaisingEvents = true; pluginInstance.Process.Exited += async(o, e) => { using (await pluginInstance.Lock.LockAsync()) OnPluginStopped(pluginInstance); }; if (await pluginInstance.ConnectedEvent.WaitAsync(PluginConnectTimeout)) { return; } if (pluginInstance.Status == PluginStatus.Stopped) { LogTo.Error($"{pluginInstance.Denomination.CapitalizeFirst()} {packageName} stopped unexpectedly."); pluginInstance.ConnectedEvent.Set(); return; } } catch (Exception ex) { LogTo.Error(ex, $"An error occured while starting {pluginInstance.Denomination} {packageName}"); return; } try { LogTo.Warning( $"{pluginInstance.Denomination.CapitalizeFirst()} {packageName} failed to connect under {PluginConnectTimeout}ms. Attempting to kill process"); pluginInstance.Process.Refresh(); if (pluginInstance.Process.HasExited) { LogTo.Warning($"{pluginInstance.Denomination.CapitalizeFirst()} {packageName} has already exited"); return; } pluginInstance.Process.Kill(); } catch (RemotingException ex) { LogTo.Warning(ex, $"StartPlugin '{pluginInstance.Denomination}' {packageName} failed."); } catch (Exception ex) { LogTo.Error(ex, $"An error occured while starting {pluginInstance.Denomination} {packageName}"); } finally { using (await pluginInstance.Lock.LockAsync()) OnPluginStopped(pluginInstance); } }
public async Task StopPlugin(PluginInstance pluginInstance) { try { using (await pluginInstance.Lock.LockAsync()) { switch (pluginInstance.Status) { case PluginStatus.Stopping: case PluginStatus.Stopped: return; default: OnPluginStopping(pluginInstance); try { pluginInstance.Plugin.Dispose(); } catch (RemotingException ex) { LogTo.Warning(ex, $"Failed to gracefully stop {pluginInstance.Denomination} '{pluginInstance.Metadata.PackageName}' failed."); } catch (Exception ex) { LogTo.Error( ex, $"An exception occured while gracefully stopping {pluginInstance.Denomination} {pluginInstance.Metadata.PackageName}."); } break; } } try { if (pluginInstance.Process is null) { LogTo.Warning($"pluginInstance.Process is null. Unable to kill {pluginInstance.Denomination} {pluginInstance.Metadata.PackageName}."); return; } if (await Task.Run(() => pluginInstance.Process.WaitForExit(PluginStopTimeout))) { return; } pluginInstance.Process.Refresh(); if (pluginInstance.Process.HasExited) { return; } LogTo.Warning( $"{pluginInstance.Denomination.CapitalizeFirst()} {pluginInstance.Metadata.PackageName} didn't shut down gracefully after {PluginStopTimeout}ms. Attempting to kill process"); pluginInstance.Process.Kill(); } catch (Exception ex) { LogTo.Error(ex, $"An exception occured while killing {pluginInstance.Denomination} {pluginInstance.Metadata.PackageName}"); } } catch (RemotingException ex) { LogTo.Warning(ex, $"StopPlugin {pluginInstance?.Denomination} '{pluginInstance?.Metadata?.PackageName}' failed."); } finally { using (await pluginInstance.Lock.LockAsync()) OnPluginStopped(pluginInstance); } }
/// <inheritdoc /> public override string GetPluginHostTypeQualifiedName(PluginInstance pluginInstance) { return("SuperMemoAssistant.Interop.Plugins.PluginHost"); }
/// <inheritdoc /> public override NuGetVersion GetPluginHostTypeAssemblyMinimumVersion(PluginInstance pluginInstance) { return(NuGetVersion.Parse(MinInteropVersion)); // NuGetVersion.Parse(typeof(SMAConst).GetAssemblyVersion()); }
/// <inheritdoc /> public override string GetPluginHostTypeAssemblyName(PluginInstance pluginInstance) { return("SuperMemoAssistant.Interop"); }