【问题标题】:Service hangs up at WaitForExit after calling batch file调用批处理文件后服务在 WaitForExit 挂起
【发布时间】:2010-09-26 13:00:03
【问题描述】:

我有一项服务有时会调用批处理文件。批处理文件执行需要 5-10 秒:

System.Diagnostics.Process proc = new System.Diagnostics.Process(); // Declare New Process
    proc.StartInfo.FileName = fileName;
    proc.StartInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;
    proc.StartInfo.CreateNoWindow = true;
    proc.Start();
    proc.WaitForExit();

当我在控制台中运行相同的代码时,该文件确实存在并且代码有效。但是,当它在服务内部运行时,它会在WaitForExit() 挂断。我必须从进程中终止批处理文件才能继续。 (我确定该文件存在,因为我可以在进程列表中看到它。)

我该如何解决这个挂断问题?

更新 #1:

Kevin 的代码可以让我得到输出。我的一个批处理文件仍然挂起。

"C:\EnterpriseDB\Postgres\8.3\bin\pg_dump.exe" -i -h localhost -p 5432 -U postgres -F p -a -D -v -f "c:\backupcasecocher\backupdateevent2008.sql " -t "\"public\".\"dateevent\"" "DbTest"

另一个批处理文件是:

"C:\EnterpriseDB\Postgres\8.3\bin\vacuumdb.exe" -U postgres -d DbTest

我检查了路径,postgresql 路径很好。输出目录确实存在并且仍然在服务之外工作。有什么想法吗?

更新 #2:

我为proc.StartInfo.FileName写了“C:\EnterpriseDB\Postgres\8.3\bin\pg_dump.exe”而不是批处理文件的路径,并将所有参数添加到proc.StartInfo.Arguments。结果没有改变,但我在进程窗口中看到了pg_dump.exe。同样,这只发生在服务内部。

更新 #3:

我已经使用管理员组中的用户运行该服务,但无济于事。我为服务的用户名和密码恢复了null

更新 #4:

我创建了一个简单的服务来在事件日志中写入跟踪并执行其中包含“dir”的批处理文件。现在它将挂在proc.Start(); - 我尝试将帐户从 LocalSystem 更改为 User 并设置管理员用户和密码,但仍然没有。

【问题讨论】:

  • @nzpcmad:你到底是从哪里想到改变 OP 的标签的?
  • 我不是 nzpcmad,但我的猜测是:既然他说“我有一个 Web 服务”,我假设他有一个 Web 服务 (.asmx) 而不是 Windows 服务。
  • @Michael:我不明白这如何保证/更改/ OP 的标签。
  • 不是webservice,第一行写的,嗯,可能很累。好的是“服务”。对不起。
  • @Daok:我想通了,但标签还是由你决定。

标签: c# .net service windows-services


【解决方案1】:

这是我用来执行批处理文件的:

proc.StartInfo.FileName                 = target;
proc.StartInfo.RedirectStandardError    = true;
proc.StartInfo.RedirectStandardOutput   = true;
proc.StartInfo.UseShellExecute          = false;

proc.Start();

proc.WaitForExit
    (
        (timeout <= 0)
            ? int.MaxValue : timeout * NO_MILLISECONDS_IN_A_SECOND *
                NO_SECONDS_IN_A_MINUTE
    );

errorMessage = proc.StandardError.ReadToEnd();
proc.WaitForExit();

outputMessage = proc.StandardOutput.ReadToEnd();
proc.WaitForExit();

我不知道这是否对你有用,但我没有挂起的问题。

【讨论】:

  • 是的.. 照凯文说的做。就像我提到的那样,他正在重定向 stderr 和 stdout,但最大的区别之一是他将“UseShellExecute”设置为 false,这是您在 Vista 中必须考虑的事情......很好的答案凯文......拉里跨度>
  • 让我试一试!之后我会回来的。
  • 为什么一秒内没有毫秒?
  • 我猜 NO 在这种情况下是数字的快捷方式。
  • 我认为这与批处理文件上的参数有关。我遇到了我的问题,不得不把它们拿出来。我没时间了,不得不在没有他们的情况下找到一个快速的解决方案。如果没有人发布解决方案,我明天早上会在工作中闲逛。
【解决方案2】:

批处理文件有什么作用?您确定该进程正在以足够的权限启动以执行批处理文件吗?服务可以被限制在他们被允许做的事情上。

还要确保您是否正在执行类似使用复制命令覆盖文件的操作:

echo Y | copy foo.log c:\backup\

此外,请确保您使用批处理命令等的完整路径。如果批处理文件以某种“控制台”模式启动 GUI 应用程序,这也可能是一个问题。请记住,服务没有“桌面”(除非您启用“与桌面交互”)来绘制任何类型的窗口或消息框。在您的程序中,您可能希望打开 stdout 和 stderr 管道并在执行期间从它们中读取,以防您收到任何错误消息或任何东西。

WebServices 可能作为 IUSR 帐户或匿名帐户执行,无论哪种方式,这对您来说可能是个问题。如果在控制台中运行它时它有效,那只是第一步。 :)

我不记得是否 System.Diagnostics。是否仅在调试中可用。可能不是,但其中一些可能是。我得帮你查一下。

希望这能给你一些想法。

拉里

【讨论】:

    【解决方案3】:

    pg_dump.exe 可能会提示用户输入。这个数据库需要认证吗?您是否依赖于服务中不存在的任何 ENVIRONMENT 变量?我不知道 pg_dump 但它会提示输入的其他可能原因是什么?

    【讨论】:

    • 我可以从 cmd.exe 的命令行中执行此命令,并且无需提示即可工作。
    【解决方案4】:

    下一步我将启动调试器,看看你是否能知道程序正在等待什么。如果您在汇编调试方面有经验,您可以使用 ProcExp、FileMon 等工具了解正在发生的事情。

    作为 Windows SERVICE,而不是 Web 服务,会有很大的不同。无论如何,您是否尝试过我设置“允许服务与桌面交互”的建议?

    如果您不顾一切,您可以尝试启动 cmd.exe 而不是您的批处理文件。然后,使用 cmd.exe 的 cmd 行参数,您可以让 IT 启动批处理文件。如果您打开与桌面交互,这可能会给您一个 cmd 提示窗口来查看实际输出。

    有关 cmd.exe 的完整帮助,只需键入 cmd /?在任何命令提示符下。

    拉里

    【讨论】:

    • 现在批处理文件只包含“dir”。我已经在真实项目之外创建了一个解决方案,我正在尝试执行。它仍然挂起。我的最后一条日志就在 proc.Start() 方法之前。
    【解决方案5】:

    这里是解决方案。解决方案不清楚,因为我已经更改了很多次代码,现在它正在工作!

    我尝试使用用户帐户,但它不起作用。使用本地系统。这是执行的代码,主要是 Kevin 给我的。

                System.Diagnostics.Process proc = new System.Diagnostics.Process();
                proc.StartInfo.FileName = fileName;
                proc.StartInfo.RedirectStandardError = true;
                proc.StartInfo.RedirectStandardOutput = true;
                proc.StartInfo.UseShellExecute = false;
    
    
                proc.Start();
                proc.WaitForExit();
                output1 = proc.StandardError.ReadToEnd();
                proc.WaitForExit();
                output2 = proc.StandardOutput.ReadToEnd();
                proc.WaitForExit();
    

    谢谢大家,我会为大家投票并接受凯文,因为他从一开始就帮助我。很奇怪,因为它现在可以工作了......

    【讨论】:

    • 如果您只是做一次性的事情,这很好,但请不要将运行脚本的软件作为本地系统发布,这很难保护
    • 是的。相反,让他们执行 ActiveScript,所以攻击者至少可以使用 vbScript 来让球滚动...... :) (当然,你们都必须知道我只是在开玩笑。我很高兴他找到了他的解决方案,只是很生气凯文先做对了。呵呵..) :) J/K AGAIN。现在没有人生我的气了……
    【解决方案6】:

    Daok,看起来您唯一更改的是初始 WaitForExit() 的超时时间。你需要非常小心。如果某些东西确实 挂起您的服务,它将永远不会返回(而且,到目前为止,您所做的工作几乎与它一样......呵呵),但它对最终用户不利。 ..

    现在,也许您知道是什么导致它挂起,您可以进一步调试它并找到完整的解决方案...

    那个,或者在你可以监控的某个线程中分拆它,如果它挂得太久,就杀死它。

    只有我的 2 美分,通常不是很多。 ;)

    【讨论】:

      【解决方案7】:
      using System;
          using System.Collections.Generic;
          using System.Linq;
          using System.Text;
          using System.Diagnostics;
          namespace VG
          {
              class VGe
              {
                  [STAThread]
                  static void Main(string[] args)
                  {
                      Process proc = null;
                      try
                      {                
                          string targetDir = string.Format(@"D:\adapters\setup");//this is where mybatch.bat lies
                          proc = new Process();
                          proc.StartInfo.WorkingDirectory = targetDir;
                          proc.StartInfo.FileName = "mybatch.bat";
                          proc.StartInfo.Arguments = string.Format("10");//this is argument
                          proc.StartInfo.CreateNoWindow = false;
                          proc.Start();
                          proc.WaitForExit();
                      }
                      catch (Exception ex)
                      {
                          Console.WriteLine("Exception Occurred :{0},{1}", ex.Message,ex.StackTrace.ToString());
                      }
                  }
              }
          }
      

      【讨论】:

      • 和我一开始的差不多,不是吗?
      【解决方案8】:
                  string targetDir = string.Format(@"D:\");//PATH
                  proc = new Process();
                  proc.StartInfo.WorkingDirectory = targetDir;
                  proc.StartInfo.FileName = "GetFiles.bat";
                  proc.StartInfo.Arguments = string.Format("10");//argument
                  proc.StartInfo.CreateNoWindow = false;
                  proc.Start();
                  proc.WaitForExit();
      

      经过测试,工作清晰。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2017-12-04
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-09-02
        相关资源
        最近更新 更多