应用程序的重定向

今天有一个网友通过“发送短消息”向我提了一个题为《关于“C#重定向问题”的探讨》问题。本着对网友的负责,我决定写下完整示例。

关于网友所提的问题,因为描述的信息不够充足,我很难告知具体是哪里出了问题,直觉判断是masm.exe中的程序可能有点问题,但也不排除其他可能。希望能够提供更多的信息进行排错。

既然我写下了示例,而且自己也不曾做过相关的内容,就博留个脚印。

基本思路

应用程序的重定向问题基本上是通过启动另外一个进程运行另外的程序来执行一些处理,如果处理结果有返回值,主进程可以通过输出流的方式来进行获取。

准备

本示例采用WPF作为主进程的宿主程序,并通过构建控制台程序作为后台处理的程序进行处理。虽然采用WPF进行构建,但并不代表该文章所描述的内容仅限于此,WPF只是作为一个可视界面方便查看。如果您熟悉Winform也可以采用Winform编程的方式进行构建,甚至您仅用控制台应用程序也是可以的。(需要做一些小的改动)

基本步骤(简略)

1.添加如图所示的项目文件和程序界面:

WProcessRedirect

2.分别为两个按钮添加事件处理程序:

左侧:其中白色文本框Name=txtInput,蓝色文本框Name=txtOutput,RunProcessSync执行同步调用进程的操作,RunProcessAsync执行异步调用进程的操作

右侧:CA_Processer用于处理具体操作的一个简单的控制台应用程序,WpfAppProcess即为左侧所示宿主程序

同步:

同步的操作相对比较简单,也就是当按下按钮后,启动另外一个进程进行处理,我们可以在处理完毕后从StandardOutput对象中获取处理进程输出流。

代码如下:


         #region  Sync

        
private   void  RunProcessSync_Click( object  sender, RoutedEventArgs e)
        {
            
string  input  =  txtInput.Text;

            ProcessStartInfo info 
=  InitializeProcessStartInfo(input);

            txtOutput.Text 
=  DoProcessSync(info);
        }

        
///   <summary>
        
///  同步处理COMMAND
        
///   </summary>
        
///   <param name="info"></param>
        
///   <param name="input"></param>
        
///   <returns></returns>
         private   string  DoProcessSync(ProcessStartInfo info)
        {
            Process pro 
=  Process.Start(info);
            
return  pro.StandardOutput.ReadToEnd();
        }

        
#endregion

异步:

异步的操作仅仅只是相对比较麻烦一点,因为是示例,所以也看不出什么麻烦来。同样是启动另外一个进程进行处理,但是在处理的过程中可以通过调用订阅到OutputDataReceived的委托将数据分批获取下来。显然异步的操作带给我们更多的便利,特别是较长进程下给用户带来了更优质的用户体验。用户不必等处理完毕后才能获取输出流。

代码如下:


         #region  Async

        
private   void  RunProcessAsync_Click( object  sender, RoutedEventArgs e)
        {
            ProcessStartInfo info 
=  InitializeProcessStartInfo(txtInput.Text);
            
try
            {
                txtOutput.Text 
=  DoProcessAsync(info);
            }
            
catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }

        
///   <summary>
        
///  异步处理COMMAND
        
///   </summary>
        
///   <param name="info"></param>
        
///   <param name="input"></param>
        
///   <returns></returns>
         private   string  DoProcessAsync(ProcessStartInfo info)
        {
            
// 异步操作需要指定委托进行异步处理
            Process pro  =   new  Process();
            pro.OutputDataReceived 
+=   new  DataReceivedEventHandler(pro_OutputDataReceived);
            pro.StartInfo 
=  info;

            pro.Start();
            
// 此调用将启动 StandardOutput 上的异步读取操作。
            pro.BeginOutputReadLine();

            pro.WaitForExit();

            pro.Close();
            
return  _output;
        }

        
string  _output  =   string .Empty;
        
void  pro_OutputDataReceived( object  sender, DataReceivedEventArgs e)
        {
            
// 可通过调用 CancelOutputRead 取消异步读取操作。可通过调用方或事件处理程序取消读取操作。取消之后,可以再次调用 BeginOutputReadLine 继续进行异步读取操作。
             if  ( ! string .IsNullOrEmpty(e.Data))
            {
                _output 
+=  e.Data;
            }
        }

        
#endregion
以下文字来自MSDN(Process.BeginOutputReadLine):

Process.BeginOutputReadLine

可同步或异步读取 StandardOutput 流。Read、ReadLine 和 ReadToEnd 等方法对进程的输出流执行同步读取操作。这些同步读取操作只有在关联的 Process 写入其 StandardOutput 流或关闭该流后才能完成。

相反,BeginOutputReadLine 在 StandardOutput 流上开始异步读取操作。此方法为流输出启用一个指定的事件处理程序并立即返回到调用方,这样当流输出被定向到该事件处理程序时,调用方可以执行其他操作。

按照这些步骤对 Process 的 StandardOutput 执行异步读取操作:

  1. 将 UseShellExecute 设置为 false。





  2. 将 RedirectStandardOutput 设置为 true。





  3. 向 OutputDataReceived 事件添加事件处理程序。事件处理程序必须与 System.Diagnostics..::.DataReceivedEventHandler 委托签名相匹配。





  4. 启动 Process。





  5. 调用 Process 的 BeginOutputReadLine。此调用将启动 StandardOutput 上的异步读取操作。





启动异步读取操作时,关联的 Process 每向其 StandardOutput 流写入一行文本时,都将调用该事件处理程序。

可通过调用 CancelOutputRead 取消异步读取操作。可通过调用方或事件处理程序取消读取操作。取消之后,可以再次调用 BeginOutputReadLine 继续进行异步读取操作。

注意:

您不能对同一个重定向流混合使用异步和同步读取操作。在异步或同步模式下打开 Process 的重定向流后,对该流的所有进一步的读取操作都必须在同一模式下进行。例如,不要对 StandardOutput 流调用 BeginOutputReadLine 后接着调用 ReadLine,反之亦然。但是,您可以在不同的模式下读取两个不同的流。例如,您可以先调用 BeginOutputReadLine,然后再为 StandardError 流调用 ReadLine。

注意到上文中有ProcessStartInfo信息,我们通过如下的代码进行初始化:


         ///   <summary>
        
///  初始化ProcessStartInfo对象
        
///   </summary>
        
///   <param name="input"></param>
        
///   <returns></returns>
         private   static  ProcessStartInfo InitializeProcessStartInfo( string  input)
        {
            
// 参数1:启动程序路径
            
// 参数2:命令行参数
            ProcessStartInfo info  =   new  ProcessStartInfo(
    
@" C:\GoCoolCenter\MyCSharpProject\WpfApplication\WpfAppProcess\CA_Processer\bin\Debug\CA_Processer.exe " ,
    input);
            
// 因为要使用StandardOutput,因此以下两个属性均需要如此设置。
            info.UseShellExecute  =   false ;
            info.RedirectStandardOutput 
=   true ;

            info.WindowStyle 
=  ProcessWindowStyle.Hidden;
            
return  info;
        }
另:我们可以通过CancelOutputRead和BeginOutputReadLine来实现“停止”和“继续”的功能。
CA_Processer是一个简单的控制台命令程序,仅作为一个示例,关键在于我们能够“输入”,也能够“输出”了。代码如下:

namespace  CA_Processer
{
    
class  Program
    {
        
static   void  Main( string [] args)
        {
            
string  result  =   string .Empty;
            
foreach  ( string  arg  in  args)
            {
                result 
+=   ""   +  arg;
            }
            Console.Write(
" Processer args = {0} " , result);
        }
    }
}

通过Console.Write的方式就可以将指定信息写入标准输出流。

除了通过刚才的参数进行传递,我们还可以使用StandardInput的方式指定同步输入流。

步骤:

A.修改宿主程序代码:


         ///   <summary>
        
///  异步处理COMMAND
        
///   </summary>
        
///   <param name="info"></param>
        
///   <param name="input"></param>
        
///   <returns></returns>
         private   string  DoProcessAsync(ProcessStartInfo info)
        {
            
// 异步操作需要指定委托进行异步处理
            Process pro  =   new  Process();
            pro.OutputDataReceived 
+=   new  DataReceivedEventHandler(pro_OutputDataReceived);
            pro.StartInfo 
=  info;

            pro.StartInfo.RedirectStandardInput 
= true ;

            pro.Start();
            
// 此调用将启动 StandardOutput 上的异步读取操作。
            pro.BeginOutputReadLine();

            System.IO.StreamWriter swInput 
= pro.StandardInput;
            swInput.Write(
"-=StandardInput=-"
);
            swInput.Close();


            pro.WaitForExit();

            pro.Close();
            
return  _output;
        }
B.修改处理程序:

namespace  CA_Processer
{
    
class  Program
    {
        
static   void  Main( string [] args)
        {
            
string  result  =   string .Empty;
            
foreach  ( string  arg  in  args)
            {
                result 
+=   ""   +  arg;
            }
            Console.Write(
" Processer args = {0}{1} " Console.ReadLine(), result);
        }
    }
}
示例下载:
WpfAppProcess(http://files.cnblogs.com/volnet/WpfAppProcess.rar)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值