【问题标题】:How can i invoke powershell that uses "graphical command window" (show-command) from c#如何从 c# 调用使用“图形命令窗口”(显示命令)的 powershell
【发布时间】:2016-06-28 22:42:40
【问题描述】:
            Collection<PSObject> PSOutput;
            using (PowerShell PowerShellInstance = PowerShell.Create())
            {               
                PowerShellInstance.AddScript("Show-Command -name Get-Content -PassThru");
                PSOutput = PowerShellInstance.Invoke();             
            }

这不返回任何输出,但 PowerShellInstance 上存在错误,其错误流具有空引用异常

在 Microsoft.PowerShell.Commands.ShowCommandInternal.ShowCommandHelper.GetHostWindow(PSCmdlet cmdlet) 在 Microsoft.PowerShell.Commands.ShowCommandInternal.ShowCommandHelper.CallShowDialog(PSCmdlet cmdlet) 在 Microsoft.PowerShell.Commands.ShowCommandInternal.ShowCommandHelper.ShowCommandWindow(PSCmdlet cmdlet、对象 commandViewModelObj、Double windowWidth、Double windowHeight, Boolean passThrough)

【问题讨论】:

  • 从反编译代码来看,这是一个错误,要求使用的PowerShell主机的PSHost.PrivateData返回不是null对象。

标签: c# powershell


【解决方案1】:

如果目标是获取 PowerShell CmdLet 的参数,您可以使用 Get-Command。您将无法在 C# 中显示 PowerShell 图形窗口。

using System;
using System.Collections.ObjectModel;
using System.Linq;
using System.Management.Automation;

namespace ConsoleTest
{
    class Program
    {
        static void Main(string[] args)
        {
            string cmdLetName = "Get-Content";
            Collection<PSObject> PSOutput;
            using (PowerShell PowerShellInstance = PowerShell.Create())
            {
                PowerShellInstance.AddCommand("Get-Command");
                PowerShellInstance.AddParameter("Name", cmdLetName);
                PSOutput = PowerShellInstance.Invoke();
            }

            foreach(var item in PSOutput)
            {
                var cmdLetInfo = item.BaseObject as System.Management.Automation.CmdletInfo;
                var defaultParamSet = cmdLetInfo.ParameterSets.Where(pSet => pSet.IsDefault == true).FirstOrDefault();

                Console.WriteLine(String.Format("Default ParameterSet for {0}.  (*) Denotes Mandatory", cmdLetName));
                foreach (var param in defaultParamSet.Parameters.OrderByDescending(p => p.IsMandatory))
                {
                    if (param.IsMandatory)
                        Console.WriteLine(String.Format("\t {0} (*)", param.Name));
                    else
                        Console.WriteLine(String.Format("\t {0}", param.Name)); ;
                }
            }

            Console.ReadLine();
        }
    }
}

输出:

Default ParameterSet for Get-Content.  (*) Denotes Mandatory
     Path (*)
     ReadCount
     TotalCount
     Tail
     Filter
     Include
     Exclude
     Force
     Credential
     Verbose
     Debug
     ErrorAction
     WarningAction
     InformationAction
     ErrorVariable
     WarningVariable
     InformationVariable
     OutVariable
     OutBuffer
     PipelineVariable
     UseTransaction
     Delimiter
     Wait
     Raw
     Encoding
     Stream

https://blogs.technet.microsoft.com/heyscriptingguy/2012/05/16/use-the-get-command-powershell-cmdlet-to-find-parameter-set-information/

【讨论】:

  • 是的,那将是另一种方式,但我试图避免为了从用户那里获取一些默认参数而不得不对自己的 UI 进行皮肤处理。必须是一种从 c# 重新创建控制台行为的方法。
  • 您的最终目标/用途是什么?
【解决方案2】:

对我来说这似乎是一个错误。 Show-Command 在访问其属性之前不会检查 PrivateData 中的 null。要解决此问题,您需要实现自己的 PSHostoverride PrivateData 以返回不 null 对象:

Add-Type -TypeDefinition @‘
    using System;
    using System.Globalization;
    using System.Management.Automation;
    using System.Management.Automation.Host;
    public class NotDefaultHost : PSHost {
        private Guid instanceId = Guid.NewGuid();
        private PSObject privateData = new PSObject();
        public override CultureInfo CurrentCulture {
            get {
                return CultureInfo.CurrentCulture;
            }
        }
        public override CultureInfo CurrentUICulture {
            get {
                return CultureInfo.CurrentUICulture;
            }
        }
        public override Guid InstanceId {
            get {
                return instanceId;
            }
        }
        public override string Name {
            get {
                return "NonDefaultHost";
            }
        }
        public override PSObject PrivateData {
            get {
                return privateData;
            }
        }
        public override PSHostUserInterface UI {
            get {
                return null;
            }
        }
        public override Version Version {
            get {
                return new Version(1, 0);
            }
        }
        public override void EnterNestedPrompt() {
            throw new NotSupportedException();
        }
        public override void ExitNestedPrompt() {
            throw new NotSupportedException();
        }
        public override void NotifyBeginApplication() { }
        public override void NotifyEndApplication() { }
        public override void SetShouldExit(int exitCode) { }
    }
’@

$Runspace = [RunspaceFactory]::CreateRunspace((New-Object NotDefaultHost));
$Runspace.Open()
$PS=[PowerShell]::Create()
$PS.Runspace = $Runspace;
$PS.AddScript('Show-Command -Name Get-Content -PassThru').Invoke()
if($PS.HadErrors) {
    '--- Errors: ---'
    $PS.Streams.Error
}
$PS.Dispose()
$Runspace.Dispose()

【讨论】:

    【解决方案3】:

    将此添加到您的代码中:

            Collection<PSObject> PSOutput;
            using (PowerShell PowerShellInstance = PowerShell.Create()) {
                PowerShellInstance.AddScript("Show-Command -name Get-Content -PassThru");
                PSOutput = PowerShellInstance.Invoke();
                if (!PowerShellInstance.HadErrors ) {
                    foreach (var item in PSOutput) {
                        Console.WriteLine(item);
                    }
                } else {
                    foreach (var item in PowerShellInstance.Streams.Error) {
                        Console.WriteLine(item);
                        Console.WriteLine(item.Exception.StackTrace);
                    }
                }
            }
    

    返回这个输出

    Object reference not set to an instance of an object.
       at Microsoft.PowerShell.Commands.ShowCommandInternal.ShowCommandHelper.GetHostWindow(PSCmdlet cmdlet)
       at Microsoft.PowerShell.Commands.ShowCommandInternal.ShowCommandHelper.CallShowDialog(PSCmdlet cmdlet)
       at Microsoft.PowerShell.Commands.ShowCommandInternal.ShowCommandHelper.ShowCommandWindow(PSCmdlet cmdlet, Object commandViewModelObj, Double windowWidth, Double windowHeight, Boolean passThrough)
    

    这几乎表明没有 HostWindow 对象,这是使用 PS 管理对象所预期的。

    使用 ILSpy 检查 Microsoft.PowerShell.Commands.Utility.dll 中的代码,Show-Command cmdlet 似乎调用了一个 ShowCommandProxy 对象,该对象正在寻找未设置的 System.Management.Automation.Internal GraphicalHostReflectionWrapper 对象。

    --更新--

    我想看看这个问题:Custom PSHostUserInterface is ignored by Runspace 这表明您将不得不实现自己的 PSHost (https://msdn.microsoft.com/en-us/library/system.management.automation.host.pshost(v=vs.85).aspx) 并实现自己的 PSHostUserInterface,最后实现自己的 PSHostRawUserInterface 到处理所有 I/O。

    MS 在此处有一个示例 https://msdn.microsoft.com/en-us/library/ee706551(v=vs.85).aspx,它执行自定义主机实现。

    【讨论】:

    猜你喜欢
    • 2018-08-18
    • 2016-05-13
    • 1970-01-01
    • 2015-12-11
    • 2015-10-13
    • 2013-08-19
    • 1970-01-01
    • 2020-11-17
    • 1970-01-01
    相关资源
    最近更新 更多