您当前的位置: 首页 >  c#

寒冰屋

暂无认证

  • 1浏览

    0关注

    2286博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

从C#调用PowerShell的另一种解决方案

寒冰屋 发布时间:2022-05-06 22:30:19 ,浏览量:1

目录

介绍

先决条件

自定义扩展库中的方法要求

使用代码

在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环境中执行命令

当然,有很多方法可以打开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

关注
打赏
1665926880
查看更多评论
立即登录/注册

微信扫码登录

0.0451s