0

I need to execute a Process (CMD executing a script) called from within a Windows Forms app, and update some controls when getting Output from the process. I am using Windows Forms in .NET Core 5.0.

I have been researching a bit and found the example Extension method InvokeIfRequired,

public static void InvokeIfRequired(this Control control, MethodInvoker action)
{
    if (control.InvokeRequired) control.Invoke(action);
    else action();
}

Though at first I tried just using that same structure but for each control I needed to update.

This is the code of the function I am calling the process from, and trying to update the controls from:

private int StartGeneration()
{
    int result = 0;

    using (Process cmd = new Process())
    {
        cmd.StartInfo.FileName = "cmd.exe";
        cmd.StartInfo.WorkingDirectory = @"C:\git\pub";
        cmd.StartInfo.RedirectStandardInput = true;
        cmd.StartInfo.RedirectStandardOutput = true;
        cmd.StartInfo.CreateNoWindow = true;
        cmd.Start();

        cmd.Exited += (sender, e) =>
        {
            Console.WriteLine("Exiting from CMD process");
        };


        cmd.OutputDataReceived += (sender, e) =>
        {
            lbResult.InvokeIfRequired(new MethodInvoker(delegate
            {
                lbResult.Text = e.Data;
            }));

            if (e.Data.ToLower().Contains("exiting"))
            {
                // The script has ended
                lbResult.InvokeIfRequired(new MethodInvoker(delegate
                {
                    lbResult.ForeColor = System.Drawing.Color.Aqua;
                    lbResult.Text = "Done";
                }));

                rtbResult.InvokeIfRequired(new MethodInvoker(delegate
                {
                    rtbResult.Visible = true; 
                    rtbResult.Text = cmd.StandardOutput.ReadToEnd();
                    Size = new System.Drawing.Size(Width, Height + 460);
                }));

                cmd.StandardInput.Flush();
                cmd.StandardInput.Close();
                cmd.Close();
            }
        };
        cmd.BeginOutputReadLine();

        cmd.StandardInput.WriteLine(BuildPublishingScriptCommand());
        cmd.WaitForExit();
    }


    return result;
}

And the BuildPublishingScriptCommand() function is just a simple function that returns a string, containing the script I need to run in the CMD.

The problem comes when the code gets in the OutputDataReceived event of the process and I try to use the InvokeIfRequired method (or any Invoke method in general).

The application just keeps running infinitely but nothing updates. Though the script completes successfully because I can confirm the work is done (creates some DLLs).

If I debug, I get until the call to InvokeIfRequired and then I lose control to debugging.

It does not give any exception.

I have not tried anything else.

Biel
  • 193
  • 1
  • 3
  • 15
  • Set the `Process.SynchronizingObject` to the Container Form so you don't need to invoke anything. The Process' events are raised in the UI Thread. -- The example here may be useful: [How do I get output from a command to appear in a control on a Form in real-time?](https://stackoverflow.com/a/51682585/7444103) -- Redirect and handle stderr, too. – Jimi Jan 12 '22 at 08:04
  • @Jimi Thank you very much! I'll try that and come back to update. – Biel Jan 12 '22 at 08:58
  • How are you calling `StartGeneration()`? If you are calling it from the UI thread (e.g. NOT using `Task.Run()` to call it) then it will block the UI when it gets to `cmd.WaitForExit();` – Matthew Watson Jan 12 '22 at 09:01
  • 1
    This code will deadlock on the WaitForExit() call. Which hangs the main thread, waiting for the process to exit. It can no longer do anything else, like painting the UI and responding to the Invoke() requests. It is a permanent hang since the code uses Invoke instead of BeginInvoke, preventing the process from cleanly exiting. You need to delete it and refactor the method so it doesn't need to return the result. – Hans Passant Jan 12 '22 at 09:03

0 Answers0