首页 > 代码库 > Cmdlet开发与学习(七)
Cmdlet开发与学习(七)
到目前的例子为止,我们使用的都是不带参数的CreateRunspace()方法创建的运行空间实例,这就意味着,我们使用的都是默认的命令集合,提供程序,初始化脚本和格式信息。
但是,这些默认的配置信息都是可以控制的,这要通过RunspaceConfiguration类实现。RunspaceConfiguration类是CreateRunspace函数的参数。创建运行空间实例后,其中的会话状态变量可以通过运行空间实例的SessionStateProxy来设置和获取。
注意:如果运行空间处于BeforeOpen状态,可以直接修改其配置。但是在runspace.open之后,不是任何配置修改都管用。具体上说,在open之后,AddPSSnapin()函数和RemovePSSnapin()函数好使,但是直接修改Assemblies,cmdlet,Formats,Scripts和Types集合的操作却是不允许的。
1 static void Main(string[] args) 2 { 3 RunspaceConfiguration configuration = RunspaceConfiguration.Create(); 4 PSSnapInException warning = null; 5 //Add PSSnapIn,系统自动调用update 6 configuration.AddPSSnapIn("MySnapin", out warning); 7 8 Runspace runspace = RunspaceFactory.CreateRunspace(configuration); 9 //通过SessionStateProxy修改会话状态变量 10 runspace.SessionStateProxy.SetVariable("factorOne", 7); 11 runspace.SessionStateProxy.SetVariable("factorTwo", 11); 12 13 //下面是关于配置集合的修改,只有在update时候才起效 14 //添加cmdlet GetWidgetCmdlet是实现cmdlet的.Net类型 15 runspace.RunspaceConfiguration.Cmdlets.Append(new CmdletConfigurationEntry("get-widget", typeof(GetWidgetCmdlet),null)); 16 runspace.RunspaceConfiguration.Cmdlets.Update(); 17 18 //添加格式文件 19 runspace.RunspaceConfiguration.Formats.Append(new FormatConfigurationEntry("C:\\myformat.ps1xml")); 20 runspace.RunspaceConfiguration.Formats.Update(); 21 22 //添加函数 23 runspace.RunspaceConfiguration.Scripts.Append(new ScriptConfigurationEntry("add","return $args[0] + $args[1]")); 24 runspace.RunspaceConfiguration.Scripts.Update(); 25 runspace.Open(); 26 27 //... 28 }
AddPSSnapIn()的PSConsoleLoadException类型的out参数在调用失败时,返回PSSnapInException实例。如果找不到snap-in,或者发生其他致命错误,则AddPSSnapIn()抛出异常。
异步执行管道
异步执行管道,调用InvokeAsync()方法,它的执行前提条件与调用Invoke类似。
这里需要注意的是,在调用InvokeAsync后,管道得在关闭输入管道后才执行。
1 Runspace runspace = RunspaceFactory.CreateRunspace(); 2 runspace.Open(); 3 4 Pipeline pipeline = runspace.CreatePipeline("Command"); 5 pipeline.InvokeAsync(); 6 pipeline.Input.Close();
如果输入管道已关闭,再次调用close不会产生异常,但是可以用PipelineWriter类的IsOpen属性检查管道是否处于关闭状态。
既然执行了异步,那么重点必然就是读取输出和错误了。主要看PipelineReader提供的可用方法。
1.Read():读取对象,如果对象不可用,就阻塞执行一直等待
2.Read(count):读取count个对象,阻塞执行直到所有的对象都可用
3.ReadToEnd():读取数据直到管道关闭
4.Peek():检查是否有可用的对象
5.NonBlockingRead():读一个对象,如果该对象不存在,则直接返回
6.NonBlockingRead(count):读取count个对象,如果该对象不存在,则直接返回
PipelineReader还有个有用的属性WaitHandle,可以用来等待输出和DataReady事件,DataReady事件只有当输出可用时才输出。
下面用一个例子来演示:
1 Runspace runspace = RunspaceFactory.CreateRunspace(); 2 runspace.Open(); 3 4 Pipeline pipeline = runspace.CreatePipeline("Command"); 5 pipeline.InvokeAsync(); 6 WaitHandle[] handles = new WaitHandle[2]; 7 handles[0] = pipeline.Output.WaitHandle; 8 handles[1] = pipeline.Error.WaitHandle; 9 pipeline.Input.Close(); 10 11 while (pipeline.PipelineStateInfo.State == PipelineState.Running) 12 { 13 switch (WaitHandle.WaitAny(handles)) 14 { 15 case 0: 16 while (pipeline.Output.Count > 0) 17 { 18 Console.WriteLine("Output: {0}", pipeline.Output.Read()); 19 } 20 break; 21 case 1: 22 while (pipeline.Error.Count > 0) 23 { 24 Console.WriteLine("Error: {0}", pipeline.Error.Read()); 25 } 26 break; 27 } 28 }
管道状态
管道存在几种状态:NotStarted,Running,Stopping,Stopped,Completed,Failed。
可以监视管道的StateChanged事件,在管道的状态发生改变时,做出相应的处理。
终结性错误
当异步管道发生终结型错误时,管道状态变为Failed,抛出StateChanged事件,PipelineStateInfo对象的Reason属性包含了ErrorRecord对象,里面记录着终结型错误信息。
停止管道
如果希望关闭正在执行的异步管道,管道对象的Stop()和StopAsync()函数都可以实现这个效果。
这里需要明确的是,调用上述的两个停止方法时,管道状态变为Stopping,StateChanged事件触发。如果管道线程正在调用外部代码,比如其他.Net方法,则管道处于无限Stopping状态,等待外部调用返回。如果管道成功停止,则变为Stopped状态。