diff --git a/GVFS/GVFS.Common/Git/GitProcess.cs b/GVFS/GVFS.Common/Git/GitProcess.cs index caca4df64..b818fd915 100644 --- a/GVFS/GVFS.Common/Git/GitProcess.cs +++ b/GVFS/GVFS.Common/Git/GitProcess.cs @@ -815,16 +815,24 @@ public Result MultiPackIndexRepack(string gitObjectDirectory, string batchSize) return this.InvokeGitAgainstDotGitFolder($"-c pack.threads=1 -c repack.packKeptObjects=true multi-pack-index repack --object-dir=\"{gitObjectDirectory}\" --batch-size={batchSize} --no-progress"); } - public Process GetGitProcess(string command, string workingDirectory, string dotGitDirectory, bool useReadObjectHook, bool redirectStandardError, string gitObjectsDirectory, bool usePreCommandHook) + public Process GetGitProcess(string command, string workingDirectory, string dotGitDirectory, bool useReadObjectHook, string gitObjectsDirectory, bool usePreCommandHook) { ProcessStartInfo processInfo = new ProcessStartInfo(this.gitBinPath); processInfo.WorkingDirectory = workingDirectory; processInfo.UseShellExecute = false; processInfo.RedirectStandardInput = true; processInfo.RedirectStandardOutput = true; - processInfo.RedirectStandardError = redirectStandardError; + processInfo.RedirectStandardError = true; processInfo.WindowStyle = ProcessWindowStyle.Hidden; - processInfo.CreateNoWindow = true; + + // CreateNoWindow=false avoids allocating a hidden conhost.exe per child + // process. This is safe because both stdout and stderr are redirected via + // pipes, so the child never needs a console for I/O. If a future change + // stops redirecting either stream (to forward output to the parent console + // instead), CreateNoWindow must be set to true for that case — otherwise + // the non-redirected stream inherits the parent's console handle, which + // may be absent when running as a service, causing lost output. + processInfo.CreateNoWindow = false; processInfo.StandardOutputEncoding = UTF8NoBOM; processInfo.StandardErrorEncoding = UTF8NoBOM; @@ -903,7 +911,7 @@ protected virtual Result InvokeGitImpl( // From https://msdn.microsoft.com/en-us/library/system.diagnostics.process.standardoutput.aspx // To avoid deadlocks, use asynchronous read operations on at least one of the streams. // Do not perform a synchronous read to the end of both redirected streams. - using (this.executingProcess = this.GetGitProcess(command, workingDirectory, dotGitDirectory, useReadObjectHook, redirectStandardError: true, gitObjectsDirectory: gitObjectsDirectory, usePreCommandHook: usePreCommandHook)) + using (this.executingProcess = this.GetGitProcess(command, workingDirectory, dotGitDirectory, useReadObjectHook, gitObjectsDirectory: gitObjectsDirectory, usePreCommandHook: usePreCommandHook)) { StringBuilder output = new StringBuilder(); StringBuilder errors = new StringBuilder(); diff --git a/GVFS/GVFS.Common/ProcessHelper.cs b/GVFS/GVFS.Common/ProcessHelper.cs index a9731d6d5..a67f4159e 100644 --- a/GVFS/GVFS.Common/ProcessHelper.cs +++ b/GVFS/GVFS.Common/ProcessHelper.cs @@ -18,7 +18,16 @@ public static ProcessResult Run(string programName, string args, bool redirectOu processInfo.RedirectStandardOutput = redirectOutput; processInfo.RedirectStandardError = redirectOutput; processInfo.WindowStyle = ProcessWindowStyle.Hidden; - processInfo.CreateNoWindow = redirectOutput; + + // CreateNoWindow=false avoids allocating a hidden conhost.exe per child + // process. When redirectOutput is true, I/O goes through pipes so no + // console is needed. When redirectOutput is false, the child inherits the + // parent's console handles — this works when the parent has a console + // (e.g., GVFS.Hooks invoked from a terminal), but output is silently lost + // when the parent has no console (e.g., service context). This is + // acceptable because CreateNoWindow=true would only send that output to + // an invisible hidden console instead. + processInfo.CreateNoWindow = false; processInfo.Arguments = args; return Run(processInfo); diff --git a/GVFS/GitHooksLoader/GitHooksLoader.cpp b/GVFS/GitHooksLoader/GitHooksLoader.cpp index dfb259b87..9f1619d0a 100644 --- a/GVFS/GitHooksLoader/GitHooksLoader.cpp +++ b/GVFS/GitHooksLoader/GitHooksLoader.cpp @@ -129,7 +129,7 @@ int ExecuteHook(const std::wstring &applicationName, wchar_t *hookName, int argc /* Git disallows stdin from hooks */ si.dwFlags = STARTF_USESTDHANDLES; - creationFlags |= CREATE_NO_WINDOW; + creationFlags |= DETACHED_PROCESS; } ZeroMemory(&pi, sizeof(pi));