Add Button to NotifyIcon - powershell

I want to see a little notification icon to indicate that the script I wrote is still active (both the script and displying the icon works). But I need a button within the context menu of the icon to stop the script immediately. And that's the part where my problem is:
$objNotifyIcon = New-Object System.Windows.Forms.NotifyIcon
$objContextMenu = New-Object System.Windows.Forms.ContextMenu
$ExitMenuItem = New-Object System.Windows.Forms.MenuItem
$ExitMenuItem.add_Click({
echo stoped
$continue = $False
$objNotifyIcon.visible = $False
})
$objContextMenu.MenuItems.Add($ExitMenuItem) | Out-Null
$objNotifyIcon.ContextMenu = $objContextMenu
$objNotifyIcon.Visible = $True
The script itself is longer, this is just the relevant part. If I run it from PowerShell ISE it works just fine. When I run it from a .bat file with
powershell .\myscript.ps1
the context menu is not working anymore.

This is just a wild guess, but try running the script in Single Thread Apartment mode:
powershell -STA -File .\myscript.ps1

Related

Can I change powershell version to 7 with "Run PowerShell script" action on Power Automate Desktop?

Can I change powershell version to 7 on "Run PowerShell script" action?
If I can, how?
I know this post but I couldn't find if I can change the version PAD execute.
https://powerusers.microsoft.com/t5/Power-Automate-Desktop/Powershell-and-other-script-SUPPORTED-VERSION/td-p/1501322
I already installed powershell version7.
I'd like to use "-UseQuotes" option on "Export-Csv".
FYI, my PSVersion is here.
PSVersion 5.1.19041.1320
Thank you,
I also checked registry about PAD but There is noregistry to manage powershell
This is a bit ugly but it will get the job done.
I have this very basic flow, it just runs the PS script and outputs the results in a message box.
Throw this into the PowerShell task and then view the output.
$psVersion = (Get-Host).Version.Major
if ($psVersion -ne 7) {
$processInfo = New-Object System.Diagnostics.ProcessStartInfo
$processInfo.FileName = "C:\Program Files\PowerShell\7\pwsh.exe"
$processInfo.RedirectStandardError = $true
$processInfo.RedirectStandardOutput = $true
$processInfo.UseShellExecute = $false
$processInfo.Arguments = $MyInvocation.MyCommand.Definition
$process = New-Object System.Diagnostics.Process
$process.StartInfo = $processInfo
$process.Start() | Out-Null
$process.WaitForExit()
$stdout = $process.StandardOutput.ReadToEnd()
$stderr = $process.StandardError.ReadToEnd()
Write-Host $stdout
} else {
# Your logic goes here!
Write-Host "PowerShell Version = $psVersion"
}
You should get this ...
Essentially, it's checking for the version and if it's not version 7, it will launch PowerShell 7 and then retrieve the output.
Take note of the file path for PS7, you may need to change that or you may be able to simplify it. I've gone with the full path to make sure it works.

Why does my script work in powershell ISE but not when it is in a .ps1 file?

It used to work but now it suddently terminates less than a second after being opened. I set the execution policy to unrestricted & re-installed Windows yet it still does not work...
The .ps1 shows up for 1 second in task manager before windows security health service when it's run using .vbs & then disappears: https://i.imgur.com/VNX7NKx.png
Here's the script (its purpose is to show notification messages):
[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
$notifyobj = New-Object System.Windows.Forms.NotifyIcon
$notifyobj.icon = "c:/users/work/Pictures/icon.ico"
$notifyobj.BalloonTipTitle = "New Message"
$notifyobj.BalloonTipText = "C"
$notifyobj.Visible = $True
$notifyobj.ShowBalloonTip(1000)
$notifyobj.Dispose()
More info on this thread.
Odd for sure, and there should have been really no reason you should have had to reinstall Windows for your effort. So, something else is impacting this. Hard to say what though.
Try this version to see if you have any / more success. It takes a different approach, but works on my systems.
Function Show-Notification
{
Param
(
[string]$MessageType,
[string]$MessageText,
[string]$MessageTitle
)
#load Windows Forms and drawing assemblies
[reflection.assembly]::loadwithpartialname("System.Windows.Forms") | Out-Null
[reflection.assembly]::loadwithpartialname("System.Drawing") | Out-Null
#define an icon image pulled from PowerShell.exe
$Icon=[system.drawing.icon]::ExtractAssociatedIcon((join-path $pshome powershell.exe))
$Notify = New-Object System.Windows.Forms.NotifyIcon
$Notify.icon = $Icon
$Notify.visible = $True
#define the tool tip icon based on the message type
switch ($messagetype)
{
"Error" {$MessageIcon = [System.Windows.Forms.ToolTipIcon]::Error}
"Info" {$MessageIcon = [System.Windows.Forms.ToolTipIcon]::Info}
"Warning" {$MessageIcon = [System.Windows.Forms.ToolTipIcon]::Warning}
Default {$MessageIcon = [System.Windows.Forms.ToolTipIcon]::None}
}
#display the balloon tipe
$Notify.showballoontip($Notification_timeout,$MessageTitle,$MessageText,$MessageType)
}
Show-Notification -MessageType Info -MessageText 'some message' -MessageTitle 'New Alert'
Update
Modifying your posted code to match a few items in mine, allows your code to work in the ISE, console as expected.
[reflection.assembly]::loadwithpartialname("System.Windows.Forms") | Out-Null
[reflection.assembly]::loadwithpartialname("System.Drawing") | Out-Null
$notifyobj = New-Object System.Windows.Forms.NotifyIcon
# I don't have your icon, so, using what I know I can reach
$notifyobj.Icon = [system.drawing.icon]::ExtractAssociatedIcon((join-path $pshome powershell.exe))
$notifyobj.BalloonTipTitle = "New Message"
$notifyobj.BalloonTipText = "C"
$notifyobj.Visible = $True
$notifyobj.ShowBalloonTip(1000)
$notifyobj.Dispose()

PowerShell Loading Animation

I would like to create a GUI that shows a loading bar while a job is running in the background. For simplicity, I've made the job an infinite loop so it should always be running. I've only included necessary parts of the code:
$Label = new-object system.windows.forms.Label
$Label.Font = 'Ariel,12pt'
$Label.Text = ""
$Label.AutoSize = $True
$Label.Location = new-object system.drawing.size(50,10)
$Form.Controls.Add($Label)
$LoadingAnimation = #(".....","0....",".0...","..0..","...0.","....0",".....")
$AnimationCount = 0
$test = start-job -Name Job -ScriptBlock { for($t=1;$t -gt 0; $t++){} }
while ($test.JobStateInfo.State -eq "Running")
{
$Label.Text = $LoadingAnimation[($AnimationCount)]
$AnimationCount++
if ($AnimationCount -eq $LoadingAnimation.Count){$AnimationCount = 0}
Start-Sleep -Milliseconds 200
}
Upon testing this code in the console, just using Write-Host instead of $Label.Text, it works just fine. What needs to be done differently to get this to work in a windows form created by PowerShell?
In PowerShell, you can create all sorts of status, including multi-level, using Write-Progress. Don't forget to call with -Completed when done (a common mistake I see).
Get-Help Write-Progress
After going through the little details of the script, I found the problem. This is how I activated my form:
$Form.Add_Shown({$Form.Activate()})
[void] $Form.ShowDialog()
This caused the script to stop when the form was launched, ShowDialog stops the script to allow interaction. The fix was:
$Form.Add_Shown({$Form.Activate()})
[void] $Form.Show()
Using Form.Show lets the script to continue to run because it doesn't require interaction.

Powershell LinkClicked event for a RichTextBox

Im using windows Windows.Forms.RichTextBox to redirect my powershell script output "$var".
Detect.Urls is already enabled and working, but unable to open them by clicking.
Can any one help me with the code for link click event handler in powershell script........
$outputBox = New-Object System.Windows.Forms.RichTextBox
$outputBox.Location = New-Object System.Drawing.Size(10,150)
$outputBox.Size = New-Object System.Drawing.Size(700,300)
$outputBox.MultiLine = $True
$outputBox.SelectionIndent = 8
$outputBox.SelectionHangingIndent = 3
$outputBox.SelectionRightIndent = 12
$outputBox.ScrollBars = "ForcedBoth"
$Form.Controls.Add($outputBox)
$outputBox.Text = $var
$Form.Add_Shown({$Form.Activate()})
[void] $Form.ShowDialog()
You have to handle click events by yourself
$outputBox.add_LinkClicked({
Start-Process -FilePath $_.LinkText
})
Will open link in the default browser when clicked.
This is how I do it with Powershell Studio...right click the control and add a new event...add the linkclicked event and then go to the script and add the following.
depending on what you want to be opened you may need to used something other than explorer, but the $_.linktext should have the link you want from the url. note if there are spaces you will have to something to replace them as the url will be broke at the first space it encounters.
$Linkclicked = $_.LinkText
explorer.exe $Linkclicked

Powershell popup while file is being moved

When my script starts it moves a file from one directory to another. After the file is completely downloaded I launch an application.
This all works, but what I would like is a popup window to appear while the file is being moved (Large files).
When I debug my code once it hits the Move-Item Cmdlet it waits until that command is completed before it moves on. What I want to do is while the Move-Item Cmdlet is running, popup an information window.
I know how to do the popup and the Move-Item, I just don't know how to get it to work the way I want. Any ideas?
Popup code
#pop up window letting mechanic know we are waiting for the files to be downloaded before opeing the SMT application
$wshell = New-Object -ComObject Wscript.Shell
$wshell.Popup("The EAFR file is still being moved to the correct directory, please wait.",0,"SMT Status",0)
#Move-Item
$MLMoveDir = "C:\move\data\AutoUpload\"
Move-Item -LiteralPath ($filePath) $MLMoveDir
One option is to use WinForms to display a Please Wait dialog, rather than a Popup that has to be dismissed by the user. Something like:
Add-Type -AssemblyName System.Windows.Forms
$Form = New-Object system.Windows.Forms.Form
$Label = New-Object System.Windows.Forms.Label
$Form.Controls.Add($Label)
$Label.Text = "Copying file, please wait."
$Label.AutoSize = $True
$Form.Visible = $True
$Form.Update()
#Move-Item
$MLMoveDir = "C:\move\data\AutoUpload\"
Move-Item -LiteralPath ($filePath) $MLMoveDir
#Hide popup
$Form.Close()
So what you could do is start the Move-Item as a job, and then do a While((get-job "jobname").state -ne completed){do popup}. I would look something like this:
#Move-Item
$MLMoveDir = "C:\move\data\AutoUpload\"
$MoveJob = Start-Job -scriptblock {Move-Item -LiteralPath ($filePath) $MLMoveDir}
#Show Popup
While($movejob.state -ne "Completed"){
$wshell = New-Object -ComObject Wscript.Shell
$wshell.Popup("The EAFR file is still being moved to the correct directory, please wait.",1,"SMT Status",0)
}
That way the popup shows for 1 second, and if the move is still happening it shows it again. I don't know that it would even appear to disappear/re-show so it would likely be seamless.