Don’t display Cancel in PowerShell script result - powershell

I have the following PowerShell script which displays file dialog to select a txt file. If user cancels dialog then provide a multiline text box
function GetDetails() {
Add-Type -AssemblyName System.Windows.Forms;
$browser = New-Object System.Windows.Forms.OpenFileDialog;
$browser.Filter = "txt (*.txt)|*.txt";
$browser.InitialDirectory = "E:\";
$browser.Title = "select txt file";
$browserResult = $browser.ShowDialog();
if($browserResult -eq [System.Windows.Forms.DialogResult]::OK) {
$nfoFile = $browser.FileName;
if([string]::IsNullOrWhiteSpace($txtFile)) {
return GetFromForm;
}
$txtFile = [System.IO.Path]::ChangeExtension($nfoFile, ".dac");
$txtFile = $temp + [System.IO.Path]::GetFileName($txtFile);
$exeArgs = "-f -S `"$txtFile`" -O `"$txtFile`"";
Start-Process $anExe -ArgumentList $exeArgs -Wait;
$result = Get-Content $txtFile | Out-String;
$browser.Dispose();
return $result;
} else {
return GetFromForm;
}
}
function GetFromForm(){
Add-Type -AssemblyName System.Windows.Forms;
$form = New-Object System.Windows.Forms.Form;
$form.Width = 800;
$form.Height = 600;
$txtBox = New-Object System.Windows.Forms.TextBox;
$txtBox.Multiline = $true;
$txtBox.AcceptsReturn = $true;
$txtBox.AcceptsTab = $true;
$txtBox.Visible = $true;
$txtBox.Name = "txtName";
$txtBox.Width = 760;
$txtBox.Height = 660;
$form.Controls.Add($txtBox);
$form.ShowDialog();
 
$form.Dispose();
return $txtBox.Text;
}
$desc = GetDetails;
cls;
Write-Host $desc;
Here I have two issues:
In Write-Host $desc, prints also Cancel hereiswhateverstrimg string if user chose to cancel dialog. How to avoid that?
If I run script in ISE, the generated form (in second function) will be always behind ISE even I call ShowDialog(), I expected to behave as modal dialog. It’s normal or there is a fix for this ?

Ok, there are a few changes that I made for efficiency and a few for functionality. Read the comments in the script for the explanations.
# Just add types once. There is no need to add the types in each function.
Add-Type -AssemblyName System.Windows.Forms;
Add-Type -AssemblyName System.Drawing
function GetDetails() {
$browser = New-Object System.Windows.Forms.OpenFileDialog;
$browser.Filter = "txt (*.txt)|*.txt";
$browser.InitialDirectory = "E:\";
$browser.Title = "select txt file";
$browserResult = $browser.ShowDialog();
# Combined the if statements
if($browserResult -eq [System.Windows.Forms.DialogResult]::OK -and [string]::IsNullOrWhiteSpace($txtFile) -ne $true) {
$nfoFile = $browser.FileName;
$txtFile = [System.IO.Path]::ChangeExtension($nfoFile, ".dac");
$txtFile = $temp + [System.IO.Path]::GetFileName($txtFile);
$exeArgs = "-f -S `"$txtFile`" -O `"$txtFile`"";
Start-Process $anExe -ArgumentList $exeArgs -Wait;
# The Raw flag should return a string
$result = Get-Content $txtFile -Raw;
$browser.Dispose();
return $result;
}
# No need for else since the if statement returns
return GetFromForm;
}
function GetFromForm(){
$form = New-Object System.Windows.Forms.Form;
$form.Text = 'Adding Arguments'
$form.Size = New-Object System.Drawing.Size(816,600)
$form.StartPosition = 'CenterScreen'
# Added a button
$OKButton = New-Object System.Windows.Forms.Button
$OKButton.Location = New-Object System.Drawing.Point(585,523)
$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)
$txtBox = New-Object System.Windows.Forms.TextBox;
$txtBox.Multiline = $true;
$txtBox.AcceptsReturn = $true;
$txtBox.AcceptsTab = $true;
$txtBox.Visible = $true;
$txtBox.Name = "txtName";
$txtBox.Size = New-Object System.Drawing.Size(660,500)
$form.Controls.Add($txtBox);
# Needed to force it to show on top
$form.TopMost = $true
# Select the textbox and activate the form to make it show with focus
$form.Add_Shown({$txtBox.Select(), $form.Activate()})
# Finally show the form and assign the ShowDialog method to a variable (this keeps it from printing out Cancel)
$result = $form.ShowDialog();
# If the user hit the OK button return the text in the textbox
if ($result -eq [System.Windows.Forms.DialogResult]::OK)
{
return $txtBox.Text
}
}
$desc = GetDetails;
cls;
Write-Host $desc;
You can see reference material here: https://learn.microsoft.com/en-us/powershell/scripting/samples/creating-a-custom-input-box?view=powershell-6

You need to suppress output of $form.ShowDialog() in GetFromForm:
$form.ShowDialog()|out-null
Powershell will add to a return value everything that was outputted to host within a function/commandlet.
Regarding your second issue - see this answer
And please do not use semi-colon at an end of line. This is not C# and will confuse you into thinking that the line is ended here but it's not quite true.

Related

PowerShell Add_Click in foreach loop

What I am trying to accomplish is to create buttons that launch exe files in a certain directory when clicked, but when I try using a foreach loop to create a few buttons, all of the buttons just launch the file the last button is supposed to launch.
Add-Type -AssemblyName System.Windows.Forms
Add-Type -AssemblyName System.Drawing
$form = New-Object System.Windows.Forms.Form
$form.Text = 'Main Window'
$form.Size = New-Object System.Drawing.Size(600,400)
$flp = New-Object System.Windows.Forms.FlowLayoutPanel
$flp.Location = New-Object System.Drawing.Point(0,0)
$flp.Height = $form.Height
$flp.Width = $form.Width
$form.Controls.Add($flp)
$files = Get-ChildItem "$home\Downloads" -Include *.exe -Name
foreach ($file in $files){
$button = New-Object System.Windows.Forms.Button
$flp.Controls.Add($button)
$button.Width = 100
$button.Height = 50
$button.Text = $file
$button.Add_Click{
Start-Process -FilePath "$home\Downloads\$file"
}
}
$form.Topmost = $true
$form.ShowDialog()
Whatever I'm doing is probably pretty stupid, so I was just looking for any alternatives or solutions to this other than to just hard code everything.
It is likely that you need to use .GetNewClosure() ScriptBlock method so that each script block (button click event) holds the current value of the $file variable at the moment of enumeration.
Example of what this means:
$blocks = foreach($i in 0..5) {
{ "hello $i" }
}
& $blocks[0] # => hello 5
& $blocks[1] # => hello 5
$blocks = foreach($i in 0..5) {
{ "hello $i" }.GetNewClosure()
}
& $blocks[0] # => hello 0
& $blocks[1] # => hello 1
In that sense, and assuming this is the issue, the following should work:
foreach ($file in $files) {
$button = New-Object System.Windows.Forms.Button
$button.Width = 100
$button.Height = 50
$button.Text = $file
$thisEvent = {
Start-Process -FilePath "$home\Downloads\$file"
}.GetNewClosure()
$button.Add_Click($thisEvent)
$flp.Controls.Add($button)
}
A nice alternative to having a need to use .GetNewClosure() can be seen on this answer. The .Tag property of the Button can be used to store the information of the file's path which then can be used on the button's .Click event:
foreach ($file in $files) {
$button = New-Object System.Windows.Forms.Button
$button.Width = 100
$button.Height = 50
$button.Text = $file
# Store the file's path in the Tag's property of this Button
$button.Tag = "$home\Downloads\$file"
$button.Add_Click({
Start-Process -FilePath $this.Tag
})
$flp.Controls.Add($button)
}

Powershell Update an already active form

So I'm trying to insert red dots into some kind of map, which I do with creating new pictureboxes after reading their coordinates out of an txt-file.
My goal now is to remove or create new boxes, while the form.ShowDialog() was already used.
I found a way of closing the whole form and running everything again, which kind of works but is very ugly in my opinion. Was wondering if there is another way of checking if new coordinates have been added to the txt-file or removed and if that is the case, creating or removing the corresponding boxes.
(I tried .Refresh but that seems to do nothing at all)
function MakeForm {
[void][reflection.assembly]::LoadWithPartialName("System.Windows.Forms")
[System.Windows.Forms.Application]::EnableVisualStyles();
$form = new-object Windows.Forms.Form
$form.Text = "Image Viewer"
$form.Width = 270;
$form.Height = 270;
$path = (Get-Item "Insert Path here")
$path2 = (Get-Item "Insert Path here")
$img = [System.Drawing.Image]::Fromfile($path);
$img2 = [System.Drawing.Image]::FromFile($path2);
$test = Get-Content -Path "Insert Path here"
$test
$pictureBoxes = New-Object 'System.Collections.Generic.List[Windows.Forms.PictureBox]'
$i=1
Foreach ($Line in $test){
if ($Line -like "*in 2*"){
$i++
$Split1= $Line.Split(" ")
$x=$Split1[5]
$y=$Split1[6]
$pictureBox = New-Object Windows.Forms.PictureBox
$pictureBox.Width = $img.Size.Width;
$pictureBox.Height = $img.Size.Height;
$pictureBox.Location = New-object System.Drawing.Size($x,$y)
$pictureBox.Image = $img;
$form.Controls.Add($pictureBox)
$pictureBoxes.Add($pictureBox)
Write-Host $pictureBoxes[$i]
$form.Add_Shown( { $form.Activate() } )
}
}
$pictureBox20 = New-Object Windows.Forms.PictureBox
$pictureBox20.Width = $img2.Size.Width;
$pictureBox20.Height = $img2.Size.Height;
$pictureBox20.Image = $img2;
$form.Controls.Add($pictureBox20)
$button1 = New-Object System.Windows.Forms.Button
$button1.Width=25
$button1.Height=223
$button1.Location = New-Object System.Drawing.Point(223,0)
$form.Controls.Add($button1)
$button1.Add_Click({
#Button for removing a box for testing purposes
$form.Controls.Remove($pictureBoxes[3])
})
$button2 = New-Object System.Windows.Forms.Button
$button2.Width=25
$button2.Height=223
$button2.Location = New-Object System.Drawing.Point(260,0)
$form.Controls.Add($button2)
$button2.Add_Click({
#This Button should refresh the whole form, if possible without doing everything again
$form.Close()
$form.Dispose()
MakeForm
})
$form.Add_Shown( { $form.Activate() } )
$form.ShowDialog()
}
MakeForm

How to display picturebox with GUI in the windows form using Powershell?

I want to show the picturebox, but it does not show once I use form.show(). But if I change to Form.showdialog, the picturebox will show but, the process can not continue until I close the GUI. The picture box show but it does not moving, it stuck like picture.
Function Handling
{
$Form.Close()
$Form.Dispose()
$Form = New-Object system.Windows.Forms.Form
$Form.ControlBox = $true
$Form.BackColor = "#d0021b"
$Form.WindowState = "Maximized"
$Form.TopMost = $false
[void]$Form.Show()
# Message Box
[System.Windows.MessageBox]::Show("OK", "[Error]", "0", "Error")
$ExitCode = "1"
if($ExitCode -ne "107A")
{
$Form.Close()
$Form.Dispose()
Exit
}
else{
$Form.Close()
$Form.Dispose()
Exit
}
}
Add-Type -AssemblyName System.Windows.Forms
[System.Windows.Forms.Application]::EnableVisualStyles()
$Form = New-Object system.Windows.Forms.Form
$form.BackgroundImageLayout = 'Center'
$Form.WindowState = 'Maximized'
$Form.BackColor = "#ffffff"
$file2 = Get-ChildItem -Path "D:\3.png"
$cover = [Drawing.Image]::FromFile($file2)
$form.BackgroundImage = $img2
[reflection.assembly]::LoadWithPartialName("System.Windows.Forms")
$file = (get-item 'D:\6.gif')
$img = [System.Drawing.Image]::Fromfile($file)
$pictureBox = new-object Windows.Forms.PictureBox
$pictureBox.Image = $img
$pictureBox.SizeMode = "Autosize"
$pictureBox.Anchor = "Bottom, left"
$Form.controls.add($pictureBox)
[reflection.assembly]::LoadWithPartialName("System.Windows.Forms")
$file3 = (get-item 'D:\6.gif')
$img3 = [System.Drawing.Image]::Fromfile($file3)
$pictureBox2 = new-object Windows.Forms.PictureBox
$pictureBox2.Image = $img3
$pictureBox2.SizeMode = "Autosize"
$pictureBox2.Anchor = "Bottom, right"
$Form.controls.add($pictureBox2)
$form.Show()
Write-Host "next process"
####
# some process
###
Start-Sleep -s 2
Handling
Anyone can give me idea please. Really appreciate for your help. Thank you.
Can you try like this? Put your background operation in place of Start-Sleep -s 2 and the gif file still show moving.
$Form = New-Object system.Windows.Forms.Form
$Form.Location= New-Object System.Drawing.Size(100,100)
$Form.Size= New-Object System.Drawing.Size(550,170)
$Form.StartPosition = "Manual"
$Form.Visible=$false
$Form.Enabled = $true
$Form.Add_Shown({$Form.Activate()})
[reflection.assembly]::LoadWithPartialName("System.Windows.Forms")
$file = (get-item 'D:\6.gif')
$img = [System.Drawing.Image]::Fromfile($file);
[System.Windows.Forms.Application]::EnableVisualStyles();
$pictureBox = new-object Windows.Forms.PictureBox
$pictureBox.Location = New-Object System.Drawing.Size(0,1)
$pictureBox.Size = New-Object System.Drawing.Size($img.Width,$img.Height)
$pictureBox.Image = $img
$Form.controls.add($pictureBox)
$WaitForm.Topmost = $True
$rs = [Management.Automation.Runspaces.RunspaceFactory]::CreateRunspace()
$rs.Open()
$rs.SessionStateProxy.SetVariable("Form", $Form)
$data = [hashtable]::Synchronized(#{text=""})
$rs.SessionStateProxy.SetVariable("data", $data)
$p = $rs.CreatePipeline({ [void] $Form.ShowDialog()})
$p.Input.Close()
$p.InvokeAsync()
## Enter the rest of your script here while you want the form to display
Start-Sleep -s 2
$WaitForm.close()

Using PowerShell For Developing A Notification For An RSS News Alert

I have this basic code that works fine for simple text message alert. Now, it would be handy to connect this script to alert the user whenever there is a new RSS feed from our ITS alert system.
[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
$objNotifyIcon = New-Object System.Windows.Forms.NotifyIcon
$objNotifyIcon.Icon = [System.Drawing.SystemIcons]::Information
$objNotifyIcon.BalloonTipIcon = "Info"
$text = 'This is just a text'
$objNotifyIcon.BalloonTipText = $text
$objNotifyIcon.BalloonTipTitle = "Tip Title"
$objNotifyIcon.Visible = $True
$objNotifyIcon.ShowBalloonTip(30000)
Here is an idea for auto-close:
Function Get-BalloonTip {
[CmdletBinding()]
Param (
[Parameter(Mandatory=$true)]$Text,
[Parameter(Mandatory=$true)]$Title,
$Icon = 'Info',
$Timeout = $10000
)
Process {
Add-Type -AssemblyName System.Windows.Forms
If ($PopUp -eq $null) {
$PopUp = New-Object System.Windows.Forms.NotifyIcon
}
$Path = Get-Process -Id $PID | Select-Object -ExpandProperty Path
$PopUp.Icon = [System.Drawing.Icon]::ExtractAssociatedIcon($Path)
$PopUp.BalloonTipIcon = $Icon
$PopUp.BalloonTipText = $Text
$PopUp.BalloonTipTitle = $Title
$PopUp.Visible = $true
$PopUp.ShowBalloonTip($Timeout)
Start-Sleep 5
$PopUp.Visible = $false
} # End of Process
} # End of Function
Get-BalloonTip -Text "Hello" "Check This out"

PS Forms - Unhandled Exception using BREAK in ForEach loop

Following on from a previous question
The actual code is a lot more complex, but the following is working example of my loop:
Add-Type -AssemblyName System.Windows.Forms
$source = '\\servera\files'
$destination = '\\server b\files'
$form = New-Object System.Windows.Forms.Form
$CopyOutput = New-Object System.Windows.Forms.Label
$CopyOutput.Location = '10,15'
$CopyOutput.Size = '350,20'
$form.Text = "$DomainName Folder/Archive Copy"
$form.Size = '380,130'
$form.CancelButton = $ExitButton
$form.Add_FormClosing({
$script:CancelLoop = $true
})
$StartButton = New-Object System.Windows.Forms.Button
$StartButton.Name = 'StartButton'
$StartButton.Location = '10,50'
$StartButton.Size = '75,23'
$StartButton.Text = 'Start Copy'
$StartButton.Enabled = $true
$PauseButton = New-Object System.Windows.Forms.Button
$PauseButton.Location = '100,50'
$PauseButton.Size = '75,23'
$PauseButton.Text = 'Pause Copy'
$PauseButton.Enabled = $true
$StopButton = New-Object System.Windows.Forms.Button
$StopButton.Location = '190,50'
$StopButton.Size = '75,23'
$StopButton.Text = 'Stop Copy'
$StopButton.Enabled = $true
$ExitButton = New-Object System.Windows.Forms.Button
$ExitButton.Name = 'ExitButton'
$ExitButton.Location = '280,50'
$ExitButton.Size = '75,23'
$ExitButton.Text = 'Exit'
$ExitButton.DialogResult = [System.Windows.Forms.DialogResult]::Cancel
$form.Controls.AddRange(#($StartButton,$PauseButton,$StopButton,$ExitButton,$CopyOutput))
$StopButton.Add_Click({
$script:PauseToggle = $false
$script:CancelLoop = $true
})
$PauseButton.Add_Click({
# Boolean change value to true/false
$script:PauseToggle = !$script:PauseToggle
})
$StartButton.Add_Click({
$script:CancelLoop = $false
$script:PauseToggle = $false
$StopButton.Enabled = $true
$StartButton.Enabled = $false
Get-ChildItem -LiteralPath $Source -Recurse -File | ForEach {
Copy-Item -LiteralPath $_.FullName -Destination $Destination
$CopyOutput.Text = ('Copying' + $_.FullName)
[System.Windows.Forms.Application]::DoEvents()
If($script:CancelLoop -eq $true) {
$CopyOutput.Text = 'Cancel copy'
#Exit the loop
Break;
}
If ($script:PauseToggle) {
$CopyOutput.Text = 'Paused'
Do {
[System.Windows.Forms.Application]::DoEvents()
} Until (!$script:PauseToggle)
}
}
$CancelButton.Enabled = $false
$StartCopyButton.Enabled = $true
})
$form.ShowDialog()
$form.Dispose()
All the articles I can find say that this code should work ok - can anyone advise how to avoid the unhandled exception which results when the "Stop" button is pressed?
All the articles I can find say that this code should work ok - can anyone advise how to avoid the unhandled exception which results when the "Stop" button is pressed?
From what I can see in my research, this is an issue with using BREAK in forms. I did however find a workaround from here
Using this commandlet will stop the current pipeline instead of using BREAK
Filter Stop-Pipeline {
$sp = { Select-Object -First 1 }.GetSteppablePipeline($MyInvocation.CommandOrigin)
$sp.Begin($true)
$sp.Process(0)
}