在 PowerShell 中为按钮事件运行空间

3

$syncHash button event with a separate runspace

不确定是否重复,查看了在线资料并与Boe Prox的解决方案合作,他在另一篇StackOverflow文章中提到了(https://dev59.com/e2_Xa4cB1Zd3GeqP5tqm#15502286),但是他是通过在线命令行/PowerShell窗口更新,通过在线程内运行的函数运行。我正在从一个按钮的事件中运行一个事件,位于线程内,并尝试运行单独的线程以进行点击事件。在线程外部,该事件正常工作,但在内部却无法工作。我做错了什么? PS. 我发现另一个博客引用了Boe Prox的工作(https://www.foxdeploy.com/blog/part-v-powershell-guis-responsive-apps-with-progress-bars.html),构建了另一个多线程应用程序,但基本上是相同的概念,通过放置在单独线程内的PowerShell命令集/函数更新窗口。

$push.Add_Click{
    $newRunspace =[runspacefactory]::CreateRunspace()
    $newRunspace.ApartmentState = "STA"
    $newRunspace.ThreadOptions = "ReuseThread"         
    $newRunspace.Open()
    $newRunspace.SessionStateProxy.SetVariable("syncHash",$syncHash)
    $powershell = [powershell]::Create().AddScript({           
        $choice = $comboBox.SelectedItem
        # $drive = Get-Location
        if(!(Test-Path -PathType Container -Path "L:\$choice"))
        {
    #        New-Item -ItemType "container" -Path . -Name $choice
            New-Item -ItemType "Directory" -Path . -Name $choice
        }

    #        $folder = $_
            # Where is it being stored at?
            [System.IO.File]::ReadLines("Y:\$choice\IPs.txt") | foreach {
                ping -a -n 2 -w 2000 $_ | Out-Null
                Test-Connection -Count 2 -TimeToLive 2 $_ | Out-Null

                if($?)
                {
                   RoboCopy /Log:"L:\$folder\$_.log" $source \\$_\c$\tools
                   RoboCopy /Log+:"L:\$folder\$folder-MovementLogs.log" $source \\$_\c$\tools
                   Start-Process "P:\psexec.exe" -ArgumentList "\\$_ -d -e -h -s cmd /c reg import C:\tools\dump.reg"
                   # Copy-Item -LiteralPath Y:\* -Destination \\$_\c$\tools
                   $listBox.Items.Add($_)
                }
            }
    })
    $powershell.Runspace = $newRunspace
    $powershell.BeginInvoke()

}


1
你的问题缺乏足够的信息,但是初步看来,1. 你的运行空间不知道 $comboBox.SelectedItem 是什么,2. 你使用同步哈希表初始化了运行空间,但没有使用它。 - Santiago Squarzon
你说的“不使用它”是什么意思?(就像我说的,我正在关注博客)。对于comboBox.selectedItem,它只是一个在我的按钮事件之外定义的变量。我选择不显示它,因为目前这并不是我所面临的问题 :) - user1546559
1
$syncHash 没有在你的脚本块中被使用。 - Santiago Squarzon
它被用于运行空间,即 $newRunspace.SessionStateProx.setVariable("syncHash", $syncHash),因此我假设你从我提供的代码(来自博客)中看到的是它从顶部被重复使用。 - user1546559
所以博客确实有一个按钮事件,但正如我所说的那样,在它的独立运行空间内,在按钮事件中,他从外部更新窗口,在控制台中,它利用命令集/函数来操作,我需要显示信息到窗口屏幕上,从窗口内部,当我“按”按钮时,我需要运行一个动作(而不冻结窗口),以便在按钮事件运行时,窗口可以被更新。 - user1546559
$newRunspace =[runspacefactory]::CreateRunspace() $newRunspace.ApartmentState = "STA" $newRunspace.ThreadOptions = "ReuseThread"
$newRunspace.Open() $newRunspace.SessionStateProxy.SetVariable("syncHash",$syncHash) 因此,相同的代码被重复使用,在顶部(在我的程序中,我的代码上方)。
- user1546559
1个回答

4
你可以将此作为所要实现的蓝图,重要的是runspace不能看到你的表格控件。如果你希望你的runspace与控件交互,它们必须被传递给它,可以通过SessionStateProxy.SetVariable(...)或者.AddParameters(..)的参数方式进行传递。
using namespace System.Windows.Forms
using namespace System.Drawing
using namespace System.Management.Automation.Runspaces

Add-Type -AssemblyName System.Windows.Forms

[Application]::EnableVisualStyles()

try {
    $form = [Form]@{
        StartPosition   = 'CenterScreen'
        Text            = 'Test'
        WindowState     = 'Normal'
        MaximizeBox     = $false
        ClientSize      = [Size]::new(200, 380)
        FormBorderStyle = 'Fixed3d'
    }

    $listBox = [ListBox]@{
        Name       = 'myListBox'
        Location   = [Point]::new(10, 10)
        ClientSize = [Size]::new(180, 300)
    }
    $form.Controls.Add($listBox)

    $runBtn = [Button]@{
        Location   = [Point]::new(10, $listBox.ClientSize.Height + 30)
        ClientSize = [Size]::new(90, 35)
        Text       = 'Click Me'
    }
    $runBtn.Add_Click({
        $resetBtn.Enabled = $true

        if($status['AsyncResult'].IsCompleted -eq $false) {
            # we assume it's running
            $status['Instance'].Stop()
            $this.Text = 'Continue!'
            return # end the event here
        }

        $this.Text = 'Stop!'
        $status['Instance']    = $instance
        $status['AsyncResult'] = $instance.BeginInvoke()
    })
    $form.Controls.Add($runBtn)

    $resetBtn =  [Button]@{
        Location   = [Point]::new($runBtn.ClientSize.Width + 15, $listBox.ClientSize.Height + 30)
        ClientSize = [Size]::new(90, 35)
        Text       = 'Reset'
        Enabled    = $false
    }
    $resetBtn.Add_Click({
        if($status['AsyncResult'].IsCompleted -eq $false) {
            $status['Instance'].Stop()
        }
        $runBtn.Text  = 'Start!'
        $this.Enabled = $false
        $listBox.Items.Clear()
    })
    $form.Controls.Add($resetBtn)

    $status = @{}
    $rs = [runspacefactory]::CreateRunspace([initialsessionstate]::CreateDefault2())
    $rs.ApartmentState = [Threading.ApartmentState]::STA
    $rs.ThreadOptions  = [PSThreadOptions]::ReuseThread
    $rs.Open()
    $rs.SessionStateProxy.SetVariable('controls', $form.Controls)
    $instance = [powershell]::Create().AddScript({
        $listBox = $controls.Find('myListBox', $false)[0]
        $ran  = [random]::new()

        while($true) {
            Start-Sleep 1
            $listBox.Items.Add($ran.Next())
        }
    })
    $instance.Runspace = $rs
    $form.Add_Shown({ $this.Activate() })
    $form.ShowDialog()
}
finally {
    ($form, $instance, $rs).ForEach('Dispose')
}

示例

示例


1
@user1546559 希望这能给你一个开始的提示 :) - Santiago Squarzon
2
不错。如果你加入了一个取消机制,我会非常印象深刻的。 :) - Doug Maurer
2
感谢 @Doug,我想我可以,如果我稍后发布更新,我会再次 @ 你。您会在单独的按钮中添加事件还是同一个按钮中? - Santiago Squarzon
2
@SantiagoSquarzon 我一点都不清楚!你在这方面远远超过我了。 - Doug Maurer
1
@Doug 谦虚啊 :) - Santiago Squarzon
显示剩余11条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接