I hope someone can help me understand what I am doing wrong.
The script works, if I enter multiple computers or multiple lines, but if I only enter 1 line (1 value). Lets say: Computer 201... the result will be 1
I have disabled the PING feature for now until I can figure out why it does not work with 1 line.
I added the whole code so you can test it yourself.
Thank you
# Load required assemblies
[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
# Drawing form and controls
$Harvester = New-Object System.Windows.Forms.Form
$Harvester.Text = "Ping Computers"
$Harvester.Size = New-Object System.Drawing.Size(490,300)
$Harvester.FormBorderStyle = "FixedDialog"
$Harvester.TopMost = $true
$Harvester.MaximizeBox = $false
$Harvester.MinimizeBox = $false
$Harvester.ControlBox = $true
$Harvester.StartPosition = "CenterScreen"
$Harvester.Font = "Segoe UI"
#======================== INPUTBOX - Computers ========================#
$label_message2 = New-Object System.Windows.Forms.Label
$label_message2.Location = New-Object System.Drawing.Size(20,10)
$label_message2.Size = New-Object System.Drawing.Size(100,15)
$label_message2.Text = "Computers"
$Harvester.Controls.Add($label_message2)
# Inputbox
$Inputbox = New-Object System.Windows.Forms.TextBox
$Inputbox.Multiline = $True;
$Inputbox.Location = New-Object System.Drawing.Size(20,30)
$Inputbox.Size = New-Object System.Drawing.Size(200,150)
$Inputbox.ScrollBars = "Vertical"
$Harvester.Controls.Add($Inputbox)
#======================== INPUTBOX - Completed ========================#
$label_message_success = New-Object System.Windows.Forms.Label
$label_message_success.Location = New-Object System.Drawing.Size(250,10)
$label_message_success.Size = New-Object System.Drawing.Size(100,15)
$label_message_success.Text = "Successful"
$Harvester.Controls.Add($label_message_success)
# Inputbox
$Inputbox_success = New-Object System.Windows.Forms.TextBox
$Inputbox_success.Multiline = $True;
$Inputbox_success.Location = New-Object System.Drawing.Size(250,30)
$Inputbox_success.Size = New-Object System.Drawing.Size(200,150)
$Inputbox_success.ScrollBars = "Vertical"
$Harvester.Controls.Add($Inputbox_success)
#======================== Ping ========================#
$button_Ping = New-Object System.Windows.Forms.Button
$button_Ping.Location = New-Object System.Drawing.Size(120,200)
$button_Ping.Size = New-Object System.Drawing.Size(80,32)
$button_Ping.TextAlign = "MiddleCenter"
$button_Ping.Text = "Ping"
$button_Ping.Add_Click({
If ($Inputbox.TextLength -eq 0){
Add-Type -AssemblyName System.Windows.Forms
[System.Windows.Forms.MessageBox]::Show('Please enter at 1 computer to perform this action')
Return
}
[collections.arraylist] $ref = #(($Inputbox.Text -split '\r?\n').Trim() -ne '')
if(-not $ref) { return } # if the textbox is empty, don't do anything
$i = 0
1..$ref.Count | ForEach-Object {
#if(Test-Connection $ref[$i] -Quiet) {
$Inputbox_success.Text += $ref[$i] + [environment]::NewLine
$ref.RemoveAt($i)
#}
$Inputbox.Text = $ref | Out-String
$Inputbox, $Inputbox_success | ForEach-Object Refresh
}
})
$Harvester.Controls.Add($button_Ping)
# show form
$Harvester.Add_Shown({$Harvester.Activate()})
[void] $Harvester.ShowDialog()
On a single object you get a value of 1 because of this line:
[collections.arraylist] $ref = #(($Inputbox.Text -split '\r?\n').Trim() -ne '')
More specifically because when a string is evaluated against -ne '' it will return $true or $false, but when you pass an array to that it will instead output all results where that evaluated as true. The way to fix this is to force it to be an array every time. That can be done like this:
[collections.arraylist] $ref = [string[]]($Inputbox.Text -split '\r?\n') -ne ''
That fixes that, but it still leaves other issues, or, at least one other issue I see. You set $i to 0, then loop through things starting at 1, but always reference $i, which will always evaluate to 0, and effectively manipulate item 0 in the $ref array for each thing you evaluate against. So if your If statement succeeds on 3 out of 6 things it will always move the first 3 things over, regardless of what succeeds. To resolve that I changed your code as little as I could, but ended up with this:
0..($ref.Count -1) | ForEach-Object {
# if(Test-Connection $ref[$i] -Quiet) {
if($ref[$_][-1]%2){
$Inputbox_success.Text += $ref[$_] + [environment]::NewLine
}
}
$Inputbox.Text = $ref |Where{$Inputbox_success.Text -notmatch ([regex]::Escape($_))}| Out-String
$Inputbox, $Inputbox_success | ForEach-Object Refresh
So rather than remove things from the array, I rather copy the successes to the $Inputbox_success box, and then rebuild $Inputbox based on the items that are not present in $Inputbox_success at the end. Now, I just set $Inputbox.text to be this:
$Inputbox.Text = #'
Computer201
Computer202
Computer203
Computer204
Computer206
Computer207
'#
And evaluated which name ended in an odd number, but you can comment out my If statement, and re-implement your own to make it actually ping things and go off that.
Related
maybe one of you experts can help a complete newbie (I don't know if what I want is even feasible).
Let's assume I have a CSV file with various data. (see csv_screenshot)csv_screenshot
I import this data via Powershell into a small GUI . How can I make it so that when I search for "Paris", I really only get the output for Paris in the GUI as a list view like this (see powershell_screenshot)
powershell_screenshot
Currently the output in the GUI looks like this (see current_result.png). How do I get it nicely formatted as a list in there. I really want to insert it like this (via Out Grid View it is no problem)
current_result.png
[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
[void] [System.Windows.Forms.Application]::EnableVisualStyles();
function search_csv {
$Input = $textbox_Search.text
$Input = "*$Input*"
$Input_Result = import-csv -path C:\Users\check.csv -Header "Location", "Client", "Mobile Device"
$output_TextBox.text = $Input_Result -like $Input
}
$search_csvtool = New-Object System.Windows.Forms.Form
$search_csvtool.Text = "CSV Search"
$search_csvtool.Size = New-Object System.Drawing.Size(674,500)
$search_csvtool.FormBorderStyle ="FixedDialog"
$search_csvtool.TopMost = $true
$search_csvtool.MaximizeBox = $false
$search_csvtool.MinimizeBox = $true
$search_csvtool.ControlBox = $true
$search_csvtool.StartPosition = "CenterScreen"
$search_csvtool.Font = "Courier New"
$label_Search = New-Object System.Windows.Forms.Label
$label_Search.Location = New-Object System.Drawing.Size(195,18)
$label_Search.Size = New-Object System.Drawing.Size(265,32)
$label_Search.TextAlign ="MiddleCenter"
$label_Search.Text = "Please enter "
$search_csvtool.Controls.Add($label_Search)
$textbox_Search = New-Object System.Windows.Forms.TextBox
$textbox_Search.Location = New-Object System.Drawing.Size(195,50)
$textbox_Search.Size = New-Object System.Drawing.Size(266,37)
$search_csvtool.Controls.Add($textbox_Search)
$button_Search = New-Object System.Windows.Forms.Button
$button_Search.Location = New-Object System.Drawing.Size(195,80)
$button_Search.Size = New-Object System.Drawing.Size(266,24)
$button_Search.TextAlign = "MiddleCenter"
$button_Search.Text = "Search"
$button_Search.Add_Click({search_csv})
$search_csvtool.Controls.Add($button_Search)
$output_TextBox = New-Object System.Windows.Forms.TextBox
$output_TextBox.Multiline = $true;
$output_TextBox.Location = New-Object System.Drawing.Size(16,130)
$output_TextBox.Size = New-Object System.Drawing.Size(627,314)
$output_TextBox.ScrollBars = "Vertical"
$output_TextBox.ReadOnly = $true;
$search_csvtool.Controls.Add($output_TextBox)
$search_csvtool.Add_Shown({$search_csvtool.Activate()})
[void] $search_csvtool.ShowDialog()
Ok, so here's what I meant in my comment:
Start the code with
Add-Type -AssemblyName System.Windows.Forms
[System.Windows.Forms.Application]::EnableVisualStyles()
$csvData = Import-Csv -path 'C:\Users\check.csv' -Header "Location", "Client", "Mobile Device"
function search_csv {
$searchThis = $textbox_Search.Text.Trim()
# use $script: scoping here to reference the $csvData variable
$data = $script:csvData | Where-Object {$_.Location -like "*$searchThis*"}
if ($data) {
$output_TextBox.Text = ($data | Format-List | Out-String).Trim()
}
else {
$output_TextBox.Text = "Not found.."
}
}
Then create the rest of the form as you did.
Important: Destroy the form when done with a last new code line:
$search_csvtool.Dispose()
You should then have this result:
As per your comment to empty the textbox when nothing (or just whitespace) has been entered, you could change the function to:
function search_csv {
$searchThis = $textbox_Search.Text.Trim()
# use $script: scoping here to reference the $csvData variable
$data = $script:csvData | Where-Object {$_.Location -like "*$searchThis*"}
if ([string]::IsNullOrWhiteSpace($searchThis)) {
$output_TextBox.Clear()
}
elseif ($data) {
$output_TextBox.Text = ($data | Format-List | Out-String).Trim()
}
else {
$output_TextBox.Text = "Not found.."
}
}
However, as postanote already commented, it would be much better to simply disable the search button and only enable it when something other that whitespace has been entered.
To do that, you need to add an eventhandler to the textbox:
$textbox_Search = New-Object System.Windows.Forms.TextBox
$textbox_Search.Location = New-Object System.Drawing.Size(195,50)
$textbox_Search.Size = New-Object System.Drawing.Size(266,37)
$textbox_Search.Add_TextChanged({
# enable the button when there is at least one non-whitespace character present
$button_Search.Enabled = $this.Text -match '\S'
# or use
# $button_Search.Enabled = (-not [string]::IsNullOrWhiteSpace($this.Text))
})
$search_csvtool.Controls.Add($textbox_Search)
And initialize the button to be disabled at startup:
$button_Search = New-Object System.Windows.Forms.Button
$button_Search.Location = New-Object System.Drawing.Size(195,80)
$button_Search.Size = New-Object System.Drawing.Size(266,24)
$button_Search.TextAlign = "MiddleCenter"
$button_Search.Text = "Search"
# initialize to Disabled; will be enabled as soon as there is text entered in the $textbox_Search
$button_Search.Enabled = $false
$button_Search.Add_Click({search_csv})
$search_csvtool.Controls.Add($button_Search)
Inside an event handler, you can refer to the object itself using automatic variable $this
You should be able to go thru your string output line by line and change this.
That stated, you probably won't like how it looks. Normalized colons will probably look better.
This code should do the trick. I'll let you be the judge of if it looks "better"
$dataLines = ($data | Format-List | Out-String).Trim() -split '(?>\r\n|\n)'
$dataText = #(
foreach ($dataLine in $dataLines) {
$dataLine -replace '\s{1,}\:', ':'
}
) -join [Environment]::Newline
$output_TextBox.Text = $dataText
I had wrote a powershell script to ping a ip address and I want to display a ping result to powershell form textboxt. I tried the below scripts below script. Script is working but not clear output.
Herewith i had attached the powershell output and powershell form textbox outputs.
[PowerShell output][1]
# Load required assemblies
Add-Type -AssemblyName System.Windows.Forms
Add-Type -AssemblyName System.Drawing
# Create Form to contain elements
$Form = New-Object System.Windows.Forms.Form
# Set Form Titlebar text
$Form.Text = "PING"
# Set size of form
# Size(<length>,<height>) in pixels
$Form.Size = New-Object System.Drawing.Size(600,600)
# Create an Input textbox
$inputBoxui = New-Object System.Windows.Forms.TextBox
$inputBoxui.Location = New-Object System.Drawing.Size(20,50)
$inputBoxui.Size = New-Object System.Drawing.Size(100,20)
# Initialize the textbox inside the Form
$Form.Controls.Add($inputBoxui)
# Create Instruction Label for inputBox
$Labelui = New-Object System.Windows.Forms.Label
$Labelui.Text = "Enter IP Address"
$Labelui.Location = New-Object System.Drawing.Size(20,30)
$Labelui.BackColor = "Transparent"
$Labelui.AutoSize = $true
# Initialize Label
$Form.Controls.Add($Labelui)
# Create an Output textbox, 10 pixels in from Form Boundary and 150 pixels down
# As we want a multiline output set textbox size to 565 px x 200 px
# .Multiline declares the textbox is multi-line
$outputBox = New-Object System.Windows.Forms.TextBox
$outputBox.Location = New-Object System.Drawing.Size(10,250)
$outputBox.Size = New-Object System.Drawing.Size(565,300)
$outputBox.MultiLine = $True
$outputBox.Scrollbars = "Vertical"
# Initialize the textbox inside the Form
$Form.Controls.Add($OutputBox)
# Add a Button which can be used to generate an action from our textboxes
$Button = New-Object System.Windows.Forms.Button
$Button.Location = New-Object System.Drawing.Size(20,80)
$Button.Size = New-Object System.Drawing.Size(100,40)
$Button.Text = "PING Start"
# Declare the action to occur when button clicked
$Button.Add_Click( { pingstart } )
# Initialize the button inside the Form
$Form.Controls.Add($Button)
# Create a Function
function pingstart {
$outputBox.Clear()
# Variable to store what user types into Input textbox
$Inputui = $inputBoxui.Text
while ($true) {
$Computer = $Inputui
$Ping = Test-Connection -Count 3 -ComputerName $Computer
ForEach ($Result in $Ping) {
If ($Result.ResponseTime -lt 100) {
$Result | Select-Object -Property Address,BufferSize,ResponseTime | Write-Host -BackgroundColor Green
}
If ( ($Result.ResponseTime -ge 100) -and ($Result.ResponseTime -lt 200) ) {
$Result | Select-Object -Property Address,BufferSize,ResponseTime | Write-Host -BackgroundColor Yellow
}
If ($Result.ResponseTime -ge 200) {
$Result | Select-Object -Property Address,BufferSize,ResponseTime | Write-Host -BackgroundColor Red
}
}
# Assign Result to OutputBox
$outputBox.Text = $Ping
}
}
# Initialize form and show it
# [void] used to suppress other messages generated by Form actions
[void] $Form.ShowDialog()
unrelated but it seems there is a mistake with this
Initialize the textbox inside the Form
$Form.Controls.Add($OutputBox)
caps lock.
$outputBox = New-Object System.Windows.Forms.TextBox
I have a full script which works just fine. The issue is, I have a system of GUI's set up which are all used for varying roles and what I need is a select all function on one of these GUI's. For example;
###############################MONTH SELECTER############################################################
[array]$DropDownArrayItems = "","01","02","03","04","05","06","07","08","09","10","11","12", 'Select all months'
[array]$DropDownArray = $DropDownArrayItems | sort
function Return-DropDown {
if ($DropDown.SelectedItem -eq $null){
$DropDown.SelectedItem = $DropDown.Items[0]
$script:Choice = $DropDown.SelectedItem.ToString()
$Form.Close()
}
else{
$script:Choice = $DropDown.SelectedItem.ToString()
$Form.Close()
}
}
function SelectGroup{
[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Drawing")
$Form = New-Object System.Windows.Forms.Form
$Form.width = 300
$Form.height = 150
$Form.Text = ”Select Filter Month”
$DropDown = new-object System.Windows.Forms.ComboBox
$DropDown.Location = new-object System.Drawing.Size(100,10)
$DropDown.Size = new-object System.Drawing.Size(130,30)
ForEach ($Item in $DropDownArray) {
[void] $DropDown.Items.Add($Item)
}
$Form.Controls.Add($DropDown)
$DropDownLabel = new-object System.Windows.Forms.Label
$DropDownLabel.Location = new-object System.Drawing.Size(10,10)
$DropDownLabel.size = new-object System.Drawing.Size(100,40)
$DropDownLabel.Text = "Select Month:"
$Form.Controls.Add($DropDownLabel)
$Button = new-object System.Windows.Forms.Button
$Button.Location = new-object System.Drawing.Size(100,50)
$Button.Size = new-object System.Drawing.Size(100,20)
$Button.Text = "OK"
$Button.Add_Click({Return-DropDown})
$form.Controls.Add($Button)
$form.ControlBox = $false
$Form.Add_Shown({$Form.Activate()})
[void] $Form.ShowDialog()
return $script:choice
}
$Group = $null
$Group = SelectGroup
while ($Group -like ""){
$Group = SelectGroup
}
###################################################################################
This selects a month and is used in things like file paths and filters the problem is, if someone wants data from every month, they have to manually repeat the script 12 times and answer all the GUI'S again.
I've tried;
if ($Group -eq 'Select all') {
1..12 | ForEach-Object { '{0:00}' -f $_ }
}
But the file path gets confused and things break so including a select all function and taking numbers 1-12 doesn't quite work.
So is there a way to loop the entire script such that with each pass through it selects a different month whilst keeping all other variables in the script the same if the 'select all months option is selected'
I don't think the problem that you're having is in the code you posted.
The easiest thing to do would be to take all the code past the Select Group, and move it into it's own function.
function DoWork { params ([int]$Month)
$Month = '{0:00}' -f $Month
Echo $Month
#Do your work- this is where you code past the UI goes
}
#This is where your drop down labels, etc goes.
if ($Group -eq 'Select All') {
1..12 | ForEach-Object {DoWork($_)}
} else {
DoWork($Group)
}
This will call your DoWork (or whatever you name it) function 12 times, each with a different $Month, if Select All is used.
Depending on variable scope- if your first run through changes a variable, your second run through might start with that changed variable. I'm worried that might be where "they file path gets confused" might be coming from.
If so, look at how you change the $Path variable- or whatever it is.
I ma working on a form that will search all connected drives for PST files.
I can get it working with the following command:-
Get-PSDrive -PSProvider "filesystem"|%{get-childitem $_.root -include *.pst -r}|select name, directoryname, #{name="Size (GB)";expression ={"{0:N2}" -f ($_.length/1GB)}}
The only problem is it takes about 45 minutes to run through all the drives and finish the search. I was thinking of trying to speed it up by using the windows search index.
I have got this....
function Searchindex{
$query="SELECT System.ItemName, system.ItemPathDisplay, System.ItemTypeText, System.Size FROM SystemIndex where system.itemtypetext = 'outlook data file'"
$objConnection = New-Object -ComObject adodb.connection
$objrecordset = New-Object -ComObject adodb.recordset
$objconnection.open("Provider=Search.CollatorDSO;Extended Properties='Application=Windows';")
$objrecordset.open($query, $objConnection)
$array=#()
Try { $objrecordset.MoveFirst() }
Catch [system.exception] { "no records returned" }
do
{
Write-host ($objrecordset.Fields.Item("System.ItemName")).value `
($objrecordset.Fields.Item("System.ItemPathDisplay")).value `
($objrecordset.Fields.Item("System.ITemTypeText")).value `
($objrecordset.Fields.Item("System.Size")).value
if(-not($objrecordset.EOF)) {$objrecordset.MoveNext()}
} Until ($objrecordset.EOF)
$objrecordset.Close()
$objConnection.Close()
$objrecordset = $null
$objConnection = $null
[gc]::collect()
}
this outputs the details to the screen in a few seconds which is perfect but I can't work out how to display it in a datagrid view.
I am using primal form to create the forms.
Once the data is populated in the datagridview I want to be able to select records and copy them to a new location
Can anyone help?
TIA
Andy
I'm not familiar with DataGridView but I feel if you had an object you would be better capable to manipulate it.
function Searchindex{
$query="SELECT System.ItemName, system.ItemPathDisplay, System.ItemTypeText, System.Size FROM SystemIndex where system.itemtypetext = 'outlook data file'"
$objConnection = New-Object -ComObject adodb.connection
$objrecordset = New-Object -ComObject adodb.recordset
$objconnection.open("Provider=Search.CollatorDSO;Extended Properties='Application=Windows';")
$objrecordset.open($query, $objConnection)
$array=#()
Try { $objrecordset.MoveFirst() }
Catch [system.exception] { "no records returned" }
do
{
$array += [pscustomobject]#{
Name = ($objrecordset.Fields.Item("System.ItemName")).value
Path = ($objrecordset.Fields.Item("System.ItemPathDisplay")).value
TypeText = ($objrecordset.Fields.Item("System.ITemTypeText")).value
Size = ($objrecordset.Fields.Item("System.Size")).value
}
If(-not($objrecordset.EOF)) {$objrecordset.MoveNext()}
} Until ($objrecordset.EOF)
$objrecordset.Close()
$objConnection.Close()
$objrecordset = $null
$objConnection = $null
[gc]::collect()
$array
}
This will send out a custom PowerShell object array. You already had the variable $array initialized. We just needed to populate it.
Then you could use something like this to filter out the files you are looking for.
Searchindex | Out-GridView -PassThru
After hitting Ok it will only output the records selected.
DataGridView
with multiselect and return
$global:results = #()
#...searchindex function is here ....
$form = New-Object System.Windows.Forms.Form
$form.Size = New-Object System.Drawing.Size(900,600)
$dataGridView = New-Object System.Windows.Forms.DataGridView
$dataGridView.Size=New-Object System.Drawing.Size(800,400)
$dataGridView.SelectionMode = 'FullRowSelect'
$dataGridView.MultiSelect = $true
$go = New-Object System.Windows.Forms.Button
$go.Location = New-Object System.Drawing.Size(300,450)
$go.Size = New-Object System.Drawing.Size(75,23)
$go.text = "Select"
$form.Controls.Add($go)
$form.Controls.Add($dataGridView)
$arraylist = New-Object System.Collections.ArrayList
$arraylist.AddRange((Searchindex))
$dataGridView.DataSource = $arraylist
$dataGridView.Columns[0].width = 240
$go.Add_Click(
{
$dataGridView.SelectedRows| ForEach-Object{
$global:results += [pscustomobject]#{
Name = $dataGridView.Rows[$_.Index].Cells[0].Value
Path = $dataGridView.Rows[$_.Index].Cells[1].Value
TypeText = $dataGridView.Rows[$_.Index].Cells[2].Value
Size = $dataGridView.Rows[$_.Index].Cells[3].Value
}
$form.Close()
}
})
$form.ShowDialog()
$global:results
There is a lot to cover here but look at the examples and let me know how this works for you. It will return all selected rows back as objects in the global variable $global:results. It needs to be global as output does not persist outside the $go.Add_Click. The searchindex function is there but omitted in the second code sample to save space.
I'm still a beginner with powershell, but love to learn and research all the things it can do.
So with that, I am looking to create a script that will output to a datagridview table. I created a simple enough form that has a multi-line text box where you can enter in a list of servers and then just a simple "check" button that calls my function to actually query each server for their services and outputs that info into the datagridview below it.
That part was easy enough for me to create. My thing is, I want it to do the following":
query each server and pull the list of all services on that server
then in the datagridview, I want it to show the service name in column 1, and then the count of how many servers have that service in column 2
Hope this make since. Can't really provide any code as I don't know how to properly write it! I'm thinking there is someway to generate the full list, then do a loop and count type thing, but not sure.
I thank you for any assistance with this. Been googling and reading up like crazy. Either not correct search criteria or something just isn't clicking for me.
Update - Adding in current script I have:
[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Drawing")
[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
$xForm = 800
$yForm = 800
$SVCForm = New-Object System.Windows.Forms.Form
$SVCForm.Text = "Automatic Services Query"
$SVCForm.Size = New-Object System.Drawing.Size($xForm,$yForm)
$SVCForm.FormBorderStyle = "FixedSingle"
$SVCForm.StartPosition = "CenterScreen"
$SVCForm.ControlBox = $true
$SVCForm.KeyPreview = $True
$SVCForm.ShowIcon = $false
$SVCForm.MinimizeBox = $True
$SVCForm.MaximizeBox = $false
$CancelButton = New-Object System.Windows.Forms.Button
$CancelButton.Location = New-Object System.Drawing.Size(365,720)
$CancelButton.Size = New-Object System.Drawing.Size(75,23)
$CancelButton.Text = "Cancel"
$CancelButton.Add_Click({$x=$CancelButton.Text;$SVCForm.Close()})
$SVCForm.Controls.Add($CancelButton)
$ServerListGroup = New-Object System.Windows.Forms.GroupBox
$ServerListGroup.Location = New-Object System.Drawing.Size(5,10)
$ServerListGroup.size = New-Object System.Drawing.Size(780,300)
$ServerListGroup.text = "Enter list of servers you want to check:"
$SVCForm.Controls.Add($ServerListGroup)
$ServerList = New-object System.Windows.Forms.TextBox
$ServerList.Location = New-object System.Drawing.Size(5,25)
$ServerList.Size = New-Object System.Drawing.Size(280,270)
$ServerList.Multiline = $True
$ServerList.ScrollBars = "Vertical"
#$ServerList.add_TextChanged({ONValButton})
$ServerListGroup.Controls.Add($ServerList)
$SVCButton = New-Object System.Windows.Forms.Button
$SVCButton.Location = New-Object System.Drawing.Size(505,140)
$SVCButton.Size = New-Object System.Drawing.Size(180,22)
$SVCButton.Text = "SVC Check"
$SVCButton.Enabled = $True
$SVCButton.Add_Click({SvcCheckCount})
$ServerListGroup.Controls.Add($SVCButton)
$SvcListGroup = New-Object System.Windows.Forms.GroupBox
$SvcListGroup.Location = New-Object System.Drawing.Size(5,330)
$SvcListGroup.size = New-Object System.Drawing.Size(780,380)
$SvcListGroup.text = "Automatic Running/Not-Running Services are listed below:"
$SVCForm.Controls.Add($SvcListGroup)
#$SvcList = New-object System.Windows.Forms.TextBox
#$SvcList.Location = New-object System.Drawing.Size(5,25)
#$SvcList.Size = New-Object System.Drawing.Size(765,350)
#$SvcList.Multiline = $True
#$SvcList.ScrollBars = "Vertical"
#$SvcList.Readonly= $True
#$SvcListGroup.Controls.Add($SvcList)
$SvcGrid = New-Object System.Windows.Forms.DataGridView
$SvcGrid.Location = New-Object System.Drawing.Size(5,15)
$SvcGrid.Size = New-Object System.Drawing.Size(760,360)
$SvcGrid.ColumnHeadersBorderStyle = [System.Windows.Forms.DataGridViewHeaderBorderStyle]::Single
$SvcGrid.CellBorderStyle = [System.Windows.Forms.DataGridViewCellBorderStyle]::Single
$SvcGrid.ColumnHeadersHeightSizeMode = [System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode]::DisableResizing
$SvcGrid.GridColor = [System.Drawing.Color]::Black
$SvcGrid.RowHeadersVisible = $false
$SvcGrid.AllowUserToAddRows = $false
$SvcGrid.AllowUserToResizeColumns = $False
$SvcGrid.AllowUserToResizeRows = $false
$SvcGrid.ColumnHeadersHeight = 23
$SvcGrid.SelectionMode = [System.Windows.Forms.DataGridViewSelectionMode]::FullRowSelect
$Scroll = New-Object System.Windows.Forms.VScrollBar
$Scroll.Dock = [System.Windows.Forms.DockStyle]::Right
$Scroll.width = 18
$Scroll.isAccessible = $false
$SvcGrid.Controls.Add($Scroll)
$SvcGrid.Columns.Add("Name","Service Name") > $null
$SvcGrid.Columns["Name"].Width = 200
$SvcGrid.Columns["Name"].HeaderCell.Style.Alignment = [System.Windows.Forms.DataGridViewContentAlignment]::MiddleLeft
$SvcGrid.Columns["Name"].DefaultCellStyle.Alignment = [System.Windows.Forms.DataGridViewContentAlignment]::MiddleLeft
$SvcGrid.Columns["Name"].ReadOnly = $False
$SvcGrid.Columns.Add("StartMode", "Start Mode") > $null
$SvcGrid.Columns["StartMode"].Width = 180
$SvcGrid.Columns["StartMode"].HeaderCell.Style.Alignment = [System.Windows.Forms.DataGridViewContentAlignment]::MiddleLeft
$SvcGrid.Columns["StartMode"].DefaultCellStyle.Alignment = [System.Windows.Forms.DataGridViewContentAlignment]::MiddleLeft
$SvcGrid.Columns["StartMode"].ReadOnly = $False
$SvcGrid.Columns.Add("Running","Running") > $null
$SvcGrid.Columns["Running"].Width = 180
$SvcGrid.Columns["Running"].HeaderCell.Style.Alignment = [System.Windows.Forms.DataGridViewContentAlignment]::MiddleCenter
$SvcGrid.Columns["Running"].DefaultCellStyle.Alignment = [System.Windows.Forms.DataGridViewContentAlignment]::MiddleCenter
$SvcGrid.Columns["Running"].ReadOnly = $true
$SvcGrid.Columns.Add("NotRunng","Not Running")>$null
$SvcGrid.Columns["NotRunng"].Width = 180
$SvcGrid.Columns["NotRunng"].HeaderCell.Style.Alignment = [System.Windows.Forms.DataGridViewContentAlignment]::MiddleCenter
$SvcGrid.Columns["NotRunng"].DefaultCellStyle.Alignment = [System.Windows.Forms.DataGridViewContentAlignment]::MiddleCenter
$SvcGrid.Columns["NotRunng"].ReadOnly = $true
$SvcListGroup.Controls.Add($SvcGrid)
Function SvcCheckCount
{
$SvcGrid.Rows.Clear()
$Servername = $ServerList.Text.Split("`n")|%{$_.trim()}
foreach($Server in $Servername)
{
$Server = $Server.ToUpper()
$svctest = Get-WmiObject Win32_Service | Where-Object {$_.StartMode -eq 'Auto'} | Select-Object Name, Startmode, State
Foreach ($svc in $svctest)
{
$name = $svc.Name
$startmode = $svc.StartMode
$state = $svc.State
If($state-eq"Running"){$SvcGrid.Rows.add($name,$startmode,$state)}
If($state-eq"Stopped"){$SvcGrid.Rows.add($name,$startmode,$null,$state)}
}
}
}
$SVCForm.Topmost = $True
$SVCForm.Add_Shown({$SVCForm.Activate()})
[void] $SVCForm.ShowDialog()
Since you already have the GUI portion of this functioning I am going to focus us the grouping of the data you are looking for. Going into this the one thing I am fuzzy on is why you have the if statements are for but we can work on that if need be. The following is meant to be in place of your foreach loop
$services = #()
foreach($Server in $Servername){
$Server = $Server.ToUpper()
$services += Get-WmiObject Win32_Service -ComputerName $Server -Filter 'StartMode="Auto"' |
Select-Object Name, Startmode, State |
Add-Member -MemberType NoteProperty -Value $Server -Name "Server" -PassThru
}
$services | Group-Object Name | ForEach-Object{
$SvcGrid.Rows.add($_.Name,$_.Count)
}
First thing: you were not using the $server variable in your wmi call so all the results would have been from the one machine you were running this from. Also, I moved the Where into a -Filter so that should speed up processing on remote machines.
Collect all of this information into an array (adding the computer name in case it becomes useful later). Then, using the results from that use Group-Object to get the counts you were looking for. Pipe that into another ForEach-Object to get the results in your grid ( which i have not tested.)
Side note: If you just want the info in a grid you can use Out-GridView
$services | Group-Object Name | Select name,count | Out-GridView