none
Pass variable inside runspace

    Question

  • I have two variables that are outside a run space, I would like to know how I can do to pass them into the script block.

    thanks in advance

    Add-Type -AssemblyName System.Windows.Forms $buttonStart = New-Object 'System.Windows.Forms.Button' $buttonStop = New-Object 'System.Windows.Forms.Button' $timer = New-Object 'System.Windows.Forms.Timer'

    $Target = '192.168.10.12'

    $count = 10

    $BGScriptBlock = { for ($i=0;$i -lt $count; $i++){ $tc = (Test-Connection -ComputerName $Target -Verbose -Count 1) $reply = "#" + $i+ "`tAddress: " + $tc.Address + "`tSize: " + $tc.ReplySize + "`tTime: " + $tc.ResponseTime + "ms`r`n" $synchash.form.Controls[2].Text = $reply + $synchash.form.Controls[2].Text start-sleep -Seconds 1 } return "OK" } $Global:synchash = [Hashtable]::Synchronized(@{ }) $script:Powershell = [PowerShell]::Create().AddScript($BGScriptBlock).AddArgument($PC) $sessionstate = [system.management.automation.runspaces.initialsessionstate]::CreateDefault() $runspace = [RunspaceFactory]::CreateRunspace($sessionstate) $runspace.ApartmentState = "STA" $runspace.ThreadOptions = "ReuseThread" $runspace.Open() $runspace.SessionStateProxy.SetVariable("synchash", $synchash) $Powershell.Runspace = $runspace $buttonStart_Click = { $script:AsyncResult = $Powershell.BeginInvoke() $timer.Enabled = $true; $timer.Start(); $script:tick = 0 } $buttonStop_Click = { $Powershell.Stop() $Form.Text = 'Stopped' $timer.Stop() $timer.Enabled = $false } $TimerTick = { $script:tick++; $script:Powershell.InvocationStateInfo.State } $Form = New-Object System.Windows.Forms.Form $Form.Size = New-Object System.Drawing.Size(600,400) $Form.StartPosition = "CenterScreen" $Form.Text = 'Form' $Form.Topmost = $true $timer.Interval = 1000 $timer.add_Tick($TimerTick) $synchash.form = $Form $Button = New-Object System.Windows.Forms.Button $Button.Location = New-Object System.Drawing.Size(20,30) $Button.Size = New-Object System.Drawing.Size(110,80) $Button.Text = "START" $Button.BackColor = "LightGreen" $Button.Cursor = [System.Windows.Forms.Cursors]::Hand $Button.Font = New-Object System.Drawing.Font("Calibri",11,[System.drawing.FontStyle]::Bold) $Button.Add_Click($buttonStart_Click) $Form.Controls.Add($Button) $StopButton = New-Object System.Windows.Forms.Button $StopButton.Location = New-Object System.Drawing.Size(140,30) $StopButton.Size = New-Object System.Drawing.Size(110,80) $StopButton.Text = "STOP" $StopButton.BackColor = "Red" $StopButton.Cursor = [System.Windows.Forms.Cursors]::Hand $StopButton.Font = New-Object System.Drawing.Font("Calibri",11,[System.drawing.FontStyle]::Bold) $StopButton.Add_Click($buttonStop_Click) $Form.Controls.Add($StopButton) $outputBox = New-Object System.Windows.Forms.TextBox $outputBox.Location = New-Object System.Drawing.Size(10,180) $outputBox.Size = New-Object System.Drawing.Size(565,100) $outputBox.MultiLine = $True $outputBox.SelectionStart = $outputBox.Text.Length; $outputBox.ScrollToCaret() = $true $outputBox.ScrollBars = "Vertical" $outputBox.Text = "" $Form.Controls.Add($outputBox) $Form.ShowDialog() $Form.add_FormClosing({ $Powershell.Dispose() $runspace.Close() })




    • Edited by Ramses147 Wednesday, April 17, 2019 4:51 PM
    Wednesday, April 17, 2019 4:35 PM

Answers

  • Here is a hint in how to do multiple pings to a text output that can be written to a textbox.

    function logit($msg){
        $synchash.OutputBox.Lines +=$msg
        $synchash.OutputBox.SelectionStart = $synchash.OutputBox.Text.Length
        $synchash.OutputBox.ScrollToCaret()
    }
    
    Test-Connection -ComputerName $address -Count $count -Delay 1 | 
        Select-Object Address, ReplySize, ResponseTime, Statuscode | 
        Format-Table -HideTableHeaders | 
        Out-String |
        ForEach-Object{log $_}

    Performs "$count" pings at "Delay" 1 second and sends to the $outputbox as $count lines.


    \_(ツ)_/


    • Edited by jrvModerator Friday, April 19, 2019 10:27 AM
    • Marked as answer by Ramses147 Friday, April 19, 2019 3:01 PM
    Friday, April 19, 2019 10:26 AM
    Moderator

All replies

  • When in doubt refer to the documentation.

    https://docs.microsoft.com/en-us/dotnet/api/system.management.automation.powershell.begininvoke?view=powershellsdk-1.1.0

    Create a data collection and pass with the Invoke command.



    \_(ツ)_/

    Wednesday, April 17, 2019 5:37 PM
    Moderator
  • Thanks jrv but i don't need to Get Output From a PowerShell Runspace, but the exact opposite.

    I simply need to use the 2 variables shown in $ scriptblock, if you have any ideas, I would like to thank you in advance.


    Wednesday, April 17, 2019 5:56 PM
  • use param()

    Invoke-Command -ScriptBlock 
    {
      param(
      [string] var1,
      [string] var2
      )
    } -ArguementList "Var1", "Var2"

    Wednesday, April 17, 2019 6:10 PM
  • Thanks jrv but i don't need to Get Output From a PowerShell Runspace, but the exact opposite.

    I simply need to use the 2 variables shown in $ scriptblock, if you have any ideas, I would like to thank you in advance.


    If you actually read the whole article it shows you explicitly how to both send values and return values.

    $powershell.BegingInvoke([[<input object>],[<output object>]])


    \_(ツ)_/


    Wednesday, April 17, 2019 6:21 PM
    Moderator
  • i read the article but I don't understand exactly how to apply it to my example

    Something like this ?

    $BGScriptBlock = Invoke-Command -ScriptBlock {
      param(
      [int] $count,
      [string]$Target
      )
    
        for ($i=0;$i -lt $count; $i++){
            $tc = (Test-Connection -ComputerName $Target -Verbose -Count 1)
            $reply = "#" + $i+ "`tAddress: " + $tc.Address + "`tSize: " + $tc.ReplySize + "`tTime: " + $tc.ResponseTime + "ms`r`n"
            $synchash.form.Controls[2].Text = $reply + $synchash.form.Controls[2].Text
            
            start-sleep -Seconds 1
        }
        return "OK"
    } -ArgumentList "$count", "$Target"

    Wednesday, April 17, 2019 6:37 PM
  • Do you want to use Invoke-Command or a runspace?  They are completely different.

    To start a runspace with BeginInvoke you need to pass the variables as a collection to the BeginInvoke.

    You can also use a SyncHash on the runspace to share variables between the invoker and the runspace.

    Search for numerous articles on how to use runspaces with PowerShell.


    \_(ツ)_/

    Wednesday, April 17, 2019 6:47 PM
    Moderator

  • I have been reading about it for hours but I asked here in the forum why I couldn't solve, otherwise I wouldn't have written.
    As you can see in the initial example script I already used the runspace, it was the only way to make what I had to do work, if you read you can see that I managed to export the output, but what I'm asking now is how use those 2 variables within the runspace.
    Thanks in advance for your help

    $Global:synchash = [Hashtable]::Synchronized(@{  })
    $script:Powershell = [PowerShell]::Create().AddScript($BGScriptBlock).AddArgument($PC)
    $sessionstate = [system.management.automation.runspaces.initialsessionstate]::CreateDefault()
    $runspace = [RunspaceFactory]::CreateRunspace($sessionstate)
    $runspace.ApartmentState = "STA"
    $runspace.ThreadOptions = "ReuseThread"
    $runspace.Open()
    $runspace.SessionStateProxy.SetVariable("synchash", $synchash)
    $Powershell.Runspace = $runspace

    Wednesday, April 17, 2019 6:53 PM
  • Just add your variables to the synchash and they will be available.

    $synchash.Add('myargq','myval2')


    \_(ツ)_/

    Wednesday, April 17, 2019 6:58 PM
    Moderator
  • The code should look like this, but there is only one small problem, when I change the value inside the $ textbox1, the value is not updated.
    If, for example, opening the form I write inside the field $ textbox1 "192.168.10.10" and I press start everything is executed regularly, changing the field ($ textbox1) with for example "192.168.10.50", the ping is still performed on " 192.168.10.10 ", the variables should synchronize instead it seems that they don't, do you have any idea why?
    Thank you in advance for your contribution.

    cls
    
    Add-Type -AssemblyName System.Windows.Forms
    Add-Type -AssemblyName System.Drawing
    
    $Form = New-Object System.Windows.Forms.Form    
    $Form.Size = New-Object System.Drawing.Size(600,400)  
    $Icon = [system.drawing.icon]::ExtractAssociatedIcon($PSHOME + "\powershell.exe")
    $Form.Icon = $Icon
    $Form.Topmost = $true
    $Form.StartPosition = "CenterScreen" #loads the window in the center of the screen
    
    $timer = New-Object 'System.Windows.Forms.Timer'
    $timer.Interval = 1000
    $timer.add_Tick($TimerTick)
    
    $BGScriptBlock = { 
    
    for ($i=0;$i -lt $synchash.count; $i++){
            $Ping = Test-Connection -ComputerName $synchash.Target -Verbose -Count 1 | 
            Select @{Label="TimeStamp";Expression={Get-Date}},
            @{Label="Source";Expression={ $_.__Server }},
            @{Label="Destination";Expression={ $_.Address }},
            IPv4Address,
            @{Label="ReplySize";Expression={ $_.ReplySize }},
            @{Label="ResponseTime";Expression={ If ($_.ResponseTime -le 0) {"<1 ms"} else {[string]($_.ResponseTime)+' ms'}}},
            @{Label="Status";Expression={ If ($_.StatusCode -ne 0) {"FAILED"} else {"OK"}}}
            
            $Result = $Ping | Select TimeStamp,Source,Destination,IPv4Address,ReplySize,ResponseTime,Status | ConvertTo-Csv -NoTypeInformation
            $Result[1] | Add-Content -Path $LogPath
            Write-verbose ($Ping | Select TimeStamp,Source,Destination,IPv4Address,ReplySize,ResponseTime,Status | Format-Table -AutoSize | Out-String)
    
            $text = $Ping.Timestamp, $Ping.Source, $Ping.Destination, $Ping.IPv4Address, $Ping.ReplySize, $Ping.ResponseTime, $Ping.Status +"`r`n" -join '     ' 
            $synchash.form.Controls[8].Text = $text + $synchash.form.Controls[8].Text
            Write-Host $text
            start-sleep -Seconds 1
            }
    
    }
    
    $script:Powershell = [PowerShell]::Create().AddScript($BGScriptBlock).AddArgument($PC)
    $sessionstate = [system.management.automation.runspaces.initialsessionstate]::CreateDefault()
    $runspace = [RunspaceFactory]::CreateRunspace($sessionstate)
    $runspace.ApartmentState = "STA"
    $runspace.ThreadOptions = "ReuseThread"
    $runspace.Open()
    $runspace.SessionStateProxy.SetVariable("synchash", $synchash)
    $synchash.form = $Form
    $Powershell.Runspace = $runspace
    
    $Target = $textBox1.Text
    $count = $textBox2.Text
    
    $Global:synchash = [Hashtable]::Synchronized(@{  })
    $synchash.Add("target",$Target)
    $synchash.Add("count",$count)
    
    
    $buttonStart_Click = {
    Write-Host -ForegroundColor Yellow $Target
        $script:AsyncResult = $Powershell.BeginInvoke()
        $timer.Enabled = $true; $timer.Start(); $script:tick = 0
        $Form.Text = 'Start'
    }
    
    $buttonStop_Click = {
        $Powershell.Stop()
        $Form.Text = 'Stopped'
        $timer.Stop()
        $timer.Enabled = $false
        
    }
    
    $TimerTick = {
        $script:tick++;
        $script:Powershell.InvocationStateInfo.State
    }
    
    ############################################## Button START
    
    $Button = New-Object System.Windows.Forms.Button 
    $Button.Location = New-Object System.Drawing.Size(20,30) 
    $Button.Size = New-Object System.Drawing.Size(110,80) 
    $Button.Text = "START" 
    $Button.BackColor = "LightGreen"
    $Button.Cursor = [System.Windows.Forms.Cursors]::Hand
    $Button.Font = New-Object System.Drawing.Font("Calibri",11,[System.drawing.FontStyle]::Bold) 
    #$Button.Add_Click({$Count = 4;$ip = $textBox1.Text;Ping -Count $count -Computer $ip -Verbose -LogPath $PathDesktop\Ping_Report.csv}) 
    $Button.Add_Click($buttonStart_Click)
    $Form.Controls.Add($Button) 
    
    ############################################## TextBox 1
    
    $textBox1 = New-Object System.Windows.Forms.TextBox
    $textBox1.Location = New-Object System.Drawing.Point(190,150)
    $textBox1.Size = New-Object System.Drawing.Size(150,5)
    $textBox1.Cursor = [System.Windows.Forms.Cursors]::Hand
    $textBox1.Font = New-Object System.Drawing.Font("Calibri",9)
    $Form.Controls.Add($textBox1)
    
    ############################################## Text Box 2
    
    $textBox2 = New-Object System.Windows.Forms.TextBox
    $textBox2.Location = New-Object System.Drawing.Point(360,150)
    $textBox2.Size = New-Object System.Drawing.Size(50,5)
    $textBox2.Cursor = [System.Windows.Forms.Cursors]::Hand
    $textBox2.Font = New-Object System.Drawing.Font("Calibri",9)
    $Form.Controls.Add($textBox2)
    
    ############################################## OutPutBox
    
    $outputBox = New-Object System.Windows.Forms.TextBox 
    $outputBox.Location = New-Object System.Drawing.Size(20,180) 
    $outputBox.Size = New-Object System.Drawing.Size(540,60) 
    $outputBox.MultiLine = $True 
    $outputBox.SelectionStart = $outputBox.Text.Length;
    $outputBox.ScrollToCaret() = $true
    #$outputBox.AutoScrollOffset = 10
    $outputBox.ScrollBars = "Vertical"
    $outputBox.Text = ""
    $Form.Controls.Add($outputBox) 
    
    
    $Form.ShowDialog()
    $Form.add_FormClosing({
        $Powershell.Dispose()
        $runspace.Close()
    })


    • Edited by Ramses147 Thursday, April 18, 2019 12:51 PM
    Thursday, April 18, 2019 10:07 AM
  • Unfortunately that won't work.  YOU cannot keep executing the same runspace.  You will also have issues with running in a tight loop and trying to set a synchash.

    What is the point of this?  Why not just ping from the form. 

    I have built literally 1000s of forms.  What you are doing doesn't make much sense to me.

    A runspace has to be independent.  It cannot try yo retain a tight relation to the caller.  That is the point of a thread.  The thread does work.  It finishes its work. If a new bit of work is needed a new thread is created to do the work.

    If you want t thread that services a queue then use a queue to stack the work for the thread and have the thread sample the queue and sleep until new work is posted to the queue.


    \_(ツ)_/

    Thursday, April 18, 2019 10:55 AM
    Moderator
  • There are numerous issues with the build of the form.  You are using things before they have even been defined.  Some of the form code is defined after the "ShowDialog" which is a blocking call so nothing that is defined after it will ever be executed.

    I recommend learning more PowerShell and Forms before trying to take on something as advanced as runspaces.  You should also try to create and use a runspace at a prompt before trying to use it from a form.


    \_(ツ)_/

    Thursday, April 18, 2019 11:03 AM
    Moderator
  • Here is a little help with using Forms with PowerShell. Run it and notice the output.   Next try to change the value in the text box to see what happens.  You will see one of the many problems you will have to design for to make this work as needed.

    Add-Type -AssemblyName System.Windows.Forms
    
    $Form = New-Object System.Windows.Forms.Form
    $Form.Size = '600, 400'
    $Form.Text = 'Script Ping Report - TT Informatica'
    $Form.Topmost = $true
    $Form.StartPosition = 'CenterScreen'
    
    $Form.add_FormClosing({
        $Powershell.Dispose()
        $runspace.Close()
    })
    
    $timer = New-Object 'System.Windows.Forms.Timer'
    $timer.Interval = 1000
    $timer.add_Tick({
        $script:tick++;
        #Write-Host $textBox1.Text -fore green
    })
    
    $buttonStart_Click = {
        $script:AsyncResult = $Powershell.BeginInvoke()
        $timer.Enabled = $true
        $timer.Start()
        $script:tick = 0
        $Form.Text = 'Start'
        Write-Host Start -ForegroundColor Yellow
    }
    
    $Button = New-Object System.Windows.Forms.Button
    $Form.Controls.Add($Button)
    $Button.Location = '20, 30'
    $Button.Size = '110, 80'
    $Button.Text = 'START'
    $Button.BackColor = 'LightGreen'
    $Button.Cursor = 'Hand'
    $Button.Font = 'Calibri, 11pt, style=Bold'
    $Button.Add_Click($buttonStart_Click)
    
    $textBox1 = New-Object System.Windows.Forms.TextBox
    $Form.Controls.Add($textBox1)
    $textBox1.Location = '190, 150'
    $textBox1.Size = '150, 5'
    $textBox1.Cursor = 'Hand'
    $textBox1.Font = 'Calibri, 9pt'
    $textBox1.Text = 'Initial value'
    
    $textBox2 = New-Object System.Windows.Forms.TextBox
    $Form.Controls.Add($textBox2)
    $textBox2.Location ='360, 150'
    $textBox2.Size = '50, 5'
    $textBox2.Cursor = 'Hand'
    $textBox2.Font = 'Calibri, 9pt'
    
    $outputBox = New-Object System.Windows.Forms.TextBox
    $Form.Controls.Add($outputBox)
    $outputBox.Location = '20, 180'
    $outputBox.Size = '540, 60'
    $outputBox.MultiLine = $True
    $outputBox.ScrollBars = 'Vertical'
    $outputBox.ScrollToCaret()
    
    # create runspace after all form variables are defined
    $BGScriptBlock = {
        While (1) {
            $synchash.OutputBox.Lines += $synchash.Textbox1.Text
            $synchash.OutputBox.SelectionStart = $synchash.OutputBox.Text.Length
            $synchash.OutputBox.ScrollToCaret()
            start-sleep -Seconds 1
        }
    }
    
    $synchash = [Hashtable]::Synchronized(@{ })
    $synchash.Textbox1 = $textbox1
    $synchash.OutputBox = $outputBox
    
    $runspace = [RunspaceFactory]::CreateRunspace()
    $runspace.ApartmentState = 'STA'
    $runspace.ThreadOptions = 'ReuseThread'
    $runspace.Open()
    $Powershell = [PowerShell]::Create() $Powershell.Runspace = $runspace $Powershell.AddScript($BGScriptBlock) $runspace.SessionStateProxy.SetVariable('synchash', $synchash) $Form.ShowDialog()

    \_(ツ)_/


    Thursday, April 18, 2019 11:57 AM
    Moderator
  • I used the runspace because it seems to me the only solution to avoid leaving the form every time.
    That was the goal, set a number of pings for example 50, set the ip, ping it by displaying it in the Outputbox.
    The problem is that when I created a stop button, to block the ping (before the 50 pings of example), I could not execute any click until the 50 pings were finished.
    This happens because when using Start-Sleep in Forms, a whole Form will be freeze until it is done and you will be able to click on the stop button.
    Thank you for the example form you shared, but the real problem is the one shown above, if I want to stop the ping before its cycle ends, without runspaces I can't do it because the form is freeze until while is done.
    Thursday, April 18, 2019 12:49 PM

  • but the real problem is the one shown above, if I want to stop the ping before its cycle ends, without runspaces I can't do it because the form is freeze until while is done.

    You will need to design a protocol that checks for some thing that tells the ping to stop. This is an advanced design issue that you will have to think about. You can end the runspace (abort) or you can use some other mechanism. The loop I provided is a start. Just figure out what to put in it to cause the pinging to end.


    \_(ツ)_/

    Thursday, April 18, 2019 12:52 PM
    Moderator
  • Here is one method:

    $BGScriptBlock = {
        While (1) {
            while($synchash.Run){
                $synchash.OutputBox.Lines += $synchash.Textbox1.Text
                $synchash.OutputBox.SelectionStart = $synchash.OutputBox.Text.Length
                $synchash.OutputBox.ScrollToCaret()
                start-sleep -Seconds 1
            }
            Start-Sleep -Milliseconds 200
        }
    }


    \_(ツ)_/

    Thursday, April 18, 2019 12:57 PM
    Moderator
  • Your code is good but there is no output in outputbox

    cls
    
    Add-Type -AssemblyName System.Windows.Forms
    
    $Form = New-Object System.Windows.Forms.Form
    $Form.Size = '600, 400'
    $Form.Text = 'Script Ping Report - TT Informatica'
    $Form.Topmost = $true
    $Form.StartPosition = 'CenterScreen'
    
    $Form.add_FormClosing({
        $Powershell.Dispose()
        $runspace.Close()
    })
    
    $timer = New-Object 'System.Windows.Forms.Timer'
    $timer.Interval = 1000
    $timer.add_Tick({
        $script:tick++;
        #Write-Host $textBox1.Text -fore green
    })
    
    $buttonStart_Click = {
        $script:AsyncResult = $Powershell.BeginInvoke()
        $timer.Enabled = $true
        $timer.Start()
        $script:tick = 0
        $Form.Text = 'Start'
        Write-Host Start -ForegroundColor Yellow
    }
    
    ############################################## Button START
    
    $Button = New-Object System.Windows.Forms.Button
    $Form.Controls.Add($Button)
    $Button.Location = '20, 30'
    $Button.Size = '110, 80'
    $Button.Text = 'START'
    $Button.BackColor = 'LightGreen'
    $Button.Cursor = 'Hand'
    $Button.Font = 'Calibri, 11pt, style=Bold'
    $Button.Add_Click($buttonStart_Click)
    
    ############################################## Button STOP
    
    $StopButton = New-Object System.Windows.Forms.Button
    $StopButton.Location = New-Object System.Drawing.Size(450,130)
    $StopButton.Size = New-Object System.Drawing.Size(110,40)
    $StopButton.Text = 'STOP'
    $StopButton.BackColor = "Magenta"
    $StopButton.Cursor = [System.Windows.Forms.Cursors]::Hand
    $StopButton.Font = New-Object System.Drawing.Font("Calibri",11,[System.drawing.FontStyle]::Bold) 
    $StopButton.Add_Click($buttonStop_Click) 
    $Form.Controls.Add($StopButton)
    
    $textBox1 = New-Object System.Windows.Forms.TextBox
    $Form.Controls.Add($textBox1)
    $textBox1.Location = '190, 150'
    $textBox1.Size = '150, 5'
    $textBox1.Cursor = 'Hand'
    $textBox1.Font = 'Calibri, 9pt'
    $textBox1.Text = 'Initial value'
    
    $textBox2 = New-Object System.Windows.Forms.TextBox
    $Form.Controls.Add($textBox2)
    $textBox2.Location ='360, 150'
    $textBox2.Size = '50, 5'
    $textBox2.Cursor = 'Hand'
    $textBox2.Font = 'Calibri, 9pt'
    
    $outputBox = New-Object System.Windows.Forms.TextBox
    $Form.Controls.Add($outputBox)
    $outputBox.Location = '20, 180'
    $outputBox.Size = '540, 60'
    $outputBox.MultiLine = $True
    $outputBox.ScrollBars = 'Vertical'
    $outputBox.ScrollToCaret()
    
    $BGScriptBlock = {
        While (1) {
            while($synchash.Run){
                $synchash.OutputBox.Lines += $synchash.Textbox1.Text
                $synchash.OutputBox.SelectionStart = $synchash.OutputBox.Text.Length
                $synchash.OutputBox.ScrollToCaret()
                start-sleep -Seconds 1
            }
            Start-Sleep -Milliseconds 200
        }
    }
    
    $synchash = [Hashtable]::Synchronized(@{ })
    $synchash.Textbox1 = $textbox1
    $synchash.OutputBox = $outputBox
    
    $runspace = [RunspaceFactory]::CreateRunspace()
    $runspace.ApartmentState = 'STA'
    $runspace.ThreadOptions = 'ReuseThread'
    $runspace.Open()
    
    $Powershell = [PowerShell]::Create()
    $Powershell.Runspace = $runspace
    $Powershell.AddScript($BGScriptBlock)
    $runspace.SessionStateProxy.SetVariable('synchash', $synchash)
    
    $Form.ShowDialog()


    • Edited by Ramses147 Thursday, April 18, 2019 3:42 PM
    Thursday, April 18, 2019 3:41 PM
  • Which shows that the code works correctly.  You need to set the run flag.

    $buttonStart_Click = {
        $script:AsyncResult = $Powershell.BeginInvoke()
        $synchash.Run = $true
        $timer.Enabled = $true
        $timer.Start()
        $script:tick = 0
        $Form.Text = 'Start'
        Write-Host Start -ForegroundColor Yellow
    }
    


    \_(ツ)_/

    Thursday, April 18, 2019 6:29 PM
    Moderator
  • I'm sorry but i try with your code, nothing appear on the outbox

    cls
    
    Add-Type -AssemblyName System.Windows.Forms
    
    $Form = New-Object System.Windows.Forms.Form
    $Form.Size = '600, 400'
    $Form.Topmost = $true
    $Form.StartPosition = 'CenterScreen'
    
    $Form.add_FormClosing({
        $Powershell.Dispose()
        $runspace.Close()
    })
    
    $timer = New-Object 'System.Windows.Forms.Timer'
    $timer.Interval = 1000
    $timer.add_Tick({
        $script:tick++;
        #Write-Host $textBox1.Text -fore green
    })
    
    ############################################## Button START
    
    $Button = New-Object System.Windows.Forms.Button
    $Form.Controls.Add($Button)
    $Button.Location = '20, 30'
    $Button.Size = '110, 80'
    $Button.Text = 'START'
    $Button.BackColor = 'LightGreen'
    $Button.Cursor = 'Hand'
    $Button.Font = 'Calibri, 11pt, style=Bold'
    $Button.Add_Click($buttonStart_Click)
    
    ############################################## Button STOP
    
    $StopButton = New-Object System.Windows.Forms.Button
    $StopButton.Location = New-Object System.Drawing.Size(450,130)
    $StopButton.Size = New-Object System.Drawing.Size(110,40)
    $StopButton.Text = 'STOP'
    $StopButton.BackColor = "Magenta"
    $StopButton.Cursor = [System.Windows.Forms.Cursors]::Hand
    $StopButton.Font = New-Object System.Drawing.Font("Calibri",11,[System.drawing.FontStyle]::Bold) 
    $StopButton.Add_Click($buttonStop_Click) 
    $Form.Controls.Add($StopButton)
    
    $textBox1 = New-Object System.Windows.Forms.TextBox
    $Form.Controls.Add($textBox1)
    $textBox1.Location = '190, 150'
    $textBox1.Size = '150, 5'
    $textBox1.Cursor = 'Hand'
    $textBox1.Font = 'Calibri, 9pt'
    $textBox1.Text = '192.168.10.10'
    
    $textBox2 = New-Object System.Windows.Forms.TextBox
    $Form.Controls.Add($textBox2)
    $textBox2.Location ='360, 150'
    $textBox2.Size = '50, 5'
    $textBox2.Cursor = 'Hand'
    $textBox2.Font = 'Calibri, 9pt'
    $textBox2.Text = 5
    
    $outputBox = New-Object System.Windows.Forms.TextBox
    $Form.Controls.Add($outputBox)
    $outputBox.Location = '20, 180'
    $outputBox.Size = '540, 60'
    $outputBox.MultiLine = $True
    $outputBox.ScrollBars = 'Vertical'
    $outputBox.ScrollToCaret()
    
    $buttonStart_Click = {
        $script:AsyncResult = $Powershell.BeginInvoke()
        $synchash.Run = $true
        $timer.Enabled = $true
        $timer.Start()
        $script:tick = 0
        $Form.Text = 'Start'
        Write-Host Start -ForegroundColor Yellow
    }
    
    #<#
    $BGScriptBlock = {
    
    
    
        While (1) {
            while($synchash.Run){
    
                $synchash.OutputBox.Lines += $synchash.Textbox1.Text
                $synchash.OutputBox.SelectionStart = $synchash.OutputBox.Text.Length
                $synchash.OutputBox.ScrollToCaret()
    
            for ($i=0;$i -lt $synchash.count; $i++){
            $Ping = Test-Connection -ComputerName $synchash.Textbox1.Text -Verbose -Count 1 | 
            Select @{Label="TimeStamp";Expression={Get-Date}},
            @{Label="Source";Expression={ $_.__Server }},
            @{Label="Destination";Expression={ $_.Address }},
            IPv4Address,
            @{Label="ReplySize";Expression={ $_.ReplySize }},
            @{Label="ResponseTime";Expression={ If ($_.ResponseTime -le 0) {"<1 ms"} else {[string]($_.ResponseTime)+' ms'}}},
            @{Label="Status";Expression={ If ($_.StatusCode -ne 0) {"FAILED"} else {"OK"}}}
            
            $Result = $Ping | Select TimeStamp,Source,Destination,IPv4Address,ReplySize,ResponseTime,Status | ConvertTo-Csv -NoTypeInformation
            $Result[1] | Add-Content -Path $LogPath
            Write-verbose ($Ping | Select TimeStamp,Source,Destination,IPv4Address,ReplySize,ResponseTime,Status | Format-Table -AutoSize | Out-String)
    
            $text = $Ping.Timestamp, $Ping.Source, $Ping.Destination, $Ping.IPv4Address, $Ping.ReplySize, $Ping.ResponseTime, $Ping.Status +"`r`n" -join '     ' 
            #$synchash.form.Controls[8].Text = $text + $synchash.form.Controls[8].Text
            Write-Host $text
            start-sleep -Seconds 1
            }
            
            }
            Start-Sleep -Milliseconds 200
        }
    }
    #>
    
    
    $synchash = [Hashtable]::Synchronized(@{ })
    $synchash.Textbox1 = $textbox1
    $synchash.OutputBox = $outputBox
    $synchash.Add("count",$count)
    
    $runspace = [RunspaceFactory]::CreateRunspace()
    $runspace.ApartmentState = 'STA'
    $runspace.ThreadOptions = 'ReuseThread'
    $runspace.Open()
    
    $Powershell = [PowerShell]::Create()
    $Powershell.Runspace = $runspace
    $Powershell.AddScript($BGScriptBlock)
    $runspace.SessionStateProxy.SetVariable('synchash', $synchash)
    
    $Form.ShowDialog()


    • Edited by Ramses147 Friday, April 19, 2019 9:13 AM refuse
    Friday, April 19, 2019 8:35 AM
  • You have added broken code and you need to spend some time learn9ing how a form works.  You have too many start events. 


    \_(ツ)_/

    Friday, April 19, 2019 8:54 AM
    Moderator
  • I can't figure out how to insert the ping foreach inside $ BGScriptBlock, the ping should capture the 2 parameters: $ textbox1 and $ textbox2 and return the results formatted in the Outpubox.
    Separately the ping code works, could you just help me to insert this part of code in such a way as to be able to then study the correct functioning of the same and reproduce it?
    thank you in advance for your help.


    cls
    
    Add-Type -AssemblyName System.Windows.Forms
    
    $Form = New-Object System.Windows.Forms.Form
    $Form.Size = '600, 400'
    $Form.Topmost = $true
    $Form.StartPosition = 'CenterScreen'
    
    $Form.add_FormClosing({
        $Powershell.Dispose()
        $runspace.Close()
    })
    
    $timer = New-Object 'System.Windows.Forms.Timer'
    $timer.Interval = 1000
    $timer.add_Tick({
        $script:tick++;
        #Write-Host $textBox1.Text -fore green
    })
    
    $buttonStart_Click = {
        $script:AsyncResult = $Powershell.BeginInvoke()
        $synchash.Run = $true
        $timer.Enabled = $true
        $timer.Start()
        $script:tick = 0
        $Form.Text = 'Start'
        Write-Host Start -ForegroundColor Yellow
    }
    
    $Button = New-Object System.Windows.Forms.Button
    $Form.Controls.Add($Button)
    $Button.Location = '20, 30'
    $Button.Size = '110, 80'
    $Button.Text = 'START'
    $Button.BackColor = 'LightGreen'
    $Button.Cursor = 'Hand'
    $Button.Font = 'Calibri, 11pt, style=Bold'
    $Button.Add_Click($buttonStart_Click)
    
    $textBox1 = New-Object System.Windows.Forms.TextBox
    $Form.Controls.Add($textBox1)
    $textBox1.Location = '190, 150'
    $textBox1.Size = '150, 5'
    $textBox1.Cursor = 'Hand'
    $textBox1.Font = 'Calibri, 9pt'
    $textBox1.Text = '192.168.10.10'
    
    $textBox2 = New-Object System.Windows.Forms.TextBox
    $Form.Controls.Add($textBox2)
    $textBox2.Location ='360, 150'
    $textBox2.Size = '50, 5'
    $textBox2.Cursor = 'Hand'
    $textBox2.Font = 'Calibri, 9pt'
    $textBox1.Text = 5
    
    $outputBox = New-Object System.Windows.Forms.TextBox
    $Form.Controls.Add($outputBox)
    $outputBox.Location = '20, 180'
    $outputBox.Size = '540, 60'
    $outputBox.MultiLine = $True
    $outputBox.ScrollBars = 'Vertical'
    $outputBox.ScrollToCaret()
    
    # create runspace after all form variables are defined
    $BGScriptBlock = {
        
        for ($i=0;$i -lt $synchash.count; $i++){
            $Ping = Test-Connection -ComputerName $synchash.Target -Verbose -Count 1 | 
            Select @{Label="TimeStamp";Expression={Get-Date}},
            @{Label="Source";Expression={ $_.__Server }},
            @{Label="Destination";Expression={ $_.Address }},
            IPv4Address,
            @{Label="ReplySize";Expression={ $_.ReplySize }},
            @{Label="ResponseTime";Expression={ If ($_.ResponseTime -le 0) {"<1 ms"} else {[string]($_.ResponseTime)+' ms'}}},
            @{Label="Status";Expression={ If ($_.StatusCode -ne 0) {"FAILED"} else {"OK"}}}
            
            $Result = $Ping | Select TimeStamp,Source,Destination,IPv4Address,ReplySize,ResponseTime,Status | ConvertTo-Csv -NoTypeInformation
            $Result[1] | Add-Content -Path $LogPath
            Write-verbose ($Ping | Select TimeStamp,Source,Destination,IPv4Address,ReplySize,ResponseTime,Status | Format-Table -AutoSize | Out-String)
    
            $text = $Ping.Timestamp, $Ping.Source, $Ping.Destination, $Ping.IPv4Address, $Ping.ReplySize, $Ping.ResponseTime, $Ping.Status +"`r`n" -join '     ' 
            $synchash.form.Controls[8].Text = $text + $synchash.form.Controls[8].Text
            Write-Host $text
            start-sleep -Seconds 1
            }
        
        While (1) {
            $synchash.OutputBox.Lines += $synchash.Textbox1.Text
            $synchash.OutputBox.SelectionStart = $synchash.OutputBox.Text.Length
            $synchash.OutputBox.ScrollToCaret()
            start-sleep -Seconds 1
        }
    }
    
    
    $synchash = [Hashtable]::Synchronized(@{ })
    $synchash.Textbox1 = $textbox1
    $synchash.Textbox2 = $textbox2
    $synchash.OutputBox = $outputBox
    
    $runspace = [RunspaceFactory]::CreateRunspace()
    $runspace.ApartmentState = 'STA'
    $runspace.ThreadOptions = 'ReuseThread'
    $runspace.Open()
    
    $Powershell = [PowerShell]::Create()
    $Powershell.Runspace = $runspace
    $Powershell.AddScript($BGScriptBlock)
    $runspace.SessionStateProxy.SetVariable('synchash', $synchash)
    
    $Form.ShowDialog()

    Friday, April 19, 2019 9:30 AM
  • You are goi g to have to learn how to work these things out.  Here is an example:

    $msg = ' create the message line here'
    $synchash.OutputBox.Lines += $msg
    $synchash.OutputBox.SelectionStart = $synchash.OutputBox.Text.Length
    $synchash.OutputBox.ScrollToCaret()
    


    \_(ツ)_/

    Friday, April 19, 2019 9:51 AM
    Moderator
  • Hint #2 -

    function logit($msg){
        $synchash.OutputBox.Lines +=$msg
        $synchash.OutputBox.SelectionStart = $synchash.OutputBox.Text.Length
        $synchash.OutputBox.ScrollToCaret()
    }
    


    \_(ツ)_/

    Friday, April 19, 2019 9:52 AM
    Moderator
  • Ok, I inserted the stop button.
    I assigned the correct variable to $ synchash.OutputBox.Lines.
    Now there are 2 problems:
    1- $ synchash.Textbox2.Text is not acquired as a parameter and the ping goes on to infinity.
    2 - changing the value of $ textbox2.text (default is 192.168.10.10) to another value such as 192.168.10.15, the ping is still performed on 192.168.10.10

    cls
    
    Add-Type -AssemblyName System.Windows.Forms
    
    $Form = New-Object System.Windows.Forms.Form
    $Form.Size = '600, 400'
    $Form.Topmost = $true
    $Form.StartPosition = 'CenterScreen'
    
    $Form.add_FormClosing({
        $Powershell.Dispose()
        $runspace.Close()
    })
    
    $timer = New-Object 'System.Windows.Forms.Timer'
    $timer.Interval = 1000
    $timer.add_Tick({
        $script:tick++;
        #Write-Host $textBox1.Text -fore green
    })
    
    $buttonStart_Click = {
        $script:AsyncResult = $Powershell.BeginInvoke()
        $synchash.Run = $true
        $timer.Enabled = $true
        $timer.Start()
        $script:tick = 0
        $Form.Text = 'Start'
        Write-Host Start -ForegroundColor Yellow
    }
    
    $buttonStop_Click = {
        $Powershell.Stop()
        $Form.Text = 'Stopped'
        $timer.Stop()
        $timer.Enabled = $false
        
    }
    
    $Button = New-Object System.Windows.Forms.Button
    $Form.Controls.Add($Button)
    $Button.Location = '20, 30'
    $Button.Size = '110, 80'
    $Button.Text = 'START'
    $Button.BackColor = 'LightGreen'
    $Button.Cursor = 'Hand'
    $Button.Font = 'Calibri, 11pt, style=Bold'
    $Button.Add_Click($buttonStart_Click)
    
    $StopButton = New-Object System.Windows.Forms.Button
    $StopButton.Location = New-Object System.Drawing.Size(450,130)
    $StopButton.Size = New-Object System.Drawing.Size(110,40)
    $StopButton.Text = 'STOP'
    $StopButton.BackColor = "Magenta"
    $StopButton.Cursor = [System.Windows.Forms.Cursors]::Hand
    $StopButton.Font = New-Object System.Drawing.Font("Calibri",11,[System.drawing.FontStyle]::Bold) 
    $StopButton.Add_Click($buttonStop_Click) 
    $Form.Controls.Add($StopButton)
    
    $textBox1 = New-Object System.Windows.Forms.TextBox
    $Form.Controls.Add($textBox1)
    $textBox1.Location = '190, 150'
    $textBox1.Size = '150, 5'
    $textBox1.Cursor = 'Hand'
    $textBox1.Font = 'Calibri, 9pt'
    $textBox1.Text = '192.168.10.10'
    
    $textBox2 = New-Object System.Windows.Forms.TextBox
    $Form.Controls.Add($textBox2)
    $textBox2.Location ='360, 150'
    $textBox2.Size = '50, 5'
    $textBox2.Cursor = 'Hand'
    $textBox2.Font = 'Calibri, 9pt'
    $textBox2.Text = 3
    
    $outputBox = New-Object System.Windows.Forms.TextBox
    $Form.Controls.Add($outputBox)
    $outputBox.Location = '20, 180'
    $outputBox.Size = '540, 60'
    $outputBox.MultiLine = $True
    $outputBox.ScrollBars = 'Vertical'
    $outputBox.ScrollToCaret()
    
    # create runspace after all form variables are defined
    $BGScriptBlock = {
       
        While (1) {
    
            for ($i=0;$i -lt $synchash.Textbox2.Text; $i++){
            $Ping = Test-Connection -ComputerName $synchash.Textbox1.Text -Verbose -Count 1 | 
            Select @{Label="TimeStamp";Expression={Get-Date}},
            @{Label="Source";Expression={ $_.__Server }},
            @{Label="Destination";Expression={ $_.Address }},
            IPv4Address,
            @{Label="ReplySize";Expression={ $_.ReplySize }},
            @{Label="ResponseTime";Expression={ If ($_.ResponseTime -le 0) {"<1 ms"} else {[string]($_.ResponseTime)+' ms'}}},
            @{Label="Status";Expression={ If ($_.StatusCode -ne 0) {"FAILED"} else {"OK"}}}
            
            $Result = $Ping | Select TimeStamp,Source,Destination,IPv4Address,ReplySize,ResponseTime,Status | ConvertTo-Csv -NoTypeInformation
            $Result[1] | Add-Content -Path $LogPath
            Write-verbose ($Ping | Select TimeStamp,Source,Destination,IPv4Address,ReplySize,ResponseTime,Status | Format-Table -AutoSize | Out-String)
    
            $text = $Ping.Timestamp, $Ping.Source, $Ping.Destination, $Ping.IPv4Address, $Ping.ReplySize, $Ping.ResponseTime, $Ping.Status -join '     ' 
            
            $synchash.OutputBox.Lines += $text
            $synchash.OutputBox.SelectionStart = $synchash.OutputBox.Text.Length
            $synchash.OutputBox.ScrollToCaret()
            start-sleep -Seconds 1
            }
    
        }
    }
    
    
    $synchash = [Hashtable]::Synchronized(@{ })
    $synchash.Textbox1 = $textbox1
    $synchash.Textbox2 = $textbox2
    $synchash.OutputBox = $outputBox
    
    $runspace = [RunspaceFactory]::CreateRunspace()
    $runspace.ApartmentState = 'STA'
    $runspace.ThreadOptions = 'ReuseThread'
    $runspace.Open()
    
    $Powershell = [PowerShell]::Create()
    $Powershell.Runspace = $runspace
    $Powershell.AddScript($BGScriptBlock)
    $runspace.SessionStateProxy.SetVariable('synchash', $synchash)
    
    $Form.ShowDialog()

    Friday, April 19, 2019 10:00 AM
  • You are trying to do too many things without a clear understanding of how any of it works.

    Start by using my original version and add your code one line at a time until you understand what is happening.

    The issue you are having is what every programmer faces in a real system.  Once you start using forms and threads you are no longer scripting.  You are writing a program.  TO write a program requires a deeper technical understanding of the system and programming.  You have to learn how to understand what is happening when you are coding in a blind way.  You cannot debug through a runspace - well you can but that is also a very advanced technical issue - so you must find a way to test your code at a prompt one simple line at a time.  Once you see how one translates to the other then the rest becomes much easier.

    Becoming a technician outside of the GUI requires a whole different set of skills.  That takes a bit of time to develop.  Doing things in small steps is the best way to approach this.

    There is no way for me to tach you what you need to know in a forum. It is beyond the scope of the forum and would be very difficult to do.

    Slow down and understand that you are trying to do something that is not easy for a non-technically trained person.  You are learning a new technology that requires new and deeper understanding of how these things work. Be patient and build incrementally and you will eventually see what is happening.


    \_(ツ)_/

    Friday, April 19, 2019 10:16 AM
    Moderator
  • Here is a hint in how to do multiple pings to a text output that can be written to a textbox.

    function logit($msg){
        $synchash.OutputBox.Lines +=$msg
        $synchash.OutputBox.SelectionStart = $synchash.OutputBox.Text.Length
        $synchash.OutputBox.ScrollToCaret()
    }
    
    Test-Connection -ComputerName $address -Count $count -Delay 1 | 
        Select-Object Address, ReplySize, ResponseTime, Statuscode | 
        Format-Table -HideTableHeaders | 
        Out-String |
        ForEach-Object{log $_}

    Performs "$count" pings at "Delay" 1 second and sends to the $outputbox as $count lines.


    \_(ツ)_/


    • Edited by jrvModerator Friday, April 19, 2019 10:27 AM
    • Marked as answer by Ramses147 Friday, April 19, 2019 3:01 PM
    Friday, April 19, 2019 10:26 AM
    Moderator
  • Many thanks for your help !

    I'll start studying about what you've written. see you next time

    Friday, April 19, 2019 3:02 PM