87

I need to make sure that a process is running before moving on with a method.

The statement is:

Process.Start("popup.exe");

Can you do a WAIT command or set a delay on this value?

Tom
  • 4,187
  • 4
  • 20
  • 10
  • What does "make sure" mean? There must be a specific thing you need running in popup.exe, right? If so, waiting for it to have run won't be enough. – Ed Bayiates Jun 17 '11 at 18:21
  • Also do you have control over popup.exe? Meaning can you add code to it to signal the spawning process that it is running? – Ed Bayiates Jun 17 '11 at 18:26

14 Answers14

147

Do you mean wait until it's done? Then use Process.WaitForExit:

var process = new Process {
    StartInfo = new ProcessStartInfo {
        FileName = "popup.exe"
    }
};
process.Start();
process.WaitForExit();

Alternatively, if it's an application with a UI that you are waiting to enter into a message loop, you can say:

process.Start();
process.WaitForInputIdle();

Lastly, if neither of these apply, just Thread.Sleep for some reasonable amount of time:

process.Start();
Thread.Sleep(1000); // sleep for one second
jason
  • 236,483
  • 35
  • 423
  • 525
  • 1
    In your first snippet, you'll have to manually call `process.Start()` before `process.WaitForExit()`; otherwise an exception will be thrown. – Bala R Jun 17 '11 at 18:35
  • 10
    Just make sure you understand what WaitForInputIdle actually does http://blogs.msdn.com/b/oldnewthing/archive/2010/03/25/9984720.aspx – ta.speot.is Apr 05 '13 at 08:47
  • 1
    @ta.speot.is In other words... _Exactly_ what the OP wants? – Basic Feb 16 '15 at 16:30
  • @Basic This is a reference website, and this answer may be read by people who are not the OP. – Eagle-Eye Aug 08 '18 at 15:08
  • @Eagle-Eye I'm not sure I understand your point? ta.speoit.is (neither the OP for Q or A) implied the feature didn't work as advertised. I checked the documentation and it appeared to do exactly what the OP requested. In what way was that making the site less useful for future visitors? – Basic Aug 08 '18 at 15:41
  • @Basic First off, the OP doesn't even make clear what exactly it is that they want. It's unclear if they just want the process running before moving on, if they want the process to have started accepting messages, or if they want it to have the UI ready for interaction. See this answer on another question: https://stackoverflow.com/a/33410894/827326 – Eagle-Eye Aug 09 '18 at 00:15
  • Well, the issue I am facing, which brought me here, is that I cannot call `process.BeginOutputReadLine()` or `Process.BeginErrorReadLine();` before the process was fully initialized, and so I must wait for its initialization somehow. `Thread.Sleep()` is a workaround solution, whose success may depend on the current workload of the machine. `process.WaitForInputIdle()` simply does not work if the process is not UI based, and `process.WaitForExit()` is somehow too late for me: I want to retrieve the program's output in real time. So unfortunately nothing here worked out for me. – sɐunıɔןɐqɐp Jan 31 '21 at 21:54
22

I also needed this once, and I did a check on the window title of the process. If it is the one you expect, you can be sure the application is running. The application I was checking needed some time for startup and this method worked fine for me.

var process = Process.Start("popup.exe");
while(process.MainWindowTitle != "Title")
{
    Thread.Sleep(10);
}
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
ChrisG
  • 294
  • 1
  • 3
21

The answer of 'ChrisG' is correct, but we need to refresh MainWindowTitle every time and it's better to check for empty.... like this:

var proc = Process.Start("popup.exe");
while (string.IsNullOrEmpty(proc.MainWindowTitle))
{
    System.Threading.Thread.Sleep(100);
    proc.Refresh();
}
Alireza Naghizadeh
  • 856
  • 10
  • 10
11

First of all: I know this is rather old but there still is not an accepted answer, so perhaps my approach will help someone else. :)

What I did to solve this is:

process.Start();

while (true)
{
    try
    {
        var time = process.StartTime;
        break;
    }
    catch (Exception) {}
}

The association var time = process.StartTime will throw an exception as long as process did not start. So once it passes, it is safe to assume process is running and to work with it further. I am using this to wait for java process to start up, since it takes some time. This way it should be independent on what machine the application is running rather than using Thread.Sleep().

I understand this is not very clean solution, but the only one that should be performance independent I could think of.

RhodryCZ
  • 129
  • 1
  • 8
  • 1
    As hacky as this is, it seems like the most straightforward way to get the info needed. – Arturo Torres Sánchez Sep 28 '17 at 16:06
  • Calling `process.BeginOutputReadLine()` or `Process.BeginErrorReadLine()` afterwards throws an exception which clearly states that the process has not been fully initialized yet. – sɐunıɔןɐqɐp Jan 31 '21 at 22:00
  • That probably depends on what process you are starting. It was ok for my Java process, yours might need more time to do what it needs. But you can still use the same approach, just instead of the association call any function that is critical for you I guess. Still, hacky as hell, but should work. – RhodryCZ Mar 05 '21 at 10:15
  • Doesn't work while using with Process.Start("Excel"); This came to next line the moment it started the splash screen, but it took another 3 seconds to load the entire application. – Chandraprakash Aug 25 '22 at 21:01
7

Like others have already said, it's not immediately obvious what you're asking. I'm going to assume that you want to start a process and then perform another action when the process "is ready".

Of course, the "is ready" is the tricky bit. Depending on what you're needs are, you may find that simply waiting is sufficient. However, if you need a more robust solution, you can consider using a named Mutex to control the control flow between your two processes.

For example, in your main process, you might create a named mutex and start a thread or task which will wait. Then, you can start the 2nd process. When that process decides that "it is ready", it can open the named mutex (you have to use the same name, of course) and signal to the first process.

Matt
  • 4,318
  • 1
  • 27
  • 28
5

I agree with Tom. In addition, to check the processes while performing Thread.Sleep, check the running processes. Something like:

bool found = 0;
while (!found)
{
    foreach (Process clsProcess in Process.GetProcesses())
        if (clsProcess.Name == Name)
            found = true;

    Thread.CurrentThread.Sleep(1000);
}
Adam Lear
  • 38,111
  • 12
  • 81
  • 101
Frank Pearson
  • 872
  • 5
  • 16
  • 2
    `while (!Process.GetProcesses().Any(p=>p.Name == myName)) { Thread.Sleep(100); }` – dss539 Feb 12 '15 at 16:28
  • Why not `GetProcessesByName()`? `while(Process.GetProcessesByName(myName).Length == 0) { Thread.Sleep(100); }` While it might work most part of time, the proper way would be wait until n mileseconds and get out of loop if the process wasn't found. – Jack Apr 07 '16 at 22:36
2

I used the EventWaitHandle class. On the parent process, create a named EventWaitHandle with initial state of the event set to non-signaled. The parent process blocks until the child process calls the Set method, changing the state of the event to signaled, as shown below.

Parent Process:

using System;
using System.Threading;
using System.Diagnostics;

namespace MyParentProcess
{
    class Program
    {
        static void Main(string[] args)
        {
            EventWaitHandle ewh = null;
            try
            {
                ewh = new EventWaitHandle(false, EventResetMode.AutoReset, "CHILD_PROCESS_READY");

                Process process = Process.Start("MyChildProcess.exe", Process.GetCurrentProcess().Id.ToString());
                if (process != null)
                {
                    if (ewh.WaitOne(10000))
                    {
                        // Child process is ready.
                    }
                }
            }
            catch(Exception exception)
            { }
            finally
            {
                if (ewh != null)
                    ewh.Close();
            }
        }
    }
}

Child Process:

using System;
using System.Threading;
using System.Diagnostics;

namespace MyChildProcess
{
    class Program
    {
        static void Main(string[] args)
        {
            try
            {
                // Representing some time consuming work.
                Thread.Sleep(5000);

                EventWaitHandle.OpenExisting("CHILD_PROCESS_READY")
                    .Set();

                Process.GetProcessById(Convert.ToInt32(args[0]))
                    .WaitForExit();
            }
            catch (Exception exception)
            { }
        }
    }
}
Roham Amini
  • 361
  • 3
  • 12
2

Are you sure the Start method returns before the child process starts? I was always under the impression that Start starts the child process synchronously.

If you want to wait until your child process finishes some sort of initialization then you need inter-process communication - see Interprocess communication for Windows in C# (.NET 2.0).

Community
  • 1
  • 1
Jakub Konecki
  • 45,581
  • 7
  • 87
  • 126
1

To extend @ChrisG's idea, a little, consider using process.MainWindowHandle and seeing if the window message loop is responding. Use p/invoke this Win32 api: SendMessageTimeout. From that link:

If the function succeeds, the return value is nonzero. SendMessageTimeout does not provide information about individual windows timing out if HWND_BROADCAST is used.

If the function fails or times out, the return value is 0. To get extended error information, call GetLastError. If GetLastError returns ERROR_TIMEOUT, then the function timed out.

Chris Charabaruk
  • 4,367
  • 2
  • 30
  • 57
agent-j
  • 27,335
  • 5
  • 52
  • 79
0

Here an implementation that uses a System.Threading.Timer. Maybe a bit much for its purpose.

    private static bool StartProcess(string filePath, string processName)
    {
        if (!File.Exists(filePath))
            throw new InvalidOperationException($"Unknown filepath: {(string.IsNullOrEmpty(filePath) ? "EMPTY PATH" : filePath)}");

        var isRunning = false;

        using (var resetEvent = new ManualResetEvent(false))
        {

            void Callback(object state)
            {
                if (!IsProcessActive(processName)) return;
                isRunning = true;
                // ReSharper disable once AccessToDisposedClosure
                resetEvent.Set();
            }

            using (new Timer(Callback, null, 0, TimeSpan.FromSeconds(0.5).Milliseconds))
            {
                Process.Start(filePath);
                WaitHandle.WaitAny(new WaitHandle[] { resetEvent }, TimeSpan.FromSeconds(9));
            }
        }

        return isRunning;
    }

    private static bool StopProcess(string processName)
    {
        if (!IsProcessActive(processName)) return true;

        var isRunning = true;

        using (var resetEvent = new ManualResetEvent(false))
        {

            void Callback(object state)
            {
                if (IsProcessActive(processName)) return;
                isRunning = false;
                // ReSharper disable once AccessToDisposedClosure
                resetEvent.Set();
            }

            using (new Timer(Callback, null, 0, TimeSpan.FromSeconds(0.5).Milliseconds))
            {
                foreach (var process in Process.GetProcessesByName(processName))
                    process.Kill();
                WaitHandle.WaitAny(new WaitHandle[] { resetEvent }, TimeSpan.FromSeconds(9));
            }
        }

        return isRunning;
    }

    private static bool IsProcessActive(string processName)
    {
        return Process.GetProcessesByName(processName).Any();
    }
Peter
  • 1,655
  • 22
  • 44
0
public static class WinApi
{

    [DllImport("user32.dll")]
    public static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);

    public static class Windows
    {
        public const int NORMAL = 1;
        public const int HIDE = 0;
        public const int RESTORE = 9;
        public const int SHOW = 5;
        public const int MAXIMIXED = 3;
    }

}

App

String process_name = "notepad"
Process process;
process = Process.Start( process_name );

while (!WinApi.ShowWindow(process.MainWindowHandle, WinApi.Windows.NORMAL))
{
    Thread.Sleep(100);
    process.Refresh();
}

// Done!
// Continue your code here ...
Jason C
  • 1
  • 2
0

You can also check if the started process is responsive or not by trying something like this: while(process.responding)

0

I'm using these two methods:

    public static void WaitUntilLoad(Process process, int? millisecond = null)
    {
        while ((!millisecond.HasValue || millisecond > 0) && string.IsNullOrEmpty(process.MainWindowTitle))
        {
            Thread.Sleep(100);
            if (millisecond.HasValue) millisecond -= 100;
            process.Refresh();
        }
    }
    public static void WaitUntilExit(Process process, int? millisecond = null)
    {
        if (millisecond.HasValue) process.WaitForExit(millisecond.Value);
        else process.WaitForExit();
    }

Enjoy...

MiMFa
  • 981
  • 11
  • 14
0

I think Tom was looking for something like this:

Process process = Process.Start(@"MyApp.exe");
Process[] processes;

do
{
    processes = Process.GetProcessesByName("MyApp");
    Console.WriteLine("{0} Processes found.", processes.Length);
    Thread.Sleep(1000);
}
while (processes.Length == 0);

Close(); // Or do whatever you want...
Simos Sigma
  • 958
  • 7
  • 29