C # - Erstellen eines Prozesses. Starten Sie, bis der Prozess gestartet ist

76

Ich muss sicherstellen, dass ein Prozess ausgeführt wird, bevor ich mit einer Methode fortfahre.

Die Aussage lautet:

Process.Start("popup.exe");

Können Sie einen WAIT-Befehl ausführen oder eine Verzögerung für diesen Wert festlegen?

Tom
quelle
Was bedeutet "sicherstellen"? Es muss eine bestimmte Sache geben, die in popup.exe ausgeführt werden muss, oder? Wenn ja, reicht es nicht aus, darauf zu warten, dass es ausgeführt wird.
Ed Bayiates
Haben Sie auch die Kontrolle über popup.exe? Das heißt, können Sie Code hinzufügen, um dem Laichprozess zu signalisieren, dass er ausgeführt wird?
Ed Bayiates

Antworten:

136

Meinst du warten, bis es fertig ist? Dann benutze Process.WaitForExit:

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

Wenn es sich um eine Anwendung mit einer Benutzeroberfläche handelt, auf deren Eingabe Sie in eine Nachrichtenschleife warten, können Sie alternativ Folgendes sagen:

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

Wenn beides nicht zutrifft, nur Thread.Sleepfür einen angemessenen Zeitraum:

process.Start();
Thread.Sleep(1000); // sleep for one second
Jason
quelle
1
In Ihrem ersten Snippet finden Sie manuell Anruf haben process.Start()vor process.WaitForExit(); Andernfalls wird eine Ausnahme ausgelöst.
Bala R
9
Stellen Sie einfach sicher, dass Sie verstehen, was WaitForInputIdle tatsächlich tut. Blogs.msdn.com/b/oldnewthing/archive/2010/03/25/9984720.aspx
ta.speot.is
1
@ ta.speot.is Mit anderen Worten ... Genau das, was das OP will?
Basic
@Basic Dies ist eine Referenz-Website, und diese Antwort kann von Personen gelesen werden, die nicht der OP sind.
Eagle-Eye
@ Eagle-Eye Ich bin nicht sicher, ob ich deinen Standpunkt verstehe? ta.speoit.is (weder das OP für Q noch A) implizierte, dass die Funktion nicht wie angekündigt funktionierte. Ich überprüfte die Dokumentation und es schien genau das zu tun, was das OP verlangte. Inwiefern war die Website dadurch für zukünftige Besucher weniger nützlich?
Basic
18

Ich brauchte das auch einmal und überprüfte den Fenstertitel des Prozesses. Wenn es das ist, was Sie erwarten, können Sie sicher sein, dass die Anwendung ausgeführt wird. Die Anwendung, die ich überprüfte, brauchte einige Zeit für den Start und diese Methode funktionierte gut für mich.

var process = Process.Start("popup.exe");
while(process.MainWindowTitle != "Title")
{
    Thread.Sleep(10);
}
ChrisG
quelle
14

Die Antwort von 'ChrisG' ist richtig, aber wir müssen MainWindowTitle jedes Mal aktualisieren und es ist besser, nach Leerzeichen zu suchen ... wie folgt:

var proc = Process.Start("popup.exe");
while (string.IsNullOrEmpty(proc.MainWindowTitle))
{
    System.Threading.Thread.Sleep(100);
    proc.Refresh();
}
Alireza Naghizadeh
quelle
2
Können wir annehmen, dass eine Befehlszeilenanwendung, sobald sie einen Titel erhalten hat, gestartet wurde?
Jack
1
Ich habe einen zusätzlichen Check-in während Anweisung hinzugefügt :! Proc.HasExited
Vangi
9

Zuallererst: Ich weiß, dass dies ziemlich alt ist, aber es gibt immer noch keine akzeptierte Antwort. Vielleicht hilft mein Ansatz jemand anderem. :) :)

Was ich getan habe, um dies zu lösen, ist:

process.Start();

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

Die Zuordnung var time = process.StartTimelöst eine Ausnahme aus, solange der Prozess nicht gestartet wurde. Sobald es vorbei ist, ist es sicher anzunehmen, dass der Prozess läuft, und weiter damit zu arbeiten. Ich verwende dies, um auf den Start des Java-Prozesses zu warten, da dies einige Zeit dauert. Auf diese Weise sollte es unabhängig davon sein, auf welchem ​​Computer die Anwendung ausgeführt wird, anstatt es zu verwenden Thread.Sleep().

Ich verstehe, dass dies keine sehr saubere Lösung ist, aber die einzige, die leistungsunabhängig sein sollte, die ich mir vorstellen kann.

RhodryCZ
quelle
1
So hackig das auch ist, es scheint der einfachste Weg zu sein, die benötigten Informationen zu erhalten.
Arturo Torres Sánchez
7

Wie andere bereits gesagt haben, ist es nicht sofort offensichtlich, was Sie fragen. Ich gehe davon aus, dass Sie einen Prozess starten und dann eine weitere Aktion ausführen möchten, wenn der Prozess "bereit" ist.

Natürlich ist das "ist fertig" das schwierige Stück. Je nachdem, was Sie brauchen, können Sie feststellen, dass es ausreicht, einfach zu warten. Wenn Sie jedoch eine robustere Lösung benötigen, können Sie einen benannten Mutex verwenden , um den Kontrollfluss zwischen Ihren beiden Prozessen zu steuern.

In Ihrem Hauptprozess können Sie beispielsweise einen benannten Mutex erstellen und einen Thread oder eine Aufgabe starten, die warten wird. Dann können Sie den 2. Prozess starten. Wenn dieser Prozess entscheidet, dass "es bereit ist", kann er den benannten Mutex öffnen (Sie müssen natürlich denselben Namen verwenden) und dem ersten Prozess ein Signal geben.

Matt
quelle
Genau das hatte ich erwartet, als ich das gesucht habe. Vielen Dank.
Nicholas Miller
4

Ich stimme Tom zu. Überprüfen Sie außerdem die laufenden Prozesse, um die Prozesse während der Ausführung von Thread.Sleep zu überprüfen. Etwas wie:

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

    Thread.CurrentThread.Sleep(1000);
}
Frank Pearson
quelle
2
while (!Process.GetProcesses().Any(p=>p.Name == myName)) { Thread.Sleep(100); }
dss539
Warum nicht GetProcessesByName()? while(Process.GetProcessesByName(myName).Length == 0) { Thread.Sleep(100); }Während es die meiste Zeit funktionieren könnte, wäre der richtige Weg, bis n Meilensekunden zu warten und aus der Schleife herauszukommen, wenn der Prozess nicht gefunden wurde.
Jack
2

Sind Sie sicher, dass die StartMethode zurückgegeben wird, bevor der untergeordnete Prozess gestartet wird? Ich hatte immer den Eindruck, dass Startder untergeordnete Prozess synchron gestartet wird.

Wenn Sie warten möchten, bis Ihr untergeordneter Prozess eine Initialisierung abgeschlossen hat, benötigen Sie eine prozessübergreifende Kommunikation - siehe Interprozesskommunikation für Windows in C # (.NET 2.0) .

Jakub Konecki
quelle
1

Um die Idee von @ ChrisG ein wenig zu erweitern, sollten Sie process.MainWindowHandle verwenden und prüfen, ob die Fenstermeldungsschleife reagiert. Verwenden Sie p / invoke diese Win32-API: SendMessageTimeout . Von diesem Link:

Wenn die Funktion erfolgreich ist, ist der Rückgabewert ungleich Null. SendMessageTimeout bietet keine Informationen zum Zeitlimit für einzelne Fenster, wenn HWND_BROADCAST verwendet wird.

Wenn die Funktion fehlschlägt oder eine Zeitüberschreitung auftritt, ist der Rückgabewert 0. Um erweiterte Fehlerinformationen zu erhalten, rufen Sie GetLastError auf. Wenn GetLastError ERROR_TIMEOUT zurückgibt, ist die Funktion abgelaufen.

Agent-j
quelle
1

Hier eine Implementierung, die a verwendet System.Threading.Timer. Vielleicht ein bisschen viel für seinen Zweck.

    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
quelle
1

Ich habe die EventWaitHandle- Klasse verwendet. Erstellen Sie im übergeordneten Prozess ein benanntes EventWaitHandle, dessen Anfangsstatus des Ereignisses auf "Nicht signalisiert" gesetzt ist. Der übergeordnete Prozess wird blockiert, bis der untergeordnete Prozess die Set- Methode aufruft und den Status des Ereignisses wie unten gezeigt in signalisiert ändert.

Übergeordneter Prozess:

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

Untergeordneter Prozess:

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
quelle
3
Die beste Lösung hier, aber Sie müssen in der Lage sein, Code im untergeordneten Prozess hinzuzufügen.
ViRuSTriNiTy
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.
quelle
0

Sie können auch überprüfen, ob der gestartete Prozess reagiert oder nicht, indem Sie Folgendes versuchen: while (process.responding)

Peter Jackson
quelle