【转】一个C#写的调用外部进程类

简介: 2008-05-21 07:00作者:肖波出处:天极网责任编辑:nancy   C# 调用外部进程的类,网上可以搜出很多来,为什么要再写一遍,实在是因为最近从网上拷贝了一个简单的例程用到项目中,运行有问题,后来研究了半天,才解决了这些问题。

      2008-05-21 07:00作者:肖波出处:天极网责任编辑:nancy

  C# 调用外部进程的类,网上可以搜出很多来,为什么要再写一遍,实在是因为最近从网上拷贝了一个简单的例程用到项目中,运行有问题,后来研究了半天,才解决了这些问题。于是打算重写,一来说说调用一个外部进程这么简单的一件事究竟会有哪些问题,二来也希望我写的这个相对比较完整的类可以为软件开发的同道们节约一些脑细胞,以便集中优势兵力解决那些真正高深复杂的软件问题。

  在开始正题之前,我们先来看一看网上比较常见的执行外部进程的函数

  


private string RunCmd(string command)
   {
   //例Process
   Process p = new Process();
  
   p.StartInfo.FileName = "cmd.exe"; //确定程序名
   p.StartInfo.Arguments = "/c " + command; //确定程式命令行
   p.StartInfo.UseShellExecute = false; //Shell的使用
   p.StartInfo.RedirectStandardInput = true; //重定向输入
   p.StartInfo.RedirectStandardOutput = true; //重定向输出
   p.StartInfo.RedirectStandardError = true; //重定向输出错误
   p.StartInfo.CreateNoWindow = true; //设置置不显示示窗口
  
   p.Start(); //00
  
   //p.StandardInput.WriteLine(command); //也可以用这种方式输入入要行的命令
   //p.StandardInput.WriteLine("exit"); //要得加上Exit要不然下一行程式
  
   return p.StandardOutput.ReadToEnd(); //输出出流取得命令行结果
   }
  

  这个方法应该是比较常见的调用外部进程的方法,我以前也一直是这样调用外部进程的,也没有碰到过什么问题。但这次调用的外部进程比较特殊,用这种方法调用就出现了两个问题。

  第一个问题是这个被调用的外部进程有时候会出现异常,出现异常后Windows会弹出错误报告框,程序于是吊死在那里,必须手工干预。这个问题比较好解决,程序中设置一下注册表搞定。

  第二个问题是调用这个外部进程(是一个控制台进程)后,程序会阻塞在p.StandardOutput.ReadToEnd();这一句,永远无法出来,被调用的那个控制台程序也被吊死。但该控制台进程在CMD 中是可以正常执行的。后来看来一些资料才发现原来原因是出在该控制台程序控制台输出大量字符串,管道重定向后,调用程序没有及时将管道中的输出数据取出,结果导致管道被阻塞,程序吊死。在这里还有另外一个问题,虽然这次没有遇到,但网上有其他人遇到,就是错误信息管道不及时取出数据,也会被阻塞,而且如果要同时取出两个管道的数据,必须要利用一个辅助线程才能实现。

  问题讲完了,下面给出这个类的完整代码

  


using System;
  using System.Collections.Generic;
  using System.Text;
  using System.Runtime.InteropServices;
  using System.Threading;
  
  namespace Laboratory.Process
  {
   class ReadErrorThread
   {
   System.Threading.Thread m_Thread;
   System.Diagnostics.Process m_Process;
   String m_Error;
   bool m_HasExisted;
   object m_LockObj = new object();
  
   public String Error
   {
   get
   {
   return m_Error;
   }
   }
  
   public bool HasExisted
   {
   get
   {
   lock (m_LockObj)
   {
   return m_HasExisted;
   }
   }
  
   set
   {
   lock (m_LockObj)
   {
   m_HasExisted = value;
   }
   }
   }
  
   private void ReadError()
   {
   StringBuilder strError = new StringBuilder();
   while (!m_Process.HasExited)
   {
   strError.Append(m_Process.StandardError.ReadLine());
   }
  
   strError.Append(m_Process.StandardError.ReadToEnd());
  
   m_Error = strError.ToString();
   HasExisted = true;
   }
  
   public ReadErrorThread(System.Diagnostics.Process p)
   {
   HasExisted = false;
   m_Error = "";
   m_Process = p;
   m_Thread = new Thread(new ThreadStart(ReadError));
   m_Thread.Start();
   }
  
   }
  
   class RunProcess
   {
   private String m_Error;
   private String m_Output;
  
   public String Error
   {
   get
   {
   return m_Error;
   }
   }
  
   public String Output
   {
   get
   {
   return m_Output;
   }
   }
  
   public bool HasError
   {
   get
   {
   return m_Error != "" && m_Error != null;
   }
   }
  
   public void Run(String fileName, String para)
   {
   StringBuilder outputStr = new StringBuilder();
  
   try
   {
   //disable the error report dialog.
   //reference: http://www.devcow.com/blogs/adnrg/archive/2006/07/14/Disable-Error-Reporting-Dialog-for-your-application-with-the-registry.aspx
   Microsoft.Win32.RegistryKey key;
   key = Microsoft.Win32.Registry.LocalMachine.OpenSubKey(@"software\microsoft\PCHealth\ErrorReporting\", true);
   int doReport = (int)key.GetValue("DoReport");
  
   if (doReport != 0)
   {
   key.SetValue("DoReport", 0);
   }
  
   int showUI = (int)key.GetValue("ShowUI");
   if (showUI != 0)
   {
   key.SetValue("ShowUI", 0);
   }
   }
   catch
   {
   }
  
  
   m_Error = "";
   m_Output = "";
   try
   {
   System.Diagnostics.Process p = new System.Diagnostics.Process();
  
   p.StartInfo.FileName = fileName;
   p.StartInfo.Arguments = para;
   p.StartInfo.UseShellExecute = false;
   p.StartInfo.RedirectStandardInput = true;
   p.StartInfo.RedirectStandardOutput = true;
   p.StartInfo.RedirectStandardError = true;
   p.StartInfo.CreateNoWindow = true;
  
   p.Start();
  
   ReadErrorThread readErrorThread = new ReadErrorThread(p);
  
   while (!p.HasExited)
   {
   outputStr.Append(p.StandardOutput.ReadLine()+"\r\n");
   }
  
   outputStr.Append(p.StandardOutput.ReadToEnd());
  
   while (!readErrorThread.HasExisted)
   {
   Thread.Sleep(1);
   }
  
   m_Error = readErrorThread.Error;
   m_Output = outputStr.ToString();
   }
   catch (Exception e)
   {
   m_Error = e.Message;
   }
   }
  
   }
  }
目录
相关文章
|
2月前
|
开发框架 .NET C#
C#|.net core 基础 - 删除字符串最后一个字符的七大类N种实现方式
【10月更文挑战第9天】在 C#/.NET Core 中,有多种方法可以删除字符串的最后一个字符,包括使用 `Substring` 方法、`Remove` 方法、`ToCharArray` 与 `Array.Copy`、`StringBuilder`、正则表达式、循环遍历字符数组以及使用 LINQ 的 `SkipLast` 方法。
|
3月前
|
存储 C# 索引
C# 一分钟浅谈:数组与集合类的基本操作
【9月更文挑战第1天】本文详细介绍了C#中数组和集合类的基本操作,包括创建、访问、遍历及常见问题的解决方法。数组适用于固定长度的数据存储,而集合类如`List<T>`则提供了动态扩展的能力。文章通过示例代码展示了如何处理索引越界、数组长度不可变及集合容量不足等问题,并提供了解决方案。掌握这些基础知识可使程序更加高效和清晰。
86 2
|
2月前
|
Java 程序员 C#
【类的应用】C#应用之派生类构造方法给基类构造方法传参赋值
【类的应用】C#应用之派生类构造方法给基类构造方法传参赋值
14 0
|
2月前
|
安全 API C#
C# 如何让程序后台进程不被Windows任务管理器强制结束
C# 如何让程序后台进程不被Windows任务管理器强制结束
71 0
|
3月前
|
C# 数据安全/隐私保护
C# 一分钟浅谈:类与对象的概念理解
【9月更文挑战第2天】本文从零开始详细介绍了C#中的类与对象概念。类作为一种自定义数据类型,定义了对象的属性和方法;对象则是类的实例,拥有独立的状态。通过具体代码示例,如定义 `Person` 类及其实例化过程,帮助读者更好地理解和应用这两个核心概念。此外,还总结了常见的问题及解决方法,为编写高质量的面向对象程序奠定基础。
28 2
|
3月前
|
SQL 网络协议 数据库连接
已解决:连接SqlServer出现 provider: Shared Memory Provider, error: 0 - 管道的另一端上无任何进程【C#连接SqlServer踩坑记录】
本文介绍了解决连接SqlServer时出现“provider: Shared Memory Provider, error: 0 - 管道的另一端上无任何进程”错误的步骤,包括更改服务器验证模式、修改sa用户设置、启用TCP/IP协议,以及检查数据库连接语句中的实例名是否正确。此外,还解释了实例名mssqlserver和sqlserver之间的区别,包括它们在默认设置、功能和用途上的差异。
|
4月前
|
C#
C#中的类和继承
C#中的类和继承
45 6
|
4月前
|
Java C# 索引
C# 面向对象编程(一)——类
C# 面向对象编程(一)——类
35 0
|
4月前
|
开发框架 .NET 编译器
C# 中的记录(record)类型和类(class)类型对比总结
C# 中的记录(record)类型和类(class)类型对比总结
|
6月前
|
开发框架 .NET 编译器
程序与技术分享:C#基础知识梳理系列三:C#类成员:常量、字段、属性
程序与技术分享:C#基础知识梳理系列三:C#类成员:常量、字段、属性
41 2