目录
介绍
先决条件
自定义扩展库中的方法要求
使用代码
在PowerShell环境中执行命令
如何使用以这种方式准备的类
C#下的PowerShell处理程序类的完整代码
介绍C#的PowerShell支持有点麻烦。在这里,我提出了一个简单的解决方案,而无需深入研究环境。本文旨在成为有关使用从C#编写的程序调用的PowerShell来支持各种Microsoft环境的循环的基础。
先决条件所有示例均在Windows 10 Pro 21H2 19044.1387环境中的MS Visual Studio Community 2019中创建、编译和测试。
为了正确编译,需要安装NuGet包:Microsoft.PowerShell.3.ReferenceAssemblies。
自定义扩展库中的方法要求您可以使用已在各自的命名空间中定义的方法和类,但我提供了一些简单的解决方案示例,以便您理解和清楚。您当然可以以不同的方式编写它们(以更简单或更清晰的方式),但我们不要过分投入。
以下是当string值为空或仅包含空格时返回true的方法:
public static bool IsNullEmptyOrWhite(this string sValue) {...}
以Name/Value形式定义PowerShell参数的类。它可以替换为任何方便的dictionary类:
public class ParameterPair
{
public string Name { get; set; } = string.Empty;
public object Value { get; set; } = null;
}
当然,有很多方法可以打开PowerShell、在其中发出命令并下载其结果。我把自己限制在两个:
- 调用一个脚本,其中所有命令及其数据必须以一行文本的形式呈现
- Cmdlet调用,我们一次只能给出一个命令,它的参数作为Name/Value对传递,其中值可以是任何类型
为此,RunPS 定义了两种具有不同参数集的方法,经过适当的参数处理后,使用统一的ExecutePS方法。
///
/// Basic method of calling PowerShell a script where all commands
/// and their data must be presented as one line of text
///
/// PowerShell environment
/// A single line of text
/// containing commands and their parameters (in text format)
/// A collection of objects that contains the feedback
/// The method returns true when executed correctly
/// and false when some errors have occurred
public static bool RunPS(PowerShell ps, string psCommand, out Collection outs)
{
//Programmer's Commandment I: Remember to reset your variables
outs = new Collection();
HasError = false;
//Cleanup of PowerShell also due to commandment I
ps.Commands.Clear();
ps.Streams.ClearStreams();
//We put the script into the PowerShell environment
//along with all commands and their parameters
ps.AddScript(psCommand);
//We are trying to execute our command
outs = ExecutePS(ps);
//The method returns true when executed correctly and false
//when some errors have occurred
return !HasError;
}
///
/// Method 2 cmdlet call where we can only give one command at a time
/// and its parameters are passed as Name/Value pairs,
/// where values can be of any type
///
/// PowerShell environment
/// Single command with no parameters
/// A collection of objects that contains the feedback
/// A collection of parameter pairs
/// in the form Name/Value
/// The method returns true when executed correctly
/// and false when some errors have occurred
public static bool RunPS(PowerShell ps, string psCommand,
out Collection outs, params ParameterPair[] parameters)
{
//Programmer's Commandment I: Remember to reset your variables
outs = new Collection();
HasError = false;
if (!psCommand.Contains(' '))
{
//Cleanup of PowerShell also due to commandment I
ps.Commands.Clear();
ps.Streams.ClearStreams();
//We put a single command into the PowerShell environment
ps.AddCommand(psCommand);
//Now we enter the command parameters in the form of Name/Value pairs
foreach (ParameterPair PP in parameters)
{
if (PP.Name.IsNullEmptyOrWhite())
{
LastException = new Exception("E1008:Parameter cannot be unnamed");
return false;
}
if (PP.Value == null) ps.AddParameter(PP.Name);
else ps.AddParameter(PP.Name, PP.Value);
}
//We are trying to execute our command
outs = ExecutePS(ps);
}
//And here we have a special exception
//if we tried to apply the method not to a single command
else LastException = new Exception
("E1007:Only one command with no parameters is allowed");
//The method returns true when executed correctly
//and false when some errors have occurred
return !HasError;
}
///
/// Internal method in which we try to execute a script or command with parameters
/// This method does not need to return a fixed value
/// that indicates whether or not the execution succeeded,
/// since the parent methods use the principal properties of the class set in it.
///
/// PowerShell environment
/// A collection of objects that contains the feedback
private static Collection ExecutePS(PowerShell ps)
{
Collection retVal = new Collection();
//We are trying to execute our script
try
{
retVal = ps.Invoke();
// ps.HadErrors !!! NO!
// The PowerShell environment has a special property that
// indicates in the assumption whether errors have occurred
// unfortunately, most often I have found that despite errors,
// its value is false or vice versa,
// in the absence of errors, it pointed to the truth.
//Therefore, we check the fact that errors have occurred,
//using the error counter in PowerShell.Streams
if (ps.Streams.Error.Count > 0) //czy są błędy wykonania
{
//We create another general exception, but we do not raise it.
LastException = new Exception("E0002:Errors were detected during execution");
//And we write runtime errors to the LastErrors collection
LastErrors = new PSDataCollection(ps.Streams.Error);
}
}
//We catch script execution errors and exceptions
catch (Exception ex)
{
//And if they do, we create a new general exception but don't raise it
LastException = new Exception("E0001:" + ex.Message);
}
//Returns a collection of results
return retVal;
}
一开始,我们为结果创建一个空集合:
Collection Results = new Collection();
然后我们需要打开单独的PowerShell环境:
PowerShell ps = PowerShell.Create();
然后尝试执行脚本:
if (PS.Basic.RunPS(ps, "Get-Service |
Where-Object {$_.canpauseandcontinue -eq \"True\"}", out Results))
{ Interpreting the results… }
else
{ Interpretation of errors… }
或带参数的命令:
if (PS.Basic.RunPS(ps, "Get-Service", out Results,
new ParameterPair { Name = "Name", Value = "Spooler"}
))
{ Interpreting the results… }
else
{ Interpretation of errors… }
下一步是解释结果:
在每个例子中,我们都会回顾Collection Results集合。
例如,对于脚本"Get-Service ...",集合由基类型(Results [0] .BaseObject.GetType()) ServiceController,其中,具有属性“name”)的对象组成。我们可以用Results [0] .Properties ["name"]. Value来读。
对结果的解释归结为查看Results并检索我们感兴趣的适当属性的值。
或者错误的解释:
当尝试执行PowerShell命令时发生错误时,基类有几个属性和变量要处理。
错误准备执行命令导致的错误
这些错误可能来自执行命令之前不正确地准备Powershell。例如,我们会忘记通过传递一个空的ps来打开这个环境。或者,我们将传递一些语法废话,而不是正确的脚本/命令。
在这种情况下,当尝试调用ps.Invoke ();时,您可以在描述中找到错误原因的异常。在基类中,LastException变量随后被定义为带有消息"E0001:" + ex.Message(即代码“E0001”前面的异常描述)。
在错误解释阶段,您可以通过检查“ErrorCode”属性(PS.Basic.ErrorCode == 1)的值来检查是否发生了此类错误,然后使用LastException.Message描述进行更详细的错误处理。
命令执行错误
在执行完全有效的命令或脚本时,我们也会遇到错误。例如,当我们指定一个对象的Identity时,其值在PowerShell环境的可见性范围内没有任何对象。然后我们会得到“未找到”或“错误名称”的错误,但只是尝试执行该命令不会导致异常。
我们可以在集合PSDataCollection LastErrors中找到这样的错误。
在给出的类中引入的错误处理模型将在LastException中导致一个新的异常,其描述形式为: “E0002: Errors were detected during execution”。通过"ErrorCode" (PS.Basic.ErrorCode == 2) 检查后,我们可以从集合中读取后续错误,并根据LastErrors [0] .Exception.Message中的异常描述确定其原因。与LastException一样,现在在此基础上,需要进行更详细的错误处理。
C#下的PowerShell处理程序类的完整代码using System;
using System.Collections.ObjectModel;
using System.Linq;
using System.Management.Automation;
namespace PS
{
public static class Basic
{
///
/// The last exception that occurred in the PS.Basic class
///
public static Exception LastException = null;
///
/// Collection of PowerShell runtime errors
///
public static PSDataCollection LastErrors =
new PSDataCollection();
///
/// Auxiliary Property that helps to check if there was an error and
/// resets the error state
///
public static bool HasError
{
get
{
return LastException != null;
}
set
{
if(!value)
{
LastException = null;
LastErrors = new PSDataCollection();
}
}
}
///
/// A helper Property to help you get the error code
///
public static int ErrorCode
{
get
{
if (HasError) return int.Parse(LastException.Message.Substring(1, 4));
return 0;
}
}
///
/// Basic method of calling PowerShell a script where all commands
/// and their data must be presented as one line of text
///
/// PowerShell environment
/// A single line of text containing commands
/// and their parameters (in text format)
/// A collection of objects that contains the feedback
/// The method returns true when executed correctly
/// and false when some errors have occurred
public static bool RunPS
(PowerShell ps, string psCommand, out Collection outs)
{
//Programmer's Commandment I: Remember to reset your variables
outs = new Collection();
HasError = false;
//Cleanup of PowerShell also due to commandment I
ps.Commands.Clear();
ps.Streams.ClearStreams();
//We put the script into the PowerShell environment
//along with all commands and their parameters
ps.AddScript(psCommand);
//We are trying to execute our command
outs = ExecutePS(ps);
//The method returns true when executed correctly and false
//when some errors have occurred
return !HasError;
}
///
/// Method 2 cmdlet call where we can only give one command
/// at a time and its parameters are passed as Name/Value pairs,
/// where values can be of any type
///
/// PowerShell environment
/// Single command with no parameters
/// A collection of objects that contains the feedback
/// A collection of parameter pairs
/// in the form Name/Value
/// The method returns true when executed correctly
/// and false when some errors have occurred
public static bool RunPS(PowerShell ps, string psCommand,
out Collection outs, params ParameterPair[] parameters)
{
//Programmer's Commandment I: Remember to reset your variables
outs = new Collection();
HasError = false;
if (!psCommand.Contains(' '))
{
//Cleanup of PowerShell also due to commandment I
ps.Commands.Clear();
ps.Streams.ClearStreams();
//We put a single command into the PowerShell environment
ps.AddCommand(psCommand);
//Now we enter the command parameters in the form of Name/Value pairs
foreach (ParameterPair PP in parameters)
{
if (PP.Name.IsNullEmptyOrWhite())
{
LastException = new Exception("E1008:Parameter cannot be unnamed");
return false;
}
if (PP.Value == null) ps.AddParameter(PP.Name);
else ps.AddParameter(PP.Name, PP.Value);
}
//We are trying to execute our command
outs = ExecutePS(ps);
}
//And here we have a special exception if we tried
//to apply the method not to a single command
else LastException = new Exception("E1007:Only one command
with no parameters is allowed");
//The method returns true when executed correctly and false
//when some errors have occurred
return !HasError;
}
///
/// Internal method in which we try to execute a script or command with parameters
/// This method does not need to return a fixed value that indicates
/// whether or not the execution succeeded,
/// since the parent methods use the principal properties of the class set in it.
///
/// PowerShell environment
/// A collection of objects that contains the feedback
private static Collection ExecutePS(PowerShell ps)
{
Collection retVal = new Collection();
//We are trying to execute our script
try
{
retVal = ps.Invoke();
// ps.HadErrors !!! NO!
// The PowerShell environment has a special property
// that indicates in the assumption whether errors have occurred
// unfortunately, most often, I have found that despite errors
// its value is false or vice versa,
// in the absence of errors, it pointed to the truth.
// Therefore, we check the fact that errors have occurred,
// using the error counter in PowerShell.Streams
if (ps.Streams.Error.Count > 0) //czy są błędy wykonania
{
//We create another general exception, but we do not raise it.
LastException = new Exception
("E0002:Errors were detected during execution");
//And we write runtime errors to the LastErrors collection
LastErrors = new PSDataCollection(ps.Streams.Error);
}
}
//We catch script execution errors and exceptions
catch (Exception ex)
{
//And if they do, we create a new general exception but don't raise it
LastException = new Exception("E0001:" + ex.Message);
}
//Returns a collection of results
return retVal;
}
}
///
/// Class defining the PowerShell parameter in the form Name/Value.
/// it can be replaced with any convenient dictionary class
///
public class ParameterPair
{
public string Name { get; set; } = string.Empty;
public object Value { get; set; } = null;
}
}
https://www.codeproject.com/Articles/5318610/One-More-Solution-to-Calling-PowerShell-from-Cshar