我正在调用 Process.Start,但它会阻塞当前线程。
pInfo = new ProcessStartInfo("C:\\Windows\\notepad.exe");
// Start process
mProcess = new Process();
mProcess.StartInfo = pInfo;
if (mProcess.Start() == false) {
Trace.TraceError("Unable to run process {0}.");
}
即使进程关闭,代码也不再响应。
但是 Process.Start 真的应该阻塞吗?发生什么事了?
(进程正确启动)
using System;
using System.Diagnostics;
using System.Threading;
using System.Windows.Forms;
namespace Test
{
class Test
{
[STAThread]
public static void Main()
{
Thread ServerThread = new Thread(AccepterThread);
ServerThread.Start();
Console.WriteLine (" --- Press ENTER to stop service ---");
while (Console.Read() < 0) { Application.DoEvents(); }
Console.WriteLine("Done.");
}
public static void AccepterThread(object data)
{
bool accepted = false;
while (true) {
if (accepted == false) {
Thread hThread = new Thread(HandlerThread);
accepted = true;
hThread.Start();
} else
Thread.Sleep(100);
}
}
public static void HandlerThread(object data)
{
ProcessStartInfo pInfo = new ProcessStartInfo("C:\\Windows\\notepad.exe");
Console.WriteLine("Starting process.");
// Start process
Process mProcess = new Process();
mProcess.StartInfo = pInfo;
if (mProcess.Start() == false) {
Console.WriteLine("Unable to run process.");
}
Console.WriteLine("Still living...");
}
}
}
控制台输出为:
--- 按 ENTER 停止服务 --- 启动过程。
找到了:
[STA线程]
使 Process.Start 阻塞。我读过 STAThread 和多线程,但我无法将这些概念与 Process.Start 行为联系起来。
据我所知,STAThread 是 Windows.Form 必需的。使用 Windows.Form 时如何解决此问题?
地狱新闻:
如果我重建我的应用程序,第一次我运行应用程序时可以正常工作,但是如果我停止调试并再次重新启动 iy,则会出现问题。
在没有调试器的情况下执行应用程序时,不会出现此问题。
不,
Process.Start
不会等待子进程完成...否则你将无法使用重定向 I/O 等功能。
控制台应用程序示例:
using System;
using System.Diagnostics;
public class Test
{
static void Main()
{
Process p = new Process {
StartInfo = new ProcessStartInfo("C:\\Windows\\notepad.exe")
};
p.Start();
Console.WriteLine("See, I'm still running");
}
}
这会打印“看,我仍在运行”,在我的盒子上没有任何问题 - 它在你的盒子上做什么?
创建一个 ProcessStartInfo 并将 UseShellExecute 设置为 false(默认值为 true)。您的代码应为:
pInfo = new ProcessStartInfo("C:\\Windows\\notepad.exe");
pInfo.UseShellExecute = false;
// Start process
mProcess = new Process();
mProcess.StartInfo = pInfo;
if (mProcess.Start() == false) {
Trace.TraceError("Unable to run process {0}.");
}
我遇到了同样的问题,并直接从可执行文件启动创建进程的可执行文件解决了该问题。
我在 WinForms 应用程序中遇到了与原始海报相同的阻塞行为,因此我创建了下面的控制台应用程序来简化测试此行为。
Jon Skeet 的示例使用记事本,只需几毫秒即可正常加载,因此线程块可能会被忽视。 我试图启动 Excel,这通常需要更长的时间。
using System;
using System.Diagnostics;
using static System.Console;
using System.Threading;
class Program {
static void Main(string[] args) {
WriteLine("About to start process...");
//Toggle which method is commented out:
//StartWithPath(); //Blocking
//StartWithInfo(); //Blocking
StartInNewThread(); //Not blocking
WriteLine("Process started!");
Read();
}
static void StartWithPath() {
Process.Start(TestPath);
}
static void StartWithInfo() {
var p = new Process { StartInfo = new ProcessStartInfo(TestPath) };
p.Start();
}
static void StartInNewThread() {
var t = new Thread(() => StartWithPath());
t.Start();
}
static string TestPath =
Environment.GetFolderPath(Environment.SpecialFolder.Desktop) +
"\\test.xlsx";
}
对
StartWithPath
和 StartWithInfo
的调用会阻止我在控制台应用程序中的线程。 在 Excel 初始屏幕关闭并且主窗口打开之前,我的控制台不会显示“进程已启动”。
StartInNewThread
将立即在控制台上显示两条消息,而 Excel 的启动屏幕仍然打开。
我们在启动位于不同域的网络驱动器上的 .bat 脚本时遇到了这个问题(我们有双受信任域)。 我运行了一个远程 C# 调试器,果然 Process.Start() 无限期地阻塞了。
在 Power shell 中以交互方式重复此任务时,会弹出一个安全对话框:
"C:\Program Files (x86)\IIS Express\iisexpress" /path:\Publish /port:8080
这可以访问操作系统树进程的子线程。
//Calling process
using (System.Diagnostics.Process ps = new System.Diagnostics.Process())
{
try
{
ps.StartInfo.WorkingDirectory = @"C:\Apps";
ps.StartInfo.FileName = @"C:\Program Files\Microsoft Office\Office14\MSACCESS.EXE"; //command
ps.StartInfo.Arguments = @"C:\Apps\xyz.accdb"; //argument
ps.StartInfo.UseShellExecute = false;
ps.StartInfo.RedirectStandardOutput = false;
ps.StartInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Maximized;
ps.StartInfo.CreateNoWindow = false; //display a windows
ps.Start();
}
catch (Exception ex)
{
MessageBox.Show(string.Format("==> Process error <=={0}" + ex.ToString(), Environment.NewLine));
}
}
ProcessStartInfo processInfo = new ProcessStartInfo
{
FileName = patcherPath,
UseShellExecute = true,
Verb = "runas"
};
Process.Start(processInfo);
Application.Current.Shutdown();
我认为 UAC 与它有关,因为没有它 Process.Start 是一个非阻塞调用。这可能不是最好的方法,但这对我有用:
ProcessStartInfo processInfo = new ProcessStartInfo
{
FileName = Environment.GetEnvironmentVariable("COMSPEC"),
Arguments = $"/c start \"\" \"{PatcherPath}\"",
UseShellExecute = true,
Verb = "runas",
CreateNoWindow = true
};
Process.Start(processInfo);
Application.Current.Shutdown();
UAC 提示后,调用应用程序将关闭,并且进程似乎可以正确执行。但缺点是它掩盖了我们实际想要启动的内容,因为 UAC 窗口请求提升控制台的权限,而不是目标可执行文件。