从ASP.NET启动外部进程

6
我需要从我的 Web 应用程序中启动一个外部进程。这意味着使用 System.Diagnostics.ProcessStartInfo 调用和执行控制台应用程序 (exe)。但是,在其执行期间,我需要以某种方式确保没有出现任何错误,并知道应用程序何时完成其工作。 最好的方法是什么,既能捕获所有可能的错误,又能找出何时完成?
6个回答

4

使用Process类并不难。但是,前面的回复者是正确的 - 你需要关注权限问题。

private string RunProcess(string cmd)
{
  System.Diagnostics.Process p; 
  p= new System.Diagnostics.Process();
  if (cmd== null || cmd=="") {
    return "no command given.";
  }
  string[] args= cmd.Split(new char[]{' '});
  if (args== null || args.Length==0 || args[0].Length==0) {
    return "no command provided.";
  }
  p.StartInfo.FileName= args[0];

  if (args.Length>1) {
    int startPoint= cmd.IndexOf(' ');
    string s= cmd.Substring(startPoint, cmd.Length-startPoint);
    p.StartInfo.Arguments= s; 
  }
  p.StartInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;
  p.StartInfo.RedirectStandardOutput = true;
  p.StartInfo.UseShellExecute = false;

  p.Start();

  // must have the readToEnd BEFORE the WaitForExit(), to avoid a deadlock condition
  string output= p.StandardOutput.ReadToEnd();
  p.WaitForExit();

  return output; 
}

你可以用 string.IsNullOrEmpty(cmd) 替换掉 cmd == null || cmd =="",而且 args == null 会一直为真。 - Sven Hecht

2
希望这个答案能够帮到您。

https://dev59.com/W2zXa4cB1Zd3GeqPVJWw#14107672

这是一段代码片段,希望能有所帮助。

http://ss64.com/nt/cmd.html 获取cmd.exe帮助信息

private int CallShell(string exeCommand, string Parameters)
        {
            //http://ss64.com/nt/cmd.html
            /*
            This function will actually take the shell string and envoke the appropriate process
             passing it the arguments to do the work
            */

            // Initialize the process and its StartInfo properties.
            System.Diagnostics.Process ProcessEXE = new System.Diagnostics.Process();

            logger.DebugFormat("About to Start Process - {0} {1}",exeCommand, Parameters);
            try
            {

                ProcessEXE.StartInfo.FileName = exeCommand;


                // Set UseShellExecute to false for redirection.
                //  false if the process should be created directly from the executable file
                ProcessEXE.StartInfo.UseShellExecute = false;
                ProcessEXE.StartInfo.WorkingDirectory = System.Environment.CurrentDirectory;

                //EnableRaisingEvents property indicates whether the component should be notified when the operating system has shut down a process

                ProcessEXE.StartInfo.Arguments = Parameters;

                ProcessEXE.StartInfo.RedirectStandardOutput = true;
                ProcessEXE.StartInfo.RedirectStandardError = true;
                ProcessEXE.EnableRaisingEvents = true;
                ProcessEXE.StartInfo.CreateNoWindow = true;

                ProcessEXE.OutputDataReceived += new System.Diagnostics.DataReceivedEventHandler(ProcessEXE_OutputDataReceived);
                ProcessEXE.ErrorDataReceived += new System.Diagnostics.DataReceivedEventHandler(ProcessEXE_OutputDataReceived);

                logger.DebugFormat("Process Started.");
                ProcessEXE.Start();


                // Start the asynchronous read of the sort output stream.
                ProcessEXE.BeginErrorReadLine();
                ProcessEXE.BeginOutputReadLine();

                //The WaitForExit overload is used to make the current thread wait until the associated process terminates
                logger.DebugFormat("Process Waiting for exit.");
                ProcessEXE.WaitForExit();

                if (ProcessEXE.ExitCode == 0)
                {
                    logger.Debug(string.Format("Shell Process exited with exit code {0}", ProcessEXE.ExitCode));
                }
                else
                {
                // throw error here if required - check the return error code
                    logger.Warn(string.Format("Shell Process exited with exit code {0}", ProcessEXE.ExitCode));
                }
            }
            catch (Exception ex)
            {
                throw new Exception(string.Format("Method:{0}", ex.TargetSite), ex);
            }

            return ProcessEXE.ExitCode;
        }


void ProcessEXE_OutputDataReceived(object sender, System.Diagnostics.DataReceivedEventArgs e)
        {
            try
            {
                StringBuilder sb = new StringBuilder(string.Empty);
                if (e == nullreturn;

                if (e.Data == null)
                {
                    //No processing
                }
                else
                {
                   // your logic to detect error msg out from output - if at all required
                }

                if (sb.ToString().ToUpper().IndexOf("ERROR") > 0)
                {
                    string smessage = "Error text found in  output.";

                // do your error response action here .

                }
            }
            catch (Exception exp)
            {
                logger.ErrorFormat("Error in ProcessEXE_OutputDataReceived Message:{0}", exp.Message);
                logger.ErrorFormat("Error in ProcessEXE_OutputDataReceived Data Received:{0}", e.Data);

            // either throw error msg or kill the process 
            }
            finally
            {
                // Not throwing the exception
            }
        }

2

我希望你能控制外部应用程序的代码,否则你将会面临很多麻烦。最重要的事情是确保该应用程序没有挂起并且可以终止。

然后,您可以使用WaitForExit(),ExitCode,StandardError,StandardOut来“捕获所有可能的错误并找出何时完成”。


2

建议您最好捕获控制台应用程序的所有输出并将其存储在某个地方,以便在状态页面上显示它,而不是等待应用程序完成。

正如其他人所说,否则你会经历痛苦。


谢谢。好的评论。我会让输出可见。 - Riri

1

你会遇到问题的。ASP.NET Web页面在严格的安全上下文中运行,甚至可能无法启动外部进程。

此外... ASP.NET将随意回收(停止/重新启动所有进程)。这意味着在任何时刻,Web页面都可能被中止执行。

您考虑过使用作业/任务调度程序吗?SQL Server(非Express版本)附带的调度程序非常好。


1
不会有任何时候 ASP.NET 中止执行任何操作。工作进程可以回收,但正在进行的事务不会丢失,除非出现应用程序错误。 - Cheeso
这个项目没有使用SQL Server,而是一个外部工具,我想开始它。它没有计划安排,但应该由Web应用程序中的用户操作触发。 - Riri

1

你无法从外部程序捕获任何错误。最多只能将其输出重定向到自己的流中,并希望在失败/成功时它会写入一些内容。您还可以使用Process.ExitCode检查进程退出代码。

您可以使用Process.Exited事件或Process.HasExited属性测试进程是否已完成。

此外,您应该注意,默认情况下,Asp.Net代码在NETWORK SERVICE进程(iis6及以上)下运行,因此它将具有受限权限,登录用户将无法看到其UI。

@rbobby:您可以在Asp.Net代码内部启动外部进程,但它将继承Asp.Net代码的安全上下文。


您可以使用模拟身份来更改安全上下文。此外,您还可以捕获从System.Diagnostics.Process发送到stdout和stderr的所有内容。 - Moose
要使用模拟身份更改安全上下文,您必须知道用户名和密码。您还可以配置AppPool以使用不同的用户。但并非每个人都会允许您这样做(更改AppPool或创建用户)。 - Alex Reitbort
我说过你可以重定向程序输出,但如果程序崩溃了,这也无济于事。 - Alex Reitbort
安全性还好。我已经在应用程序池的用户设置下以不同的账户运行它了。 - Riri

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接