运行批处理文件并在暂停后读取最后一行输出
我有一个Web应用程序,其中有一个用户可以运行批处理文件的页面,查看输出并发送输入。当进程遇到导致其暂停的事件(例如暂停)或需要用户按Y或N以继续的问题时,会出现我的问题。为了这个问题我们会暂停。运行批处理文件并在暂停后读取最后一行输出
这是我的批处理文件:
pause
当在Windows中运行,我得到我的屏幕“按任意键继续......”上显示的输出,我按enter键,并退出。但是当我的应用程序运行这个批处理文件时,我没有得到任何输出,但是我知道它在等待什么,所以我按下回车键,然后才看到输出“按任意键继续...”。
我在控制台应用程序中创建了我的代码的简化版本,并且发生了同样的情况,我得到一个空白屏幕,然后按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);
}
}
}
我意识到它并没有读取最后一行,因为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> ></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;
}
}
}
刚刚检查,我注意到暂停不会返回到下一行文本,直到您按下键。这可能是为什么它不显示,因为你的代码正在寻找线返回。 而不是使用ReadLine(),试着看看是否有一个会显示所有打印的字符。 (^应该给你里面有什么事情的更多的实时视图解决您的问题)
不幸的是解决方案。我的主应用程序全部都是异步完成的,并且不断输出数据到屏幕上,直到没有剩余的数据,并且在这种情况下,在我按下回车键之前,它没有剩余数据输出。 如果我用“randomText”替换了Console.ReadLine(),它不会有任何区别。我正在做的是“按任意键继续” ReadLine()正在寻找用户输入,而不是处理输出这是这里的问题。 – Owen
我还以为你需要将你的:
string inputText = Console.ReadLine();
proc.StandardInput.WriteLine(inputText);
到OutputDataReceived处理程序。
在主然后,调用proc.WaitForExit()
与一点点运气(我没有测试过这一点)以下应该发生:
- PROC的输出缓冲器刷新
- 您OutputDataReceived处理程序执行
- Console.Write与PROC的stdout
- Console.Read和输入发送给PROC的标准输入
- PROC退出
不是我不认为的解决方案。我对我的输入没有任何问题,但我认为我已经将一些人与我的临时输入法混淆了。删除这两行:string inputText = Console.ReadLine(); proc.StandardInput.WriteLine(inputText的);并更改问题:为什么“按任意键继续...”没有显示在屏幕上。 – Owen
很好的问题。 +1。 – Kangkan
我想我已经用我的临时输入法混淆了人。忽略输入,这不是问题,如果我从代码中删除它,我仍然看不到输出“按任意键继续...”。这是我想解决的问题。 – Owen
也从批处理文件中删除@echo。它使问题更清楚,暂停显示在屏幕上,但没有“按任意键继续...” – Owen