将GUI命令导出到PowerShell运行空间?

时间:2015-03-04 21:42:16

标签: powershell runspace

在托管PowerShell运行空间的.NET应用程序中,是否可以将主机应用程序功能导出到该运行空间,以便我拥有对我的UI产生影响的PowerShell cmdlet?

我的目标只是能够从长时间运行的PowerShell脚本向我的主机应用程序UI报告进度消息(因为托管PowerShell我没有控制台窗口。)我想象的是:

C#:

// Speculative pseudocode:

class ReportProgressCmdlet : Cmdlet { ... } // Reports a message on the UI.
...
myRunspace.Register(new ReportProgressCmdlet(myUI)); 

的PowerShell:

Report-Progress "Starting step 1." # UI shows "Starting step 1."
...
Report-Progress "Step 1 failed."

这可能吗?如果我错了,很高兴听到不同的解决方案(活动?)我已经阅读了以下所有问题以及许多其他类似的问题,我并不认为他们是我正在寻找的:< / p>

How to expose functionality to PowerShell from within your application

Powershell Call Assembly Delegate

How to programmatically add a PSCmdlet to a Powershell pipeline?

Provide a .NET method as a delegate callback

2 个答案:

答案 0 :(得分:0)

UPDATE :我在下面介绍的技术适用于任意GUI操作,但出于我的目的(以清理后的格式显示PowerShell输出),实现自定义更容易PSHost,PSHostUserInterface和PSHostRawUserInterface使脚本端更简单(您使用熟悉的写输出,写主机,写错误等,而不是一些定制的输出API。这也使脚本保持灵活性,以防您想要在你的主机之外运行它)。请参阅https://msdn.microsoft.com/en-us/library/ee706570%28v=vs.85%29.aspx以获取一个好例子。


Cmdlets被证明是错误的方法 - 要做的是通过“SessionState”API将GUI对象实例暴露给PowerShell,然后PowerShell代码可以直接使用它们。这是你如何做到的:

带来正确的命名空间:

using System.Management.Automation;
using System.Management.Automation.Runspaces;

然后创建一个Runspace并将您的GUI对象实例注入PowerShell变量。 (这里我传递的是一个暴露一个Messages集合的viewmodel,但你可能希望在现实世界中更好地分层。)你可以在创建运行空间时使用InitialSessionState,如:

var iss = InitialSessionState.CreateDefault();
iss.Variables.Add(new SessionStateVariableEntry("thing", this.DataContext, "it's the data context"));

using (Runspace runspace = RunspaceFactory.CreateRunspace(iss))
{
    runspace.ThreadOptions = PSThreadOptions.UseCurrentThread;
    runspace.Open();

    using (PowerShell ps = PowerShell.Create())
    {
        ps.Runspace = runspace;
        ps.AddScript(@"$thing.Messages.Add('in which PowerShell can make stuff happen on the GUI via InitialSessionState')");
        ps.Invoke();
    }

    runspace.Close();
}

或者,您可以在使用Runspace.SessionStateProxy.SetVariable(name,object)创建和打开Runspace后注入对象实例。除了你不必注入InitialSessionState之外,那个代码的代码是相同的。

Doug Finke得到了这个奖励,因为我从他发现的截屏视频中学到了这个技术:

http://www.dougfinke.com/blog/index.php/2009/09/02/how-to-host-powershell-in-a-wpf-application/

答案 1 :(得分:0)

我在WPF + XAML中创建了一个进度条,并在不同的Runspace中运行它。这段代码可能正是您所需要的。

https://gallery.technet.microsoft.com/Script-New-ProgressBar-329abbfd

这是一个关于如何使用我运行循环的函数,更新新的Runspace的示例。并检测窗口/进度条是否已关闭,并停止执行代码。

# Create the window form in a different namespace 
Load-Window 

For ($i = 0; $i -lt 100; $i++) { 

  #Update the progressbar 
  $Result = Update-ProgressBar -Percent $i -Label1Text "$i / 100" 

  # If you want to simply check if the window is still active
  # Update-Window will return if the form/window is active 
  # $Result = Update-Window 

  #If the window is close, stop the loop 
  If ($Result -eq $False) { Break } 

  Sleep -Milliseconds 100 

} 

Close-Window