Powershell Timer without pausing form - timer

Good day all
As usual I am stuck, I have a simple script that is designed to show you the status of a list of systems bitlocker. Give it a txt of system names, it does the rest. All works as intended; however its updating the list on a ticking Timer, which when executing will make the window unresponsive and appear to be broken (to those users who dont understand what its doing). Is there a way to branch this off in some fashion to avoid this hangingup?
I considered doing a branch but I do now know how to make that branch update an object in its parent... if thats even possible.
CODE:
[void] [Reflection.Assembly]::LoadWithPartialName( 'System.Windows.Forms' )
$d = New-Object Windows.Forms.OpenFileDialog
$d.ShowHelp = $true
$d.filter = "System ID List (*.txt)| *.txt"
$result = $d.ShowDialog( )
$names = #()
$names = Get-Content $d.filename
[System.Reflection.Assembly]::LoadWithPartialName("System.Drawing")
[System.Reflection.Assembly]::LoadWithPartialName("System.windows.forms")
$myWindow = new-object System.Windows.Forms.form
$myDataGrid = new-object System.windows.forms.DataGridView
$myDataGrid.Location = new-object System.Drawing.Size(20,30)
$myDataGrid.size = new-object System.Drawing.Size(450,480)
$myDataGrid.AllowUserToAddRows = $False
$myDataGrid.AutoSizeColumnsMode = [System.Windows.Forms.DataGridViewAutoSizeColumnsMode]::Fill
$myDataGrid.RowsDefaultCellStyle.BackColor = [System.Drawing.Color]::Bisque
$myDataGrid.AlternatingRowsDefaultCellStyle.BackColor = [System.Drawing.Color]::Beige
$myDataGrid.BorderStyle = [System.Windows.Forms.BorderStyle]::Fixed3D
$myDataGrid.ColumnHeadersDefaultCellSTyle.ForeColor = [System.Drawing.Color]::Maroon
$myDataGrid.ColumnHeadersDefaultCellStyle.BackColor = [System.Drawing.Color]::Tan
$myDataGrid.RowHeadersDefaultCellStyle.BackColor = [System.Drawing.Color]::Tan
$myDataGrid.ColumnHeadersHeightSizeMode = [System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode]::AutoSize
$myWindow.Controls.Add($myDataGrid)
# Define menus
$myMenuStrip = new-object System.Windows.Forms.MenuStrip
$FileExit = new-object System.Windows.Forms.ToolStripMenuItem("&Exit")
$FileExit.add_Click({ $myWindow.close() })
$myMenuStrip.Items.Add($FileMenu)
$myWindow.Controls.Add($myMenuStrip)
$timer = New-Object System.Windows.Forms.Timer
$timer.Interval = 1000
$timer.add_tick({
$dataTable = New-Object System.Data.DataTable
$dataTable.Columns.Add("System") | Out-Null
$dataTable.Columns.Add("BitLocker % (C:)") | Out-Null
foreach ($name in $names) {
$stat = (manage-bde.exe -cn $name -status C:)[11].split(":")[1]
$row = $dataTable.NewRow()
$row["System"] = $name
$row["BitLocker % (C:)"] = $stat
$dataTable.Rows.Add($row)
}
$myDataGrid.DataSource = $dataTable
})
# main program body
$myWindow.Text = "BitLocker Status"
$myWindow.size = new-object System.Drawing.Size(500,600)
$myWindow.autoscroll = $true
$myWindow.Add_Shown({$myWindow.Activate()})
$timer.Start()
$myWindow.ShowDialog()

I cannot believe nobody answered this, perhaps I wasn't clear.
Eitherway the solution was easy, write-output $object, then receive-job.
Done

Related

Powershell script equivalent of database -> Generate scripts

I wrote a PowerShell script which stores schema, and data info in .sql file.
But in that file I am missing the initiate part of create database. The file starts with queries of create table.
PowerShell script:
$Filepath= 'C:\Temp' # local directory to save build-scripts to
$DataSource= 'ServerName' #'SERVERNAME' # server name and instance
$Database= 'DBName'
$DateTime = Get-Date -Format s
$dateStamp = $(get-date).ToString("yyyyMMdd");
$ErrorActionPreference = "stop"
$ms='Microsoft.SqlServer'
$v = [System.Reflection.Assembly]::LoadWithPartialName( "$ms.SMO")
if ((($v.FullName.Split(','))[1].Split('='))[1].Split('.')[0] -ne '9') {
[System.Reflection.Assembly]::LoadWithPartialName("$ms.SMOExtended") | out-null
}
$My="$ms.Management.Smo" #
$s = new-object ("$My.Server") $DataSource
if ($s.Version -eq $null ){Throw "Can't find the instance $Datasource"}
$db= $s.Databases[$Database]
if ($db.name -ne $Database){Throw "Can't find the database '$Database' in $Datasource"};
$transfer = new-object ("$My.Transfer") $db
$transfer.Options.ScriptBatchTerminator = $true
$transfer.Options.ToFileOnly = $true
$transfer.Options.ScriptData = $true
$transfer.Options.IncludeHeaders = $true
$transfer.Options.ExtendedProperties = $true
$transfer.Options.IncludeDatabaseContext = $true
$transfer.Options.DriForeignKeys = $true
$transfer.Options.DriIndexes = $true
$transfer.Options.DriPrimaryKey = $true
$transfer.Options.DriUniqueKeys = $true
$transfer.CreateTargetDatabase = $true
$transfer.Options.Filename = "$($FilePath)\$($Database)_$($dateStamp)_Build.sql";
$transfer.EnumScriptTransfer()
Any help on how this can be achieved?
Basically I am missing create database block in my .sql file.

Powershell: Click button on GUI to move to next item in array

I have created a GUI with the purpose of deleting a row from a SQL table.
I am now stuck on having the results show one at a time as a button is pressed.
Screenshot of GUI:
If I have three results come back, it will show the first result in the text box, then when the Next button is pressed, it skips over the second result and shows the last one.
Debugging shows that it definitely finds three results, but its like I need to stop it between results until the button is pressed to move to the next one. I have tried many things, but have had no luck.
Here is the code (sorry there is quite a bit. The part I am stuck on it near the bottom):
<#
.NAME
Remove A File
#>
Start-Transcript -path "c:\path\RemoveScipt.txt"
$StaticData = "C:\path\Static Data.csv"
$SQLServer = "SERVERNAME"
$DB = "DATABASENAME"
Add-Type -AssemblyName System.Windows.Forms
[System.Windows.Forms.Application]::EnableVisualStyles()
$RemoveFile = New-Object system.Windows.Forms.Form
$RemoveFile.ClientSize = New-Object System.Drawing.Point(530,510)
$RemoveFile.text = "Remove a script"
$RemoveFile.TopMost = $false
$ScriptName = New-Object system.Windows.Forms.Label
$ScriptName.text = "Script Name"
$ScriptName.AutoSize = $true
$ScriptName.width = 25
$ScriptName.height = 10
$ScriptName.location = New-Object System.Drawing.Point(14,20)
$ScriptName.Font = New-Object System.Drawing.Font('Microsoft Sans Serif',10)
$ReportName = New-Object system.Windows.Forms.Label
$ReportName.text = "Report Name"
$ReportName.AutoSize = $true
$ReportName.width = 25
$ReportName.height = 10
$ReportName.location = New-Object System.Drawing.Point(14,50)
$ReportName.Font = New-Object System.Drawing.Font('Microsoft Sans Serif',10)
$Result = New-Object system.Windows.Forms.Label
$Result.text = "Is this the script you wish to remove?"
$Result.AutoSize = $true
$Result.width = 25
$Result.height = 10
$Result.location = New-Object System.Drawing.Point(14,110)
$Result.Font = New-Object System.Drawing.Font('Microsoft Sans Serif',10)
$ScriptNameCombo = New-Object system.Windows.Forms.ComboBox
$ScriptNameCombo.width = 337
$ScriptNameCombo.height = 20
$ScriptNameCombo.location = New-Object System.Drawing.Point(120,19)
$ScriptNameCombo.Font = New-Object System.Drawing.Font('Microsoft Sans Serif',10)
$ScriptNameSQL ="SELECT distinct Script_Name FROM TABLE"
$ScriptNameCall = Invoke-Sqlcmd -ServerInstance $SQLServer -Database $DB -Query $ScriptNameSQL
$ScriptNameCombo.Items.AddRange($ScriptNameCall.Script_Name) #ADDS ITEMS TO A DROPDOWN LIST
$ReportNameCombo = New-Object system.Windows.Forms.ComboBox
$ReportNameCombo.width = 337
$ReportNameCombo.height = 20
$ReportNameCombo.location = New-Object System.Drawing.Point(120,49)
$ReportNameCombo.Font = New-Object System.Drawing.Font('Microsoft Sans Serif',10)
$GoBut = New-Object system.Windows.Forms.Button
$GoBut.text = "Go!"
$GoBut.width = 45
$GoBut.height = 25
$GoBut.location = New-Object System.Drawing.Point(470,18)
$GoBut.Font = New-Object System.Drawing.Font('Microsoft Sans Serif',10)
$GoBut.BackColor =[System.Drawing.Color]::LightGreen
$Go2But = New-Object system.Windows.Forms.Button
$Go2But.text = "Go!"
$Go2But.width = 45
$Go2But.height = 25
$Go2But.location = New-Object System.Drawing.Point(470,48)
$Go2But.Font = New-Object System.Drawing.Font('Microsoft Sans Serif',10)
$Go2But.BackColor =[System.Drawing.Color]::LightGreen
$NextBut = New-Object system.Windows.Forms.Button
$NextBut.text = "Next"
$NextBut.width = 70
$NextBut.height = 30
$NextBut.location = New-Object System.Drawing.Point(242,103)
$NextBut.Font = New-Object System.Drawing.Font('Microsoft Sans Serif',10)
$NextBut.BackColor =[System.Drawing.Color]::FloralWhite
$ResultText = New-Object system.Windows.Forms.TextBox
$ResultText.multiline = $true
$ResultText.width = 500
$ResultText.height = 325
$ResultText.scrollbars = "Both"
$ResultText.WordWrap = $false
$ResultText.location = New-Object System.Drawing.Point(14,135)
$ResultText.Font = New-Object System.Drawing.Font('Microsoft Sans Serif',10)
$RemoveScript = New-Object system.Windows.Forms.Button
$RemoveScript.text = "Remove the script"
$RemoveScript.width = 150
$RemoveScript.height = 30
$RemoveScript.location = New-Object System.Drawing.Point(14,470)
$RemoveScript.Font = New-Object System.Drawing.Font('Microsoft Sans Serif',10)
$CancelBut = New-Object system.Windows.Forms.Button
$CancelBut.text = "Cancel"
$CancelBut.width = 150
$CancelBut.height = 30
$CancelBut.location = New-Object System.Drawing.Point(365,470)
$CancelBut.Font = New-Object System.Drawing.Font('Microsoft Sans Serif',10)
$CancelBut.BackColor =[System.Drawing.Color]::IndianRed
$RemoveFile.controls.AddRange(#($ScriptName,$ReportName,$RemoveScript,$ScriptNameCombo,$ReportNameCombo,$GoBut,$ResultText,$Result,$Go2But,$CancelBut,$NextBut))
$GoBut.Add_Click({
#USES WHAT WAS CHOSEN FROM THE FIRST DROPDOWN LIST TO FIND ANY ENTRIES WITH THAT ITEM IN THE FIRST COLUMN TO CREATE A SECOND DROPDOWN LIST. THIS IS TO SHORTEN THE RESULTS
$ReportNameLinkedSQL="SELECT distinct Report_Name FROM TABLE where Script_Name = '$($ScriptNameCombo.text)'"
$ReportNameCallLinked= Invoke-Sqlcmd -ServerInstance $SQLServer -Database $DB -Query $ReportNameLinkedSQL
if ($ScriptNameCombo.text.length -eq 0){[System.Windows.Forms.MessageBox]::Show('Ya gotta select a Script, buddy!','Uh oh','OK','Error')}else{
$ReportNameCombo.Items.AddRange($ReportNameCallLinked.Report_Name)}
})
$Go2But.Add_Click({
#THIS USES THE PREVIOUS TWO SELECTIONS TO FIND ALL MATCHING ROWS IN THE DATABASE WITH THOSE TWO ITEMS IN THE FIRST TWO COLUMNS. IT WILL SHOW THE FIRST RESULT IN THE TEXT BOX
$FullScriptSQL="SELECT * FROM TABLE where Script_Name = '$($ScriptNameCombo.text)' and Report_Name = '$($ReportNameCombo.text)'"
$global:FullScriptCallLinked= #(Invoke-Sqlcmd -ServerInstance $SQLServer -Database $DB -Query $FullScriptSQL)
$count=$FullScriptCallLinked.count
$nl = "`r`n"
if ($ScriptNameCombo.text.length -eq 0){[System.Windows.Forms.MessageBox]::Show('Ya gotta select a Script, buddy!','Uh oh','OK','Error')}
elseif($ReportNameCombo.text.length -eq 0){[System.Windows.Forms.MessageBox]::Show('Ya gotta select a Report, buddy!','Uh oh','OK','Error')}
elseif($count -eq $null -and $FullScriptCallLinked.Email_or_file -eq 'E'){$ResultText.text = ($nl),"Script Name = ",$FullScriptCallLinked.script_name,($nl),($nl),"Report Name = ",$FullScriptCallLinked.Report_name,($nl),($nl),"View Name = ",$FullScriptCallLinked.View_name,($nl),($nl),"Module = ",$FullScriptCallLinked.Module,($nl),($nl),"Email Address = ",$FullScriptCallLinked.email,($nl),($nl),"Email Subject = ",$FullScriptCallLinked.email_subject,($nl),($nl),"Email Body = ",$FullScriptCallLinked.email_message,($nl),($nl),"File Name = ",$FullScriptCallLinked.filename,($nl),($nl),"File Type = ",$FullScriptCallLinked.file_type}
elseif($count -eq $null -and $FullScriptCallLinked.Email_or_file -eq 'F'){$ResultText.text = ($nl),"Script Name = ",$FullScriptCallLinked.script_name,($nl),($nl),"Report Name = ",$FullScriptCallLinked.Report_name,($nl),($nl),"View Name = ",$FullScriptCallLinked.View_name,($nl),($nl),"Module = ",$FullScriptCallLinked.Module,($nl),($nl),"File Name = ",$FullScriptCallLinked.filename,($nl),($nl),"File Path = ",$FullScriptCallLinked.file_path,($nl),($nl),"Todays Date = ",$FullScriptCallLinked.today_date,($nl),($nl),$FullScriptCallLinked.yesterday_date,($nl),($nl),"File Type = ",$FullScriptCallLinked.file_type}
elseif($FullScriptCallLinked[0].Email_or_file -eq 'E'){$ResultText.text = ($nl),"Script Name = ",$FullScriptCallLinked[0].script_name,($nl),($nl),"Report Name = ",$FullScriptCallLinked[0].Report_name,($nl),($nl),"View Name = ",$FullScriptCallLinked[0].View_name,($nl),($nl),"Module = ",$FullScriptCallLinked[0].Module,($nl),($nl),"Email Address = ",$FullScriptCallLinked[0].email,($nl),($nl),"Email Subject = ",$FullScriptCallLinked[0].email_subject,($nl),($nl),"Email Body = ",$FullScriptCallLinked[0].email_message,($nl),($nl),"File Name = ",$FullScriptCallLinked[0].filename,($nl),($nl),"File Type = ",$FullScriptCallLinked[0].file_type}
elseif($FullScriptCallLinked[0].Email_or_file -eq 'F'){$ResultText.text = ($nl),"Script Name = ",$FullScriptCallLinked[0].script_name,($nl),($nl),"Report Name = ",$FullScriptCallLinked[0].Report_name,($nl),($nl),"View Name = ",$FullScriptCallLinked[0].View_name,($nl),($nl),"Module = ",$FullScriptCallLinked[0].Module,($nl),($nl),"File Name = ",$FullScriptCallLinked[0].filename,($nl),($nl),"File Path = ",$FullScriptCallLinked[0].file_path,($nl),($nl),"Todays Date = ",$FullScriptCallLinked[0].today_date,($nl),($nl),$FullScriptCallLinked[0].yesterday_date,($nl),($nl),"File Type = ",$FullScriptCallLinked[0].file_type}
})
$Nextbut.Add_Click({
#HERE IS WHERE I AM STUCK. MY INTENT IS TO SCROLL THROUGH EACH RESULT WHEN THE BUTTON IS CLICKED
function onClick {
foreach($i in $FullScriptCallLinked){
$ResultText.text = ($nl),"Script Name = ",$i.script_name,($nl),($nl),"Report Name = ",$i.Report_name,($nl),($nl),"View Name = ",$i.View_name,($nl),($nl),"Module = ",$i.Module,($nl),($nl),"Email Address = ",$i.email,($nl),($nl),"Email Subject = ",$i.email_subject,($nl),($nl),"Email Body = ",$i.email_message,($nl),($nl),"File Name = ",$i.filename,($nl),($nl),"File Type = ",$i.file_type
}
}
onClick
})
$RemoveScript.Add_Click({
#NOT COMPLETE
$deleteSQL = "DELETE FROM TABLE WHERE Script_Name = '$($FullScriptCallLinked.script_name)' and Report_Name = '$($FullScriptCallLinked.Report_name)' and View_Name = '$($FullScriptCallLinked.View_name)' and FILENAME = '$($FullScriptCallLinked.filename)' and File_type = '$($FullScriptCallLinked.file_type)'"
write-host $deleteSQL
#[environment]::exit(0)
})
$CancelBut.Add_Click({
#CLEARS ALL FIELDS IN THE FORM
$ScriptNameCombo.text=$null
$ReportNameCombo.items.clear()
$ReportNameCombo.resettext()
$ResultText.clear()
})
[void]$RemoveFile.ShowDialog()
Any help with this would be amazing! Thanks in advance
Here is the problem:
foreach($i in $FullScriptCallLinked){
$ResultText.text = ....
}
The foreach statement iterates through all the items, one by one. So if you have three items in $FullScriptCallLinked, the above code will set $ResultText.text three times. The first two values will be overwritten by the last one. So what you see in the UI is just the last item.
You can do something like this:
$currentResultIndex = 0
$Nextbut.Add_Click({
function onClick {
$currentResultIndex = $currentResultIndex+1
if($currentResultIndex -ge $FullScriptCallLinked.Length) {
$currentResultIndex = 0;
}
$i = $FullScriptCallLinked[$currentResultIndex]
$ResultText.text = ($nl),"Script Name = ",$i.script_name,($nl),($nl),"Report Name = ",$i.Report_name,($nl),($nl),"View Name = ",$i.View_name,($nl),($nl),"Module = ",$i.Module,($nl),($nl),"Email Address = ",$i.email,($nl),($nl),"Email Subject = ",$i.email_subject,($nl),($nl),"Email Body = ",$i.email_message,($nl),($nl),"File Name = ",$i.filename,($nl),($nl),"File Type = ",$i.file_type
}
onClick
})
Please read more about PowerShell collections and the foreach statement.
Oops! I forgot about this one. I did figure out how to both scroll through each result, then cycle back to the first result when it reaches the end.
I made some changes to the GUI to make it easier for me.
I have completed the script. I updated the 'Go' button and finished the 'Next' button:
$GoBut.Add_Click({
#Establish SQL statements which include a search and a count to verify it completed successfully at the end
$FullScriptSQLEmail = "SELECT * FROM TABLE where email = '$($EmailAddressCombo.text)' and Email_or_file = 'E'"
$FullScriptSQLFile = "SELECT * FROM TABLE where Filename = '$($fileNameCombo.text)' and Email_or_file = 'F'"
$CountSelectE = "SELECT count (*) FROM TABLE where email = '$($EmailAddressCombo.text)' and Email_or_file = 'E'"
$CountSelectF = "SELECT count (*) FROM TABLE where Filename = '$($fileNameCombo.text)' and Email_or_file = 'F'"
$FullScriptCallEmail = #(Invoke-Sqlcmd -ServerInstance $SQLServer -Database $DB -Query $FullScriptSQLEmail)
$FullScriptCallFile = #(Invoke-Sqlcmd -ServerInstance $SQLServer -Database $DB -Query $FullScriptSQLFile)
$CountResultE = Invoke-Sqlcmd -ServerInstance $SQLServer -Database $DB -Query $CountSelectE
$CountResultF = Invoke-Sqlcmd -ServerInstance $SQLServer -Database $DB -Query $CountSelectF
$nl = "`r`n"
#Get a count. This works for a 'sanity check', but counts one as zero if you attempt to output it
$countEmail = $FullScriptCallEmail.count
$countFile = $FullScriptCallFile.count
#Bit of a cleanup to prevent errors
$IfExist = Test-Path "./artifacts/*.txt"
if ($IfExist -eq $true){[System.Windows.Forms.MessageBox]::Show('Whoops! Just gonna clear out some stuff to give you a fresh start Bucko.','Oh dear','OK','Error')
$EmailAddressCombo.text=$null
$fileNameCombo.text=$null
$ScriptCount.text=$null
$ResultText.clear()
Remove-Item ".\Artifacts\*.txt"
return}
#Only allow one combobox to be chosen, but must be one
Function Clear_fields{
$EmailAddressCombo.text=$null
$fileNameCombo.text=$null
}
if ($EmailAddressCombo.text.length -ne 0 -and $fileNameCombo.text.length -ne 0){[System.Windows.Forms.MessageBox]::Show('THERE CAN ONLY BE ONE! You can only choose an Email OR a File Path, champ!','Uh oh','OK','Error'); Clear_fields; return}
elseif($EmailAddressCombo.text.length -eq 0 -and $fileNameCombo.text.length -eq 0){[System.Windows.Forms.MessageBox]::Show('Ya gotta select a Report, buddy!','Uh oh','OK','Error'); return}
#Using the Count SQL statment to get a real count and then putting the number of results into a box so you know how many you got
if ($CountResultE.Column1 -gt 0){$ScriptCount.text = $CountResultE.Column1}else
{$ScriptCount.text = $CountResultF.Column1}
#Put all the data into a text file and format it at the same time
if($countEmail -gt 0)
{foreach($i in $FullScriptCallEmail){
add-content -path "./Artifacts/results.txt" -value "$($nl) Script Name = $($i.script_name)$($nl)$($nl) Report Name = $($i.Report_name)$($nl)$($nl) View Name = $($i.View_name)$($nl)$($nl) Module = $($i.Module)$($nl)$($nl) Email Address = $($i.email)$($nl)$($nl) Email Subject = $($i.email_subject)$($nl)$($nl) Email Body = $($i.email_message)$($nl)$($nl) File Name = $($i.filename)$($nl)$($nl) File Type = $($i.file_type)"}
}
if($countFile -gt 0)
{foreach($i in $FullScriptCallFile){
add-content -path "./Artifacts/results.txt" -value "$($nl) Script Name = $($i.script_name)$($nl)$($nl) Report Name = $($i.Report_name)$($nl)$($nl) View Name = $($i.View_name)$($nl)$($nl) Module = $($i.Module)$($nl)$($nl) File Name = $($i.filename)$($nl)$($nl) File Path = $($i.file_path)$($nl)$($nl) Todays Date = $($i.today_date)$($nl)$($nl) Yesterdays Date = $($i.yesterday_date)$($nl)$($nl) File Type = $($i.file_type)"}
}
#Establish the results file and create a copy. Show the first 18 lines in the result box
$resultfile = "./Artifacts/results.txt"
copy-item "./Artifacts/results.txt" -destination "./Artifacts/result_copy.txt"
#($content = get-content $resultfile)
$ResultText.Lines = $content[0..17]
})
$Nextbut.Add_Click({
$resultfile = "./Artifacts/results.txt"
#Function to delete the top 18 lines of the result file so the next 18 are grabbed. If there is only one set left (counts how many times Script Name appears), then the data in the copy is copied and pasted into the results file, starting it all over again
function del-line {
$Check1 = get-content $resultfile
$Check2 = $Check1 -match "Script Name"
$check2.count
if ($check2.count -eq 1){
$copy = "./Artifacts/result_copy.txt"
get-content $copy | set-content $resultfile
$ResultText.clear()
}else{
$ResultText.clear()
(Get-Content $resultfile | Select-Object -Skip 18) | Set-Content $resultfile
}
}
#Funciton to get the first 18 lines of the result file and display it after using the del-line function
function onClick{
del-line
$LineCount = get-content $resultfile
if (($LineCount.count) -eq 1){
$content = get-content $resultfile
$ResultText.text = $content}
else{
$content = get-content $resultfile
$ResultText.Lines = $content[0..17]
}
}
#Start the loop
onClick
})
If anyone wants the whole script let me know.
I have written 5 GUI scripts to make life easier for my team to:
Add a SQL entry
Remove a SQL entry
Amend a SQL entry
Search for and export a SQL entry or multiple entries
I am no expert and I know it is a little clunky, but works for us. I would think large DB's might cause some strain on resources

PowerShell: Duplicate windows forms with different properties

I am trying make copies of windows forms objects and change the properties of new objects. For example:
$List1 = New-Object System.Windows.Forms.ListBox
$List1.Location = New-Object System.Drawing.Size(10,10)
$List1.Size = New-Object System.Drawing.Size(280,310)
$List2 = $List1
$List2.Location = New-Object System.Drawing.Size(350,10)
The problem is that $List2 is a pointer of $List1. Whatever I change on $List2 always change the properties on $List1. Is there a solution for this?
$List1.Location
IsEmpty X Y
------- - -
False 350 10
$List1.Location
IsEmpty X Y
------- - -
False 350 10
Whatever I change on $List2 always change the properties on $List1. Is there a solution for this?
Yes, the solution is to create a new instance of ListBox:
$List1 = New-Object System.Windows.Forms.ListBox
$List1.Location = New-Object System.Drawing.Size(10,10)
$List1.Size = New-Object System.Drawing.Size(280,310)
$List2 = New-Object System.Windows.Forms.ListBox
$List2.Size = $List1.Size
$List2.Location = New-Object System.Drawing.Size(350,10)
Notice that $List2.Size = $List1.Size is safe, because Size is a struct, and structs are copied on assignment
If you have many properties to reference, you could wrap the common property values in a hashtable to pass to New-Object -Property:
$ListBoxDefaultProperties = #{
Location = New-Object System.Drawing.Size (10,10)
Size = New-Object System.Drawing.Size (280,310)
BackColor = 'Beige'
DisplayMember = 'SomePropertyName'
# etc...
}
$List1 = New-Object System.Windows.Forms.ListBox -Property $ListBoxDefaultProperties
$List2 = New-Object System.Windows.Forms.ListBox -Property $ListBoxDefaultProperties
$List3 = New-Object System.Windows.Forms.ListBox -Property $ListBoxDefaultProperties
$List1 and $List2 reference the same object because you did $List1 = $List2.
You have to create 2 separate instances. You can set all common properties in a loop and then only change the properties that differ:
# 1. create two separate instances
$list1 = New-Object System.Windows.Forms.ListBox
$list2 = New-Object System.Windows.Forms.ListBox
# 2. set properties on both instances
foreach ($list in ($list1, $list2)) {
$list.Location = New-Object System.Drawing.Size(10, 10)
$list.Size = New-Object System.Drawing.Size(280, 310)
# [...]
}
# 3. set all different properties on the 2nd instance only
$list2.Location = New-Object System.Drawing.Size(350, 10)
# [...]

Powershell Windows Form listbox selection validation

I have created a powershell script using windows forms containing list boxes to run a cmd command (helps eliminate user-error and expedites process a bit). The only problem I run into is the command will still attempt to run even if an item is not selected on one of the forms I have populate. This can cause the script to simply not run, or it can cause mass amounts of data to be downloaded (the script pulls log files from a server, the listbox helps narrow down the data to be pulled). Is there a way to create error checking for the listbox that will essentially say "hey, you didn't select anything!" before continuing?
Thanks!
Edit (example of first list box):
$form = New-Object System.Windows.Forms.Form
$form.Text = "Select a production environment"
$form.Size = New-Object System.Drawing.Size(190,250)
$form.StartPosition = "CenterScreen"
$OKButton = New-Object System.Windows.Forms.Button
$OKButton.Location = New-Object System.Drawing.Point(10,180)
$OKButton.Size = New-Object System.Drawing.Size(75,23)
$OKButton.Text = "OK"
$OKButton.DialogResult = [System.Windows.Forms.DialogResult]::OK
$form.AcceptButton = $OKButton
$form.Controls.Add($OKButton)
$CancelButton = New-Object System.Windows.Forms.Button
$CancelButton.Location = New-Object System.Drawing.Point(85,180)
$CancelButton.Size = New-Object System.Drawing.Size(75,23)
$CancelButton.Text = "Cancel"
$CancelButton.DialogResult = [System.Windows.Forms.DialogResult]::Cancel
$form.CancelButton = $CancelButton
$form.Controls.Add($CancelButton)
$label = New-Object System.Windows.Forms.Label
$label.Location = New-Object System.Drawing.Point(10,20)
$label.Size = New-Object System.Drawing.Size(280,20)
$label.Text = "Please select a production environment:"
$form.Controls.Add($label)
$listBox = New-Object System.Windows.Forms.ListBox
$listBox.Location = New-Object System.Drawing.Point(10,40)
$listBox.Size = New-Object System.Drawing.Size(150,20)
$listBox.Height = 140
[void] $listBox.Items.Add("server1")
[void] $listBox.Items.Add("server2")
[void] $listBox.Items.Add("server3")
$form.Controls.Add($listBox)
$form.Topmost = $True
$result = $form.ShowDialog()
if ($result -eq [System.Windows.Forms.DialogResult]::OK)
{
$Prod = $listBox.SelectedItem
$Prod
}
There might be a way to perform this kind of validation using Windows Forms methods, but this uses PowerShell.
Perform $result = $form.ShowDialog() in a Do..Until or Do..While loop and continue showing the dialogue until the user has selected at least one item, and pressed OK - or the user exits the dialogue - else, show a warning and show the dialogue again.
Here is the relevant PowerShell code:
do
{
$result = $form.ShowDialog()
if ($ListBox.SelectedIndices.Count -lt 1 -and $result -eq [System.Windows.Forms.DialogResult]::OK)
{
Write-Warning 'Nothing was selected, please select a server.'
}
}
until (($result -eq [System.Windows.Forms.DialogResult]::OK -and $listBox.SelectedIndices.Count -ge 1) -or $result -ne [System.Windows.Forms.DialogResult]::OK)
Of course, you can replace Write-Warning with whatever you want, such as a message box.
PS: You can use the AddRange method to add an array of items to the $listBox collection, like so: [void] $listBox.Items.AddRange(#("server1", "server2", "server3"))

In PowerShell and Windows Forms, how to capture user-entered data using multiple dynamically created input controls

I have a script I need to use for multiple parameter data collection, as follows:
function Build-FormPanel($FormTitle){
Add-Type -Assembly System.Windows.Forms ## Load the Windows Forms assembly
## Create the main form
$form = New-Object Windows.Forms.Form
$form.FormBorderStyle = "FixedToolWindow"
$form.Text = $FormTitle
$form.AutoScroll = $True
$form.StartPosition = "CenterScreen"
$form.Width = 740 ; $form.Height = 480 # Make the form wider
#Add Buttons- ## Create the button panel to hold the OK and Cancel buttons
$buttonPanel = New-Object Windows.Forms.Panel
$buttonPanel.Size = New-Object Drawing.Size #(400,40)
$buttonPanel.Dock = "Bottom"
$cancelButton = New-Object Windows.Forms.Button
$cancelButton.Top = $buttonPanel.Height - $cancelButton.Height - 10; $cancelButton.Left = $buttonPanel.Width - $cancelButton.Width - 10
$cancelButton.Text = "Cancel"
$cancelButton.DialogResult = "Cancel"
$cancelButton.Anchor = "Right"
## Create the OK button, which will anchor to the left of Cancel
$okButton = New-Object Windows.Forms.Button
$okButton.Top = $cancelButton.Top ; $okButton.Left = $cancelButton.Left - $okButton.Width - 5
$okButton.Text = "Ok"
$okButton.DialogResult = "Ok"
$okButton.Anchor = "Right"
## Add the buttons to the button panel
$buttonPanel.Controls.Add($okButton)
$buttonPanel.Controls.Add($cancelButton)
## Add the button panel to the form
$form.Controls.Add($buttonPanel)
## Set Default actions for the buttons
$form.AcceptButton = $okButton # ENTER = Ok
$form.CancelButton = $cancelButton # ESCAPE = Cancel
return $form
}
$LeftMargin = 25
$BottomMargin = 30
$i = 0
$form = Build-FormPanel "Please update server configurations"
foreach($param in $hash){#Where $hash is an "dictionary" of key/value pairs
$k = $param.Key
$v = $param.Value
$lblValue = New-Object System.Windows.Forms.Label
$lblValue.Text = $k+":"
$lblValue.Top = 20*$i ; $lblValue.Left = $LeftMargin; $lblValue.Width=150 ;$lblValue.AutoSize = $true
$form.Controls.Add($lblValue) # Add to Form
#
$txtValue = New-Object Windows.Forms.TextBox
$txtValue.Top = 20*$i; $txtValue.Left = 160; $txtValue.Width = 320;
$txtValue.Text = $v
$form.Controls.Add($txtValue) # Add to Form
$i++
}
$form.Topmost = $True
$form.Add_Shown( { $form.Activate(); } )
$result = $form.ShowDialog()
if($result -eq "OK")
{
$j = 0;
foreach($param in $hash){
${"txtValue_$j"}.Text
$j++
}
}
else {Write-Host "Cancel"}
Basically, this works OK to display the form and inputs. But after submission, I am unable to capture all the user inputs. Only the last input value is captured, obviously because the variables get overwritten in the loop.
How can I achieve capturing the data as described?
The issue as you have mentioned is because of the its getting overwritten.
I can give you a logical set off.
YOu can use it in a loop and in the loop , you store all the data either in array or if its dynamic then you can use arraylist by using
New-Object System.Collections.ArrayList
. But recommended is to create PSCustomObject , store in that and add that to the arraylist each time.
Finally you can get the output captured in the arraylist.
Further you can try making the arraylist Global so that it will be available for the entire script.
Hope it helps.

Resources