如何通过输入流手动向 cmd.exe 进程提供命令?

2024-01-01

这个问题听起来有点晦涩难懂。这是一个稍长的版本:

我需要让主循环等待用户输入,并且还需要运行一个进程并等待来自用户输入要发送到的流的输入。

全文:我正在构建一个 Cmd 模拟器,起初一切看起来都很好:用户输入命令,它会回显到输出区域,进行处理,捕获 StdOut 和 StdErrOut 并将其添加到输出文本框。

唯一的问题是,当 cmd 进程是为每个命令单独创建和启动时,没有保留任何状态。既不是变量,也不是代码页,也不是工作目录等。

所以我决定发明一个小技巧:输入左括号或右括号开始和停止收集命令而不是执行它们。在右括号之后,processBatch 方法中使用命令列表(“batch”),通过其重定向输入将它们全部提供给 cmd 进程。工作得很好。

显然,唯一的问题是,现在我得到了状态,但失去了立即响应,因此在运行批处理之前不会弹出任何错误。

所以我决定将好的部分结合起来,当我意识到为了保持两个循环的工作和等待我必须使用线程时,我知道我遇到了麻烦。我已经很多年没做过了..

在布局中,我选择了 main() 循环等待用户输入,而 startCMDtask() 在任务中运行 startCMD() 。这里扫描输入流直到有数据,然后 cmd 进程将处理它们。

但这不起作用。

List<string> batch = new List<string>();

public volatile string output = "+";
public volatile string outputErr = "-";

Process CMD;
Task cmdTask;

volatile Queue<string> cmdQueue = new Queue<string>();
volatile public bool CMDrunning = false;

这很好用

private void processBatch()
{
    Process p = new Process();
    ProcessStartInfo info = new ProcessStartInfo();
    info.FileName = "cmd.exe";
    info.RedirectStandardOutput = true;
    info.RedirectStandardError = true;
    info.RedirectStandardInput = true;
    info.UseShellExecute = false;

    p.StartInfo = info;
    p.Start();

    using (StreamWriter sw = p.StandardInput)
    {
        if (sw.BaseStream.CanWrite)
            foreach(string line in batch) sw.WriteLine(line);
    }
    output = "^"; outputErr = "~";
    try { output = p.StandardOutput.ReadToEnd(); } catch { }
    try { outputErr = p.StandardError.ReadToEnd(); } catch { }
    try { p.WaitForExit(); } catch { }
    tb_output.AppendText(output + "\r\n" + outputErr + "\r\n");
}

这些不完全是,但几乎..

private void setupCMD()
{
    CMD = new Process();
    ProcessStartInfo info = new ProcessStartInfo();
    info.FileName = "cmd.exe";
     // info.Arguments = "/K";   // doesn't make a difference
    info.CreateNoWindow = true;
    info.RedirectStandardOutput = true;
    info.RedirectStandardError = true;
    info.RedirectStandardInput = true;
    info.UseShellExecute = false;
    CMD.StartInfo = info;
}


private void startCMDtask()
{
    var task = Task.Factory.StartNew(() => startCMD());
    cmdTask = task;
}


private void startCMD()
{
    try   { CMD.Start(); CMDrunning = true; } 
    catch { output = "Error starting cmd process.\r\n"; CMDrunning = false; }

    using (StreamWriter sw = CMD.StandardInput)
    {
        if (sw.BaseStream.CanWrite)
            do {  
                try 
                {
                    string cmd = cmdQueue.Dequeue();
                    if (cmd != null & cmd !="")
                    {
                        sw.WriteLine(cmd);
                        processOutputStreams();
                    }
                } 
                catch {} 
            } while (CMDrunning);
    }


private void processOutputStreams()
{
    string newOutput = ""; string newOutputErr = "";
    while (CMD.StandardOutput.Peek() > 0)
              newOutput += (char)(CMD.StandardOutput.Read());

    newOutput += "!?";    // at this point stdout is correctly captured  (1)  

    try {
      while (CMD.StandardError.Peek() > 0)    // from here execution jumps away (2)
      { newOutputErr += (char)(CMD.StandardError.Read()); }
    } catch { 
        newOutputErr = "?";   // never comes here
    }



    lock (output)    // no noticable difference
    lock (outputErr) //
    {                // if I jump here (3) from (1) the result is displayed
                     // but not if i comment out the 2nd while loop (2)
        if (newOutput != null & newOutput != "") output += newOutput + "\r\n";
        if (newOutputErr != null & newOutputErr != "") outputErr += newOutputErr + "\r\n";
    }
}

这是来自主线程中输入处理器的调用:

lock (cmdQueue) cmdQueue.Enqueue(cmd);

我不知道哪个部分出了问题:进程、cmd shell、输入流、输出流、线程、锁或所有这些......?


我终于让它工作了。我在代码示例中描述的不稳定行为的原因是 3 个流不是以异步方式访问的。

为了纠正,我放弃了 processOutput 函数,并将其替换为进程本身触发的两个调用。 MS文档给出了一个很好的例子here http://msdn.microsoft.com/de-de/library/system.diagnostics.process.beginerrorreadline%28v=vs.110%29.aspx

我还使 StreamWriter 同步,为进程及其运行的整个任务提供数据。

这是新代码:

private void startCMDtask()
{
    var task = Task.Factory.StartNew(() => startCMD());
    cmdTask = task;
}

private async void startCMD()
{
    try   { CMD.Start(); CMDrunning = true; } 
    catch { cmdErrOutput.Append("\r\nError starting cmd process."); 
            CMDrunning = false; }

    CMD.BeginOutputReadLine();
    CMD.BeginErrorReadLine();

    using (StreamWriter sw = CMD.StandardInput)
    {

        if (sw.BaseStream.CanWrite)
            do {  
                try 
                {
                    string cmd = cmdQueue.Dequeue();
                    if (cmd != null & cmd !="")  await sw.WriteLineAsync(cmd);
                } 
                catch { } 
            }   while (CMDrunning);
        try   { CMD.WaitForExit(); } 
        catch { cmdErrOutput.Append("WaitForExit Error.\r\n"); }
    }
}

现在流程是这样设置的:

private void setupCMD()
{
    CMD = new Process();
    ProcessStartInfo info = new ProcessStartInfo();
    info.FileName = "cmd.exe";
    info.CreateNoWindow = true;
    info.RedirectStandardOutput = true;
    info.RedirectStandardError = true;
    info.RedirectStandardInput = true;
    info.UseShellExecute = false;

    CMD.OutputDataReceived += new DataReceivedEventHandler(cmdOutputDataHandler);
    CMD.ErrorDataReceived += new DataReceivedEventHandler(cmdErrorDataHandler);
    cmdOutput = new StringBuilder();
    cmdErrOutput = new StringBuilder();
    CMD.StartInfo = info;
}

这是输出处理程序:

private static void cmdOutputDataHandler(object sendingProcess, DataReceivedEventArgs outLine)
{
    if (!String.IsNullOrEmpty(outLine.Data))
    {  // Add the text to the collected output.
        cmdOutput.Append(Environment.NewLine + outLine.Data);
    }
}

private static void cmdErrorDataHandler(object sendingProcess, DataReceivedEventArgs outLine)
{
    if (!String.IsNullOrEmpty(outLine.Data))
    {  // Add the text to the collected error output.
        cmdErrOutput.Append(Environment.NewLine + outLine.Data);
    }
}

在用户输入处理结束时,这是输入队列和获取输出的方式:

    cmdUnDoStack.Push(cmd);
    Application.DoEvents();
    TaskScheduler uiScheduler = TaskScheduler.FromCurrentSynchronizationContext();
    Task.Factory.StartNew(() => updateOutputArea(uiScheduler));

使用这个小例程:

private void updateOutputArea(TaskScheduler uiScheduler)
{
    Task.Factory.StartNew(() =>
    {
        tb_output.AppendText(cmdOutput + "\r\n" + cmdErrOutput + "\r\n");
        cmdOutput.Clear();
        cmdErrOutput.Clear();
    }, System.Threading.CancellationToken.None, TaskCreationOptions.None, uiScheduler);


    }

现在为了特殊处理一些好的旧命令,如 CLS 或 COLOR 需要..;-)

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何通过输入流手动向 cmd.exe 进程提供命令? 的相关文章

随机推荐

  • 如何使用 Java 和 XmlSchema Core 解析 XSD 并读取所有复杂元素及其子元素

    我正在使用Apache XmlSchema Core库来解析XSD文件并获取所有元素及其子类型 数据类型 maxOccurs 等 我正在关注文档Apache XML 架构核心 http ws apache org xmlschema xml
  • 我想用 Proguard 混淆 AndroidManifest.xml

    我可以使用 Proguard 混淆 AndroidManifest xml 文件吗 我想在我的 proguard config 文件中写入源代码 adaptresourcefilenames AndroidManifest xml 但这没用
  • 进度条会减慢应用程序速度吗?

    当我有进度条时 与没有进度条的启动相比 我的应用程序启动需要更长的时间 有可能吗 或者这只是视觉效果 我想 并且也注意到 进度条的创建和修改需要一些时间 时间开销对于长时间运行的操作不太重要 但对于短时间操作 几秒 来说很明显 然而 我更喜
  • 共享元素活动过渡动画中的 Z 顺序

    我正在使用与此类似的代码制作一些基本的共享元素活动转换动画 Intent i new Intent AnActivity this AnotherActivity class ActivityOptionsCompat options Ac
  • Opentok 屏幕共享音频

    我尝试使用 opentok JS 客户端创建一个屏幕共享应用程序 该应用程序也共享发布者的音频 屏幕共享工作正常 但音频从未被共享 现在 我注意到控制台 Firefox 中有一条警告说Invalid audioSource passed t
  • Jupyter nbconvert LaTex 导出主题

    我正在使用 Jupyter Notebook nbconvert 另存为菜单 通过 Latex 导出为 pdf 然而 pdf 文件的状态并不好 例如 一些宽表显示得很好 我希望有一个用于将表格大小调整为页面宽度的框 是否有任何样式 模板可以
  • 循环遍历 Android 视图的所有子视图?

    我正在开发一款 Android 游戏 为了帮助实现它 我的想法是创建视图的子类 然后 我将插入此类的几个实例作为主视图的子视图 每个实例都会处理按下时的检测 通过 OnTouchListener 我现在遇到的问题是如何循环遍历所有这些子视图
  • 获取 org.elasticsearch.transport.NodeDisconnectedException:[][inet[localhost/127.0.0.1:9300]][cluster/nodes/info] 已断开连接

    我是 Elastic Search Java Api 5 0 的新手 我正在使用elasticsearch 5 0 0 我尝试使用 Spring Boot 创建一个 Java 应用程序 Maven 运行应用程序后 显示 2016 11 04
  • 在heroku上的设计设置中找不到ENV['SECRET_KEY']

    这个要点不对您的密钥进行版本控制 https gist github com cjolly 6265302太棒了 我已经使用它几次了 不再对我的 Rails 密钥库进行版本控制 我尝试将它用于heroku 上的设计secret key 但我
  • 寻找比 GDI 更快的解决方案来渲染动态数据图

    我使用 C CLI 编写了一个简单的基于 GDI 的数据绘图仪 但它不是特别快 一些基本分析表明问题在于屏幕渲染 有没有办法为 UserControl 启用硬件加速 或者是否有用于 direct3D 的 net 接口 或者我可以考虑其他一些
  • Angular - ui-router 获取之前的状态

    有没有办法获取当前状态的先前状态 例如 我想知道当前状态 B 之前的前一个状态是什么 其中前一个状态是状态 A 我无法在 ui router github 文档页面中找到它 在移动到新状态之前 我使用解析来保存当前状态数据 angular
  • 我的 CREATE View 查询中不需要锁定吗

    我正在 MS SQL Server 上创建一个视图 我对 MS SQL 没有太多接触 并且不太熟悉 NO LOCK 提示 我明白它的作用 但我不知道我是否需要在我的情况下使用它 有人问我是否应该包括它 但我不知道 我是否需要在用于创建视图的
  • 将 Spark DataFrame 列转换为 Python 列表

    我处理一个有两列的数据框 mvv 和 count mvv count 1 5 2 9 3 3 4 1 我想获得两个包含 mvv 值和计数值的列表 就像是 mvv 1 2 3 4 count 5 9 3 1 所以 我尝试了以下代码 第一行应该
  • Ironpython调用numpy问题

    铁蟒2 6 蟒蛇2 6 5 麻木 科学Py import sys sys path append r D Python26 dll sys path append r D Python26 Lib sys path append r D P
  • 为什么 Maven 报告“校验和验证失败,存储库中没有可用的校验和”?

    我正在尝试使用此处描述的 3 个步骤创建自定义 Maven 存储库 http www javaworld com community node 3968 http www javaworld com community node 3968
  • scikit-learn 中奇怪的 SVM 预测性能 (SVMLIB)

    我在 10000x1000 的大型数据集 10000 个对象 1000 个特征 上使用 scikit learn 的 SVC 我已经在其他来源中看到 SVMLIB 的扩展范围不能超过约 10000 个对象 我确实观察到了这一点 traini
  • 在 php 中组合重叠字符串序列的内置函数?

    PHP 中是否有内置函数可以将 2 个字符串合并为 1 个 Example string1 abcde string2 cdefg 合并可得 abcdefg 如果确切的重叠序列和位置已知 则可以编写代码来合并它们 我发现 substr re
  • 何时使用 TaskCreationOptions.LongRunning?

    我想知道这个问题有一段时间了 但从未真正找到答案 我知道这是任务调度程序在其中运行任务的提示 并且任务调度程序可以 或现在将 决定为该任务实例化一个非线程池线程 我不知道 令人惊讶的是在互联网上找不到任何地方 是何时将任务指定为长时间运行的
  • 如何在 MySQL 8 中永久禁用列统计?

    Since MySQL 8 the column statistics默认情况下启用标志 所以如果你尝试转储一些表MySQL工作台8 0 12 您会收到以下错误消息 14 50 22 转储数据库 表名 运行 mysqldump exe de
  • 如何通过输入流手动向 cmd.exe 进程提供命令?

    这个问题听起来有点晦涩难懂 这是一个稍长的版本 我需要让主循环等待用户输入 并且还需要运行一个进程并等待来自用户输入要发送到的流的输入 全文 我正在构建一个 Cmd 模拟器 起初一切看起来都很好 用户输入命令 它会回显到输出区域 进行处理