1

在托管 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)); 

电源外壳:

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

这可能吗?如果我做错了,很高兴听到不同的解决方案(事件?)

如何从应用程序中向 PowerShell 公开功能

Powershell 调用程序集委托

如何以编程方式将 PSCmdlet 添加到 Powershell 管道?

提供 .NET 方法作为委托回调

4

2 回答 2

0

更新:我在下面提到的技术适用于任意 GUI 操作,但出于我的目的(以清理格式显示 PowerShell 输出),结果证明更容易实现自定义 PSHost、PSHostUserInterface 和 PSHostRawUserInterface,以便脚本侧保持更简单(您使用熟悉的 Write-Output、Write-Host、Write-Error 等,而不是一些定制的输出 API。这也使脚本保持灵活,以防您也想在主机之外运行它) . 有关一个很好的示例,请参阅https://msdn.microsoft.com/en-us/library/ee706570%28v=vs.85%29.aspx


Cmdlet 被证明是错误的方法——要走的路是通过“SessionState”API 向 PowerShell 公开 GUI 对象实例,然后 PowerShell 代码可以直接使用这些。这是你如何做到的:

引入正确的命名空间:

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

然后创建一个运行空间并将您的 GUI 对象实例作为 PowerShell 变量注入。(在这里,我传递了一个公开消息集合的视图模型,但您可能希望在现实世界中更好地分层。)您可以在创建运行空间时使用 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) 注入对象实例。那个代码是一样的,除了你不必注入 InitialSessionState。

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

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

于 2015-03-09T16:10:25.247 回答
0

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

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

这是一个关于如何使用我创建的函数来运行循环、更新新的运行空间的示例。并且还检测窗口/进度条是否关闭,并停止执行您的代码。

# 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 
于 2015-05-05T01:53:36.607 回答