我已经编写了一个 C# 程序来充当 Mercurial 的预提交挂钩,但我能让它可靠工作的唯一方法有点复杂,所以我一定错过了一些东西。我在 Windows 10 Pro 64 位上运行 TortoiseHg 6.4.5,该程序面向 .NET 6.0。
目标是获取已更改文件的列表并在提交之前对其进行处理。最明显的事情一开始似乎有效:
IEnumerable<string> GetHgStatus()
{
// never terminates when a large number of files have been changed, and if I
// kill the hung hg.exe process there's nothing in the stdout/stderr streams.
// run exe: hg status -n -m -a
var psi = new ProcessStartInfo
{
// check status:
// -n = don't output leading status indicator, eg. M | A | R
// -m = show only modified files
// -a = show only files that have been added
FileName = config.HgPath,
Arguments = $"status -n -m -a",
UseShellExecute = false,
RedirectStandardOutput = true,
RedirectStandardError = true,
CreateNoWindow = true,
WorkingDirectory = config.RepoPath,
};
var exe = Process.Start(psi);
try
{
exe.WaitForExit(); // never returns
while (!exe.StandardOutput.EndOfStream)
yield return exe.StandardOutput.ReadLine();
}
finally
{
exe.Dispose();
}
}
但是,如果更改的文件列表很长,则等待永远不会完成。我在任务列表中看到一个 hg.exe 实例,分配了大约 57-60MB 的空间,但什么也不做。如果我杀死它,则不会将任何结果输出到 stdout 或 stderr。
从命令行运行此命令时我从未遇到过问题,因此我尝试在 cmd shell 中运行它:
IEnumerable<string> GetHgStatus()
{
// never terminates when a large number of files have been changed, and if I
// kill the hung hg.exe process there's nothing in the stdout/stderr streams.
// run exe: hg status -n -m -a
var psi = new ProcessStartInfo
{
// check status:
// -n = don't output leading status indicator, eg. M | A | R
// -m = show only modified files
// -a = show only files that have been added
FileName = "cmd.exe",
Arguments = $"/c \"{config.HgPath}\" status -n -m -a",
UseShellExecute = false,
RedirectStandardOutput = true,
RedirectStandardError = true,
CreateNoWindow = true,
WorkingDirectory = config.RepoPath,
};
var exe = Process.Start(psi);
try
{
exe.WaitForExit(); // never returns
while (!exe.StandardOutput.EndOfStream)
yield return exe.StandardOutput.ReadLine();
}
finally
{
exe.Dispose();
}
}
这与第一个版本具有完全相同的行为。如果我将 CreateNoWindow 选项切换为 false,那么我可以看到 hg 创建一个命令 shell 窗口,然后它就坐在那里。
我可以让它可靠地工作的唯一方法是放弃重定向 stdout/stderr,并将输出重定向到我然后读入的临时文件:
IEnumerable<string> GetHgStatus()
{
// have to loop catching file access exceptions until the file is accessible
// run exe: hg status -n -m -a
var output = Path.GetTempFileName();
var psi = new ProcessStartInfo
{
// check status:
// -n = don't output leading status indicator, eg. M | A | R
// -m = show only modified files
// -a = show only files that have been added
FileName = "cmd.exe",
Arguments = $"/c \"{config.HgPath}\" status -n -m -a > {output}",
UseShellExecute = false,
CreateNoWindow = true,
WorkingDirectory = config.RepoPath,
};
var exe = Process.Start(psi);
try
{
exe.WaitForExit();
// have to call Close() then poll until file is accessible
exe.Close();
do
{
try
{
return File.ReadAllLines(output);
}
catch (IOException e) when (e.Message.StartsWith("The process cannot access the file"))
{
continue;
}
} while (true);
}
finally
{
exe.Dispose();
File.Delete(output);
}
}
那么两个问题:
hg.exe 是否在等待某些内容消耗其输出缓冲区,然后再写入更多内容?也许需要在进程对象上设置一些附加属性,以告诉它始终在 sdout 可用时立即读取它。
这里有一些例子...也许您需要在直播中
ReadToEnd
。 https://learn.microsoft.com/en-us/dotnet/api/system.diagnostics.process.standardoutput?view=net-7.0#remarks