I'm writing a script that will download the selected programs I need (thank you Santiago Squarzon for your help). The script works well when I need to download one program, but to download several in a row I need to start the next one at the end of the download. I made a list of downloads $FileList
and I want to pass its value to runspace in order to start downloading the next program (and remove the already downloaded one from the list). And so on until I go through the entire list.
There will be more programs later. For now, two are enough for the test.
Add-Type -assembly System.Windows.Forms
$webMain = New-Object System.Net.WebClient
$FileList = @()
$MainForm = New-Object System.Windows.Forms.Form
$MainForm.Width = 420
$MainForm.Height = 200
$MainForm.FormBorderStyle = "Fixed3d"
$MainForm.MaximizeBox = $false
$MainForm.StartPosition = "CenterScreen"
$Button1 = New-Object System.Windows.Forms.Button
$Button1.Location = New-Object System.Drawing.Size(120,50)
$Button1.Size = New-Object System.Drawing.Size(160,50)
$Button1.Text = "Download selected"
$Button1.Name = "Button1"
$Button1.Add_Click({
$Button1.Enabled = $false
if ($CheckBox1.Checked) {
$FileList += @{Link = "https://dl.google.com/chrome/install/standalonesetup64.exe"; Path = "D:\chrome.exe"}
}
if ($CheckBox2.Checked) {
$FileList += @{Link = "https://download.mozilla.org/?product=firefox-latest-ssl&os=win64&lang=ru"; Path = "D:\firefox.exe"}
}
$FileList = [System.Collections.ArrayList]$FileList
Wait-Event -Timeout 5
if ($FileList.count -gt 0) {
$webMain.DownloadFileAsync($FileList[0].Link, $FileList[0].Path)
$FileList.Remove($FileList[0])
}
})
$MainForm.Controls.Add($Button1)
$CheckBox1 = New-Object System.Windows.Forms.CheckBox
$CheckBox1.Location = New-Object System.Drawing.Size(10,10)
$CheckBox1.Size = New-Object System.Drawing.Size(100,20)
$CheckBox1.Text = "Chrome"
$MainForm.Controls.Add($CheckBox1)
$CheckBox2 = New-Object System.Windows.Forms.CheckBox
$CheckBox2.Location = New-Object System.Drawing.Size(10,30)
$CheckBox2.Size = New-Object System.Drawing.Size(100,20)
$CheckBox2.Text = "Firefox"
$MainForm.Controls.Add($CheckBox2)
$ProgressBar1 = New-Object System.Windows.Forms.ProgressBar
$ProgressBar1.Location = New-Object System.Drawing.Size(5,120)
$ProgressBar1.Size = New-Object System.Drawing.Size(400,40)
$ProgressBar1.Name = "ProgressBar1"
$MainForm.Controls.Add($ProgressBar1)
$rs = [runspacefactory]::CreateRunspace($Host)
$rs.Open()
$rs.SessionStateProxy.PSVariable.Set([psvariable]::new('form', $MainForm))
$rs.SessionStateProxy.PSVariable.Set([psvariable]::new('FileList', $FileList))
$ps = [powershell]::Create().AddScript({
Register-ObjectEvent -InputObject $args[0] -EventName 'DownloadProgressChanged' -SourceIdentifier 'WebMainDownloadProgressChanged' -Action {
[System.Threading.Monitor]::Enter($form)
$progress = $form.Controls.Find('ProgressBar1', $false)[0]
$progress.Value = $eventArgs.ProgressPercentage
[System.Threading.Monitor]::Exit($form)
}
Register-ObjectEvent -InputObject $args[0] -EventName 'DownloadFileCompleted' -SourceIdentifier 'WebMainDownloadFileCompleted' -Action {
[System.Threading.Monitor]::Enter($form)
$progress = $form.Controls.Find('ProgressBar1', $false)[0]
$progress.Value = 0
[System.Threading.Monitor]::Exit($form)
if ($FileList.count -gt 0) {
$webMain.DownloadFileAsync($FileList[0].Link, $FileList[0].Path)
$FileList.Remove($FileList[0])
}
if ($FileList.count -eq 0) {
$form.Controls.Find('Button1', $false)[0].Enabled = $true
}
}
}).AddArgument($webMain)
$ps.Runspace = $rs
$task = $ps.BeginInvoke()
$MainForm.ShowDialog()
As far as I understand, $rs.SessionStateProxy.PSVariable.Set([psvariable]::new('FileList', $FileList))
transmits the current value at the time the script starts, and not at the time the button is pressed. How can I correctly pass the $FileList
variable to runspace so that it contains the current value at the time the button is pressed?
Although I still haven’t figured out why $rs.SessionStateProxy.PSVariable.Set([psvariable]::new('form', $MainForm))
transmits the "current" form with which you can work, and $rs.SessionStateProxy.PSVariable.Set ([psvariable]::new('FileList', $FileList))
transmits only the value of the variable at the moment the script is launched and does not “update” it when changed, but I realized that you can simply transfer all processing to that separate runspace and then there is no need will transmit nothing.
This is what I ended up with:
Add-Type -assembly System.Windows.Forms
$MainForm = New-Object System.Windows.Forms.Form
$MainForm.Width = 420
$MainForm.Height = 200
$MainForm.FormBorderStyle = "Fixed3d"
$MainForm.MaximizeBox = $false
$MainForm.StartPosition = "CenterScreen"
$Button1 = New-Object System.Windows.Forms.Button
$Button1.Location = New-Object System.Drawing.Size(120,50)
$Button1.Size = New-Object System.Drawing.Size(160,50)
$Button1.Text = "Download selected"
$Button1.Name = "Button1"
$MainForm.Controls.Add($Button1)
$CheckBox1 = New-Object System.Windows.Forms.CheckBox
$CheckBox1.Location = New-Object System.Drawing.Size(10,10)
$CheckBox1.Size = New-Object System.Drawing.Size(100,20)
$CheckBox1.Text = "Chrome"
$CheckBox1.Name = "CheckBox1"
$MainForm.Controls.Add($CheckBox1)
$CheckBox2 = New-Object System.Windows.Forms.CheckBox
$CheckBox2.Location = New-Object System.Drawing.Size(10,30)
$CheckBox2.Size = New-Object System.Drawing.Size(100,20)
$CheckBox2.Text = "Firefox"
$CheckBox2.Name = "CheckBox2"
$MainForm.Controls.Add($CheckBox2)
$ProgressBar1 = New-Object System.Windows.Forms.ProgressBar
$ProgressBar1.Location = New-Object System.Drawing.Size(5,120)
$ProgressBar1.Size = New-Object System.Drawing.Size(400,40)
$ProgressBar1.Name = "ProgressBar1"
$MainForm.Controls.Add($ProgressBar1)
$rs = [runspacefactory]::CreateRunspace($Host)
$rs.Open()
$rs.SessionStateProxy.PSVariable.Set([psvariable]::new('form', $MainForm))
$ps = [powershell]::Create().AddScript({
$FileList = @()
$webMain = New-Object System.Net.WebClient
$Button1 = $form.Controls.Find('Button1', $false)[0]
$Button1.Add_Click({
$Button1.Enabled = $false
if ($form.Controls.Find('CheckBox1', $false)[0].Checked) {
$Global:FileList += @{Link = "https://dl.google.com/chrome/install/standalonesetup64.exe"; Path = "D:\chrome.exe"}
}
if ($form.Controls.Find('CheckBox2', $false)[0].Checked) {
$Global:FileList += @{Link = "https://download.mozilla.org/?product=firefox-latest-ssl&os=win64&lang=ru"; Path = "D:\firefox.exe"}
}
$Global:FileList = [System.Collections.ArrayList]$Global:FileList
Wait-Event -Timeout 5
if ($Global:FileList.count -gt 0) {
$webMain.DownloadFileAsync($Global:FileList[0].Link, $Global:FileList[0].Path)
$Global:FileList.Remove($Global:FileList[0])
}
})
Register-ObjectEvent -InputObject $webMain -EventName 'DownloadProgressChanged' -SourceIdentifier 'WebMainDownloadProgressChanged' -Action {
[System.Threading.Monitor]::Enter($form)
$progress = $form.Controls.Find('ProgressBar1', $false)[0]
$progress.Value = $eventArgs.ProgressPercentage
[System.Threading.Monitor]::Exit($form)
}
Register-ObjectEvent -InputObject $webMain -EventName 'DownloadFileCompleted' -SourceIdentifier 'WebMainDownloadFileCompleted' -Action {
[System.Threading.Monitor]::Enter($form)
$progress = $form.Controls.Find('ProgressBar1', $false)[0]
$progress.Value = 0
if ($Global:FileList.count -eq 0) {
$form.Controls.Find('Button1', $false)[0].Enabled = $true
}
[System.Threading.Monitor]::Exit($form)
if ($Global:FileList.count -gt 0) {
$webMain.DownloadFileAsync($Global:FileList[0].Link, $Global:FileList[0].Path)
$Global:FileList.Remove($FileList[0])
}
}
})
$ps.Runspace = $rs
$ps.BeginInvoke()
$MainForm.ShowDialog()