当批处理文件简单地调用"start some.exe"时,以编程方式启动批处理文件(具有输出重定向)时冻结. [英] Freeze when programmatically launching a batch file (with output redirection), when the batch file simply calls "start some.exe"

查看:80
本文介绍了当批处理文件简单地调用"start some.exe"时,以编程方式启动批处理文件(具有输出重定向)时冻结.的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

找到了一些没有答案的重复项:

Found some duplicates with no answers:

  1. 批量进行输出重定向的问题
  2. 如何我如何使用开始"命令而不在子进程中继承句柄?
  1. Issue with output redirection in batch
  2. How do I use the "start" command without inheriting handles in the child process?


在较大的长期运行的程序中,我有一些C#代码试图成为通用的进程启动器.此代码需要捕获启动过程的输出,并等待它们完成.通常,我们使用此代码启动批处理文件,并且一切正常,除非要从批处理文件内部启动另一个子进程,这样子进程的寿命将超过批处理文件进程.例如,假设我只想从批处理文件内部执行" start notepad.exe ".

我遇到了与此问题相同的问题: Process.WaitForExit也不会返回

I encountered the same problems as in this question: Process.WaitForExit doesn't return even though Process.HasExited is true

基本上,即使批处理文件进程似乎正在快速退出(如预期的那样),我的程序也会冻结直到子进程(例如记事本)也退出.但是,在我的情况下,记事本不是要退出的.

Basically, even though the batch file process appears to be exiting very quickly (as expected), my program freezes until the child process (eg. notepad) also exits. However, notepad is not meant to exit, in my scenario.

我尝试在链中的所有位置注入"cmd.exe/C",但是没有运气.我尝试用"exit"或"exit/B"显式终止批处理文件.我尝试同步或异步读取输出-有或没有工作线程.我在这里尝试了以下模式: https://github.com/alabax/CsharpRedirectStandardOutput/tree/master/RedirectStandardOutputLibrary (请参见FixedSimplePattern.cs和AdvancedPattern.cs),再次没有运气.

I tried injecting "cmd.exe /C" at all points in the chain, with no luck. I tried explicitly terminating the batch file with "exit" or "exit /B". I tried reading the output both synchronously and asynchronously - with or without worker threads. I tried the patterns here: https://github.com/alabax/CsharpRedirectStandardOutput/tree/master/RedirectStandardOutputLibrary (see FixedSimplePattern.cs and AdvancedPattern.cs), again with no luck.

我还尝试了一些C#P/Invoke代码,这些代码通过Windows API(CreatePipe/CreateProcess等)启动进程,因此我认为此问题并非特定于C#Process API.

I also tried with some C# P/Invoke code that does the process launching via the Windows API (CreatePipe/CreateProcess, etc), so I don't think this problem is specific to the C# Process API.

我发现的唯一解决方法是用一个调用带有DETACHED_PROCESS标志的CreateProcess的工具替换启动命令(CREATE_NO_WINDOW也可以).

The only workaround I found was to replace the start command with a tool that calls CreateProcess with the DETACHED_PROCESS flag (CREATE_NO_WINDOW also works).

上述SO问题( https://stackoverflow.com/a/26722542/5932003 上可接受的答案)是整个Internet上看起来最接近的东西,但事实证明,启动的每个批处理文件都在泄漏线程.我会在那儿发表评论,但我还没有这样做的名声:).

The accepted answer in the aforementioned SO question (https://stackoverflow.com/a/26722542/5932003) is the closest thing in the entire Internet that would appear to work, but it turns out it's leaking threads with every batch file you launch. I would have left a comment there, but I don't have the reputation to do that yet :).

修改后的代码演示线程泄漏:

Modified code that demonstrates thread leakage:

using System;
using System.Diagnostics;
using System.IO;
using System.Threading;
using System.Threading.Tasks;

namespace TestSO26713374WaitForExit
{
    class Program
    {
    static void Main(string[] args)
    {
        while(true)
        {
            string foobat =
@"@echo off
START ping -t localhost
REM START ping -t google.com
REM ECHO Batch file is done!
EXIT /B 123
";

            File.WriteAllText("foo.bat", foobat);

            Process p = new Process
            {
                StartInfo =
                new ProcessStartInfo("foo.bat")
                {
                    UseShellExecute = false,
                    RedirectStandardOutput = true,
                    RedirectStandardError = true
                }
            };

            p.Start();

            var _ = ConsumeReader(p.StandardOutput);
            _ = ConsumeReader(p.StandardError);

            //Console.WriteLine("Calling WaitForExit()...");
            p.WaitForExit();
            //Console.WriteLine("Process has exited. Exit code: {0}", p.ExitCode);
            //Console.WriteLine("WaitForExit returned.");


            ThreadPool.GetMaxThreads(out int max, out int max2);
            ThreadPool.GetAvailableThreads(out int available, out int available2);

            Console.WriteLine(
                $"Active thread count: {max - available} (thread pool), {System.Diagnostics.Process.GetCurrentProcess().Threads.Count} (all).");

            Thread.Sleep(8000);
        }
    }

    async static Task ConsumeReader(TextReader reader)
    {
        string text;

        while ((text = await reader.ReadLineAsync()) != null)
        {
            Console.WriteLine(text);
        }
    }
}
}

上面的输出:

Active thread count: 2 (thread pool), 15 (all).
Active thread count: 4 (thread pool), 18 (all).
Active thread count: 6 (thread pool), 19 (all).
Active thread count: 8 (thread pool), 20 (all).
Active thread count: 9 (thread pool), 21 (all).
Active thread count: 11 (thread pool), 23 (all).
Active thread count: 13 (thread pool), 25 (all).
Active thread count: 15 (thread pool), 27 (all).
Active thread count: 17 (thread pool), 29 (all).
Active thread count: 19 (thread pool), 31 (all).
Active thread count: 21 (thread pool), 33 (all).
...

我的问题:

  1. 为什么start命令不能完全打破输出重定向的链条?
  2. 我坚持使用上述调用CreateProcess(... DETACHED_PROCESS ...)的工具吗?

谢谢!

推荐答案

这里是start命令的替代品:

Here's a drop-in replacement for the start command:

开始/b powershell.exe启动过程-FilePath"notepad.exe"

start /b powershell.exe Start-Process -FilePath "notepad.exe"

启动/b"仅在不显示其窗口的情况下启动powershell(否则将闪烁一秒钟-烦人).之后,Powershell接管并启动了我们的流程,而没有任何副作用.

"start /b" is only there to start powershell without showing its window (which would otherwise flash for a second - annoying). Powershell takes over after that and launches our process, without any of the side-effects.

如果没有选择powershell,那么事实证明,这个超级简单的C#程序可以达到相同的目的:

If powershell isn't an option, it turns out that this super simple C# program can serve the same purpose:

using System.Diagnostics;

namespace Test
{
    class Program
    {
        static void Main(string[] args)
        {
            // You may want to expand this to set CreateNoWindow = true
            Process.Start(args[0]);
        }
    }
}

不需要CreateProcess(... DETACHED_PROCESS ...).

No need for CreateProcess(...DETACHED_PROCESS...).

这篇关于当批处理文件简单地调用"start some.exe"时,以编程方式启动批处理文件(具有输出重定向)时冻结.的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆