Запуск пакетных файлов и чтение последней строки вывода после паузы - PullRequest
3 голосов
/ 16 ноября 2011

У меня есть веб-приложение, в котором есть страница, заполненная пакетными файлами, которые пользователь может запустить, просмотреть вывод и отправить ввод. Моя проблема возникает, когда процесс сталкивается с чем-то, что вызывает его приостановку, например, с паузой или вопросом, который требует от пользователя нажатия 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);
        }
    }
}

Ответы [ 3 ]

0 голосов
/ 16 ноября 2011

Я бы подумал, что вам нужно переместить:

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

в обработчик OutputDataReceived.

Затем на вашем главном вызовите proc.WaitForExit() и, если повезет (я не проверял это), должно произойти следующее:

  • Буфер вывода proc очищается
  • ваш обработчик OutputDataReceived выполнен
  • Console.Write с выходом proc
  • Консоль. Чтение и отправка ввода в стандартную строку процесса
  • proc выходы
0 голосов
/ 28 ноября 2011

Я понял, что это не чтение последней строки, потому что событие 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;
        }
    }
}
0 голосов
/ 16 ноября 2011

Только что проверил, и я заметил, что пауза не возвращается к следующей строке текста, пока вы не нажмете клавишу. Вероятно, поэтому он не отображается, поскольку ваш код ищет возврат строки. Вместо того, чтобы использовать ReadLine (), попробуйте посмотреть, есть ли один, который покажет все символы, когда они напечатаны. (^ это должно решить вашу проблему, предоставив вам более точное представление о том, что происходит внутри)

...