2011-11-16 63 views
3

我有一個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); 
     } 
    } 
} 
+1

很好的問題。 +1。 – Kangkan

+0

我想我已經用我的臨時輸入法混淆了人。忽略輸入,這不是問題,如果我從代碼中刪除它,我仍然看不到輸出「按任意鍵繼續...」。這是我想解決的問題。 – Owen

+0

也從批處理文件中刪除@echo。它使問題更清楚,暫停顯示在屏幕上,但沒有「按任意鍵繼續...」 – Owen

回答

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

剛剛檢查,我注意到暫停不會返回到下一行文本,直到您按下鍵。這可能是爲什麼它不顯示,因爲你的代碼正在尋找線返回。 而不是使用ReadLine(),試着看看是否有一個會顯示所有打印的字符。 (^應該給你裏面有什麼事情的更多的實時視圖解決您的問題)

+0

不幸的是解決方案。我的主應用程序全部都是異步完成的,並且不斷輸出數據到屏幕上,直到沒有剩餘的數據,並且在這種情況下,在我按下回車鍵之前,它沒有剩餘數據輸出。 如果我用「randomText」替換了Console.ReadLine(),它不會有任何區別。我正在做的是「按任意鍵繼續」 ReadLine()正在尋找用戶輸入,而不是處理輸出這是這裏的問題。 – Owen

0

我還以爲你需要將你的:

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

到OutputDataReceived處理程序。

在主

然後,調用proc.WaitForExit()與一點點運氣(我沒有測試過這一點)以下應該發生:

  • PROC的輸出緩衝器刷新
  • 您OutputDataReceived處理程序執行
  • Console.Write與PROC的stdout
  • Console.Read和輸入發送給PROC的標準輸入
  • PROC退出
+0

不是我不認爲的解決方案。我對我的輸入沒有任何問題,但我認爲我已經將一些人與我的臨時輸入法混淆了。刪除這兩行:string inputText = Console.ReadLine(); proc.StandardInput.WriteLine(inputText的);並更改問題:爲什麼「按任意鍵繼續...」沒有顯示在屏幕上。 – Owen

相關問題