Recently, I posted an entry on how to ping an IP subnet with PowerShell. Using objects in the PowerShell pipeline is a good thing. But sometimes we want a GUI and I figured the ping subnet script would make a good WinForm script.
ManageEngine ADManager Plus - Download Free Trial
Exclusive offer on ADManager Plus for US and UK regions. Claim now!
When creating graphical PowerShell scripts, I always recommend starting with a PowerShell script or function that you know works in the console. Don't try to create a WinForm (or ShowUI) script at the same time you are trying to develop the core functionality. Start with something that already works and transform that into a GUI. For my script I turned to PrimalForms 2011 from SAPIEN Technologies. I quickly put together a form using form controls for all of the script's original parameters. I then took my existing code and started with that as the body for the Click event on my Ping button.
[cc lang="PowerShell"]
$buttonPing_Click={
$formSubnetPing.Cursor="WaitCursor"
$array = New-Object System.Collections.ArrayList
$bindings=New-Object System.Windows.Forms.BindingSource
$datagridview1.DataSource = $bindings
$datagridview1.Refresh()
$formSubnetPing.Refresh()
[string]$Subnet=$textboxSubNet.Text
[int[]]$Range=($numericStart.Value)..($numericEnd.Value)
[int]$Count=$textboxCount.Text
[int]$Delay=$textboxDelay.Text
[int]$Buffer=$textboxBuffer.Text
[int]$TTL=$textboxTTL.Text
$range | foreach {
$target="$subnet.$_"
$statusbar1.Text="Pinging $target"
$ping=Test-Connection -ComputerName $target -count $count -Delay $delay -BufferSize $Buffer -TimeToLive $ttl -Quiet
$obj=New-Object -TypeName PSObject -Property @{
IPAddress=$Target
Pinged=$ping.ToString()
TTL=$TTL
BufferSize=$buffer
Delay=$Delay
TestDate=Get-Date
}
#add hostname if checked
if ($checkboxResolveNames.Checked -and ($obj.Pinged -eq 'True')) {
#turn off error pipeline
$ErrorActionPreference="SilentlyContinue"
$dns=[system.Net.Dns]::GetHostByAddress($obj.IPAddress)
if ($dns.HostName) {
$hostname=$dns.HostName
}
$ErrorActionPreference="Continue"
}
else {
$hostname=$obj.IPAddress
}
$obj | Add-Member -MemberType "Noteproperty" -Name "HostName" -Value $Hostname -PassThru
#uncomment for debugging
#Write-Host ($obj | Out-String)
#write result to grid
if ($checkboxOnlyShowPinged.Checked -and ($obj.Pinged -eq 'True')) {
$array.Add($obj)
}
elseif (!($checkboxOnlyShowPinged.Checked)) {
$array.Add($obj)
}
#add to the datasource if there is something in $array
if ($array.Count -gt 0) {
$bindings.DataSource=$array
$bindings.ResetBindings($false)
}
#wait for the form to finish processing tasks. This is critical
#when not running in STA mode.
[system.Windows.Forms.Application]::DoEvents()
} #foreach
$statusbar1.Text="Ready"
$formSubnetPing.Cursor="Default"
} #click
[/cc]
I'm not going to go through every line but I do want to point out a few things. I wanted each ping result to be written to a datagridview control as it was created. In order to get that to work, instead of setting the control's DataSource property to the array, I created a BindingSource object and set that for the DataSource property.
[cc lang="PowerShell"]
$bindings=New-Object System.Windows.Forms.BindingSource
$datagridview1.DataSource = $bindings
[/cc]
Then, as each object was added to the array, I set the array as the DataSource for the binding which in turn passes the values on automatically to the DataGridView control. Remember, I'm not a developer so there may be more technically accurate descriptions about what is happening, but this works which is all I care about.
[cc lang="PowerShell"]
if ($array.Count -gt 0) {
$bindings.DataSource=$array
$bindings.ResetBindings($false)
}
[/cc]
The other important element I added is this:
[cc lang="PowerShell"]
[system.Windows.Forms.Application]::DoEvents()
[/cc]
When you run a WinForm script like this, everything runs in a single thread. This includes my PowerShell code as well as all of the graphic elements. What happens is that things can get backed up and you end up with the form not responding. If you run the script in a PowerShell session in STA mode, such as the ISE, you don't run into this problem. My DoEvents() method essentially tells the form to take care of any outstanding work and then move on. With this in place the form runs and I don't get the dreaded Not Responding problem.
I took the liberty of adding a few other items such as resolving names using the DNS .NET class.
[cc lang="PowerShell"]
if ($checkboxResolveNames.Checked -and ($obj.Pinged -eq 'True')) {
#turn off error pipeline
$ErrorActionPreference="SilentlyContinue"
$dns=[system.Net.Dns]::GetHostByAddress($obj.IPAddress)
if ($dns.HostName) {
$hostname=$dns.HostName
}
$ErrorActionPreference="Continue"
}
[/cc]
I temporarily turn off the Error pipeline because the GetHostByAddress() method will throw an exception if it can't be found. In that case I'm going to set the hostname property to the IP address. This assumes you have a valid reverse lookup zone.
Here's what the finished product looks like:
The script is far from perfect or finished. For example, there is no error checking for validation for items like the subnet or start and stop values. You might like the option to export or print results. I'm including my PrimalForms source PFF file so that if you have it, you can edit the source and generate a new script or package.
Download TestSubnetForm.zip and let me know what you think.
Hi,
I hope you don’t mind asking me for some code 🙂
I have just a simple question (at least the question is simple 😉
How can I block temporarily (2 sec, 5 min, 8h, ..) a local running program all its internet connection?
I know this program by its name an path:
...
if ( $allProcesses = get-process -name $pN -errorAction SilentlyContinue | Sort-Object Path ) {
foreach ($oneProcess in $allProcesses) {
if ($oneProcess.path -match $block ) {
....
Thanks for your effort and your patience,
Carl
I’m not aware of any technique you could apply to a process to block any associated network connections. The only thing I can think of is to dynamically create a firewall rule that blocks anything associated with the process. Of course you would then need to remove the rule.
And your code is more complicated than it needs to be, if I understand it. If you wanted to do something with a specific process by name, assuming you’ll only get a single result, this is better PowerShell:
$proc=Get-Process -name “myProcess”
$proc.SomeMethod()
or
SomeCommand $proc
If this is something you need to pursue, I suggest posting in the PowerShell forum at ScriptingAnswers.com. It will be much easier to help you there.