/// <summary> /// Invoked asynchronously when the process has written a line to stdout or stderr. /// Implements a very rough progress estimation system based on the current number /// of output lines and a total estimate. /// </summary> protected override void OnLineWritten(System.Diagnostics.DataReceivedEventArgs e) { const double MIN_PERCENTAGE_DELTA = 0.1; base.OnLineWritten(e); if (_expectedNumOutputLines <= 0 || ProgressChanged == null) { return; } double expectedPercentage; lock (_syncObject) { int numLines = ++_currentNumOutputLines; expectedPercentage = (100.0 * numLines) / _expectedNumOutputLines; if (expectedPercentage - _lastReportedProgressPercentage < MIN_PERCENTAGE_DELTA || _lastReportedProgressPercentage >= 100) // no more progress reports if last percentage >= 100% { return; } expectedPercentage = Math.Min(100, expectedPercentage); // max 100% _lastReportedProgressPercentage = expectedPercentage; } SmartEventInvoker.FireEvent(ProgressChanged, this, new ProgressEventArgs(expectedPercentage)); }
private void WaitForExit(bool kill = false) { // after _process.WaitForExit(), we cannot simply wait for _fullyExitedWaitHandle because // there may be an Exited event handler dispatched in this thread, resulting in a deadlock // (this thread would wait for _fullyExitedWaitHandle and hence never execute the dispatched // handler while all event handlers need to complete before _fullyExitedWaitHandle is signaled) // what we do is hijacking these event handlers from the Exited event and invoking them // here in this thread before waiting for _fullyExitedWaitHandle // for this to work, this call must be either before the process exits (OnExited()) or // after it has exited and all Exited event handlers have finished if (HasExited) { if (HasFullyExited) { return; } throw new ProcessExitingException(); } EventHandler compatibleHandlers; lock (_syncObject) { compatibleHandlers = SmartEventInvoker.HijackCompatibleHandlers(ref Exited); if (kill) { KillInternal(); } } _process.WaitForExit(); int numHandlers = GetNumDelegates(compatibleHandlers); if (numHandlers > 0) { compatibleHandlers(this, EventArgs.Empty); } int numPending = OnExitedEventHandlersFinished(numHandlers); if (numPending > 0) { _fullyExitedWaitHandle.WaitOne(); } }
/// <summary> /// Invoked asynchronously when the process has exited. /// </summary> private void OnExited(EventArgs e) { // the Exited event is sometimes triggered a second time // I guess that has to do with disposing of the process in another thread // while the Exited event hasn't been handled completely yet if (Interlocked.Exchange(ref _onExitedInvoked, 1) == 1) { return; } // hijack all (remaining) event handlers from the Exited event EventHandler exitedEventHandlers; lock (_syncObject) { exitedEventHandlers = Exited; Exited = null; } // invoke/dispatch them var iasyncs = SmartEventInvoker.FireEvent(exitedEventHandlers, this, e); int numHandlers = GetNumDelegates(exitedEventHandlers); // incl. dispatched ones int numDispatched = (iasyncs == null ? 0 : iasyncs.Count); int numInvoked = numHandlers - numDispatched; OnExitedEventHandlersFinished(numInvoked); if (numDispatched > 0) { // in a new thread: var thread = new Thread(() => { // wait for all dispatched handlers to complete SmartEventInvoker.Wait(iasyncs); OnExitedEventHandlersFinished(numDispatched); }); thread.Start(); } }
/// <summary> /// Invoked asynchronously when the process has written a line to stdout or stderr. /// </summary> protected virtual void OnLineWritten(DataReceivedEventArgs e) { SmartEventInvoker.FireEvent(LineWritten, this, e); }
private void OnError(string text) { SmartEventInvoker.FireEvent(Error, this, new TextEventArgs(text)); }
private void OnReady() { SmartEventInvoker.FireEvent(Ready, this, EventArgs.Empty); }