PowerShell: How to display arrays in CheckListBox - arrays

I'm trying to make arrays which lead to different files on my computer. Then I want to display the arrays in a check box, so I can just click and open my files from there.
The array can be static, but would be nice if I could add new stuff dynamically.
$menulist.DataBindings.DefaultDataSourceUpdateMode = 0
$menulist.FormattingEnabled = $True
$System_Drawing_Point = New-Object System.Drawing.Point
$System_Drawing_Point.X = 12
$System_Drawing_Point.Y = 24
$menulist.Location = $System_Drawing_Point
$menulist.Name = "menulist"
$System_Drawing_Size = New-Object System.Drawing.Size
$System_Drawing_Size.Height = 328
$System_Drawing_Size.Width = 235
$TemplateArray = $template1,$template2;
$template1 = Get-ChildItem -Path C:\Users\$Env:USERNAME\Documents\test.tx
$template2 = Get-ChildItem -Path C:\Users\$Env:USERNAME\Documents\test2.txt
$menulist.Items.AddRange($TemplateArray)
$menulist.Size = $System_Drawing_Size
$menulist.TabIndex = 7

While we are still discussing what you need, I'll take a guess:
$TemplateArray = $template1,$template2;
$template1 = Get-ChildItem -Path C:\Users\$Env:USERNAME\Documents\test.tx
$template2 = Get-ChildItem -Path C:\Users\$Env:USERNAME\Documents\test2.txt
Notice above - $template1 and $template2 are used before they are declared. You probably get two empty strings in it. Instead, initialize variables first:
$template1 = Get-ChildItem -Path C:\Users\$Env:USERNAME\Documents\test.tx
$template2 = Get-ChildItem -Path C:\Users\$Env:USERNAME\Documents\test2.txt
$TemplateArray = $template1,$template2;

Related

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 timer : no action is launched

I want my timer to consult a file (consigne.csv) and display the "consigne" column according to the date I defined.
The data of this file would be regularly modified, hence the choice of the timer.
The form opens with the correct information but the information is not updated.
Thanks for your help.
Script :
Add-Type -AssemblyName System.Windows.Forms
Add-Type -AssemblyName System.Drawing
[System.Windows.Forms.Application]::EnableVisualStyles();
################ TIMER ##################################
$Timer = new-object System.Windows.forms.timer
$Timer.Interval = 1000
#Action.
$Timer.Add_Tick({
#Interval after boot.
$Timer.Interval = (5000)
#Command
$global:today = get-date -format "dd/MM/yy"
$global:DesktopPath = [System.Environment]::GetFolderPath([System.Environment+SpecialFolder]::Desktop)
$global:TxtConsigne= (import-csv "$DesktopPath\consigne.csv" -encoding UTF8 -delimiter ";")
$global:Message = ($TxtConsigne | where-object {$_.EXPIRATION -ge $today})
})
################ Form #####################################
#Form
$Form = New-Object System.Windows.Forms.Form
$Form.Location = New-Object System.Drawing.Point 0,570
$Form.Size = New-Object System.Drawing.Size(1920,260)
# Consigne
$Consigne = New-Object system.windows.Forms.Label
$Consigne.Location = New-Object System.Drawing.Point 210,20
$Consigne.Size = New-Object System.Drawing.Size(1500,220)
$Form.Controls.Add($Consigne)
################################################################################################################
# $DesktopPath = [System.Environment]::GetFolderPath([System.Environment+SpecialFolder]::Desktop)
# $TxtConsigne= (import-csv "$DesktopPath\consigne.csv" -encoding UTF8 -delimiter ";")
# $Message = ($TxtConsigne | where-object {$_.EXPIRATION -ge $today})
$Timer.Start()
if ($Message){
$Message | FOREACH{
$Mess += "- " + $_.Consigne + "`n"
$Consigne.Text = $Mess
}
$Mess=$null
#Affichage de la fenĂȘtre uniquement si consigne Ă  passer
[void]$Form.ShowDialog()
}
$Timer.Stop()
and content of consigne.csv
DATE;EXPIRATION;CONSIGNE; 12/09/20;15/09/20;Message 4;
11/09/20;11/09/20;Message 3; 10/09/20;11/09/20;Message 2;
10/09/20;10/09/20;Message 1;
You don't need all those $global:xxx variables in the Timer event handler; just one for the path to the CSV file would be enough. Also, it could do with a lesser scope: $script:xxx.
Then, you are comparing a date as formatted string in format dd/MM/yy which does not compare like you think, since the day comes earlier than the month. (it is a non-sortable date format).
Always try to compare datetime objects to other datetime objects.
Try:
Add-Type -AssemblyName System.Windows.Forms
Add-Type -AssemblyName System.Drawing
[System.Windows.Forms.Application]::EnableVisualStyles();
# the path to the csv file used in the timer event handler.
$csvPath = Join-Path -Path ([Environment]::GetFolderPath("Desktop")) -ChildPath 'consigne.csv'
################ Form #####################################
#Form
$Form = New-Object System.Windows.Forms.Form
$Form.Location = New-Object System.Drawing.Point 0,570
$Form.Size = New-Object System.Drawing.Size(1920,260)
# Consigne
$Consigne = New-Object System.Windows.Forms.Label
$Consigne.Location = New-Object System.Drawing.Point 210,20
$Consigne.Size = New-Object System.Drawing.Size(1500,200)
$Form.Controls.Add($Consigne)
################ TIMER ##################################
$Timer = New-Object System.Windows.forms.timer
$Timer.Enabled = $false # disabled at first
$Timer.Interval = 50 # initial timer interval
# Timer Action.
$Timer.Add_Tick({
$this.Interval = 5000 # inside the event handler, you can refer to the control with `$this`
$Consigne.Text = [string]::Empty # clear the label
# read the CSV file, get the items with an EXPIRATION date greater
# or equal to today and write the CONSIGNE messages to the label
(Import-Csv -Path $script:csvPath -Encoding UTF8 -Delimiter ";") |
Where-Object { [datetime]::ParseExact($_.EXPIRATION, 'dd/MM/yy', $null) -ge (Get-Date).Date } |
ForEach-Object {$Consigne.Text += "- {0}`r`n" -f $_.CONSIGNE }
})
# start the timer as soon as the form is shown
$Form.Add_Shown({$Timer.Enabled = $true; $Timer.Start()})
[void]$Form.ShowDialog()
# clean up the Timer and Form objects
$Timer.Dispose()
$Form.Dispose()

Run Multiple Script Instances

I work in an operations center with 10 screens on each computer. The main computer shows a lot of telemetry data and dashboards, graphs, animations, and a lot of other useful information via Chrome in a browser window. This is tedious to set up every time you reboot the computer, so I was very happy to find this original project: Chrome-Kiosk from https://alextomin.wordpress.com/2015/04/10/kiosk-mode-in-windows-chrome-on-multiple-displays/
It works very well as an auto starter in our operations center, so I am happy with that aspect of its functionality. But then I got to thinking: wouldn't it be useful if I could somehow temporarily display a webpage on the board, and have it close after a set time period so that the whole office can monitor an important event.
What I came up with is quite cool so far, and the people at work think that it is very useful, BUT, it needs more features. These are simple but I am stuck, I have been trying to figure this out for a while now. Below is the code that I am using.
Things that I can't get to work:
Kill Timer: I can get this to work by adding start-wait in front of the kill function that I have created, but that makes the looper.ps1 script sit and wait while the launcher.ps1 completes. How can I get the script to run without making the looper script stop until the other script finishes?
How can I track each new instance of Chrome that gets opened? Using the system ID does not seem to work, the best I can do is get-process and sort by chrome and the newest instance, which means that I can kill chrome instances that are younger than the timer value, but I am struggling to get this to work.
Any advice here would be appreciated.
SERVER SCRIPTS
I have put these into a folder called c:\scripts\ICVT
looper.ps1 - This will run constantly
Set-Location -Path C:\scripts\ICVT
while ($true) {
.\file_checker.ps1;
}
file_checker.ps1 - This is the script that looper runs. file_checker scans the folder for web.txt and mon.txt . Both must be present for the rest of the script to execute.
#Checks folder for web.txt and mon.txt . Both must be present for the rest of the script to execute
Set-Location -Path C:\scripts\ICVT
$a = Test-Path web.txt
$b = Test-Path mon.txt
if (($a -and $b -eq $True)) {
.\launcher.ps1
} else {
Write-Host "Scanning For Files"
}
Start-Sleep -Seconds 5
launcher.ps1 - This is just a modified version of the original script
$chromePath = 'C:\Program Files (x86)\Google\Chrome\Application\chrome.exe'
$chromeArguments = '--new-window'
$web = (Get-Content -Path web.txt)
$mon = (Get-Content -Path mon.txt)
$timer = (Get-Content -Path $timer.txt)
# if Window not moved (especially on machine start) - try increasing the
# delay.
$ChromeStartDelay = 5
Set-Location $PSScriptRoot
. .\HelperFunctions.ps1
Chrome-Kiosk $web -MonitorNum $mon
#Delete parameters after use
Start-Sleep -Seconds 5
del web.txt
del mon.txt
del timer.txt
Here is the GUI that creates the web.txt, timer.txt and mon.txt files over the network
Set-Location -Path \\networkpc\folder
Add-Type -AssemblyName System.Windows.Forms
[System.Windows.Forms.Application]::EnableVisualStyles()
# Hide PowerShell Console
Add-Type -Name Window -Namespace Console -MemberDefinition '
[DllImport("Kernel32.dll")]
public static extern IntPtr GetConsoleWindow();
[DllImport("user32.dll")]
public static extern bool ShowWindow(IntPtr hWnd, Int32 nCmdShow);
'
$consolePtr = [Console.Window]::GetConsoleWindow()
[Console.Window]::ShowWindow($consolePtr, 0)
function button ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11) {
$Form = New-Object "system.Windows.Forms.Form";
$Form.ClientSize = '653,436'
$Form.text = "Display Board URL Tool"
$Form.TopMost = $false
$form.StartPosition = [System.Windows.Forms.FormStartPosition]::CenterScreen;
$iconBytes = [Convert]::FromBase64String($iconBase64)
$stream = New-Object IO.MemoryStream($iconBytes, 0, $iconBytes.Length)
$stream.Write($iconBytes, 0, $iconBytes.Length);
$iconImage = [System.Drawing.Image]::FromStream($stream, $true)
$Form.Icon = [System.Drawing.Icon]::FromHandle((New-Object System.Drawing.Bitmap -Argument $stream).GetHIcon())
$Button1 = New-Object "system.Windows.Forms.Button";
$Button1.text = "Screen 1"
$Button1.width = 132
$Button1.height = 77
$Button1.location = New-Object "System.Drawing.Point"(8,14);
$Button1.Font = 'Microsoft Sans Serif,10'
$Button2 = New-Object "system.Windows.Forms.Button";
$Button2.text = "Screen 2"
$Button2.width = 132
$Button2.height = 77
$Button2.location = New-Object "System.Drawing.Point"(165,14);
$Button2.Font = 'Microsoft Sans Serif,10'
$Button3 = New-Object "system.Windows.Forms.Button";
$Button3.text = "Screen 3"
$Button3.width = 132
$Button3.height = 77
$Button3.location = New-Object "System.Drawing.Point"(326,14);
$Button3.Font = 'Microsoft Sans Serif,10'
$Button4 = New-Object "system.Windows.Forms.Button";
$Button4.text = "Screen 4"
$Button4.width = 132
$Button4.height = 77
$Button4.location = New-Object "System.Drawing.Point"(483,15);
$Button4.Font = 'Microsoft Sans Serif,10'
$WinForm1 = New-Object "system.Windows.Forms.Form";
$WinForm1.ClientSize = '653,400'
$WinForm1.text = "Form"
$WinForm1.TopMost = $false
$Button5 = New-Object "system.Windows.Forms.Button";
$Button5.text = "Screen 5"
$Button5.width = 132
$Button5.height = 77
$Button5.location = New-Object "System.Drawing.Point"(8,117);
$Button5.Font = 'Microsoft Sans Serif,10'
$Button6 = New-Object "system.Windows.Forms.Button";
$Button6.text = "Screen 6"
$Button6.width = 132
$Button6.height = 77
$Button6.location = New-Object "System.Drawing.Point"(165,119);
$Button6.Font = 'Microsoft Sans Serif,10'
$Button7 = New-Object "system.Windows.Forms.Button";
$Button7.text = "Screen 7"
$Button7.width = 132
$Button7.height = 77
$Button7.location = New-Object "System.Drawing.Point"(326,119);
$Button7.Font = 'Microsoft Sans Serif,10'
$Button8 = New-Object "system.Windows.Forms.Button";
$Button8.text = "Screen 8"
$Button8.width = 132
$Button8.height = 77
$Button8.location = New-Object "System.Drawing.Point"(483,119);
$Button8.Font = 'Microsoft Sans Serif,10'
$Button9 = New-Object "system.Windows.Forms.Button";
$Button9.text = "Screen 9"
$Button9.width = 132
$Button9.height = 77
$Button9.location = New-Object "System.Drawing.Point"(9,220);
$Button9.Font = 'Microsoft Sans Serif,10'
$Button10 = New-Object "system.Windows.Forms.Button";
$Button10.text = "Screen 10"
$Button10.width = 132
$Button10.height = 77
$Button10.location = New-Object "System.Drawing.Point"(165,220);
$Button10.Font = 'Microsoft Sans Serif,10'
$Button11 = New-Object "system.Windows.Forms.Button";
$Button11.text = "Screen 11"
$Button11.width = 132
$Button11.height = 77
$Button11.location = New-Object "System.Drawing.Point"(326,220);
$Button11.Font = 'Microsoft Sans Serif,10'
$TextBox1 = New-Object "system.Windows.Forms.TextBox";
$TextBox1.multiline = $false
$TextBox1.width = 400
$TextBox1.height = 20
$TextBox1.location = New-Object "System.Drawing.Point"(220,314);
$TextBox1.Font = 'Microsoft Sans Serif,10'
$TextBox2 = New-Object "system.Windows.Forms.TextBox";
$TextBox2.multiline = $false
$TextBox2.width = 50
$TextBox2.height = 20
$TextBox2.location = New-Object "System.Drawing.Point"(220,348);
$TextBox2.Font = 'Microsoft Sans Serif,10'
$Label1 = New-Object "system.Windows.Forms.Label";
$Label1.text = "Display Time (Minutes)"
$Label1.AutoSize = $true
$Label1.width = 25
$Label1.height = 10
$Label1.location = New-Object "System.Drawing.Point"(15,348);
$Label1.Font = 'Microsoft Sans Serif,10'
$Label2 = New-Object "system.Windows.Forms.Label";
$Label2.text = "URL for Timed Monitoring"
$Label2.AutoSize = $true
$Label2.width = 25
$Label2.height = 10
$Label2.location = New-Object "System.Drawing.Point"(15,314);
$Label2.Font = 'Microsoft Sans Serif,10'
$Button12 = New-Object "system.Windows.Forms.Button";
$Button12.text = "Kill Current Screen"
$Button12.width = 132
$Button12.height = 77
$Button12.location = New-Object "System.Drawing.Point"(483,220);
$Button12.Font = 'Microsoft Sans Serif,10'
$Button12.ForeColor = "#d0021b"
$Form.controls.AddRange(#($Button1,$Button2,$Button3,$Button4,$Button5,$Button6,$Button7,$Button8,$Button9,$Button10,$Button11,$TextBox1,$TextBox2,$Label1,$Label2,$Button12))
############# This is when you have to close the form after getting values
$eventHandler1 = [System.EventHandler]{
$textBox1.Text;
$textBox2.Text;
############# Create mon.txt per button
$text = '1'
$text | Set-Content 'mon.txt'
$form.Close();};
$eventHandler2 = [System.EventHandler]{
$textBox1.Text;
$textBox2.Text;
############# Create mon.txt per button
$text = '2'
$text | Set-Content 'mon.txt'
$form.Close();};
$eventHandler3 = [System.EventHandler]{
$textBox1.Text;
$textBox2.Text;
############# Create mon.txt per button
$text = '3'
$text | Set-Content 'mon.txt'
$form.Close();};
$eventHandler4 = [System.EventHandler]{
$textBox1.Text;
$textBox2.Text;
############# Create mon.txt per button
$text = '4'
$text | Set-Content 'mon.txt'
$form.Close();};
$eventHandler5 = [System.EventHandler]{
$textBox1.Text;
$textBox2.Text;
############# Create mon.txt per button
$text = '5'
$text | Set-Content 'mon.txt'
$form.Close();};
$eventHandler6 = [System.EventHandler]{
$textBox1.Text;
$textBox2.Text;
############# Create mon.txt per button
$text = '6'
$text | Set-Content 'mon.txt'
$form.Close();};
$eventHandler7 = [System.EventHandler]{
$textBox1.Text;
$textBox2.Text;
############# Create mon.txt per button
$text = '7'
$text | Set-Content 'mon.txt'
$form.Close();};
$eventHandler8 = [System.EventHandler]{
$textBox1.Text;
$textBox2.Text;
############# Create mon.txt per button
$text = '8'
$text | Set-Content 'mon.txt'
$form.Close();};
$eventHandler9 = [System.EventHandler]{
$textBox1.Text;
$textBox2.Text;
############# Create mon.txt per button
$text = '9'
$text | Set-Content 'mon.txt'
$form.Close();};
$eventHandler10 = [System.EventHandler]{
$textBox1.Text;
$textBox2.Text;
############# Create mon.txt per button
$text = '10'
$text | Set-Content 'mon.txt'
$form.Close();};
$eventHandler11 = [System.EventHandler]{
$textBox1.Text;
$textBox2.Text;
############# Create mon.txt per button
$text = '11'
$text | Set-Content 'mon.txt'
$form.Close();};
$eventHandler12 = [System.EventHandler]{
$textBox1.Text;
$textBox2.Text;
############# Kill the last Screen that was launched
$text = '1'
$text | Set-Content 'mon.txt'
$form.Close();};
$button1.Add_Click($eventHandler1) ;
$button2.Add_Click($eventHandler2) ;
$button3.Add_Click($eventHandler3) ;
$button4.Add_Click($eventHandler4) ;
$button5.Add_Click($eventHandler5) ;
$button6.Add_Click($eventHandler6) ;
$button7.Add_Click($eventHandler7) ;
$button8.Add_Click($eventHandler8) ;
$button9.Add_Click($eventHandler9) ;
$button10.Add_Click($eventHandler10) ;
$button11.Add_Click($eventHandler11) ;
$button12.Add_Click($eventHandler12) ;
#############Add controls to all the above objects defined
$form.Controls.Add($button1);
$form.Controls.Add($button2);
$form.Controls.Add($button3);
$form.Controls.Add($button4);
$form.Controls.Add($button5);
$form.Controls.Add($button6);
$form.Controls.Add($button7);
$form.Controls.Add($button8);
$form.Controls.Add($button9);
$form.Controls.Add($button10);
$form.Controls.Add($button11);
$form.Controls.Add($textLabel1);
$form.Controls.Add($textLabel2);
$form.Controls.Add($textBox1);
$form.Controls.Add($textBox2);
$ret = $form.ShowDialog();
#################return values
return $textBox1.Text, $textBox2.Text
}
#Creates the 3 txt files for web, mon and timer
$return = button "Monitoring Screen Selector" "Enter URL" "Enter Screen # from 1 to 11"
if ($return[0] -ne "") {
$return[0] > web.txt
}
if ($return[0] -eq "") {
exit
}
if ($return[1] -ne "") {
$return[1] > timer.txt
}
if ($return[1] -eq "") {
exit
}
#multiply by 60 to get minutes
if (Test-Path timer.txt) {
if ((Get-Item timer.txt).Length -gt 0kb) {
$result = Get-Content timer.txt
60 * $result > timer.txt
}
}
In order to do a lot of that functionality, you first need to get a Process Id for the Chrome window. In order to do that we need to make a few changes to the Chrome-Kiosk function to return that information. First I change the Start-Process to include the -PassThru parameter. That way I get a process object that I can get the PID from. Another edit to Get-Process to use the Id instead of "chrome", and finally we Write-Output the $Process object.
function Chrome-Kiosk($Url, $MonitorNum)
{
Write-Output "starting chrome $Url , monitor: $MonitorNum"
$Process = Start-Process $chromePath "$chromeArguments $Url" -PassThru
Start-Sleep -Seconds $ChromeStartDelay
$window = (Get-Process -Id $Process.Id | where MainWindowHandle -ne ([IntPtr]::Zero) | select -First 1).MainWindowHandle
$WinAPI::ShowWindow($window, [Tomin.Tools.KioskMode.Enums.ShowWindowCommands]::Restore)
$Helpers::MoveToMonitor($window, $MonitorNum)
$Helpers::SendKey($window, '{F11}')
Start-Sleep -Seconds $ChromeStartDelay
Write-Output $Process
}
The rest of the work is to modify the launcher script to have a kill timer. Essentially we pull the .txt deletion portion back into your file_checker.PS1 script so that we can launch the launcher.ps1 as a new self contained PowerShell process.
file_checker.PS1
#Checks folder for web.txt and mon.txt . Both must be present for the rest of the script to execute
Set-Location -Path C:\scripts\ICVT
$a = Test-Path web.txt
$b = Test-Path mon.txt
IF (($a -and $b -eq $True)) {
. .\launcher.ps1
#Delete parameters after use
del web.txt
del mon.txt
del timer.txt
}
else {
Write-Host "Scanning For Files"
}
Start-Sleep -Seconds 5
By using the modified Chrome-Kiosk function, where it returns the Process object of the Chrome window, we can now have the launcher script launch, sleep, and then kill only the Chrome window that we launched:
launcher.ps1
$chromePath = 'C:\Program Files (x86)\Google\Chrome\Application\chrome.exe'
$chromeArguments = '--new-window'
$web = (Get-Content -Path web.txt)
$mon = (Get-Content -Path mon.txt)
$timer = (Get-Content -Path $timer.txt)
# if Window not moved (especially on machine start) - try increasing the
# delay.
$ChromeStartDelay = 5
Set-Location $PSScriptRoot
. .\HelperFunctions.ps1
$Process = Chrome-Kiosk $web -MonitorNum $mon
#Wait some period of time
Start-Sleep -Seconds 60
#Kill the process
Stop-Process $Process

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)
# [...]

How to limit number of lines in a TextBox and split the lines to strings?

How can I achieve that the input of the TextBox with multilines is only possible to a specific number of lines e.g. 10 lines only.
Further I want to get the input of each line and write each line to a separate variable to work later with this variables. It would be nice if the user gets a messagebox with warning that only 10 lines are possible.
Any help would be appreciated
[reflection.assembly]::loadwithpartialname("System.Windows.Forms") | Out-Null
[reflection.assembly]::loadwithpartialname("System.Drawing") | Out-Null
#Assembly PresentationFramework wird geladen
Add-Type -AssemblyName PresentationFramework
$form = New-Object System.Windows.Forms.Form
$form.StartPosition = 'CenterScreen' #Formstartposition Zentrum
$form.Size = New-Object System.Drawing.Size(500,400)
$textBox = New-Object System.Windows.Forms.TextBox
$textBox.DataBindings.DefaultDataSourceUpdateMode = 0
$textBox.Location = New-Object System.Drawing.Point((110),(90))
$textBox.Size = New-Object System.Drawing.Size(288,150)
$textBox.TabIndex = 0
$textBox.Multiline =$true
$form.Controls.Add($textBox)
$form.ShowDialog() | Out-Null
1.Get Line count on the event of TextChanged.
2.Turn the iList into a ArrayList.
3.Get the difference from max line to current line count.
4.Remove the range from the ArrayList.
5.Set the content of the Textbox to the Arraylist
6.Set the curser to end of textbox.
$TextboxMaxLines = 10
$textBox.Add_TextChanged({
If($textBox.Lines.Count -gt $TextboxMaxLines){
[System.Collections.ArrayList]$AL = $textBox.Lines
[int]$LC = ($textBox.Lines.Count - $TextboxMaxLines)
$Al.RemoveRange($TextboxMaxLines, $LC)
$textbox.Lines = $AL
$textbox.SelectionStart = ($textbox.Text.Length)
$textbox.SelectionLength = 0
}
})

Resources