
如何用 ConPTY API 创建自定义终端宿主MiniTerm、EchoCon 与 GUIConsole 示例解析【免费下载链接】terminalThe new Windows Terminal and the original Windows console host, all in the same place!项目地址: https://gitcode.com/GitHub_Trending/term/terminal如果你在 Windows 上想自己实现一个终端宿主——无论是控制台程序、C 工具还是 WPF 图形应用——核心任务都是同一件事调用 ConPTYPseudo ConsoleAPI 创建一个伪控制台把一个子进程挂接到它上面再通过输入/输出管道读写 VT100 文本。terminal 仓库的samples/ConPTY/目录下提供了三个可直接编译运行的示例覆盖了三种典型宿主形态C 最小示例 EchoCon、C# 控制台宿主 MiniTerm、WPF 图形宿主 GUIConsole。本文按“先讲三个示例共用的 API 流程再逐个拆解每个示例”的顺序展开帮助你在动手前把调用链和各文件职责看清楚。三个示例的运行环境要求来自各自文档需要注意它们给出的版本不完全相同EchoConWindows 10 Insider build 17733 或更高以及最新的 Windows 10 Insider SDK见 EchoCon/readme.md。MiniTerm文档说明在 Windows 10 Pro Build 17744 与 Windows_InsiderPreview_SDK_en-us_17749 上测试通过见 MiniTerm/README.md其 Program.cs 代码注释中给出的要求是“截至 2018 年 9 月需要安装了 Redstone 5 的 Windows Insider Program 的 Windows 10以及 Windows Insider Preview SDK”。三个示例共用的 ConPTY 创建流程无论 C 还是 C#EchoCon 与 MiniTerm 实现的都是同一条 API 调用链。对照 EchoCon.cpp 和 MiniTerm 的 PseudoConsole.cs、ProcessFactory.cs流程如下创建两条匿名管道分别作为伪控制台的输入和输出端各调用一次CreatePipe。宿主控制台开启 VT 处理通过GetConsoleMode读取当前模式再用SetConsoleMode加上ENABLE_VIRTUAL_TERMINAL_PROCESSING标志这样宿主窗口才能解释从输出管道读到的 VT 序列。MiniTerm 在 Terminal.cs 中还额外加上了DISABLE_NEWLINE_AUTO_RETURN。创建伪控制台调用CreatePseudoConsole(COORD size, 输入端句柄, 输出端句柄, 0, out hPC)其中COORD是字符宽高X为宽、Y为高。准备子进程的启动信息先用InitializeProcThreadAttributeList计算并初始化包含 1 个属性的线程属性列表再用UpdateProcThreadAttribute把PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE属性设为上一步拿到的hPC。MiniTerm 的 PInvoke 定义 PseudoConsoleApi.cs 中给出了该属性常量值0x00020016以及CreatePseudoConsole、ResizePseudoConsole、ClosePseudoConsole、CreatePipe四个 kernel32 函数签名。创建进程CreateProcess时必须使用EXTENDED_STARTUPINFO_PRESENT创建标志并传入配置好的STARTUPINFOEX子进程如cmd.exe、ping.exe、powershell.exe就会附着到伪控制台。读写与清理从输出管道读取 VT100 文本写到宿主窗口把用户输入写入输入管道结束时调用ClosePseudoConsoleEchoCon 的代码注释指出这会终止仍在运行的客户端进程。EchoCon最小的 C 验证路径EchoCon 是理解 API 顺序最快的入口它做的事情见 EchoCon/readme.md创建输入/输出管道、调用CreatePseudoConsole()创建 ConPTY 实例、启动一个连接 ConPTY 的ping.exe并运行一个线程监听ping.exe的输出、把收到的文本写到 Console。对照 EchoCon.cpp 的main()有几个实现细节值得注意伪控制台的尺寸来自宿主控制台GetConsoleScreenBufferInfo取得窗口信息后用srWindow.Right - srWindow.Left 1和srWindow.Bottom - srWindow.Top 1计算宽高。管道创建后ConPTY 端的句柄hPipePTYIn/hPipePTYOut可以立即关闭因为句柄已被 dup 进 ConHost会在 ConPTY 销毁时释放。监听线程PipeListener以 512 字节缓冲ReadFile读管道然后用WriteFile写宿主控制台而不是printf/puts——代码注释明确说明这是为了防止部分读取的 VT 序列破坏输出。ping启动后等待最多 10 秒WaitForSingleObject(piClient.hThread, 10 * 1000)之后额外Sleep(500)让监听线程追平最后一段输出再做资源清理。验证方式文档说明成功构建后运行 EchoCon 应清屏并显示命令的结果readme 给出的示例输出为文档示例不是每次必然完全一致的固定输出Pinging Rincewind [::1] with 32 bytes of data: Reply from ::1: time1ms Reply from ::1: time1ms Reply from ::1: time1ms Reply from ::1: time1ms Ping statistics for ::1: Packets: Sent 4, Received 4, Lost 0 (0% loss), Approximate round trip times in milli-seconds: Minimum 0ms, Maximum 0ms, Average 0ms注意 readme 的文字描述提到 “display the results of the echo command”而代码实际执行的是ping localhost以上示例块以 readme 原文给出的 ping 输出为准。编译入口是 EchoCon.sln。MiniTermC# 交互式终端宿主MiniTerm/README.md 对它的定位是使用微软新版 PTY API 的实验性终端用 C# 编写基于原生代码示例改写“Demonstrates the basic API calls required, but not intended for real-world usage”演示所需的基础 API 调用但不面向真实世界使用。Program.cs 的入口只做一件事new Terminal().Run(cmd.exe)即在一个普通控制台窗口里跑出一个受自己管理的 cmd。核心逻辑都在 Terminal.cs 的Run(string command)中用两个PseudoConsolePipe封装 PseudoConsolePipe.cs分别承载输入和输出PseudoConsole.Create(inputPipe.ReadSide, outputPipe.WriteSide, Console.WindowWidth, Console.WindowHeight)以宿主控制台窗口尺寸创建伪控制台失败时抛出带错误码的InvalidOperationExceptionProcessFactory.Start完成上文第 4、5 步的属性列表配置与CreateProcess两个Task分别负责把输出管道内容拷贝到标准输出CopyPipeToOutput以及逐字符读取Console.ReadKey写入输入管道CopyInputToPipeCtrlC 会被拦截并转发为\x3字符写进管道注释说明目的是“不要让它杀死终端而应发给终端内的进程”通过SetConsoleCtrlHandler注册CTRL_CLOSE_EVENT回调在窗口被强行关闭时释放进程、伪控制台和管道的资源。文件布局上Native/目录放 PInvoke 签名ConsoleApi.cs、ProcessApi.cs、PseudoConsoleApi.csProcesses/目录放进程封装编译入口是 MiniTerm.sln。GUIConsoleWPF 图形宿主骨架GUIConsole 演示的是把 ConPTY 封装成库、再挂到 WPF 窗口上的形态见 GUIConsole/README.md由两个工程组成GUIConsole.ConPTY.NET Standard 2.0 类库负责创建控制台并启用伪控制台行为。公开交互面集中在 Terminal.cs暴露两样东西ConsoleOutStream连接到伪控制台输出管道的FileStream输出 VT100和WriteToPseudoConsole(string input)把字符串经输入管道写入伪控制台接受 VT100。Start方法的默认参数是宽 80、高 30 字符GUIConsole.WPF面向 .NET 4.6.1 的 WPF 应用创建一个充当控制台的Window文档说明它会保留底层控制台可见。MainWindow.xaml.cs 展示了宿主侧的完整接线窗口加载时Task.Run(() _terminal.Start(powershell.exe))启动伪控制台订阅OutputReady事件后用Task.Factory.StartNew(..., TaskCreationOptions.LongRunning)起一个长驻线程从ConsoleOutStream逐字符读取并把结果追加到 TextBlock注释说明真实场景中“应该在这里解析和分词收到的 VT100 文本然后再渲染”当前只是输出原始 VT100按键事件里则调用_terminal.WriteToPseudoConsole(e.Key.ToString())把按键转成文本发回。编译入口是 GUIConsole.sln。验证方式上代码在OutputReady触发后把窗口标题设置为 “GUIConsole - powershell.exe”并逐字符累积输出到TerminalHistoryBlock。限制与适用条件三个示例都面向 ConPTY 的早期版本版本要求以各自文档为准EchoCon 要求 Windows 10 Insider build 17733 或更高MiniTerm 的测试环境为 Build 17744 加 Insider Preview SDK 17749二者给出的具体数字不一致按你要编译的示例对应的文档执行即可。MiniTerm 的文档明确其定位是演示基础 API 调用不面向真实世界使用GUIConsole 的 README 也把它描述为 “the skeleton of a custom WPF console”即骨架示例渲染层目前是原始 VT100 直出。文档指出的共性前提宿主必须处理输出管道中的 VT100 文本否则无法正确显示内容关闭 ConPTY 会终止挂在上面的子进程清理顺序先等子进程结束、再关 ConPTY 和管道请参考 EchoCon 的main()收尾段。进一步学习的资料在三个 README 中均以微软 ConPTY 介绍博文与 MSDN “Creating a pseudo console session” 文档作为延伸阅读列出可按名称自行检索。【免费下载链接】terminalThe new Windows Terminal and the original Windows console host, all in the same place!项目地址: https://gitcode.com/GitHub_Trending/term/terminal创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考