@@ -55,8 +55,8 @@ public static int Start([MarshalAs(UnmanagedType.LPArray, ArraySubType = Unmanag
5555 // On Windows with consoleAllocationPolicy=detached in the manifest,
5656 // no console is auto-allocated by the OS. We must allocate one ourselves
5757 // before anything touches CONOUT$/CONIN$ handles.
58- // On older Windows the manifest element is ignored and this is a no-op
59- // (AllocConsole returns false when a console already exists) .
58+ // On older Windows the manifest element is ignored and the OS auto-allocates
59+ // a console, so GetConsoleWindow() != 0 and EarlyConsoleInit is a no-op .
6060 EarlyConsoleInit ( args ) ;
6161#endif
6262
@@ -134,52 +134,48 @@ public static int Start([MarshalAs(UnmanagedType.LPArray, ArraySubType = Unmanag
134134 /// <summary>
135135 /// Allocates a console early in startup to support consoleAllocationPolicy=detached.
136136 /// On newer Windows (with the detached policy active), the OS does not auto-allocate
137- /// a console for CUI apps. On older Windows, AllocConsole() returns false (no-op).
137+ /// a console for CUI apps. On older Windows the manifest is ignored, so the OS
138+ /// auto-allocates a console and GetConsoleWindow() returns non-zero (early return).
138139 /// </summary>
139140 private static void EarlyConsoleInit ( string [ ] args )
140141 {
141142 nint existingConsole = Interop . Windows . GetConsoleWindow ( ) ;
142143 if ( existingConsole != nint . Zero )
143144 {
144145 // Console already exists (inherited from parent or auto-allocated on older Windows).
145- // If -WindowStyle Hidden was requested, hide the window at the earliest possible moment
146- // to minimize the flash on older Windows where the detached policy is not supported.
147- if ( EarlyCheckForHiddenWindowStyle ( args ) )
148- {
149- Interop . Windows . ShowWindow ( existingConsole , Interop . Windows . SW_HIDE ) ;
150- }
151-
146+ // Leave -WindowStyle handling to the existing SetConsoleMode code path.
152147 return ;
153148 }
154149
155- // No console exists. This means the detached policy is active (newer Windows)
156- // and we were launched without console inheritance (e.g. from Explorer, Task Scheduler).
150+ // No console exists (GetConsoleWindow() == 0). This means either:
151+ // (a) The detached manifest policy is active (newer Windows), or
152+ // (b) DETACHED_PROCESS — no console at all, or
153+ // (c) CREATE_NO_WINDOW — console session exists but no window.
154+ //
155+ // For (c), AllocConsoleWithOptions returns ExistingConsole (no-op).
156+ // For (a) and (b), behavior depends on the mode:
157+ // Default mode: allocates if the parent would have given us a console
158+ // on prior Windows versions, returns NoConsole for DETACHED_PROCESS.
159+ // NoWindow mode: always creates a console session (overrides DETACHED).
160+ //
161+ // When -WindowStyle Hidden is specified, we intentionally use NoWindow
162+ // even though it overrides DETACHED_PROCESS — the user explicitly asked
163+ // for invisible PowerShell with working I/O, and the alternative (no
164+ // console, crashing on stdin/stdout access) is strictly worse.
165+ //
166+ // If the API is not available (older Windows), TryAlloc* returns false.
167+ // On older Windows the manifest is ignored and the OS auto-allocates
168+ // a console, so GetConsoleWindow() would have returned non-zero above.
169+ // The only older-Windows path here is DETACHED_PROCESS, where the
170+ // existing behavior is no console I/O; we preserve that by not
171+ // falling back to plain AllocConsole() (per DHowett's guidance).
157172 if ( EarlyCheckForHiddenWindowStyle ( args ) )
158173 {
159- // Hidden: allocate an invisible console session so CONOUT$/CONIN$ work
160- // (Write-Host, native commands, etc.) but no window is ever shown.
161- if ( ! Interop . Windows . TryAllocConsoleNoWindow ( ) )
162- {
163- // Fallback (should not happen since we only reach here on newer Windows,
164- // but be defensive): alloc + hide.
165- Interop . Windows . AllocConsole ( ) ;
166- nint hwnd = Interop . Windows . GetConsoleWindow ( ) ;
167- if ( hwnd != nint . Zero )
168- {
169- Interop . Windows . ShowWindow ( hwnd , Interop . Windows . SW_HIDE ) ;
170- }
171- }
174+ Interop . Windows . TryAllocConsoleNoWindow ( ) ;
172175 }
173176 else
174177 {
175- // Normal interactive launch: allocate a visible console.
176- // Use AllocConsoleWithOptions(Default) when available — it respects
177- // DETACHED_PROCESS from the parent's CreateProcess call, whereas
178- // plain AllocConsole() would override it and force-create a console.
179- if ( ! Interop . Windows . TryAllocConsoleDefault ( ) )
180- {
181- Interop . Windows . AllocConsole ( ) ;
182- }
178+ Interop . Windows . TryAllocConsoleDefault ( ) ;
183179 }
184180 }
185181
0 commit comments