使用System.Diagnostics.Process运行vb脚本,但只能部分成功地将输入文本输入到进程的stdin中

im_*_*chc 8 c# stdin stdout process wsh

编辑:第一件事

vbscript的要点是充当REPL或命令提示符/ bash环境,它简化为仅重印用户输入

因此,换句话说,cscript进程应保持活动状态,每次通过的用户输入应仅发送给该进程。

并且这也意味着应该为每次通过保留脚本的内部状态(一次=每次单击C#winform中的“发送”按钮,或者在vbscript的上下文中,一次=每次^ Z输入)。

例如,如果要修改vbscript以演示状态保持行为,则可以进行以下修改:

  1. 在行上dim wsh,stmt,l...附加: dim passcnt : passcnt=1
  2. 在第行wsh.Echo("Enter lines of strings, press ctrl-z...最后一个右括号替换& " (pass #" & passcnt & ")")
  3. 在行上wsh.Echo("End output")附加代码: passcnt = passcnt + 1

运行vbscript控制台将显示通过次数在每次通过时递增。

  1. 只要上述条件仍然成立,就可以以任何方式修改C#winform。
  2. 尝试观察脚本的作用cscript ask_SO.vbs,这应该使事情足够清楚

我认为这是我能够做到的最明确的目标。


我想使用System.Diagnostics.Process的stdout / stdin重定向将输入文本输入以下VBScript。

vbscript的作用在于,它允许用户向控制台输入多行字符串,并且当输入^ z字符时,脚本会将所有内容直接输出到控制台:

样本输出

Microsoft (R) Windows Script Host Version 5.812
Copyright (C) Microsoft Corporation. All rights reserved.

Enter lines of strings, press ctrl-z when you are done (ctrl-c to quit):
I come with no wrapping or pretty pink bows.
got line
I am who I am, from my head to my toes.
got line
I tend to get loud when speaking my mind.
got line
Even a little crazy some of the time.
got line
I'm not a size 5 and don't care to be.
got line
You can be you and I can be me.
got line

got line
Source: https://www.familyfriendpoems.com/poem/be-proud-of-who-you-are
got line
^Z
=====================================
You have entered:
I come with no wrapping or pretty pink bows.
I am who I am, from my head to my toes.
I tend to get loud when speaking my mind.
Even a little crazy some of the time.
I'm not a size 5 and don't care to be.
You can be you and I can be me.

Source: https://www.familyfriendpoems.com/poem/be-proud-of-who-you-are

End output
Enter lines of strings, press ctrl-z when you are done (ctrl-c to quit):
Run Code Online (Sandbox Code Playgroud)

之后,用户可以输入另一段文本并重复该过程。

这是脚本代码:

ask_SO.vbs


dim wsh,stmt,l : set wsh = WScript


do
    wsh.Echo("Enter lines of strings, press ctrl-z when you are done (ctrl-c to quit):")
    'stmt=wsh.StdIn.ReadAll()
    do
        l=wsh.StdIn.ReadLine()
        wsh.echo("got line")
        stmt = stmt & l & vbcrlf
    loop while (not wsh.StdIn.AtEndOfStream)
    wsh.Echo("=====================================")
    wsh.Echo("You have entered:")
    wsh.Echo(stmt)
    wsh.Echo("End output")
loop

Run Code Online (Sandbox Code Playgroud)

这是调用脚本的方法:

cscript ask_SO.vbs
Run Code Online (Sandbox Code Playgroud)

我出现了以下C#代码(项目类型设置为Console Application而不是Windows Forms):

frmPostSample

public class frmPostSample : Form

{
    Process proc_cscr;
    StreamWriter sw;
    public frmPostSample()
    {
        InitializeComponent2();
    }

    #region Copied from generated code
    private System.ComponentModel.IContainer components = null;

    /// <summary>
    /// Clean up any resources being used.
    /// </summary>
    /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
    protected override void Dispose(bool disposing)
    {
        if (disposing && (components != null))
        {
            components.Dispose();
        }
        base.Dispose(disposing);
    }
    private void InitializeComponent2()
    {
        this.txt_lines = new System.Windows.Forms.TextBox();
        this.Btn_Send = new System.Windows.Forms.Button();
        this.SuspendLayout();
        // 
        // txt_lines2
        // 
        this.txt_lines.Location = new System.Drawing.Point(41, 75);
        this.txt_lines.Multiline = true;
        this.txt_lines.Name = "txt_lines2";
        this.txt_lines.Size = new System.Drawing.Size(689, 298);
        this.txt_lines.TabIndex = 0;
        // 
        // Btn_Send2
        // 
        this.Btn_Send.Location = new System.Drawing.Point(695, 410);
        this.Btn_Send.Name = "Btn_Send2";
        this.Btn_Send.Size = new System.Drawing.Size(75, 23);
        this.Btn_Send.TabIndex = 1;
        this.Btn_Send.Text = "&Send";
        this.Btn_Send.UseVisualStyleBackColor = true;
        this.Btn_Send.Click += new System.EventHandler(this.Btn_Send_Click);
        // 
        // Form1
        // 
        this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F);
        this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
        this.ClientSize = new System.Drawing.Size(800, 450);
        this.Controls.Add(this.Btn_Send);
        this.Controls.Add(this.txt_lines);
        this.Name = "Form1";
        this.Text = "Form1";
        this.ResumeLayout(false);
        this.PerformLayout();

    }


    private System.Windows.Forms.TextBox txt_lines;
    private System.Windows.Forms.Button Btn_Send;

    #endregion
    private void Btn_Send_Click(object sender, EventArgs e)
    {
        if (proc_cscr == null)
        {
            if (!File.Exists("ask_SO.vbs"))
            {
                MessageBox.Show("Script file not exist");
                return;
            }
            ProcessStartInfo startInfo = new ProcessStartInfo();
            startInfo.FileName = "cscript";
            startInfo.Arguments = "//nologo ask_SO.vbs";

            startInfo.RedirectStandardInput = true;
            startInfo.RedirectStandardOutput = true;
            startInfo.UseShellExecute = false;


            proc_cscr = new Process();

            proc_cscr.StartInfo = startInfo;

            proc_cscr.Start();
            sw = proc_cscr.StandardInput;
        }
        OutPrint();


        foreach (var vbsline in txt_lines.Lines)
        {
            sw.WriteLine(vbsline);     // <-------- SW WRITELINE
            sw.Flush();
            OutPrint();


        }
        //sw.Flush();
        sw.Close();
        while (true)
        {
            var s2 = proc_cscr.StandardOutput.ReadLineAsync();
            s2.Wait();
            Console.WriteLine(s2.Result);
            if (proc_cscr.StandardOutput.Peek() == -1) break;
        }

    }
    private void OutPrint()
    {
        string l;
        while (proc_cscr.StandardOutput.Peek() != -1)
        {
            l = proc_cscr.StandardOutput.ReadLine();
            Console.WriteLine(l);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

运行该程序,并且如果您已将项目类型正确设置为“ Console Application”,则应显示一个控制台窗口和一个GUI窗口。您只需将文本粘贴到文本输入区域,然后按发送,然后在控制台窗口中观察结果。

但是,C#表单的行为与直接运行脚本不同cscript ask_SO.vbs

  1. 该脚本只能接受一遍输入-第二遍在注释行中引发错误“无法写入关闭的TextWriter” SW WRITELINE-我知道这是因为我已经关闭了stdin流,但是否则我无法脚本前进
  2. 另外,我还显示了错误:...\ask_SO.vbs(8, 9) Microsoft VBScript runtime error: Input past end of file
  3. 在c#代码将行输入再次输入到stdin之后(同样,在带有comment的行SW WRITELINE),立即没有显示“获取行”回显。

我已经在网上搜索以找到解决方案,但是大多数材料仅显示输入而没有使用^ z字符,换句话说,仅接受一次输入。

您可以在此处下载C#Visual Studio解决方案(包括vbscript-您只需在Visual Studio 2019中加载该解决方案并按F5键即可运行)。

注意

编码从我得到了proc_cscr.StandardOutput.CurrentEncoding.BodyNameproc_cscr.StandardInput.Encoding.BodyNamebig5,它是一个DBCSCodePageEncoding,用于编码字符的中国。

当我尝试将答案中提到的建议写入(char)26到stdin流中时,我意识到需要提及这一点。由于Encoding.GetEncoding("big5").GetBytes(new char[]{(char)26})仅返回一个字节(对于unicode:,为两个字节{byte[2]} [0]: 26 [1]: 0),我做了一个sw.Write((char)26);,并添加了一个sw.flush()。仍然没有用。

小智 1

我不认为这是可能做到的。

你的观点3:

C# 代码将行输入写入标准输入后,不会立即显示“got line”回显

这是因为您已重定向输出 ( startInfo.RedirectStandardOutput = true)。如果您重定向它,您写入的所有内容都会进入StandardOutput流,并且您必须手动读取它。因此,只要不重定向输出,您的got line消息就会立即生效。如果输出未重定向,则无法使用StandardOutput属性(但无论如何您都不需要它)。

剩下的就比较困难了。问题是,似乎没有办法发送流结束,因为这就是停止你的内部循环的原因vbs。当您完成流时,流就会结束 - 从技术上讲,当您关闭流或完成进程时。值 26 的字符表示为流的结尾(Ctrl + Z) 某处。但它在这里不起作用(我尝试过sw.Write(Convert.ToChar(26))。

我不知道是否可能(我不知道vbs),但也许你可以改变你的逻辑,而不检查流结束。它可以按字节(字符)读取并检查特定字符(例如 char(26))以跳出内部循环。