private void HotKeyChanged(
            HotKeyData hkData,
            HotKey actualBefore,
            HotKey actualAfter)
        {
            if (actualBefore != null)
            {
                _hotKeyDataMap.TryRemove(actualBefore, out _);
                _userHotKeyMap.Reverse.Remove(actualBefore);

                if (hkData.IsGlobal)
                {
                    _kbHookSvc.UnregisterHotKey(actualBefore);
                }
            }

            if (actualAfter != null)
            {
                if (_hotKeyDataMap.ContainsKey(actualAfter))
                {
                    throw new ArgumentException("Hotkey already used");
                }

                // Remap hotkey
                _hotKeyDataMap[actualAfter] = hkData;

                // Remove ghost user hotkey if necessary, update hotkey
                _userHotKeyMap.Reverse.Remove(actualAfter);
                _userHotKeyMap[hkData.Id] = actualAfter;

                if (hkData.IsGlobal)
                {
                    _kbHookSvc.RegisterHotKey(actualAfter, hkData.Callback);
                }
            }

            LogTo.Debug($"Hotkey {hkData.Id} is now bound to {actualAfter}.");

            _delayedTask.Trigger(1000);
        }
Ejemplo n.º 2
0
 private void OnXamlChanged(XamlLayout xamlLayout, string before, string after)
 {
     _parseXamlTask.Trigger(500);
 }
 private void PDFWindow_SizeChanged(object sender,
                                    SizeChangedEventArgs e)
 {
     _saveConfigDelayed.Trigger(ResizeSaveDelay);
 }
        /// <summary>
        /// Start plugin <paramref name="pluginInstance"/>
        /// </summary>
        /// <param name="pluginInstance">The plugin to start</param>
        /// <param name="attachDebugger">Whether to attach the debugger to the plugin host on start</param>
        /// <returns>Success of operation</returns>
        public async Task <bool> StartPlugin(TPluginInstance pluginInstance, bool attachDebugger = false)
        {
            var pluginPackage = pluginInstance.Package;
            var packageName   = pluginPackage.Id;

            try
            {
                // Make sure the plugin is stopped and can be started
                using (await pluginInstance.Lock.LockAsync())
                {
                    if (pluginInstance.Status != PluginStatus.Stopped)
                    {
                        return(true);
                    }

                    if (CanPluginStartOrPause(pluginInstance) == false)
                    {
                        throw new InvalidOperationException("A plugin with the same Package name is already running");
                    }

                    OnPluginStarting(pluginInstance);
                }

                // Determine the plugin and its dependencies assemblies' information. This includes the assembly which contains the PluginHost type
                var packageRootFolder = Locations.PluginPackageDir.FullPathWin;
                var pluginAndDependenciesAssembliesPath = new List <string>();
                var pluginHostTypeAssemblyName          = GetPluginHostTypeAssemblyName(pluginInstance);
                var pluginHostTypeMinAssemblyVersion    = GetPluginHostTypeAssemblyMinimumVersion(pluginInstance);

                if (pluginInstance.IsDevelopment == false)
                {
                    using (await PMLock.ReaderLockAsync())
                    {
                        var pluginAndDependenciesPackageFilePaths = new List <FilePath>();
                        var pluginPkg = PackageManager.FindInstalledPluginById(packageName);

                        if (pluginPkg == null)
                        {
                            throw new InvalidOperationException($"Cannot find requested plugin package {packageName}");
                        }

                        PackageManager.GetInstalledPluginAssembliesFilePath(
                            pluginPkg.Identity,
                            out var tmpPluginAssemblies,
                            out var tmpDependenciesAssemblies);

                        pluginAndDependenciesPackageFilePaths.AddRange(tmpPluginAssemblies);
                        pluginAndDependenciesPackageFilePaths.AddRange(tmpDependenciesAssemblies);

                        var pluginHostTypeAssemblyPath =
                            pluginAndDependenciesPackageFilePaths.FirstOrDefault(a => a.FileNameWithoutExtension == pluginHostTypeAssemblyName);

                        if (pluginHostTypeAssemblyPath == null)
                        {
                            OnPluginStartFailed(
                                pluginInstance,
                                PluginStartFailure.InteropAssemblyNotFound,
                                $"{pluginInstance} failed to start: Unable to find the PluginHost type's \"{pluginHostTypeAssemblyName}\" dependency assembly's package");

                            return(false);
                        }

                        // Make sure the assembly version is equal or higher to the required minimum version
                        if (pluginHostTypeMinAssemblyVersion != null)
                        {
                            var pluginHostTypeAssemblyInfo = FileVersionInfo.GetVersionInfo(pluginHostTypeAssemblyPath.FullPath);

                            if (NuGetVersion.TryParse(pluginHostTypeAssemblyInfo.ProductVersion, out var pluginHostTypeAssemblyVersion) == false)
                            {
                                OnPluginStartFailed(
                                    pluginInstance,
                                    PluginStartFailure.InteropAssemblyNotFound,
                                    $"{pluginInstance} failed to start: Invalid interop version '{pluginHostTypeAssemblyInfo.ProductVersion}'");

                                return(false);
                            }

                            if (pluginHostTypeAssemblyVersion < pluginHostTypeMinAssemblyVersion)
                            {
                                OnPluginStartFailed(
                                    pluginInstance,
                                    PluginStartFailure.InteropAssemblyNotFound,
                                    $"{pluginInstance} failed to start: Outdated interop version '{pluginHostTypeAssemblyInfo.ProductVersion}'. Either update the plugin, downgrade SMA, or ask the plugin developer to publish a new version to fix the issue.");

                                return(false);
                            }
                        }

                        foreach (var pkgFilePath in pluginAndDependenciesPackageFilePaths)
                        {
                            string pkgRelativeFilePath = pkgFilePath.FullPathWin.After(packageRootFolder);

                            if (string.IsNullOrWhiteSpace(pkgRelativeFilePath))
                            {
                                LogTo.Warning(
                                    $"Package {pkgFilePath} isn't located underneath the package folder {packageRootFolder}. Skipping, this might cause issues with the plugin");
                                continue;
                            }

                            pluginAndDependenciesAssembliesPath.Add(pkgRelativeFilePath);
                        }
                    }
                }

                // Build command line
                var cmdLineParams = new PluginHostParameters
                {
                    PackageRootFolder = packageRootFolder,
                    PluginAndDependenciesAssembliesPath = string.Join(";", pluginAndDependenciesAssembliesPath),
                    PluginHostTypeAssemblyName          = pluginHostTypeAssemblyName,
                    PluginHostTypeQualifiedName         = GetPluginHostTypeQualifiedName(pluginInstance),
                    PackageName      = packageName,
                    HomePath         = pluginPackage.HomeDir.FullPath,
                    SessionString    = pluginInstance.Guid.ToString(),
                    ChannelName      = IpcServerChannelName,
                    ManagerProcessId = Process.GetCurrentProcess().Id,
                    IsDevelopment    = pluginInstance.IsDevelopment,
                    AttachDebugger   = attachDebugger
                };

                // Build process parameters
                var processArgs = Parser.Default.FormatCommandLine(cmdLineParams);

                pluginInstance.Process = new Process
                {
                    StartInfo = new ProcessStartInfo(
                        Locations.PluginHostExeFile.FullPath,
                        processArgs)
                    {
                        UseShellExecute        = false,
                        RedirectStandardOutput = true,
                        RedirectStandardError  = true,
                    },
                    EnableRaisingEvents = true,
                };

                // Setup error output logging
                {
                    var           pluginStr        = pluginInstance.ToString(); // Avoids keeping a pointer to PluginInstance around
                    StringBuilder pluginErrBuilder = new StringBuilder();

                    void LogPluginErrorOutput()
                    {
                        if (string.IsNullOrWhiteSpace(pluginErrBuilder.ToString()))
                        {
                            return;
                        }

                        lock (pluginErrBuilder)
                        {
                            LogTo.Warning($"{pluginStr} standard error output:\n--------------------------------------------\n{pluginErrBuilder.ToString().Trim()}\n--------------------------------------------");
                            pluginErrBuilder.Clear();
                        }
                    }

                    DelayedTask logTask = new DelayedTask(LogPluginErrorOutput, 200);

                    void AggregatePluginErrorOutput(object _, DataReceivedEventArgs e)
                    {
                        lock (pluginErrBuilder)
                        {
                            pluginErrBuilder.AppendLine(e.Data);
                            logTask.Trigger(750);
                        }
                    }

                    pluginInstance.Process.ErrorDataReceived += AggregatePluginErrorOutput;
                }

                // Start plugin
                if (pluginInstance.Process.Start() == false)
                {
                    OnPluginStartFailed(
                        pluginInstance,
                        PluginStartFailure.ProcessDidNotStart,
                        $"{pluginInstance} failed to start: Failed to start process");

                    return(false);
                }

                OnPluginStarted(pluginInstance);

                pluginInstance.Process.EnableRaisingEvents = true;
                pluginInstance.Process.BeginErrorReadLine();
                pluginInstance.Process.Exited += (o, e) =>
                {
                    UISynchronizationContext.Post(_ =>
                    {
                        using (pluginInstance.Lock.Lock())
                            OnPluginStopped(pluginInstance);
                    }, null);
                };

                var connected = await pluginInstance.ConnectedEvent.WaitAsync(PluginConnectTimeout);

                if (connected && pluginInstance.Status == PluginStatus.Connected)
                {
                    return(true);
                }

                if (pluginInstance.Status == PluginStatus.Stopped)
                {
                    OnPluginStartFailed(
                        pluginInstance,
                        connected ? PluginStartFailure.ProcessDidNotConnect : PluginStartFailure.Unknown,
                        $"{pluginInstance} failed to start: process stopped unexpectedly.");

                    pluginInstance.ConnectedEvent.Set();

                    return(false);
                }
            }
            catch (Exception ex)
            {
                LogTo.Error(ex, $"{pluginInstance} failed to start: An unknown exception occured during startup");
                return(false);
            }

            try
            {
                LogTo.Warning(
                    $"{pluginInstance.ToString().CapitalizeFirst()} failed to connect under {PluginConnectTimeout}ms. Attempting to kill process");

                pluginInstance.Process.Refresh();

                if (pluginInstance.Process.HasExited)
                {
                    LogTo.Warning($"{pluginInstance.ToString().CapitalizeFirst()} has already exited");
                    return(false);
                }

                pluginInstance.Process.Kill();
            }
            catch (RemotingException ex)
            {
                LogTo.Warning(ex, $"StartPlugin '{pluginInstance} failed.");
            }
            catch (Exception ex)
            {
                LogTo.Error(ex, $"An error occured while starting {pluginInstance}");
            }
            finally
            {
                try
                {
                    using (await pluginInstance.Lock.LockAsync())
                        OnPluginStopped(pluginInstance);
                }
                catch (Exception ex)
                {
                    LogTo.Error(ex, "Exception thrown while calling OnPluginStopped");
                }
            }

            return(false);
        }