Çıkış beklenirken işlem bazen askıda kalıyor


13

Çıkışı beklerken işlemimin askıda kalmasının nedeni ne olabilir?

Bu kod, içeride birçok eylem gerçekleştiren powershell betiğini başlatmalıdır, örneğin kodu MSBuild aracılığıyla yeniden derlemeye başla, ancak muhtemelen sorun çok fazla çıktı üretmesi ve güç kabuğu betiği doğru bir şekilde yürütüldükten sonra bile çıkmayı beklerken bu kodun sıkışmasıdır

biraz "garip" çünkü bazen bu kod iyi çalışıyor ve bazen sıkışmış.

Kod şurada asılı:

process.WaitForExit (ProcessTimeOutMiliseconds);

Powershell betiği 1-2sn gibi çalışır, bu arada zaman aşımı 19sn'dir.

public static (bool Success, string Logs) ExecuteScript(string path, int ProcessTimeOutMiliseconds, params string[] args)
{
    StringBuilder output = new StringBuilder();
    StringBuilder error = new StringBuilder();

    using (var outputWaitHandle = new AutoResetEvent(false))
    using (var errorWaitHandle = new AutoResetEvent(false))
    {
        try
        {
            using (var process = new Process())
            {
                process.StartInfo = new ProcessStartInfo
                {
                    WindowStyle = ProcessWindowStyle.Hidden,
                    FileName = "powershell.exe",
                    RedirectStandardOutput = true,
                    RedirectStandardError = true,
                    UseShellExecute = false,
                    Arguments = $"-ExecutionPolicy Bypass -File \"{path}\"",
                    WorkingDirectory = Path.GetDirectoryName(path)
                };

                if (args.Length > 0)
                {
                    var arguments = string.Join(" ", args.Select(x => $"\"{x}\""));
                    process.StartInfo.Arguments += $" {arguments}";
                }

                output.AppendLine($"args:'{process.StartInfo.Arguments}'");

                process.OutputDataReceived += (sender, e) =>
                {
                    if (e.Data == null)
                    {
                        outputWaitHandle.Set();
                    }
                    else
                    {
                        output.AppendLine(e.Data);
                    }
                };
                process.ErrorDataReceived += (sender, e) =>
                {
                    if (e.Data == null)
                    {
                        errorWaitHandle.Set();
                    }
                    else
                    {
                        error.AppendLine(e.Data);
                    }
                };

                process.Start();

                process.BeginOutputReadLine();
                process.BeginErrorReadLine();

                process.WaitForExit(ProcessTimeOutMiliseconds);

                var logs = output + Environment.NewLine + error;

                return process.ExitCode == 0 ? (true, logs) : (false, logs);
            }
        }
        finally
        {
            outputWaitHandle.WaitOne(ProcessTimeOutMiliseconds);
            errorWaitHandle.WaitOne(ProcessTimeOutMiliseconds);
        }
    }
}

Senaryo:

start-process $args[0] App.csproj -Wait -NoNewWindow

[string]$sourceDirectory  = "\bin\Debug\*"
[int]$count = (dir $sourceDirectory | measure).Count;

If ($count -eq 0)
{
    exit 1;
}
Else
{
    exit 0;
}

nerede

$args[0] = "C:\Program Files (x86)\Microsoft Visual Studio\2019\Professional\MSBuild\Current\Bin\MSBuild.exe"

Düzenle

@ İngen'in çözümüne, asılmış MS Build'u yürütmeyi deneyen küçük bir paketleyici ekledim

public static void ExecuteScriptRx(string path, int processTimeOutMilliseconds, out string logs, out bool success, params string[] args)
{
    var current = 0;
    int attempts_count = 5;
    bool _local_success = false;
    string _local_logs = "";

    while (attempts_count > 0 && _local_success == false)
    {
        Console.WriteLine($"Attempt: {++current}");
        InternalExecuteScript(path, processTimeOutMilliseconds, out _local_logs, out _local_success, args);
        attempts_count--;
    }

    success = _local_success;
    logs = _local_logs;
}

InternalExecuteScriptIngen'in kodu nerede


hangi satırda işlem asılır? ve kodunuzu çok daha fazla
tanıtın

@ Mr.AF haklısın.
Joelty

1
Powershell'in gerçek çağrılması bir şeydir, ancak sağladığınız şey, FORIN Powershell sırasında işlemeye çalıştığınız komut dosyasının geri kalan kısmıdır. Powershell'i çağırmak problem değil, yapmaya çalıştığınız şeyin içinde. Yayınınızı düzenleyin ve yürütmeye çalıştığınız açık aramayı / komutları yerleştirin.
DRapp

1
Hatayı çoğaltmaya çalıştığım gerçekten garip. 20 denemede iki kez rastgele bir şey oldu ve tekrar tetikleyemiyorum.
KiKoS

1
@Joelty, ohh harika ilginç, Rxyaklaşımın süresiz MSBuild süreci ile bile belirsiz beklemeye yol açtığını bile (zaman aşımına uğramadı) işe yaradığını mı söylüyorsunuz ? nasıl ele alındığını bilmek isteyen
Clint

Yanıtlar:


9

İlgili bir gönderide kabul edilen cevabın bir özeti ile başlayalım .

Sorun, StandardOutput ve / veya StandardError yönlendirirseniz, iç arabellek dolu olabilir. Kullandığınız sipariş ne olursa olsun, bir sorun olabilir:

  • StandardOutput'u okumadan önce işlemin çıkmasını beklerseniz işlem, yazmaya çalışmayı engelleyebilir, böylece işlem hiçbir zaman bitmez.
  • StandardToutput'tan ReadToEnd kullanarak okursanız, işlem hiçbir zaman StandardOutput'u kapatmazsa (örneğin, hiçbir zaman sonlandırılmazsa veya StandardError'a yazılması engellenirse) işleminiz engellenebilir.

Bununla birlikte, kabul edilen cevap bile, bazı durumlarda infaz emriyle mücadele etmektedir.

EDIT: Zaman aşımı oluşursa bir ObjectDisposedException önlemek için aşağıdaki yanıtlara bakın .

Rx'in gerçekten parladığı, çeşitli olayları düzenlemek istediğiniz bu tür durumlarda.

Rx .NET uygulaması System.Reactive NuGet paketi olarak kullanılabilir.

Şimdi Rx'in olaylarla çalışmayı nasıl kolaylaştırdığını görelim.

// Subscribe to OutputData
Observable.FromEventPattern<DataReceivedEventArgs>(process, nameof(Process.OutputDataReceived))
    .Subscribe(
        eventPattern => output.AppendLine(eventPattern.EventArgs.Data),
        exception => error.AppendLine(exception.Message)
    ).DisposeWith(disposables);

FromEventPatternbir olayın farklı oluşumlarını birleşik bir akışla (yani gözlemlenebilir olarak) eşlememize olanak tanır. Bu, olayları bir boru hattındaki (LINQ benzeri semantik ile) ele almamızı sağlar. SubscribeBurada kullanılan aşırı bir ile sağlanır Action<EventPattern<...>>ve bir Action<Exception>. Gözlenen olay yükseltilir zaman, onun senderve argstarafından sarılmış olacak EventPatternve itilir Action<EventPattern<...>>. Boru hattında bir istisna Action<Exception>oluştuğunda kullanılır.

EventBu kullanım durumunda (ve başvurulan gönderideki tüm geçici çözümlerle) açıkça gösterilen desenin dezavantajlarından biri , olay işleyicilerinin aboneliğini ne zaman / nerede iptal edeceğinin belirgin olmamasıdır.

Rx ile IDisposablebir abonelik yaptığımızda geri döneriz . Bertaraf ettiğimizde, aboneliği etkili bir şekilde sonlandırıyoruz. Ekleme DisposeWithyönteminin eklenmesiyle ( RxUI'den ödünç alındı ), a'ya birden çok IDisposables ekleyebiliriz CompositeDisposable( disposableskod örneklerinde adlandırılır ). İşimiz bittiğinde, tüm abonelikleri tek bir çağrı ile sonlandırabiliriz disposables.Dispose().

Emin olmak için, Rx ile yapabileceğimiz hiçbir şey yoktur, vanilla .NET ile yapamayacağız. Ortaya çıkan kodun akıl yürütmesi çok daha kolaydır, işlevsel düşünme tarzına adapte olduktan sonra.

public static void ExecuteScriptRx(string path, int processTimeOutMilliseconds, out string logs, out bool success, params string[] args)
{
    StringBuilder output = new StringBuilder();
    StringBuilder error = new StringBuilder();

    using (var process = new Process())
    using (var disposables = new CompositeDisposable())
    {
        process.StartInfo = new ProcessStartInfo
        {
            WindowStyle = ProcessWindowStyle.Hidden,
            FileName = "powershell.exe",
            RedirectStandardOutput = true,
            RedirectStandardError = true,
            UseShellExecute = false,
            Arguments = $"-ExecutionPolicy Bypass -File \"{path}\"",
            WorkingDirectory = Path.GetDirectoryName(path)
        };

        if (args.Length > 0)
        {
            var arguments = string.Join(" ", args.Select(x => $"\"{x}\""));
            process.StartInfo.Arguments += $" {arguments}";
        }

        output.AppendLine($"args:'{process.StartInfo.Arguments}'");

        // Raise the Process.Exited event when the process terminates.
        process.EnableRaisingEvents = true;

        // Subscribe to OutputData
        Observable.FromEventPattern<DataReceivedEventArgs>(process, nameof(Process.OutputDataReceived))
            .Subscribe(
                eventPattern => output.AppendLine(eventPattern.EventArgs.Data),
                exception => error.AppendLine(exception.Message)
            ).DisposeWith(disposables);

        // Subscribe to ErrorData
        Observable.FromEventPattern<DataReceivedEventArgs>(process, nameof(Process.ErrorDataReceived))
            .Subscribe(
                eventPattern => error.AppendLine(eventPattern.EventArgs.Data),
                exception => error.AppendLine(exception.Message)
            ).DisposeWith(disposables);

        var processExited =
            // Observable will tick when the process has gracefully exited.
            Observable.FromEventPattern<EventArgs>(process, nameof(Process.Exited))
                // First two lines to tick true when the process has gracefully exited and false when it has timed out.
                .Select(_ => true)
                .Timeout(TimeSpan.FromMilliseconds(processTimeOutMilliseconds), Observable.Return(false))
                // Force termination when the process timed out
                .Do(exitedSuccessfully => { if (!exitedSuccessfully) { try { process.Kill(); } catch {} } } );

        // Subscribe to the Process.Exited event.
        processExited
            .Subscribe()
            .DisposeWith(disposables);

        // Start process(ing)
        process.Start();

        process.BeginOutputReadLine();
        process.BeginErrorReadLine();

        // Wait for the process to terminate (gracefully or forced)
        processExited.Take(1).Wait();

        logs = output + Environment.NewLine + error;
        success = process.ExitCode == 0;
    }
}

Olaylarımızı gözlemlenebilirlerle eşleştirdiğimiz ilk kısmı zaten tartıştık, böylece doğrudan etli kısma atlayabiliriz. Burada gözlemlenebilirimizi processExiteddeğişkene atarız , çünkü onu bir kereden fazla kullanmak istiyoruz.

İlk olarak, etkinleştirdiğimizde arayarak Subscribe. Ve sonra ilk değerini 'ne zaman' beklemek istediğimizde.

var processExited =
    // Observable will tick when the process has gracefully exited.
    Observable.FromEventPattern<EventArgs>(process, nameof(Process.Exited))
        // First two lines to tick true when the process has gracefully exited and false when it has timed out.
        .Select(_ => true)
        .Timeout(TimeSpan.FromMilliseconds(processTimeOutMilliseconds), Observable.Return(false))
        // Force termination when the process timed out
        .Do(exitedSuccessfully => { if (!exitedSuccessfully) { try { process.Kill(); } catch {} } } );

// Subscribe to the Process.Exited event.
processExited
    .Subscribe()
    .DisposeWith(disposables);

// Start process(ing)
...

// Wait for the process to terminate (gracefully or forced)
processExited.Take(1).Wait();

OP ile ilgili sorunlardan biri, process.WaitForExit(processTimeOutMiliseconds)zaman aşımına uğradığında işlemi sonlandıracağını varsaymasıdır . Gönderen MSDN :

Talimatını Süreç bileşeninin çıkışına ilişkili süreç için milisaniye belirtilen sayıda beklemek.

Bunun yerine, zaman aşımına uğradığında, denetimi yalnızca geçerli iş parçacığına döndürür (yani engellemeyi durdurur). İşlem zaman aşımına uğradığında sonlandırmayı manuel olarak zorlamanız gerekir. Zaman aşımının ne zaman gerçekleştiğini bilmek için, Process.Exitedetkinliği processExitedişleme tabi tutulabilir bir şeyle eşleştirebiliriz . Bu şekilde girişi Dooperatör için hazırlayabiliriz .

Kod oldukça açıklayıcı. Eğer exitedSuccessfullysüreç incelikle sonlandırılır olacaktır. Değilse exitedSuccessfully, feshin zorlanması gerekecektir. Not process.Kill()uyumsuz yürütülür, ref açıklamalar . Ancak, process.WaitForExit()hemen sonra tekrar çağrı yapmak kilitlenme olasılığını tekrar açacaktır. Dolayısıyla, zorla sonlandırma durumunda bile, usingkapsam sona erdiğinde tüm tek kullanımlık malzemelerin temizlenmesine izin vermek daha iyidir , çünkü çıktı yine de kesilmiş / bozulabilir.

try catchYapı Eğer hizaladığınızda istisnai durumlarda (hayır cinas tasarlamak) için ayrılmıştır processTimeOutMillisecondskomple sürecin ihtiyaç duyduğu gerçek zamanla. Başka bir deyişle, Process.Exitedolay ve zamanlayıcı arasında bir yarış durumu oluşur . Bu olma olasılığı, eşzamansız doğasıyla tekrar büyütülür process.Kill(). Test sırasında bir kez karşılaştım.


Tamlık için, DisposeWithgenişletme yöntemi.

/// <summary>
/// Extension methods associated with the IDisposable interface.
/// </summary>
public static class DisposableExtensions
{
    /// <summary>
    /// Ensures the provided disposable is disposed with the specified <see cref="CompositeDisposable"/>.
    /// </summary>
    public static T DisposeWith<T>(this T item, CompositeDisposable compositeDisposable)
        where T : IDisposable
    {
        if (compositeDisposable == null)
        {
            throw new ArgumentNullException(nameof(compositeDisposable));
        }

        compositeDisposable.Add(item);
        return item;
    }
}

4
IMHO, kesinlikle lütufa değer. Güzel cevap ve RX için konu üzerine güzel bir giriş.
quetzalcoatl

Teşekkürler!!! Kişisel ExecuteScriptRxkolları hangsmükemmel. Maalesef asılmalar hala gerçekleşiyor, ancak ExecuteScriptRxperformansınıza küçük bir sargı ekledim Retryve sonra iyi çalışıyor. MSBUILD askıda kalmanın nedeni @ Clint cevabı olabilir. PS: Bu kod beni aptal hissettirdi <lol> Bu ilk gördüğümdeSystem.Reactive.Linq;
Joelty

Wrapper kodu ana yazıda
Joelty

3

İçin fayda okuyucuların Ben 2 Bölüm bu bölmek için gidiyorum

Bölüm A: Sorun ve benzer senaryoların nasıl ele alınacağı

Bölüm B: Problem yaratma ve Çözüm

Bölüm A: Sorun

Bu sorun oluştuğunda - işlem görev yöneticisinde görünür, sonra 2-3 saniye kaybolduktan sonra (iyi), zaman aşımını bekler ve sonra istisna atılır Sistem.InvalidOperationException: İstenen bilgiler belirlenmeden önce işlemden çıkılmalıdır.

& Aşağıdaki Senaryo 4'e bakın

Kodunuzda:

  1. Process.WaitForExit(ProcessTimeOutMiliseconds); Bu ile bekliyoruz Processüzere Zaman Aşımı veya Çıkış yaşanmadığına, ilk .
  2. OutputWaitHandle.WaitOne(ProcessTimeOutMiliseconds)ve errorWaitHandle.WaitOne(ProcessTimeOutMiliseconds); bu ile bekliyoruz OutputData& ErrorDataakışından onun tam sinyal okuma işlemi
  3. Process.ExitCode == 0 Çıkıldığında işlemin durumunu alır

Farklı ayarlar ve uyarıları:

  • Senaryo 1 (Mutlu Yol) : Süreç zaman aşımından önce tamamlanır ve böylece stdoutput ve stderror da ondan önce biter ve her şey yolunda.
  • Senaryo 2 : Process, OutputWaitHandle & ErrorWaitHandle zaman aşımı ancak stdoutput ve stderror WaitHandlers zaman aşımından sonra hala okunuyor ve tamamlanıyor. Bu başka bir istisnaya yol açarObjectDisposedException()
  • Senaryo 3 : Önce işlem zaman aşımı (19 sn) ancak stdout ve stderror çalışıyor, WaitHandler'in zaman aşımına uğramasını (19 sn) beklersiniz ve + 19 saniye gecikme olur.
  • Senaryo 4 : İşlem zaman aşımına uğradı ve kod Process.ExitCode, hatayla sonuçlanan vaktinden önce sorgulamaya çalışır System.InvalidOperationException: Process must exit before requested information can be determined.

Bu senaryoyu bir düzine kez test ettim ve iyi çalışıyor, test sırasında aşağıdaki ayarlar kullanıldı

  • Yaklaşık 2-15 proje oluşturmaya başlayarak 5KB'den 198KB'ye kadar Çıktı akışının boyutu
  • Zaman aşımı penceresinde erken zaman aşımı ve işlem çıkışları


Güncellenmiş Kod

.
.
.
    process.BeginOutputReadLine();
    process.BeginErrorReadLine();

    //First waiting for ReadOperations to Timeout and then check Process to Timeout
    if (!outputWaitHandle.WaitOne(ProcessTimeOutMiliseconds) && !errorWaitHandle.WaitOne(ProcessTimeOutMiliseconds)
        && !process.WaitForExit(ProcessTimeOutMiliseconds)  )
    {
        //To cancel the Read operation if the process is stil reading after the timeout this will prevent ObjectDisposeException
        process.CancelOutputRead();
        process.CancelErrorRead();

        Console.ForegroundColor = ConsoleColor.Red;
        Console.WriteLine("Timed Out");
        Logs = output + Environment.NewLine + error;
       //To release allocated resource for the Process
        process.Close();
        return  (false, logs);
    }

    Console.ForegroundColor = ConsoleColor.Green;
    Console.WriteLine("Completed On Time");
    Logs = output + Environment.NewLine + error;
    ExitCode = process.ExitCode.ToString();
    // Close frees the memory allocated to the exited process
    process.Close();

    //ExitCode now accessible
    return process.ExitCode == 0 ? (true, logs) : (false, logs);
    }
}
finally{}

DÜZENLE:

MSBuild ile saatlerce oynadıktan sonra nihayet sorunu sistemimde yeniden üretebildim


Bölüm B: Sorun Giderme ve Çözüm

MSBuild ,-m[:number]oluşturma sırasında kullanılacak maksimum eşzamanlı işlem sayısını belirtmek için kullanılan bir anahtara sahiptir.

Bu etkinleştirildiğinde, MSBuild Yapı tamamlandıktan sonra bile yaşayan bir dizi düğümü ortaya çıkarır. Şimdi, Process.WaitForExit(milliseconds)asla çıkmak ve sonunda zaman aşımı bekleyecekti

Bunu birkaç şekilde çözebildim

  • CMD aracılığıyla dolaylı olarak MSBuild süreci ortaya çıkar

    $path1 = """C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\MSBuild\15.0\Bin\MSBuild.exe"" ""C:\Users\John\source\repos\Test\Test.sln"" -maxcpucount:3"
    $cmdOutput = cmd.exe /c $path1  '2>&1'
    $cmdOutput
  • MSBuild kullanmaya devam edin, ancak nodeReuse öğesini False olarak ayarladığınızdan emin olun.

    $filepath = "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\MSBuild\15.0\Bin\MSBuild.exe"
    $arg1 = "C:\Users\John\source\repos\Test\Test.sln"
    $arg2 = "-m:3"
    $arg3 = "-nr:False"
    
    Start-Process -FilePath $filepath -ArgumentList $arg1,$arg2,$arg3 -Wait -NoNewWindow
  • Paralel derleme etkin olmasa bile, Derlemeyi CMD ileWaitForExit başlatarak işleminizin askıda kalmasını engelleyebilirsiniz ve bu nedenle Derleme işlemine doğrudan bağımlılık oluşturmazsınız

    $path1 = """C:\....\15.0\Bin\MSBuild.exe"" ""C:\Users\John\source\Test.sln"""
    $cmdOutput = cmd.exe /c $path1  '2>&1'
    $cmdOutput

Çok sayıda MSBuild düğümünün etrafta yatmasını istemediğiniz için 2. yaklaşım tercih edilir.


Yani, yukarıda söylediğim gibi, teşekkürler, bu "-nr:False","-m:3", Rx solutiontüm süreci biraz güvenilir hale getiren (zaman gösterecek) MSBuild hang-ish davranışını düzeltti . Her iki yanıtı da kabul edebilsem ya da iki
ödül verebilsem

@Joelty RxDiğer çözümdeki yaklaşımın sorunu uygulamadan çözüp çözemeyeceğini anlamaya çalışıyordum -nr:False" ,"-m:3". Anladığım kadarıyla, 1. bölümde ele aldığım kilitlenmelerden ve diğer şeylerden belirsiz beklemeyi ele alıyor. Ve Bölüm 2'deki kök neden, karşılaştığınız sorunun kök nedeni olduğuna inandığım;) Yanlış olabilirim Ben sordum, sadece zaman söyleyecek ... Şerefe !!
Clint

3

Sorun, StandardOutput ve / veya StandardError yönlendirirseniz, iç arabellek dolu olabilir.

Yukarıda belirtilen sorunları çözmek için işlemi ayrı evrelerde çalıştırabilirsiniz. WaitForExit kullanmıyorum, işlemin ExitCode'u zaman uyumsuz olarak tamamlandığından emin olarak döndürecek olan işlemden çıkmış olayı kullanıyorum.

public async Task<int> RunProcessAsync(params string[] args)
    {
        try
        {
            var tcs = new TaskCompletionSource<int>();

            var process = new Process
            {
                StartInfo = {
                    FileName = 'file path',
                    RedirectStandardOutput = true,
                    RedirectStandardError = true,
                    Arguments = "shell command",
                    UseShellExecute = false,
                    CreateNoWindow = true
                },
                EnableRaisingEvents = true
            };


            process.Exited += (sender, args) =>
            {
                tcs.SetResult(process.ExitCode);
                process.Dispose();
            };

            process.Start();
            // Use asynchronous read operations on at least one of the streams.
            // Reading both streams synchronously would generate another deadlock.
            process.BeginOutputReadLine();
            string tmpErrorOut = await process.StandardError.ReadToEndAsync();
            //process.WaitForExit();


            return await tcs.Task;
        }
        catch (Exception ee) {
            Console.WriteLine(ee.Message);
        }
        return -1;
    }

Yukarıdaki kod, FFMPEG.exe'yi komut satırı argümanlarıyla çağırarak savaşta test edilmiştir. MP4 dosyalarını mp3 dosyalarına dönüştürüyordum ve başarısız olmadan bir seferde 1000'den fazla video yapıyordum. Ne yazık ki doğrudan güç kabuk deneyimim yok ama umarım bu yardımcı olur.


Bu kod garip, diğer çözümlere benzer şekilde İLK denemede başarısız oldu (sıkışmış) ve sonra iyi çalışıyor gibi görünüyordu (diğer 5 deneme gibi, ben daha test edeceğim). Btw neden uygularım BegingOutputReadlineve gerçekleştirmek ardından ReadToEndAsyncüzerinde StandardError?
Joelty

OP zaten eşzamansız olarak okuyor, bu yüzden konsol tamponundaki bir kilitlenmenin burada olması olası değildir.
yaakov

0

Sorunun bu olup olmadığından emin değilim, ancak MSDN'ye bakarken, çıkışı eşzamansız olarak yeniden yönlendirirken aşırı yüklenmiş WaitForExit ile bazı tuhaflıklar var gibi görünüyor. MSDN makalesi, aşırı yüklenmiş yöntemi çağırdıktan sonra hiçbir bağımsız değişken almayan WaitForExit çağrılmasını önerir.

Burada bulunan Dokümanlar sayfası . Alakalı metin:

Standart çıktı eşzamansız olay işleyicilerine yeniden yönlendirildiğinde, bu yöntem döndüğünde çıktı işlemenin tamamlanmamış olması mümkündür. Eşzamansız olay işlemenin tamamlandığından emin olmak için, bu aşırı yükten bir doğru aldıktan sonra parametre almayan WaitForExit () aşırı yükünü çağırın. Exited olayının Windows Forms uygulamalarında doğru şekilde işlenmesini sağlamak için SynchronizingObject özelliğini ayarlayın.

Kod değişikliği şöyle görünebilir:

if (process.WaitForExit(ProcessTimeOutMiliseconds))
{
  process.WaitForExit();
}

Bu cevabaprocess.WaitForExit() yapılan yorumlarda belirtildiği gibi kullanımı ile ilgili bazı karışıklıklar vardır .
ingen
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.