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

时间:2011-11-16 11:54:21

标签: c# process batch-file outputstream

我有一个Web应用程序,其中包含一个页面,其中包含用户可以运行的批处理文件,查看输出和发送输入。当进程遇到导致其暂停的某些内容(例如暂停)或需要用户按Y或N继续的问题时,会出现问题。出于这个问题的目的,我们会暂停。

这是我的批处理文件:

pause

在Windows中运行时,我会在屏幕上显示输出“按任意键继续...”,然后按Enter键退出。但是当我的应用程序运行这个批处理文件时,我没有得到任何输出,但是我知道它在等什么,所以我按下回车键,然后我才看到输出“按任意键继续......”。

我在控制台应用中创建了我的代码的简化版本,同样的事情发生了,我得到一个空白的屏幕,我按回车,然后我看到“按任意键继续...”

在我需要按键之前,我知道如何获得这一系列输出?

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);
        }
    }
}

3 个答案:

答案 0 :(得分:0)

刚检查过,我注意到暂停不会返回到下一行文字,直到您按下该键。这可能就是为什么它没有显示,因为你的代码正在寻找一个换行符。 而不是使用ReadLine(),尝试查看是否有一个将在打印时显示所有字符。 (^应该通过让你更实时地了解内部发生的事情来解决你的问题)

答案 1 :(得分:0)

我原以为你需要搬家:

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

进入OutputDataReceived处理程序。

然后在你的主要部分,打电话给proc.WaitForExit(),稍等运气(我没有测试过),应该会发生以下情况:

  • proc的输出缓冲区被刷新
  • 执行了OutputDataReceived处理程序
  • Console.Write with proc's stdout
  • Console.Read并将输入发送到proc的stdin
  • proc退出

答案 2 :(得分:0)

我意识到它没有读取最后一行,因为data received事件仅在输出整行后被触发,但是当它暂停或提出问题时,光标仍然在同一行上线。一个连续检查输出流的新线程是这个小问题的解决方案。

我还设法解决了我的整个问题,所以现在我运行脚本时得到的东西就像命令提示符窗口一样。

以下是有关其工作原理的基本摘要:

我启动了一个运行批处理文件的新进程。同时我启动一个新的线程,不断循环询问进程输出更多,并将该输出存储在队列中。我的.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;
        }
    }
}