.NET 中通过 Process.Start 生成的进程会挂起线程
我们的应用程序有一个后台线程,它通过 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:
作为解决方法,启动 cmd.exe 而不使用 shell 来执行。非常感谢!不过,我仍然想知道为什么电话没有回电。
更新 3:
Shell 挂钩听起来确实像是对可能导致调用不返回的原因的逻辑解释。我找不到流氓模块,但在最后一次尝试通过 shell 执行运行内容后,调用did返回。
无论如何,用户可能加载了 shell 扩展,这可能会扰乱进程启动并导致我的代码无法返回。我们对此无能为力,因此正确的答案是使用启动 cmd.exe 进程的解决方法。
Our application has a background thread which spawns a process through System.Diagnostics.Process
:
Process.Start(
new ProcessStartInfo
{
FileName = url,
UseShellExecute = true
}
);
This used to have no issues at all. But now, the background thread is silently dying; it never returns from the call to Process.Start
. The catch block for this code, which handles System.Exception
, is not getting reached either. Even if I enable handling exceptions when thrown in the Visual Studio debugger, I see no exceptions. Strangely, the process is getting spawned just fine; the default browser for the user is launched with the expected URL.
Our process's entry point is marked with [STAThread]
as recommended.
What could be causing our thread to silently terminate? Are there any techniques I can use to debug what's happening during thread termination?
Update:
It looks like the thread is alive after all; it's just not returning from the call. Here's its stack trace:
- [In a sleep wait or join]
- System.dll!System.Diagnostics.ShellExecuteHelper.ShellExecuteOnSTAThread() + 0x63 bytes
- System.dll!System.Diagnostics.Process.StartWithShellExecuteEx(System.Diagnostics.ProcessStartInfo startInfo) + 0x19d bytes
- System.dll!System.Diagnostics.Process.Start() + 0x39 bytes
- System.dll!System.Diagnostics.Process.Start(System.Diagnostics.ProcessStartInfo startInfo) + 0x32 bytes
- My method
Update 2:
Launching cmd.exe without using the shell to execute works as a workaround. Thanks a bunch! However, I'd still like to know why the call isn't returning.
Update 3:
Shell hooks do sound like a logical explanation for what could be causing the call to not return. I couldn't find the rogue module, but after the last attempt to run things through shell execution, the call did return.
In any case, it's possible that users may have shell extensions loaded that could be messing with the process launching and causing my code to not return. We can't do anything about that, so the right answer is to use the workaround of launching a cmd.exe process.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
正如 Hans Passant 所提到的,挂起的 Process.Start 调用可能是原因。当在
UseShellExecute
设置为true
的情况下使用Process.Start
时,会在后台调用 Windows API 函数ShellExecuteEx
,该函数会执行以下操作:在某些情况下可能不会返回。您可以通过向代码中添加跟踪消息来检查是否属于这种情况:
要侦听跟踪消息,您可以使用
TraceListener
、检查 Visual Studio 的输出窗口或使用 DebugView 等工具。作为解决方法,您可以使用
start
命令。以下代码启动一个隐藏的 shell 窗口,该窗口“启动”该 url:As mentioned by Hans Passant, a hanging
Process.Start
call might be the reason. When usingProcess.Start
withUseShellExecute
set totrue
, the Windows API functionShellExecuteEx
is called under the hood which might not return under certain circumstances.You can check whether this is the case by adding trace messages to your code:
To listen to the trace messages you can either use a
TraceListener
, check the output window of Visual Studio or use a tool such as DebugView.As a workaround you may use the
start
command. The following code launches a hidden shell window which "starts" the url:不,线程不会默默地终止,它们会发出响亮的卡隆声。至少您会在“输出”窗口中看到线程退出通知。 Process.Start() 方法阻塞将是另一种解释,尽管没有任何解释。您的片段太短,无法得出像样的诊断结果。也许是环保的东西。
您的堆栈跟踪有帮助,ShellExecuteOnSTAThread() 实际上确实在一个小帮助线程上执行了阻塞 Thread.Join() 。该线程是调用本机 ShellExecuteEx() API 函数所必需的,只能从 STA 线程调用。但它有一个缺陷,STA 线程还必须泵送消息循环。这个小帮手却没有。
这会导致您的计算机出现问题,但仍然指向环境问题,即某种劫持 ShellExecuteEx() 调用的系统附加组件。并依赖于运行真正的 STA 线程。您应该能够在“调试 + Windows + 线程”窗口中找到该辅助线程。它应该在堆栈上包含“ShellExecuteFunction”。例如,病毒扫描程序就是具有此类特技的“系统附加组件”。在项目的“调试”选项卡中选中“启用非托管调试”后,您应该能够在“调试 + Windows + 模块”窗口中找到该 Alienware。
顺便说一句,使用 UseShellExecute = false 的解决方法在这里是完全可以接受的。事实上,你的机器看起来有点混乱,当然不是。
Nah, threads don't silently terminate, they make a loud kaboom sound. At the very least you'll see the thread exit notification in the Output window. The Process.Start() method blocking would be another explanation, albeit that there's no explanation for it. You're snippet is far too short to come up with a decent diagnostic. Something environmental perhaps.
Your stack trace helps, ShellExecuteOnSTAThread() does in fact perform a blocking Thread.Join() on a little helper thread. This thread is necessary to call the native ShellExecuteEx() API function, it can only be called from an STA thread. It has a flaw though, an STA thread must also pump a message loop. This little helper doesn't.
That this causes a problem on your machine still points to an environmental problem, some kind of system add-on that hijacks the ShellExecuteEx() call. And counts on running a real STA thread. You should be able to find that helper thread back in the Debug + Windows + Threads window. It should contain "ShellExecuteFunction" on the stack. The kind of 'system add-on' that pulls stunts like this are virus scanners, for example. You should be able to find that alienware back in the Debug + Windows + Modules window after you checked the "Enable unmanaged debugging" in the project's Debugging tab.
The workaround to use UseShellExecute = false is quite acceptable here btw. Just the fact that your machine is kinda messed up, by the looks of it, isn't of course.