From 95082666cf60dc61b736e6de4e763ac4fc8720c0 Mon Sep 17 00:00:00 2001 From: Tyrie Vella Date: Thu, 4 Jun 2026 10:43:54 -0700 Subject: [PATCH] Use Console.IsOutputRedirected instead of P/Invoke for redirect detection The previous IsConsoleOutputRedirectedToFile() used Win32 GetFileType to check if stdout was redirected to a disk file. This missed the pipe case (GetFileType returns Pipe, not Disk), so the console spinner would still run when stdout was captured via Process.Start with RedirectStandardOutput (e.g. in functional tests and CI), polluting captured output with \r and spinner characters. Replace with .NET's Console.IsOutputRedirected which returns true for any non-console handle (files, pipes, NUL). Remove the now-unused P/Invoke declarations (GetStdHandle, GetFileType) and the platform abstraction (IsConsoleOutputRedirectedToFile) from GVFSPlatform, WindowsPlatform, GVFSHooksPlatform, and MockPlatform. Assisted-by: Claude Opus 4.6 Signed-off-by: Tyrie Vella --- GVFS/GVFS.Common/GVFSPlatform.cs | 2 -- .../HooksPlatform/GVFSHooksPlatform.cs | 5 ---- GVFS/GVFS.Hooks/Program.cs | 4 +-- .../WindowsPlatform.Shared.cs | 28 ------------------- GVFS/GVFS.Platform.Windows/WindowsPlatform.cs | 5 ---- .../Mock/Common/MockPlatform.cs | 5 ---- GVFS/GVFS/CommandLine/GVFSVerb.cs | 2 +- GVFS/GVFS/CommandLine/UnmountVerb.cs | 2 +- 8 files changed, 4 insertions(+), 49 deletions(-) diff --git a/GVFS/GVFS.Common/GVFSPlatform.cs b/GVFS/GVFS.Common/GVFSPlatform.cs index d5132066b..8935c72b6 100644 --- a/GVFS/GVFS.Common/GVFSPlatform.cs +++ b/GVFS/GVFS.Common/GVFSPlatform.cs @@ -103,8 +103,6 @@ public static void Register(GVFSPlatform platform) public abstract Dictionary GetPhysicalDiskInfo(string path, bool sizeStatsOnly); - public abstract bool IsConsoleOutputRedirectedToFile(); - public abstract bool TryKillProcessTree(int processId, out int exitCode, out string error); public abstract bool TryGetGVFSEnlistmentRoot(string directory, out string enlistmentRoot, out string errorMessage); diff --git a/GVFS/GVFS.Hooks/HooksPlatform/GVFSHooksPlatform.cs b/GVFS/GVFS.Hooks/HooksPlatform/GVFSHooksPlatform.cs index 0c7cf7239..e551787f2 100644 --- a/GVFS/GVFS.Hooks/HooksPlatform/GVFSHooksPlatform.cs +++ b/GVFS/GVFS.Hooks/HooksPlatform/GVFSHooksPlatform.cs @@ -22,11 +22,6 @@ public static string GetNamedPipeName(string enlistmentRoot) return WindowsPlatform.GetNamedPipeNameImplementation(enlistmentRoot); } - public static bool IsConsoleOutputRedirectedToFile() - { - return WindowsPlatform.IsConsoleOutputRedirectedToFileImplementation(); - } - public static bool TryGetGVFSEnlistmentRoot(string directory, out string enlistmentRoot, out string errorMessage) { return WindowsPlatform.TryGetGVFSEnlistmentRootImplementation(directory, out enlistmentRoot, out errorMessage); diff --git a/GVFS/GVFS.Hooks/Program.cs b/GVFS/GVFS.Hooks/Program.cs index aee260928..00db23872 100644 --- a/GVFS/GVFS.Hooks/Program.cs +++ b/GVFS/GVFS.Hooks/Program.cs @@ -317,7 +317,7 @@ private static void AcquireGVFSLockForProcess(bool unattended, string[] args, in fullCommand, pid, GVFSHooksPlatform.IsElevated(), - isConsoleOutputRedirectedToFile: GVFSHooksPlatform.IsConsoleOutputRedirectedToFile(), + isConsoleOutputRedirectedToFile: Console.IsOutputRedirected, checkAvailabilityOnly: checkGvfsLockAvailabilityOnly, gvfsEnlistmentRoot: null, gitCommandSessionId: gitCommandSessionId, @@ -337,7 +337,7 @@ private static void ReleaseGVFSLock(bool unattended, string[] args, int pid, Nam fullCommand, pid, GVFSHooksPlatform.IsElevated(), - GVFSHooksPlatform.IsConsoleOutputRedirectedToFile(), + Console.IsOutputRedirected, response => { if (response == null || response.ResponseData == null) diff --git a/GVFS/GVFS.Platform.Windows/WindowsPlatform.Shared.cs b/GVFS/GVFS.Platform.Windows/WindowsPlatform.Shared.cs index 99def6841..59088bc88 100644 --- a/GVFS/GVFS.Platform.Windows/WindowsPlatform.Shared.cs +++ b/GVFS/GVFS.Platform.Windows/WindowsPlatform.Shared.cs @@ -3,7 +3,6 @@ using System; using System.Diagnostics; using System.IO; -using System.Runtime.InteropServices; using System.Security.Principal; namespace GVFS.Platform.Windows @@ -15,22 +14,6 @@ public partial class WindowsPlatform private const int StillActive = 259; /* from Win32 STILL_ACTIVE */ - private enum StdHandle - { - Stdin = -10, - Stdout = -11, - Stderr = -12 - } - - private enum FileType : uint - { - Unknown = 0x0000, - Disk = 0x0001, - Char = 0x0002, - Pipe = 0x0003, - Remote = 0x8000, - } - public static bool IsElevatedImplementation() { using (WindowsIdentity id = WindowsIdentity.GetCurrent()) @@ -153,11 +136,6 @@ public static string GetSecureDataRootForGVFSComponentImplementation(string comp return Path.Combine(GetSecureDataRootForGVFSImplementation(), componentName); } - public static bool IsConsoleOutputRedirectedToFileImplementation() - { - return FileType.Disk == GetFileType(GetStdHandle(StdHandle.Stdout)); - } - public static bool TryGetGVFSEnlistmentRootImplementation(string directory, out string enlistmentRoot, out string errorMessage) { enlistmentRoot = null; @@ -177,11 +155,5 @@ public static bool TryGetGVFSEnlistmentRootImplementation(string directory, out return true; } - - [DllImport("kernel32.dll")] - private static extern IntPtr GetStdHandle(StdHandle std); - - [DllImport("kernel32.dll")] - private static extern FileType GetFileType(IntPtr hdl); } } diff --git a/GVFS/GVFS.Platform.Windows/WindowsPlatform.cs b/GVFS/GVFS.Platform.Windows/WindowsPlatform.cs index b8593f417..7e15340da 100644 --- a/GVFS/GVFS.Platform.Windows/WindowsPlatform.cs +++ b/GVFS/GVFS.Platform.Windows/WindowsPlatform.cs @@ -331,11 +331,6 @@ public override string GetSystemInstallerLogPath() public override Dictionary GetPhysicalDiskInfo(string path, bool sizeStatsOnly) => WindowsPhysicalDiskInfo.GetPhysicalDiskInfo(path, sizeStatsOnly); - public override bool IsConsoleOutputRedirectedToFile() - { - return WindowsPlatform.IsConsoleOutputRedirectedToFileImplementation(); - } - public override bool IsGitStatusCacheSupported() { return File.Exists(Path.Combine(GVFSPlatform.Instance.GetSecureDataRootForGVFSComponent(GVFSConstants.Service.ServiceName), GVFSConstants.GitStatusCache.EnableGitStatusCacheTokenFile)); diff --git a/GVFS/GVFS.UnitTests/Mock/Common/MockPlatform.cs b/GVFS/GVFS.UnitTests/Mock/Common/MockPlatform.cs index 41876f953..e7dca80cc 100644 --- a/GVFS/GVFS.UnitTests/Mock/Common/MockPlatform.cs +++ b/GVFS/GVFS.UnitTests/Mock/Common/MockPlatform.cs @@ -131,11 +131,6 @@ public override string GetSystemInstallerLogPath() return "MockPath"; } - public override bool IsConsoleOutputRedirectedToFile() - { - throw new NotSupportedException(); - } - public override bool IsElevated() { throw new NotSupportedException(); diff --git a/GVFS/GVFS/CommandLine/GVFSVerb.cs b/GVFS/GVFS/CommandLine/GVFSVerb.cs index 069ac1661..98ac6318c 100644 --- a/GVFS/GVFS/CommandLine/GVFSVerb.cs +++ b/GVFS/GVFS/CommandLine/GVFSVerb.cs @@ -193,7 +193,7 @@ protected bool ShowStatusWhileRunning( action, message, this.Output, - showSpinner: !this.Unattended && this.Output == Console.Out && !GVFSPlatform.Instance.IsConsoleOutputRedirectedToFile(), + showSpinner: !this.Unattended && this.Output == Console.Out && !Console.IsOutputRedirected, gvfsLogEnlistmentRoot: gvfsLogEnlistmentRoot, initialDelayMs: 0); } diff --git a/GVFS/GVFS/CommandLine/UnmountVerb.cs b/GVFS/GVFS/CommandLine/UnmountVerb.cs index 4804d9415..0de886a50 100644 --- a/GVFS/GVFS/CommandLine/UnmountVerb.cs +++ b/GVFS/GVFS/CommandLine/UnmountVerb.cs @@ -261,7 +261,7 @@ private void AcquireLock(string pipeName, string enlistmentRoot) "gvfs unmount", currentProcess.Id, GVFSPlatform.Instance.IsElevated(), - isConsoleOutputRedirectedToFile: GVFSPlatform.Instance.IsConsoleOutputRedirectedToFile(), + isConsoleOutputRedirectedToFile: Console.IsOutputRedirected, checkAvailabilityOnly: false, gvfsEnlistmentRoot: enlistmentRoot, gitCommandSessionId: string.Empty,