Not sure if this is a duplicate, checked online, and worked with what I found, Working with Boe Prox's solutions, which from another StackOverflow article references (https://stackoverflow.com/a/15502286/1546559), but in his, he is updating from a command line/powershell window, via a function run inside a thread. I'm running an event from a button, inside of a thread and trying to run a separate thread, for the click event(s). Outside of the thread, the event works fine, but inside, it doesn't work, at all.. What am I doing wrong? PS. I found another blog referencing Boe Prox's work (https://www.foxdeploy.com/blog/part-v-powershell-guis-responsive-apps-with-progress-bars.html), building another multi-threaded application, but pretty much the same concept, updating a window, through powershell commandlet/function, placed inside of a separate thread.
$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()
}
You can use this as a blueprint of what you want to do, the important thing is that the runspace can't see the controls of your form. If you want your runspace to interact with the controls, they must be passed to it, either by SessionStateProxy.SetVariable(...)
or as argument with .AddParameters(..)
for example.
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')
}
Demo