`

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

    博客分类:
  • c#
阅读更多

作者:肖波 

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

 

 

 

 

分享到:
评论

相关推荐

    C#实现异步调用外部程序

    总结起来,C#实现异步调用外部程序的关键在于理解和运用`async/await`、`Process`类以及异步等待的策略。通过这种方式,我们可以在不阻塞主线程的情况下执行外部程序,并获取其输出,从而提高应用程序的性能和用户...

    C# 调用外部dll的方法

    【C#调用外部DLL的方法】是C#开发者在处理特定功能或集成其他库时的常见需求。DLL(Dynamic Link Library)是Windows操作系统的核心组成部分,它包含一系列可供不同程序调用的函数或方法。这些函数在需要时由系统...

    c#的winform调用外部exe作为子窗体

    - 如果外部exe是一个WinForm应用,可以通过反编译工具(如ILSpy或Reflector)查看其窗体类,然后在项目中创建一个对应的窗体类。 2. **调用外部exe作为子窗体** - 使用`System.Diagnostics.Process`类来启动和...

    C#写的Windows服务调用外部EXE通用服务可配置多个外部程序,注册后就可以

    单开一看是外部EXE的界面,我晕,一看这个exe的界面有点吓到我,显示很是蹩脚啊,哈哈,感觉这个仍然不是一个解决办法,后面有继续百度啊,后面发现一些博客里面有一个关于调用一个第三方的Cjwdev.WindowsApi.dll就...

    C#_调用外部dll

    标题所述的"C#_调用外部dll"是指在C#...总结,C#调用外部DLL涉及到对DLL的理解、`DllImport`特性的使用以及可能的数据封送规则。正确配置这些参数,开发者可以灵活地利用各种非托管库的功能,扩大C#应用程序的能力。

    C#调用外部程序(整理)

    要调用外部程序,我们需要创建`Process`类的一个实例,并设置其`ProcessStartInfo`属性。`ProcessStartInfo`包含了多个用于控制进程启动方式的属性,如`FileName`(程序路径和名称)、`Arguments`(程序参数)等。 ...

    winform窗体怎么来调用外部*.exe文件

    2. 创建Process对象:这是调用外部进程的第一步,通过Process类的实例化,我们可以获取或设置与进程相关的属性。 ```csharp Process process = new Process(); ``` 3. 设置进程启动信息:通过ProcessStartInfo对象...

    C# 调用WindowAPI 挂起、恢复 外部进程的源码

    在.NET框架中,有时我们需要对操作系统进行更底层的控制,比如挂起或恢复外部进程的操作。这通常通过调用Windows API来实现,因为.NET Framework的标准库并不直接提供这些功能。本示例主要介绍如何在C#中利用Window...

    C#关闭外部程序

    在C#编程中,关闭外部程序是一个常见的任务,特别是在系统集成、自动化测试或者需要管理其他应用程序的状态时。本文将深入探讨如何使用C#来实现这一功能,并提供相关的知识点。 首先,我们要了解C#中用于与操作系统...

    使用C#8.0中的异步流简化调用外部进程。_C#_下载.zip

    "使用C#8.0中的异步流简化调用外部进程"这个资源包可能包含了如何在C# 8.0中利用异步流优化`Process`类的示例和指导,帮助开发者编写更高效、更易读的代码,同时保持应用的响应性。通过理解异步流的概念以及如何与`...

    C#调用外部可执行exe文件

    首先,让我们了解C#调用外部exe的基本方法。在C#中,可以使用`System.Diagnostics.Process`类来启动、控制和与外部应用程序交互。以下是一个简单的示例代码: ```csharp using System; using System.Diagnostics; ...

    C# 判断并激活外部程序窗口 并实现外部程序窗口最大化

    C# 判断并激活外部程序窗口 并实现外部程序窗口最大化,不错的代码与大家分享

    c# winform调用cmd命令+写bat脚本并执行

    (2)调用外部工具:有些功能无法用 C# 直接实现,但可以通过调用 cmd 执行相应的命令行工具来实现。这样可以充分利用现有的工具和资源,提高开发效率。 (3)灵活性和可扩展性:cmd 提供了丰富的命令和选项,可以...

    C#调用WPS2016方法和常见问题处理代码

    首先,让我们深入理解C#调用外部程序的方法。在C#中,可以使用`System.Diagnostics.Process`类来启动、控制和与外部应用程序交互。通过创建`Process`对象,设置其`StartInfo`属性,然后调用`Start()`方法,即可启动...

    C#怎样调用外部的Exe文件

    总的来说,C#调用外部Exe文件的能力使得我们可以灵活地集成各种工具和资源,极大地扩展了应用程序的功能。通过熟练掌握`System.Diagnostics.Process`类的使用,我们可以实现复杂的应用场景,例如自动化测试、数据...

    C#调用外部程序.zip

    - 在VS2010中创建一个新的Winform项目,主界面通常会包含一个或多个控件,如按钮(Button)。 - 按钮控件提供了点击事件(Click),我们可以在事件处理函数中编写调用外部程序的代码。 2. **Process类**: - ...

    C#中利用process类调用外部程序以及执行dos命令

    在C#编程中,`System.Diagnostics.Process` 类是用于与外部进程交互的重要工具。通过这个类,我们可以启动、控制和管理应用程序,甚至可以执行DOS命令。以下将详细阐述如何利用`Process`类来调用外部程序并执行DOS...

    使用C#调用外部Ping命令获取网络连接情况

    以下是如何使用C#调用外部Ping命令获取网络连接情况的详细步骤: 首先,我们需要引入`System.Diagnostics`命名空间,因为其中包含了`Process`类,它是用于创建和管理进程的关键类。在代码顶部添加`using System....

    C#调用wps用例CSharp_WPSStartKit

    首先,我们来看标题"C#调用wps用例CSharp_WPSStartKit",这表明这是一个使用C#语言编写的具体示例,用于演示如何在代码中启动和控制WPS应用程序。这个项目可能是通过COM(Component Object Model)接口来实现的,...

Global site tag (gtag.js) - Google Analytics