运行批处理文件和在暂停后读取输出的最后一行

3

我有一个Web应用程序,其中有一个包含批处理文件的页面,用户可以运行、查看输出和发送输入。我的问题出现在流程遇到某些导致其暂停的情况,比如暂停或需要用户按Y或N键才能继续的问题。为了解决这个问题,我们将以暂停为例。

这是我的批处理文件:

pause

当我在windows上运行时,我会在屏幕上显示输出“按任意键继续...”,我按回车键,然后退出。但是当我的应用程序运行此批处理文件时,我不会获得任何输出,但我知道它在等待,所以我按回车键,只有这样我才能看到输出“按任意键继续...”。
我已经在控制台应用程序中创建了一个简化版的代码,发生了同样的事情,我得到了一个空白屏幕,我按回车键,然后我看到“按任意键继续...”
有什么办法让我在需要按键之前获得此输出行?
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using System.IO;

namespace BatchCaller
{
    class Program
    {
        static void Main(string[] args)
        {
            ProcessStartInfo psi = new ProcessStartInfo()
            {
                FileName = @"C:\Projects\BatchCaller\BatchCaller\Test2.bat",
                RedirectStandardOutput = true,
                RedirectStandardInput = true,
                UseShellExecute = false,
                CreateNoWindow = true
            };

            Process proc = new Process();

            proc.StartInfo = psi;
            proc.OutputDataReceived += new DataReceivedEventHandler(proc_OutputDataReceived);
            proc.Start();
            proc.BeginOutputReadLine();

            // Problem is not here, ignore this, just my temporary input method.
            // Problem still occurs when these two lines are removed.
            string inputText = Console.ReadLine();
            proc.StandardInput.WriteLine(inputText);

            proc.WaitForExit();
        }

        static void proc_OutputDataReceived(object sender, DataReceivedEventArgs e)
        {
            // This method doesnt get called for output: "Press any key to continue..."
            // Why?
            if (e.Data != null)
                Console.WriteLine(e.Data);
        }
    }
}

1
非常好的问题。+1。 - Kangkan
我觉得我的临时输入方法让人们感到困惑。忽略输入,它不是问题所在。如果我从代码中删除它,我仍然看不到输出“按任意键继续...”。这就是我要解决的问题。 - Owen
也要从批处理文件中删除@echo off。这会使问题更清晰,暂停会显示在屏幕上,但没有“按任意键继续…”的提示。 - Owen
经过进一步的研究,看起来只有在批处理文件到达新行时才会调用proc_OutputDataReceived事件,因此当它说“按任意键继续…”时,光标仍然在同一行上,因此我的应用程序不会收到该文本。 - Owen
3个回答

0
刚刚检查了一下,我发现暂停不会返回到下一行文本,直到你按下键。这可能是为什么它没有显示的原因,因为你的代码正在寻找一个换行符。 与其使用ReadLine(),不妨看看是否有一个可以在打印每个字符时显示所有字符的函数。 (^通过给你更实时的视图解决你的问题)

很遗憾,这并不是解决方案。我的主应用程序全部异步完成,并持续将数据输出到屏幕上,直到没有数据可输出为止,在这种情况下,除非我按回车键,否则它没有任何数据可输出。如果我用“randomText”替换Console.ReadLine(),也不会有任何区别。我所做的只是“按任意键继续”。ReadLine()正在寻找用户输入,而不是进程输出,这就是问题所在。 - Owen

0

我认为你需要移动你的:

string inputText = Console.ReadLine();             
proc.StandardInput.WriteLine(inputText); 

进入OutputDataReceived处理程序。

然后在您的主函数中调用proc.WaitForExit(),运气好的话(我没有测试过),应该会发生以下情况:

  • proc的输出缓冲区被刷新
  • 执行您的OutputDataReceived处理程序
  • 使用proc的stdout进行Console.Write
  • 使用Console.Read并将输入发送到proc的stdin
  • proc退出

不是解决方案,我认为。我的输入没有问题,但我想我的临时输入方法可能让人有些困惑。删除这两行:string inputText = Console.ReadLine(); proc.StandardInput.WriteLine(inputText); 并更改问题:为什么屏幕上没有显示“按任意键继续…”? - Owen

0

我意识到它没有读取最后一行,因为只有在输出了整行内容后才会触发“数据接收”事件,但当它暂停或询问时,光标仍然停留在同一行。一个不断检查输出流的新线程是解决这个小问题的方法。

我还成功解决了整个问题,现在当我运行脚本时,得到的结果与命令提示符窗口非常相似。

以下是它的基本工作原理:

我启动一个运行批处理文件的新进程。同时,我启动一个新线程,不断循环向进程请求更多输出,并将该输出存储在队列中。我的.NET计时器不断检查队列中的文本,并将其输出到我的ajax面板中。我使用文本框和ajax将文本输入到进程输入和我的ajax面板中。我还需要大量的javascript,并且必须使用会话变量使其平稳运行。

<%@ Page Title="" Language="C#" MasterPageFile="~/Site.Master" AutoEventWireup="true"
    CodeBehind="Scripts.aspx.cs" Inherits="MITool.Scripts" %>

<asp:Content ID="Content1" ContentPlaceHolderID="HeadContent" runat="server">
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
    <script type="text/javascript" language="javascript">
        var script = '';

        function ShowScriptModal() {
            $('#overlay').css({ width: $(document).width(), height: $(document).height(), 'display': 'block' }).animate({ opacity: 0.85 }, 0, function () { $('#scriptModal').show(); });
        }

        function ScriptInputKeypress(e) {
            if (e.keyCode == 13) {
                ScriptInput();
            }
        }

        function ScriptInput() {
            var txtInput = document.getElementById("txtInput");
            var input = txtInput.value;
            var hiddenInput = document.getElementById("hiddenInput");

            if (input == '')
                return;

            hiddenInput.value = input;

            txtInput.value = '';
        }

        function CheckForNewOutput() {
            var outputUpdatePanel = document.getElementById("OutputUpdatePanel");
            var pageScript = outputUpdatePanel.innerHTML;
            if (script != pageScript) {
                script = pageScript;
                ScrollToBottom();
            }
            setTimeout("CheckForNewOutput()", 100);
        }

        function ScrollToBottom() {
            $('#OutputPanel').scrollTop($('#OutputUpdatePanel').height());
        }
    </script>
    <asp:ScriptManager ID="ScriptManager1" runat="server" EnablePageMethods="true" EnablePartialRendering="true" LoadScriptsBeforeUI="true" />
    <div id="scriptModal">
        <div id="ScriptInputOutput">
            <asp:Panel ID="OutputPanel" runat="server" Width="700" Height="250" ScrollBars="Vertical"
                Style="margin: 10px auto; border: 1px solid #CCC; padding: 5px;" ClientIDMode="Static">
                <controls:ScriptOutput ID="ScriptOutputControl" runat="server" />
            </asp:Panel>
            <asp:Panel ID="InputPanel" runat="server" DefaultButton="btnScriptInput" >
                <asp:TextBox ID="txtInput" runat="server" ClientIDMode="Static" onkeypress="ScriptInputKeypress(event)" />
                <asp:HiddenField ID="hiddenInput" runat="server" ClientIDMode="Static" />
                <asp:Button ID="btnScriptInput" runat="server" Text="Script Input" ClientIDMode="Static" OnClick="btnScriptInput_Click" style="display:none" />           
                <asp:Button ID="btnExit" runat="server" CssClass="floatRight" Text="Exit" OnClick="btnExit_Click" />
            </asp:Panel>
        </div>
    </div>
    <asp:Literal ID="litScript" runat="server" />
    <ul id="breadcrumb">
        <li><a href="/dashboard.aspx">Main page</a> &gt;</li>
        <li class="current">Scripts</li>
    </ul>
    <div class="content">
        <h2>
            <asp:Label ID="lblHeader" runat="server" Text="Scripts" /></h2>
        <div class="clear">
        </div>
        <div class="table-content">
            <asp:Label ID="lblMessage" runat="server" CssClass="redMessage" />
            <asp:Repeater ID="rptScripts" runat="server" OnItemCommand="rptScripts_ItemCommand">
                <HeaderTemplate>
                    <table class="table" cellpadding="0" cellspacing="0">
                        <tr>
                            <th>
                                ID
                            </th>
                            <th>
                                Name
                            </th>
                            <th>
                                Location
                            </th>
                            <th>
                            </th>
                        </tr>
                </HeaderTemplate>
                <ItemTemplate>
                    <tr>
                        <td>
                            <%# Eval("ScriptId") %>
                        </td>
                        <td>
                            <%# Eval("Name") %>
                        </td>
                        <td>
                            <%# Eval("Path") %>
                        </td>
                        <td>
                            <asp:LinkButton ID="btnRunScript" runat="server" Text="Run" CommandName="Run" CommandArgument='<%# Eval("ScriptId") %>' />
                        </td>
                    </tr>
                </ItemTemplate>
                <FooterTemplate>
                    </table>
                </FooterTemplate>
            </asp:Repeater>
            <div>
            </div>
        </div>
    </div>
    <script type="text/javascript" language="javascript">
        CheckForNewOutput();
    </script>
</asp:Content>

// ====== ====== ====== ====== ====== ====== ====== ====== ====== ====== ======//

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using MITool.Data.ScriptManager;
using System.Diagnostics;
using System.IO;
using System.Web.Security;
using System.Web.Services;
using System.Collections.Concurrent;
using System.Threading;

namespace MITool
{
    public partial class Scripts : System.Web.UI.Page
    {
        ConcurrentQueue<char> ScriptOutputQueue
        {
            get
            {
                return (ConcurrentQueue<char>)Session["ScriptOutputQueue"];
            }
            set
            {
                Session["ScriptOutputQueue"] = value;
            }
        }
        Process CurrentProcess
        {
            get
            {
                return (Process)Session["CurrentProcess"];
            }
            set
            {
                Session["CurrentProcess"] = value;
            }
        }
        bool ScriptRunning
        {
            get
            {
                if (CurrentProcess == null)
                    return false;
                if (!CurrentProcess.HasExited || !CurrentProcess.StandardOutput.EndOfStream)
                    return true;
                return false;
            }
        }
        bool OutputProcessing
        {
            get
            {
                if (ScriptOutputQueue != null && ScriptOutputQueue.Count != 0)
                    return true;
                return false;
            }
        }

        Thread OutputThread;

        void Reset()
        {
            ScriptOutputControl.SetTimerEnabled(false);
            ScriptOutputControl.ClearOutputText();
            if (CurrentProcess != null && !CurrentProcess.HasExited)
                CurrentProcess.Kill();
            if (OutputThread != null && OutputThread.IsAlive)
                OutputThread.Abort();
            ScriptOutputQueue = new ConcurrentQueue<char>();

            litScript.Text = string.Empty;
            txtInput.Text = string.Empty;
        }

        void Page_Load(object sender, EventArgs e)
        {
            if (IsPostBack) return;

            Reset();

            FormsIdentity id = (FormsIdentity)HttpContext.Current.User.Identity;
            string role = id.Ticket.UserData;

            ScriptData data = new ScriptData();
            List<Script> scripts = data.GetScriptsByRole(role);
            rptScripts.DataSource = scripts;
            rptScripts.DataBind();
        }

        protected void rptScripts_ItemCommand(object source, RepeaterCommandEventArgs e)
        {
            switch (e.CommandName)
            {
                case "Run": StartScript(Int32.Parse(e.CommandArgument.ToString()));
                    break;
            }
        }

        void StartScript(int id)
        {
            if (ScriptRunning || OutputProcessing)
                return;

            Reset();

            ScriptData data = new ScriptData();
            History history = new History()
            {
                UserName = HttpContext.Current.User.Identity.Name,
                BatchFileId = id,
                DateRun = DateTime.Now
            };
            data.CreateHistory(history);            

            Script script = data.GetScript(id);

            ProcessStartInfo psi = new ProcessStartInfo()
            {
                FileName = script.Path,
                RedirectStandardOutput = true,
                RedirectStandardInput = true,
                UseShellExecute = false,
                CreateNoWindow = true
            };

            CurrentProcess = new Process();
            CurrentProcess.StartInfo = psi;

            OutputThread = new Thread(Output);

            CurrentProcess.Start();
            OutputThread.Start();

            ScriptOutputControl.SetTimerEnabled(true);

            litScript.Text = "<script type=\"text/javascript\" language=\"javascript\">ShowScriptModal();</script>";

            SetFocus("txtInput");
        }

        void Output()
        {
            while (ScriptRunning)
            {
                var x = CurrentProcess.StandardOutput.Read();
                if (x != -1)
                    ScriptOutputQueue.Enqueue((char)x);
            }
        }

        public void btnScriptInput_Click(object sender, EventArgs e)
        {
            string input = hiddenInput.Value.ToString();

            ScriptOutputControl.Input(input);

            foreach (char x in input.ToArray())
            {
                if (CurrentProcess != null && !CurrentProcess.HasExited)
                {
                    CurrentProcess.StandardInput.Write(x);
                }
                Thread.Sleep(1);
            }
        }

        protected void btnExit_Click(object sender, EventArgs e)
        {
            Reset();
        }
    }
}

// ====== ====== ====== ====== ====== ====== ====== ====== ====== ====== ======//

<%@ Control Language="C#" AutoEventWireup="true" CodeBehind="ScriptOutput.ascx.cs"
    Inherits="MITool.Controls.ScriptOutput" %>
<asp:Label ID="lblStats" runat="server" Style="color: Red" />
<br />
<asp:UpdatePanel ID="OutputUpdatePanel" runat="server" ClientIDMode="Static">
    <Triggers>
        <asp:AsyncPostBackTrigger ControlID="UpdateTimer" EventName="Tick" />
        <asp:AsyncPostBackTrigger ControlID="btnScriptInput" EventName="Click" />
    </Triggers>
    <ContentTemplate>
        <asp:Literal ID="litOutput" runat="server" />
    </ContentTemplate>
</asp:UpdatePanel>
<asp:Timer ID="UpdateTimer" Interval="100" runat="server" OnTick="UpdateTimer_Tick" Enabled="false" />

// ====== ====== ====== ====== ====== ====== ====== ====== ====== ====== ======//

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Collections.Concurrent;
using System.Diagnostics;

namespace MITool.Controls
{
    public partial class ScriptOutput : System.Web.UI.UserControl
    {
        string Output
        {
            get
            {
                if (Session["Output"] != null)
                    return Session["Output"].ToString();
                return string.Empty;
            }
            set
            {
                Session["Output"] = value;
            }
        }

        ConcurrentQueue<char> ScriptOutputQueue
        {
            get
            {
                return (ConcurrentQueue<char>)Session["ScriptOutputQueue"];
            }
            set
            {
                Session["ScriptOutputQueue"] = value;
            }
        }
        Process CurrentProcess
        {
            get
            {
                return (Process)Session["CurrentProcess"];
            }
            set
            {
                Session["CurrentProcess"] = value;
            }
        }
        bool ScriptRunning
        {
            get
            {
                if (CurrentProcess == null)
                    return false;
                if (!CurrentProcess.HasExited || CurrentProcess.StandardOutput.Peek() != -1)
                    return true;
                return false;
            }
        }
        bool OutputProcessing
        {
            get
            {
                if (ScriptOutputQueue != null && ScriptOutputQueue.Count != 0)
                    return true;
                return false;
            }
        }

        public void SetTimerEnabled(bool enabled)
        {
            UpdateTimer.Enabled = enabled;
        }
        public void ClearOutputText()
        {
            Output = string.Empty;
            litOutput.Text = Output;
        }

        protected void UpdateTimer_Tick(object sender, EventArgs e)
        {
            ProcessOutput();

            if (!ScriptRunning && !OutputProcessing)
            {
                UpdateTimer.Enabled = false;

                Output += "<br />// SCRIPT END //<br />";
                litOutput.Text = Output;
            }
        }

        public void Input(string s) 
        {
            Output += "<br />// " + s + "<br />";
        }

        void ProcessOutput()
        {
            string s = string.Empty;

            while (ScriptOutputQueue != null && ScriptOutputQueue.Any())
            {
                char x;
                if (ScriptOutputQueue.TryDequeue(out x))
                {
                    s += x;
                }
            }

            if (s != string.Empty)
            {
                s = Server.HtmlEncode(s);
                s = s.Replace("\r\n", "<br />");

                Output += s;
            }

            litOutput.Text = Output;
        }
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接