public IWin32Process CreateProcessWithPseudoConsole()

in sources/Google.Solutions.Platform/Dispatch/Win32ProcessFactory.cs [151:272]


        public IWin32Process CreateProcessWithPseudoConsole(
            string executable,
            string? arguments,
            PseudoTerminalSize pseudoConsoleSize)
        {
            using (PlatformTraceSource.Log.TraceMethod()
                .WithParameters(executable, arguments))
            {
                //
                // Create a STARTUPINFOEX as described in 
                // https://docs.microsoft.com/en-us/windows/console/creating-a-pseudoconsole-session
                //

                var size = IntPtr.Zero;
                NativeMethods.InitializeProcThreadAttributeList(
                    IntPtr.Zero,
                    1,
                    0,
                    ref size);
                if (size == IntPtr.Zero)
                {
                    throw DispatchException.FromLastWin32Error(
                        "Calculating the number of bytes for the " +
                        "thread attribute list failed");
                }

                using (var attributeListHandle =
                    GlobalAllocSafeHandle.GlobalAlloc((uint)size.ToInt32()))
                {
                    var startupInfo = new NativeMethods.STARTUPINFOEX();
                    startupInfo.StartupInfo.cb =
                        Marshal.SizeOf<NativeMethods.STARTUPINFOEX>();
                    startupInfo.lpAttributeList =
                        attributeListHandle.DangerousGetHandle();

                    if (!NativeMethods.InitializeProcThreadAttributeList(
                        startupInfo.lpAttributeList,
                        1,
                        0,
                        ref size))
                    {
                        throw DispatchException.FromLastWin32Error(
                            "Creating the thread attribute list failed");
                    }

                    var pseudoConsole = new Win32PseudoConsole(pseudoConsoleSize);
                    try
                    {
                        if (!NativeMethods.UpdateProcThreadAttribute(
                            startupInfo.lpAttributeList,
                            0,
                            (IntPtr)NativeMethods.PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE,
                            pseudoConsole.Handle.DangerousGetHandle(),
                            (IntPtr)IntPtr.Size,
                            IntPtr.Zero,
                            IntPtr.Zero))
                        {
                            throw DispatchException.FromLastWin32Error(
                                "Attaching the pseudo-console failed");
                        }

                        var processSecurityAttributes =
                            new NativeMethods.SECURITY_ATTRIBUTES
                            {
                                nLength = Marshal
                                .SizeOf<NativeMethods.SECURITY_ATTRIBUTES>()
                            };
                        var threadSecurityAttributes =
                            new NativeMethods.SECURITY_ATTRIBUTES
                            {
                                nLength = Marshal
                                .SizeOf<NativeMethods.SECURITY_ATTRIBUTES>()
                            };

                        if (!NativeMethods.CreateProcess(
                            null,
                            $"{Quote(executable)} {arguments}",
                            ref processSecurityAttributes,
                            ref threadSecurityAttributes,
                            false,
                            NativeMethods.CREATE_SUSPENDED | NativeMethods.EXTENDED_STARTUPINFO_PRESENT,
                            IntPtr.Zero,
                            null,
                            ref startupInfo,
                            out var processInfo))
                        {
                            throw DispatchException.FromLastWin32Error(
                                $"Launching process for {executable} failed");
                        }

                        var process = new Win32Process(
                            new FileInfo(executable).Name,
                            processInfo.dwProcessId,
                            new SafeProcessHandle(processInfo.hProcess, true),
                            new SafeThreadHandle(processInfo.hThread, true))
                        {
                            PseudoTerminal = pseudoConsole
                        };

                        process.Exited += (_, __) =>
                        {
                            //
                            // Close pseudo console stream to unblock readers.
                            //
                            _ = pseudoConsole.CloseAsync();
                        };

                        InvokeOnProcessCreated(process);
                        return process;
                    }
                    catch
                    {
                        pseudoConsole.Dispose();
                        throw;
                    }
                    finally
                    {
                        NativeMethods.DeleteProcThreadAttributeList(startupInfo.lpAttributeList);
                    }
                }
            }
        }