【问题标题】:Process spawned through Process.Start in .NET hangs the thread在 .NET 中通过 Process.Start 生成的进程会挂起线程
【发布时间】:2011-03-31 03:16:37
【问题描述】:

我们的应用程序有一个后台线程,它通过System.Diagnostics.Process 产生一个进程:

Process.Start(
    new ProcessStartInfo
    {
        FileName = url,
        UseShellExecute = true
    }
);

这过去完全没有问题。但是现在,后台线程正在悄然消亡;它永远不会从对Process.Start 的调用中返回。处理System.Exception 的这段代码的catch 块也没有到达。即使我在 Visual Studio 调试器中引发异常时启用处理,我也看不到异常。奇怪的是,这个过程正在生成就好了。用户的默认浏览器使用预期的 URL 启动。

我们流程的入口点按照推荐标记为[STAThread]。

什么可能导致我们的线程静默终止?有什么技术可以用来调试线程终止期间发生的事情吗?

更新:

看起来线程毕竟是活着的;它只是没有从通话中返回。这是它的堆栈跟踪:

  • [在睡眠等待或加入]
  • System.dll!System.Diagnostics.ShellExecuteHelper.ShellExecuteOnSTAThread() + 0x63 字节
  • System.dll!System.Diagnostics.Process.StartWithShellExecuteEx(System.Diagnostics.ProcessStartInfo startInfo) + 0x19d 字节
  • System.dll!System.Diagnostics.Process.Start() + 0x39 字节
  • System.dll!System.Diagnostics.Process.Start(System.Diagnostics.ProcessStartInfo startInfo) + 0x32 字节
  • 我的方法

更新 2:

在不使用 shell 执行的情况下启动 cmd.exe 是一种解决方法。非常感谢!但是,我仍然想知道为什么电话没有返回。

更新 3:

Shell 挂钩听起来确实是对可能导致调用不返回的原因的合乎逻辑的解释。我找不到流氓模块,但在最后一次尝试通过 shell 执行运行之后,调用 did 返回了。

在任何情况下,用户都可能加载了 shell 扩展,这可能会干扰进程启动并导致我的代码无法返回。我们对那个无能为力,所以正确的答案是使用启动 cmd.exe 进程的解决方法。

【问题讨论】:

    标签: .net multithreading shell browser process


    【解决方案1】:

    不,线程不会静默终止,它们会发出响亮的咔嚓声。至少您会在“输出”窗口中看到线程退出通知。 Process.Start() 方法阻塞将是另一种解释,尽管没有任何解释。你的 sn-p 太短了,无法做出像样的诊断。也许是一些环保的东西。


    您的堆栈跟踪有所帮助,ShellExecuteOnSTAThread() 实际上确实在一个小辅助线程上执行了阻塞 Thread.Join()。该线程是调用本机 ShellExecuteEx() API 函数所必需的,它只能从 STA 线程调用。但是它有一个缺陷,STA 线程还必须泵送消息循环。这个小帮手没有。

    这会导致您的机器出现问题,这仍然指向环境问题,即某种劫持 ShellExecuteEx() 调用的系统插件。并依靠运行真正的 STA 线程。您应该能够在 Debug + Windows + Threads 窗口中找到该辅助线程。它应该在堆栈上包含“ShellExecuteFunction”。例如,具有这种特技功能的“系统插件”就是病毒扫描程序。在项目的“调试”选项卡中选中“启用非托管调试”后,您应该能够在“调试 + Windows + 模块”窗口中找到该外星人软件。

    顺便说一句,使用 UseShellExecute = false 的解决方法在这里是完全可以接受的。只是你的机器看起来有点乱,这当然不是事实。

    【讨论】:

    • 我在Visual Studio中检查了输出窗口,启动Process.Start后没有任何输出。我考虑过调用被阻塞的可能性,但即使在终止 Chrome 的所有实例之后,调用也从未返回(尽管UseShellExecute 我不知道为什么调用会阻塞)
    • 好吧,使用调试器。查看调用堆栈。配置了 Microsoft 符号服务器的非托管调试器可能是最好的。
    • 很好的建议,@Hans。找到更多信息。查看我的问题的更新。
    • 我无法弄清楚是什么劫持了我的进程开始,过了一会儿事情开始按预期工作。我认为我的 shell 中没有发生任何彻底的恶意活动。更有可能是某些 shell 扩展的代码很差。感谢您的提示;我以前从未见过“模块”窗口。
    • 我遇到了完全相同的问题,结果证明 Avast 劫持了我的,所以你就在那里,Hans。很抱歉恢复这个问题,只是想在应有的地方给予赞扬,我已经花了一整天的时间来解决这个问题!
    【解决方案2】:

    正如 Hans Passant 所说,挂起的Process.Start 电话可能是原因。当使用Process.Start 并将UseShellExecute 设置为true 时,Windows API 函数ShellExecuteEx 在后台被调用,在某些情况下可能不会返回。

    您可以通过在代码中添加跟踪消息来检查是否是这种情况:

    System.Diagnostics.Trace.WriteLine("About to start process.");
    Process.Start(
       new ProcessStartInfo
       {
           FileName = url,
           UseShellExecute = true
       }
    );
    System.Diagnostics.Trace.WriteLine("Process started.");
    

    要收听跟踪消息,您可以使用TraceListener、检查 Visual Studio 的输出窗口或使用 DebugView 等工具。

    作为一种解决方法,您可以使用start 命令。以下代码启动一个隐藏的 shell 窗口,它“启动” url:

    Process.Start(
        new ProcessStartInfo()
        {
            FileName = "cmd.exe",
            Arguments = "/c start http://www.google.com",
            WindowStyle = ProcessWindowStyle.Hidden,
            UseShellExecute = false
        });
    

    【讨论】:

    • 添加Trace.WriteLine 并不意外; “进程已启动”。未到达线路。
    • 启动cmd.exe时调用仍然没有返回。
    • 呸,但如果我将 UseShellExecute 设置为 false,它就会发生。
    猜你喜欢
    • 2019-03-22
    • 1970-01-01
    • 2012-08-02
    • 2014-03-16
    • 1970-01-01
    • 2010-10-01
    • 2013-04-13
    • 1970-01-01
    相关资源
    最近更新 更多