您的位置:首页 > 运维架构 > Shell

C#下写GUI - 管道操作 - 读取标准输出流 - 截获shell程序输出

2009-03-22 18:49 351 查看
这是个老话题了,可是折磨了我N个各月,我太笨了。某天终于很灵异地成功了,苦心人,天不负……


现在还有太多的程序用命令行(为啥都喜欢用呢,看起来很professional?),可整天敲参数、看黑白屏幕总不太爽吧?


GUI作用就是把这种“黑白屏幕”变成图形界面,看起来多舒服,是不是?


最初这个问题是在x264编码器出现的。这是个命令行程序,想写个GUI。我真是自不量力呀,刚接触程序没几天就敢充胖子,一上来就碰壁,求google大神,有所收获。不过我模仿就是实现不了,太笨了。


终于好了,有两个方法:一个是用StreamReader主动读取;另一个是用异步读取,应用回调函数。

个人认为前一种适合内容比较大多的情况,后一种适合比较少的情况。

先说第一种吧,

 先引用需要的命名空间
 using System.Diagnostics;

 using System.Threading;
然后添加一个方法,我直接用了Button,触发整个代码,下面是一些类级的声明

 Process p;

 delegate void chandle(string s);

        private void button1_Click(object sender, EventArgs e)

        {

            Thread tt = new Thread(new ThreadStart(t));

            tt.Start();

        }
在这里,新建了一个线程,我可不喜欢在主线程上跑,影响美观,下面写t()这个方法。

 1         private void t()

 2         {

 3             p = new Process();

 4             p.StartInfo.FileName = "azid.exe";

 5             p.StartInfo.Arguments = @"I:\kn\m12\m12..ac3 I:\kn\m12\au2.wav";

 6 

 7             p.StartInfo.CreateNoWindow = true;

 8             p.StartInfo.UseShellExecute = false;

 9             p.StartInfo.RedirectStandardError = true;

 

           

            

             p.Start();

            

             Thread rt = new Thread(new ThreadStart(read));

             rt.Start();

             p.WaitForExit();

            

             p.Dispose(); 

         }

 
在这里,创建一个Process类的实例,然后对StartInfo属性进行设置,主要是为了不要让他显示命令行窗口,并且把输出流RedirectStandardError导出,

这里多说一句,我这个程序azid的输出都被塞到了StandardError(标准错误输出)里面,貌似x264也是,你要注意一下你的程序输出是在标准输出还是错误输出,依次尝试一下即可,之后再创建一个线程,用来运行我们的读取函数,具体关系看下面这张涂鸦。

下面写读取函数read()

         private void read()

         {

             StreamReader sr = p.StandardError;

             while (!p.HasExited)

             {

                 Invoke(new chandle(ct), sr.ReadLine());

                 Thread.Sleep(20);

             }

         }
这里用Invoke来控制主界面上的控件,因为这里是线程rt,不能直接操作主线程厄,我在这里弱智了N久,一直研究怎么做线程同步,其实是迷失了方向,偶然才发现Invoke方法的强大。


这里挂接的委托ct用来更新主界面的textbox,显示读取出来的内容,把sr.ReadLine()做参数传进去。

 
  private void ct(string s)

   {

   textBox1.AppendText(s.Trim() + "\n");

   textBox1.SelectionStart = textBox1.Text.Length - 1;

   Application.DoEvents();

6  }
就是这样了哈……于是就可以工作了,不过这个程序有点问题,while (!p.HasExited)这里的条件有点单薄,容易异常掉,我比较懒


 下面说说异步读取,这个好像比上面的简单。

 1         Process p;

 2         delegate void chandle(string s);

 3 

 4 

 5         private void button1_Click(object sender, EventArgs e)

 6         {

 7             Thread tt = new Thread(new ThreadStart(t));

 8             tt.Start();

 9         }

         private void t()

         {

             p = new Process();

             p.StartInfo.FileName = "azid.exe";

             p.StartInfo.Arguments = @"I:\kn\m12\m12..ac3 I:\kn\m12\au2.wav";

            p.ErrorDataReceived += new DataReceivedEventHandler(p_ErrorDataReceived);//这里多了这句

             p.StartInfo.CreateNoWindow = true;

             p.StartInfo.UseShellExecute = false;

             p.StartInfo.RedirectStandardError = true;

            p.Start(); 

             p.BeginErrorReadLine(); //这里多了这句

             p.WaitForExit();

           

             p.Dispose(); 

         }

 
和第一种方法差不多,只不过多了2句,第一句为ErrorDataReceived添加一个事件处理函数,就是那个回调函数,第二句开始读取
 

   void p_ErrorDataReceived(object sender, DataReceivedEventArgs e)

         {

             if (!string.IsNullOrEmpty(e.Data))

             {

                // 做点工作

             }

         }
 程序每向输出流写入一行时,触发一次这个事件,e.Data属性可以获得这一行数据,然后怎么处理就看你了。

这个方法如果你的输出流行数很多,那就要不断地调用这个函数,很费资源orz,到底用哪个,根据情况判断吧,当然你也可以2个交错使用,在msdn上有写限制条件

地址在这里:http://msdn.microsoft.com/zh-cn/library/system.diagnostics.process.standarderror(VS.80).aspx
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐