Alexander
Alexander

Reputation: 149

How to wait until event is handled in c#

Let's imagine we have such code:

public class MyProcess
{
    private Process _process;
    public event EventHandler OnFinish;
    public MyProcess(string pathToExe)
    {
         _process = new Process();
         _process.StartInfo.FileName = pathToExe;
    }
    public int Start()
    {
         _process.Exited += _processExited;
         _process.Start();
         return _process.Id;
    }
    public void Stop()
    {
         _process.Stop();
    }
    private void _processExited(object sender, EventArgs e)
    {
         OnFinish?.Invoke();
    }
}

public class MyProgram
{
    private static int stoppedProcs = 0;

    public static void Main(string[] args)
    {
         var proc = new MyProcess("foo.exe");
         proc.OnFinish += proc_OnFinish;
         proc.Start();
         proc.Stop();
         //Wait here to display 1 instead of 0
         Console.WriteLine(stoppedProcs);
    }

    private static void proc_OnFinish(object sender, EventArgs e)
    {
         stoppedProcs++;
    }
}

I create and launch process. Then I want to stop it and continue only after I handle the event. The problem is I do not invoke event by myself as it is done by OS. How can I understand that event handlers are finished?

Upvotes: 2

Views: 3371

Answers (3)

Alexander
Alexander

Reputation: 149

Thanks everyone for answering my question. The problem was solved easily with @Fildor' s suggestion to use ManualResetEvent

public class MyProgram
{
private static int stoppedProcs = 0;
private static ManualResetEvent mre = new ManualResetEvent(false);
public static void Main(string[] args)
{
     var proc = new MyProcess("foo.exe");
     proc.OnFinish += proc_OnFinish;
     proc.Start();
     proc.Stop();
     //Wait here to display 1 instead of 0
     mre.WaitOne();
     mre.Reset();
     Console.WriteLine(stoppedProcs);
}

private static void proc_OnFinish(object sender, EventArgs e)
{
     stoppedProcs++;
     mre.Set();
}
}

Upvotes: 4

weichch
weichch

Reputation: 10035

To extend answers mentioning Task and await stuff

If you want to turn the wait into an async flow, you could combine the Exited event with a TaskCompletionSource like this:

public static class ProcessExtension
{
    public static Task WaitForExitAsync(this Process proc, Action callback)
    {
        return new WaitForExitExt(proc, callback).WaitTask;
    }

    class WaitForExitExt
    {
        private TaskCompletionSource<int> _tcs;
        private Action _callback;

        public WaitForExitExt(Process proc, Action callback)
        {
            _callback = callback;
            proc.EnableRaisingEvents = true;

            _tcs = new TaskCompletionSource<int>();

            if (proc.HasExited)
            {
                _tcs.TrySetResult(proc.ExitCode);
            }
            else
            {
                proc.Exited += OnProcessExited;

                // Process already exited by the time the handler was attached.
                if (proc.HasExited)
                {
                    proc.Exited -= OnProcessExited;
                    _tcs.TrySetResult(proc.ExitCode);
                }
            }
        }

        public Task WaitTask => _tcs.Task;

        private void OnProcessExited(object sender, EventArgs e)
        {
            var proc = (Process)sender;
            proc.Exited -= OnProcessExited;

            _callback();

            _tcs.TrySetResult(proc.ExitCode);
        }
    }
}

The above extension enables the ability to run a callback once the process exited, and the callback is guaranteed to be run before the task is completed, so you could use it in your code like:

var process = new Process();
process.StartInfo.FileName = "notepad.exe";
process.EnableRaisingEvents = true;
process.Start();

var task = process.WaitForExitAsync(() => Console.WriteLine("Exited"));
process.Kill();
await task;

Console.WriteLine("Task finished");

And the output is:

Exited
Task finished

Upvotes: 0

Theodor Zoulias
Theodor Zoulias

Reputation: 43384

Assuming that you want to wait synchronously (blocking the current thread), just expose through your class the Process.WaitForExit method.

public void WaitForExit()
{
    _process.WaitForExit();
}

Upvotes: 1

Related Questions