/// <summary>
        /// Runs specified process with specified arguments.
        /// </summary>
        /// <param name="fileName">The process to start.</param>
        /// <param name="arguments">The set of arguments to use when starting the process.</param>
        /// <returns>The process completion status.</returns>
        /// <exception cref="System.IO.FileNotFoundException">Occurs when the file to run is not found.</exception>
        /// <exception cref="InvalidOperationException">Occurs when this class instance is already running another process.</exception>
        public virtual CompletionStatus Run(string fileName, string arguments)
        {
            if (string.IsNullOrEmpty(fileName))
            {
                throw new ArgumentException("Filename cannot be null or empty.", nameof(fileName));
            }
            IProcess P;

            lock (lockToken) {
                if (WorkProcess != null)
                {
                    throw new InvalidOperationException("This instance of ProcessWorker is busy. You can run concurrent commands by creating other class instances.");
                }
                P           = factory.Create();
                WorkProcess = P;
            }
            output.Clear();
            cancelWork = new CancellationTokenSource();
            if (Options == null)
            {
                Options = new ProcessOptions();
            }

            P.StartInfo.FileName  = fileName;
            P.StartInfo.Arguments = arguments;
            CommandWithArgs       = $@"""{fileName}"" {arguments}".TrimEnd();

            if (OutputType == ProcessOutput.Output)
            {
                P.OutputDataReceived += OnDataReceived;
            }
            else if (OutputType == ProcessOutput.Error)
            {
                P.ErrorDataReceived += OnDataReceived;
            }

            if (Options.DisplayMode != ProcessDisplayMode.Native)
            {
                if (Options.DisplayMode == ProcessDisplayMode.Interface && Config.UserInterfaceManager != null)
                {
                    Config.UserInterfaceManager.Display(this);
                }
                P.StartInfo.CreateNoWindow = true;
                P.StartInfo.WindowStyle    = ProcessWindowStyle.Hidden;
                if (OutputType == ProcessOutput.Output)
                {
                    P.StartInfo.RedirectStandardOutput = true;
                }
                else if (OutputType == ProcessOutput.Error)
                {
                    P.StartInfo.RedirectStandardError = true;
                }
                P.StartInfo.UseShellExecute = false;
            }

            ProcessStarted?.Invoke(this, new ProcessStartedEventArgs(this));

            P.Start();
            try {
                if (!P.HasExited)
                {
                    P.PriorityClass = Options.Priority;
                }
            } catch { }
            if (Options.DisplayMode != ProcessDisplayMode.Native)
            {
                if (OutputType == ProcessOutput.Output)
                {
                    P.BeginOutputReadLine();
                }
                else if (OutputType == ProcessOutput.Error)
                {
                    P.BeginErrorReadLine();
                }
            }

            bool Timeout = Wait();

            // ExitCode is 0 for normal exit. Different value when closing the console.
            CompletionStatus Result = Timeout ? CompletionStatus.Timeout : cancelWork.IsCancellationRequested ? CompletionStatus.Cancelled : P.ExitCode == 0 ? CompletionStatus.Success : CompletionStatus.Failed;

            cancelWork = null;
            // Allow changing CompletionStatus in ProcessCompleted.
            ProcessCompletedEventArgs CompletedArgs = new ProcessCompletedEventArgs(Result);

            ProcessCompleted?.Invoke(this, CompletedArgs);
            Result = CompletedArgs.Status;
            LastCompletionStatus = Result;
            if ((Result == CompletionStatus.Failed || Result == CompletionStatus.Timeout) && Options.DisplayMode == ProcessDisplayMode.ErrorOnly)
            {
                Config.UserInterfaceManager?.DisplayError(this);
            }

            WorkProcess = null;
            return(Result);
        }
 public ProcessWorker(IMediaConfig config, IProcessFactory processFactory, IFileSystemService fileSystemService, ProcessOptions options = null)
 {
     this.Config     = config ?? throw new ArgumentNullException(nameof(config));
     this.factory    = processFactory ?? throw new ArgumentNullException(nameof(processFactory));
     this.fileSystem = fileSystemService ?? throw new ArgumentNullException(nameof(fileSystem));
     this.Options    = options ?? new ProcessOptions();
 }