示例工程【免费下载链接】Windows-classic-samplesThis repo contains samples that demonstrate the API used in Windows classic desktop applications.项目地址https://gitcode.com/gh_mirrors/wi/Windows-classic-samples点击查看免费下载导读本指南围绕 Windows-classic-samples 仓库中的 RemotingDisconnectConnect01 示例展开系统讲解如何通过System.Management.Automation.Runspaces命名空间构建远程 Runspace 与 Pipeline、启动长时脚本后断开连接、在脚本于远端继续运行期间重建/重连对象并最终取回命令执行状态与输出数据。读完本文你将掌握远程 PowerShell 会话的断开—继续运行—重连—取回结果完整链路并能在自己的宿主应用中复刻这套异步断开/重连模型。一、示例解决的问题与核心 API在 Windows PowerShell 宿主应用中常规做法是建立远程 Runspace、执行命令、取回输出、关闭连接。但当脚本运行时间很长例如数十秒甚至更久时客户端往往不希望一直占用一条活动连接也不希望在客户端进程退出后丢失远端正在执行的任务。PowerShell 3.0 起提供了**会话断开Disconnect/重连Reconnect**机制允许运行中的命令在断开后继续在远端执行客户端对象在断开状态下被销毁后基于远端会话信息重建新的对象新对象重新连接后取回脚本状态与输出数据。RemotingDisconnectConnect01 正是这一机制的完整演示。其核心调用链如下对应 cs/RemotingDisconnectConnect01.cs阶段API说明连接定义WSManConnectionInfo定义一条 WSMan/WinRM 连接默认构造器指向 localhost建立远程会话RunspaceFactory.CreateRunspace(connectionInfo)runspace.Open()创建并打开远程 Runspace创建命令管线runspace.CreatePipeline(script)生成承载脚本的 Pipeline异步启动pipeline.InvokeAsync()立即返回脚本在远端异步运行断开runspace.Disconnect()断开 Runspace 与 Pipeline脚本继续在远端运行重连场景一pipeline.ConnectAsync()异步重连 PipelineRunspace 被隐式重连重建场景二Runspace.GetRunspaces(connectionInfo)runspace1.CreateDisconnectedPipeline()基于远端会话信息重建对象取回输出pipeline.Output集合 PSObject.ToString()阻塞直至脚本完成并打印全部输出示例的 Objectives目标清单完整覆盖了从创建WSManConnectionInfo、用其建立远程连接、创建长时脚本 Pipeline、断开、重连、重建、查询状态/输出直到关闭远程连接的 8 个环节。二、前置条件与运行环境2.1 操作系统要求README 明确给出了示例的兼容基线客户端Windows 8.1服务器Windows Server 2012 R2从工程配置看cs/RemotingDisconnectConnect01.csproj 以TargetFrameworkVersion为 v4.0Client Profile、OutputType为 Exe 编译引用的System.Management.Automation程序集版本为3.0.0.0这与断开/重连能力随 PowerShell 3.0 引入的历史事实相符——运行环境需要提供 PowerShell 3.0 及以上的程序集。2.2 启用 PowerShell 远程管理运行示例前必须确认目标主机已启用 PowerShell 远程管理WinRM。README 建议直接在 PowerShell 中执行help about_remote该帮助主题会给出启用远程处理Enable-PSRemoting的完整指引。示例默认通过WSManConnectionInfo的默认构造器连接localhost因此只需在本地启用远程处理即可验证全部流程。2.3 编译与运行步骤按 README 的 Build/Run 流程启动 Visual Studio选择File Open Project/Solution进入 RemotingDisconnectConnect01 目录双击打开 RemotingDisconnectConnect01.sln确认项目已引用System.Management.Automation.dll程序集csproj 中已声明 Version3.0.0.0 的引用见 csproj 的 Reference 节点按F7或选择Build Build Solution编译可执行文件输出到默认的\bin或\bin\Debug目录对应 csproj 中的bin\Debug\/bin\Release\输出路径以管理员身份打开命令提示符进入可执行文件所在目录运行RemotingDisconnectConnect01.exe观察控制台输出并与源码对应。三、场景一同一对象断开后重连场景一演示的是最直接的重连路径保留原有的 Runspace 与 Pipeline 对象断开后由同一对象重新连回远端会话。3.1 建立连接与启动长时脚本// 默认构造器创建指向 localhost 的 WSMan/WinRM 连接 WSManConnectionInfo connectionInfo new WSManConnectionInfo(); using (Runspace runspace RunspaceFactory.CreateRunspace(connectionInfo)) { runspace.Open(); // 60 秒长时脚本每秒输出一行 Output N using (Pipeline pipeline runspace.CreatePipeline(1..60 | foreach {sleep 1; Output $_})) { // 异步调用立即返回 pipeline.InvokeAsync();这里有一个值得注意的工程细节WSManConnectionInfo的默认构造器连接的是localhost而示例注释特意将远端描述为 remote (localhost) server目的是让示例在单机即可完整跑通断开/重连逻辑。若需连接真实远程主机可改用带计算机名的构造重载如new WSManConnectionInfo(false, serverName, ...)或指定 URI 的版本关于连接参数的更多细节仓库中的 RemoteRunspace01 示例 展示了如何通过OperationTimeout默认毫秒示例中设为 4 分钟与OpenTimeout示例中设为 1 分钟控制连接与操作的超时行为可作为本示例的配套参考资料。脚本1..60 | foreach {sleep 1; Output $_}会在远端运行 60 秒每秒产出一行输出为断开/重连提供了充足的时间窗口。3.2 断开连接脚本继续运行// 让脚本先跑一会儿 Thread.Sleep(2000); // 断开 Runspace 与 Pipeline脚本继续在远端会话中运行 runspace.Disconnect(); // 校验断开后的状态 rsState runspace.RunspaceStateInfo.State; // 预期Disconnected plState pipeline.PipelineStateInfo.State; // 预期DisconnectedDisconnect()是同步调用执行后RunspaceStateInfo.State变为RunspaceState.DisconnectedPipelineStateInfo.State变为PipelineState.Disconnected远端脚本不受影响继续执行。源码在主流程开始处即初始化了这两个状态变量RunspaceState.BeforeOpen/PipelineState.NotStarted并在断开后立即回读便于对比状态迁移。3.3 重连并取回输出// 再让脚本在服务端多运行一会儿 Thread.Sleep(2000); // 异步重连 PipelineRunspace 会被隐式重连 pipeline.ConnectAsync(); Thread.Sleep(2000); // 校验重连后的状态 rsState runspace.RunspaceStateInfo.State; // 预期Opened plState pipeline.PipelineStateInfo.State; // 预期Running // 持续取回输出直到脚本完成 while (!pipeline.Output.EndOfPipeline) { pipeline.Output.WaitHandle.WaitOne(); while (pipeline.Output.Count 0) { PSObject psObject pipeline.Output.Read(); Console.WriteLine(psObject.ToString()); } }ConnectAsync()是异步重连入口调用立即返回脚本状态可随时通过PipelineStateInfo.State查询同时Runspace 被隐式重连其状态恢复为Opened。随后通过 Pipeline 的Output数据流Output.EndOfPipeline判断脚本是否结束Output.WaitHandle.WaitOne()等待新输出到达Output.Read()逐个读出PSObject并ToString()打印。由于脚本总共运行 60 秒而断开/重连只占用了约 6 秒重连后程序仍会在剩余的 50 多秒内持续打印输出直观证明远端脚本在断开期间未中断、输出被完整保留。四、场景二断开后销毁对象基于远端会话重建场景二是更贴近生产环境的重建路径客户端在断开状态下销毁DisposeRunspace 与 Pipeline 对象之后再依据远端会话信息重建全新对象并重连。这在客户端崩溃、进程重启或需要从其他进程接管任务的场景中非常关键。4.1 断开并销毁客户端对象Guid runspaceInstanceId; using (Runspace runspace RunspaceFactory.CreateRunspace(connectionInfo)) { runspace.Open(); // 保存实例 ID供后续重建时定位 runspaceInstanceId runspace.InstanceId; using (Pipeline pipeline runspace.CreatePipeline(1..60 | foreach {sleep 1; Output $_})) { pipeline.InvokeAsync(); Thread.Sleep(2000); // 断开后脚本继续在服务端运行 runspace.Disconnect(); Thread.Sleep(2000); } // 离开 using 块Runspace 与 Pipeline 均被 Dispose // 但远端会话中的脚本仍在运行 }与场景一的关键差异断开后不重连而是让两个using块自然结束Dispose()掉 Runspace 与 Pipeline 对象在此之前通过runspace.InstanceId保存了一个Guid 类型的实例 ID它是唯一标识远端会话的凭证是后续重建的锚点。4.2 通过 InstanceId 重建 RunspaceRunspace runspace1 null; Runspace[] runspaces Runspace.GetRunspaces(connectionInfo); foreach (Runspace rs in runspaces) { if (rs.InstanceId runspaceInstanceId) { runspace1 rs; break; } }Runspace.GetRunspaces(connectionInfo)是一个静态查询方法它基于同一连接信息向远端主机此处为 localhost发起查询返回所有仍处于活动状态、且有命令正在运行的 Runspace 数组。客户端通过比对InstanceId精确找回自己先前那条会话对应的新 Runspace 对象。从源码结构看新对象runspace1处于Disconnected 状态但携带了该会话中正在运行的脚本的完整元数据——这正是重建的实质不是恢复旧对象而是由远端会话信息物化出一个状态等价的新对象。4.3 重建断开的 Pipeline 并重连if (runspace1 ! null) { using (runspace1) { // 为远端正在运行的脚本创建处于断开状态的 Pipeline using (Pipeline pipeline runspace1.CreateDisconnectedPipeline()) { plState pipeline.PipelineStateInfo.State; // 预期Disconnected // 异步连接该方法会隐式连接 Runspace pipeline.ConnectAsync(); Thread.Sleep(2000); rsState runspace1.RunspaceStateInfo.State; // 预期Opened plState pipeline.PipelineStateInfo.State; // 预期Running // 与场景一相同的输出取回逻辑 while (!pipeline.Output.EndOfPipeline) { pipeline.Output.WaitHandle.WaitOne(); while (pipeline.Output.Count 0) { PSObject psObject pipeline.Output.Read(); Console.WriteLine(psObject.ToString()); } } } } }CreateDisconnectedPipeline()是本场景的核心 API它在已重建的 Runspace 上为远端正在运行的那条命令创建对应的 Pipeline 对象初始即处于Disconnected状态。随后ConnectAsync()异步接入Runspace 被隐式连接并回到OpenedPipeline 转为Running之后复用与场景一完全一致的Output数据流读取逻辑将脚本剩余的输出全部打印出来。两个场景形成了清晰的对照维度场景一场景二断开后的对象保留原 Runspace/PipelineDispose 销毁原对象重连方式原对象ConnectAsync()GetRunspaces()CreateDisconnectedPipeline()重建后ConnectAsync()依赖的会话锚点无对象自身携带状态InstanceId适用场景同一客户端内短暂断开/恢复客户端退出、崩溃或跨进程接管五、工程配置与项目结构5.1 工程配置要点RemotingDisconnectConnect01.csproj 中的关键配置OutputTypeExe编译为控制台可执行程序TargetFrameworkVersionv4.0Client ProfilePlatformTargetx86Debug 与 Release 均固定程序集引用System.Management.Automation, Version3.0.0.0SpecificVersionFalse另引用System、System.Core、System.Data、System.Xml输出路径Debug 为bin\Debug\Release 为bin\Release\。需要注意csproj 中System.Management.Automation.dll的HintPath是示例作者本地开发机的绝对路径E:\binaries_mae.amd64chk\...在克隆仓库后打开解决方案时Visual Studio 会改为通过 GAC 解析该程序集一般无需修改即可编译若遇到程序集解析问题请确认本机已安装对应版本的 Windows PowerShell。5.2 项目文件清单Samples/PowerShell/Remoting/RemotingDisconnectConnect01/ ├── README.md # 官方说明文档本文主体依据 └── cs/ ├── AssemblyInfo.cs # 程序集元数据版本 1.0.0.0 ├── Readme.txt # 同 README 的本地说明 ├── RemotingDisconnectConnect01.cs # 全部示例源码两个场景 ├── RemotingDisconnectConnect01.csproj # MSBuild 工程文件 └── RemotingDisconnectConnect01.sln # Visual Studio 解决方案5.3 源码结构速览RemotingDisconnectConnect01.cs 只有一个RemotingDisconnectConnect01.Main入口逻辑按注释清晰划分为Scenario 1与Scenario 2两段全程使用usingRunspace、Pipeline确保资源自动释放状态校验点均通过RunspaceStateInfo.State与PipelineStateInfo.State读取便于调试比对。仓库中同属 Remoting 主题的姊妹示例还包括 RemoteRunspace01远程 Runspace 的建立与超时设置和 RemoteRunspacePool01远程 Runspace 池三者组合可构成对 PowerShell 远程宿主编程的完整学习路径。六、总结与实践建议RemotingDisconnectConnect01 用不到 220 行代码完整演示了 PowerShell 远程会话断开/重连/重建的三段式生命周期断开runspace.Disconnect()后远端脚本继续运行客户端立即释放对连接资源的使用重连轻量对象未销毁时pipeline.ConnectAsync()一条调用同时恢复 Runspace 与 Pipeline重建重量对象已销毁时用InstanceId通过Runspace.GetRunspaces()找回会话再以CreateDisconnectedPipeline()物化新 Pipeline 并重连取回输出。实际工程中可据此实现提交长任务后客户端离线、稍后或由其他进程接管并收割结果的异步任务模式。需要留意的是断开/重连机制依赖 PowerShell 3.0 的System.Management.Automation程序集与已启用远程处理的 WinRM 环境示例默认面向 localhost连接真实远程主机时应在WSManConnectionInfo中补充目标地址并按需设置OperationTimeout/OpenTimeout参考 RemoteRunspace01.cs 的用法。所有输出均通过pipeline.Output的EndOfPipeline/WaitHandle/Read()组合可靠回收该模式同样适用于其他基于 Runspace 的宿主程序。赞分享示例工程【免费下载链接】Windows-classic-samplesThis repo contains samples that demonstrate the API used in Windows classic desktop applications.项目地址https://gitcode.com/gh_mirrors/wi/Windows-classic-samples点击查看免费下载相关推荐Windows PowerShell 远程 Runspace 连接实战用 WSManConnectionInfo 配置超时并建立远程连接RemoteRunspace01 示例深度解析Windows PowerShell 远程 Runspace 连接实战用 WSManConnectionInfo 配置超时并建立远程连接RemoteRuns示例工程Windows-classic-samples 自定义 PowerShell 宿主入门Host01 示例的 Runspace 与 exit 退出码回传机制深度解析Windows classic samples 自定义 PowerShell 宿主入门Host01 示例的 Runspace 与 exit 退出码回传机制深度示例工程Unity Transport 客户端示例深度解析ClientBehaviour 的连接、收发与断开全流程Unity Transport 客户端示例深度解析ClientBehaviour 的连接、收发与断开全流程 本指南以 com.unity.transport游戏开发示例工程上一篇NocoBase CLI 应用管理实战用 nb app 完成启动、停止、日志排查与升级下一篇WinUI 3Microsoft.UI.Xaml动画体系全解析从 Storyboard 到 Composition 动画的完整技术脉络创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
阅读完成 · 觉得有帮助?