C #-制作一个进程。开始等待,直到进程已经启动

在继续使用方法之前,我需要确保进程正在运行。

声明如下:

Process.Start("popup.exe");

可以执行 WAIT 命令或设置此值的延迟吗?

150937 次浏览

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

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).

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);
}

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);
}

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.

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.

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();
}

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.

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();
}
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 ...

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)
{ }
}
}
}

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

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...

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...