2

Sorry it's my fault. Read my answer below first pls.

It's a winform program (.net6). At the beginning, or when a button on mainform is clicked, a bat file should be run in the background. The bat runs for 10~120 minutes so I do not want to wait for it's completion. So the winform should continue normally, waiting for users' operations.

But it didn't. The black window of the shell stays in front, and the winform starts to response only after the process is finished.

Here are the codes:

public void SomeButtonClicked()
{
    Task task = Task.Run((Action)RunStaticCodeCheck); //Does not work.
}

private static void RunStaticCodeCheck()
{
    _currentCmdFileName = GenerateCmdFile(); //CmdFile is a bat with program-able parameters in it.
    RunCmdAsync(); //Warning from ide: "because this call is not awaited, execution of current method continues before the call is completed." No, the current method does NOT continue.
}

private static string? _currentCmdFileName; //A bat file name generated programmatically.
private static async Task RunCmdAsync()
{
    if (string.IsNullOrEmpty(_currentCmdFileName)) 
        return;

    var cmdFileName = _currentCmdFileName;
    _currentCmdFileName = null;

    //Run bat.
    var cmdline = new ProcessStartInfo("CMD.exe", $"/C \"{cmdFileName}\"") // The "" does not work.
    {
        UseShellExecute = true //Does not work.
    };

    //Solution 1.
    // await Task.Run(() => { Process.Start(cmdline); }); //Does not work.

    //Solution 2.
    Task.Run(() => { Process.Start(cmdline); }); //Does not work.

    // Solution 3. 
    // Process.Start(cmdline); //Does not work.

    // Solution 4. 
    // var process = Process.Start(cmdline);
    // if (process != null)
    //     process.WaitForExitAsync(); //Does not work.
}

Solutions tried:

Start process and allow caller to end without waiting for process to finish says that UseShellExecute = true should make the Process.Start() running in background. But it did not.

C# Threading/Async: Running a task in the background while UI is interactable says Task.Run((Action) MyFunction); without waiting will make it continue, but not.

How to start an application without waiting in a batch file? says blank in path might make the bat file wait or something like that. I tried using "" to wrap the cmd file name, and it did not help.

Ironically, I found many questions asked how to wait for a process/cmdline until finished. I deleted everything from those answers, but my program is still waiting!

I think there might be 2 solutions: Run a method async-ly without waiting; let the Shell start a bat file without waiting.

Any help? Thanks~

cheny
  • 2,545
  • 1
  • 24
  • 30

3 Answers3

1

As you have used async on async Task RunCmdAsync()you should use await inside the function at least once.

Abdus Salam Azad
  • 5,087
  • 46
  • 35
0

You can try to run the Bat File in another Thread, It will prevent the form from freezing.

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

namespace Program
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Press any Key to Start the Thread ...");
            Console.ReadKey();

            // Creating the new thread
            Thread batStart = new Thread(new ThreadStart(RunBat));
            batStart.Start(); // Starting it

            Console.WriteLine("Bat File Started! ....");
            Console.ReadKey();
        }

        static void RunBat()
        {
            // make new process info with bat file
            ProcessStartInfo bat = new ProcessStartInfo(@"E:\test.bat");
            // to hide the window
            bat.WindowStyle = ProcessWindowStyle.Hidden; 
            Process.Start(bat); // starting the prcoess
        }
    }
}
  • I just tried something like var thread = new Thread(new ThreadStart(RunStaticCodeCheck)); thread.IsBackground = true; thread.Start(); but it did not work. I will try to build a clear console solution too see what's happening. Thanks anyway. – cheny Mar 26 '22 at 07:17
  • To hide the Shell or CMD window you can try `bat.WindowStyle = ProcessWindowStyle.Hidden;` I have tried it successfully hide it – Madasir Ali Mar 26 '22 at 07:44
  • @Masasir Ali, Yes I tried that as well. It can hide the black window of shell, but still wait until it completed. – cheny Mar 26 '22 at 07:48
0

After more work, I realized that it was already working( going on without waiting). But the task it started has 10 threads (totally 12 in my laptop), so it occupied most of the CPU and the main thread was very slow as if it's waiting.

To make it work better I made a mechanism:

  1. Instead of to start the task immediately, leave a "todo" flag.
  2. Go on until everything is ok (for me, when the winform is idle).
  3. Check if there is a "todo" flag.
  4. If yes, start the task now and leave a "doing" flag.
  5. Keep checking if the task is finished, until we get the result.
  6. Clear all flag when you want.

The name in the following code is an identity to distinguish different tasks.

        public static class SimpleFlow
        {
            public enum Steps
            {
                Todo, Doing, Done
            }
            public static void CreateTag(Steps step, string name)
            {
                var tagFileName = TagFileName(step, name);
                if (!File.Exists(tagFileName))
                    File.Create(tagFileName);
            }

            public static bool ExistsTag(Steps step, string name)
            {
                var tagFileName = TagFileName(step, name);
                return File.Exists(tagFileName);
            }

            public static void DeleteTag(Steps step, string name)
            {
                var tagFileName = TagFileName(step, name);
                if (File.Exists(tagFileName))
                    File.Delete(tagFileName);
            }

            public static void ClearTags(string name)
            {
                foreach (var step in Enum.GetValues<Steps>())
                {
                    DeleteTag(step, name);
                }
            }

            private static string TagFileName(Steps step, string name)
            {
                var tagFileName = $@"{Directory.GetCurrentDirectory()}\_{step}_{name}.tag";
                return tagFileName;
            }
        }
cheny
  • 2,545
  • 1
  • 24
  • 30