How do you get Windows PowerShell to play a sound after .bat job has finished running? - powershell

As the title states, I have a .bat job running within PowerShell that when finished running, I would like a sound notification to go off. I was wondering if there was a PowerShell command that I can add to my existing PowerShell command.

In addition to the solutions #TheGameiswar suggests, you can have some fun by making the system actually speak to you:
# Create a new SpVoice objects
$voice = New-Object -ComObject Sapi.spvoice
# Set the speed - positive numbers are faster, negative numbers, slower
$voice.rate = 0
# Say something
$voice.speak("Hey, Harcot, your BAT file is finished!")
Note: I only tested this on Windows 10, so it may not work on other versions, but give it a go and see.

Besides the excellent solutions of boxdog (here) and TheGameiswar (here), I want to mention another possibility, which lets you play some standard system sounds:
[System.Media.SystemSounds]::Asterisk.Play()
[System.Media.SystemSounds]::Beep.Play()
[System.Media.SystemSounds]::Exclamation.Play()
[System.Media.SystemSounds]::Hand.Play()
[System.Media.SystemSounds]::Question.Play()
Another text-to-speech approach
Add-Type -AssemblyName System.Speech
$synth = New-Object -TypeName System.Speech.Synthesis.SpeechSynthesizer
$synth.Speak("Hey $env:USERNAME, your job is finished!")
Customization
For full details, read the docs.
Voice
Select a voice:
$synth.SelectVoice("Microsoft Zira Desktop")
You can view available voices with:
$synth.GetInstalledVoices() | Select-Object -ExpandProperty VoiceInfo
Rate
Set the speaking rate from -10 (slow) through 10 (fast):
$synth.Rate = 5
Volume
Set the volume from 0 (quiet) through 100 (loud):
$synth.Volume = 75

you could use powershell automatic variables to check bat file status ..As per this,$? returns true ,if command is successfull..
below is sample code
$a =Invoke-Command -ScriptBlock {
& "C:\temp1\test.bat"
}
if($?){
[console]::beep(500,300)
}
You could also play custom sounds,
$PlayWav=New-Object System.Media.SoundPlayer
$PlayWav.SoundLocation=’C:\Foo\Soundfile.wav’
$PlayWav.playsync()
references:
https://devblogs.microsoft.com/scripting/powertip-use-powershell-to-play-wav-files/

Related

Is there a way to run a popup window while a script runs?

I am creating a gui, and want a popup to let you know it's busy, but then close when it's completed that specific task. The only thing i can find is the following...
$popup = New-Object -ComObject wscript.shell
$popup.popup("Running Script, Please Wait....",0,"Running...",0x1)
But the issue is, this is waiting for a response, and then it will run the script. I am not asking for some to write me a script, but some guidelines on where to find this information.
I need powershell to popup a window, and then leave it up, while a script is ran, and then close it when the script is done running.
Would it be best just just have another windows form, that runs the script with a label on it? That seems like an excessive amount of work for a simple task. But it IS powershell...
Is there something like...
$popup = New-Object -ComObject wscript.shell
$popup.popup("Running Script, Please Wait....",0,"Running...",0x1)
###RUN SCRIPT HERE...
$popup.close()
EDIT:::
To the question "Why am I trying to have a popup, instead of writeprogress or whatnot" ... The reason is because I am doing this in a gui. Not in the command line. So i need the gui to basically inform the person it's busy, some of the tasks can take over 6 hours to complete, and i don't want them clicking around, and doing other things while the current task at hand is running.
EDIT 2:::
I will leave this open, as the original question was not answered, but I created a work around with the following code.
$LabelAlert = New-Object system.windows.forms.label
$LabelAlert.Text = "Working, Please wait."
$LabelAlert.location = New-Object System.Drawing.Point(0,180)
$LabelAlert.width = 590
$LabelAlert.height = 25
$LabelAlert.Visible = $false
$LabelAlert.TextAlign = "TopCenter"
$Form.Controls.Add($LabelAlert)
$FormGroupBox = New-Object System.Windows.Forms.GroupBox
$FormGroupBox.Location = New-Object System.Drawing.Size(0,0)
$FormGroupBox.width = 600
$FormGroupBox.height = 375
$Form.Controls.Add($FormGroupBox)
$startAlert = {
$LabelAlert.Visible = $true
$FormGroupBox.Visible = $false
}
$stopAlert = {
$LabelAlert.Visible = $false
$FormGroupBox.Visible = $true
}
Every form part was moved inside the group box. And the group box is the same size as my window.
And for every time consuming Script i run
&$startAlert
....script commands go here...
&$stopAlert
You could use Start-Job to run the popup within a background job, which would allow the script to continue after it has appeared:
$Job = Start-Job -ScriptBlock {
$popup = New-Object -ComObject wscript.shell
$popup.popup("Running Script, Please Wait....",0,"Running...",0x1)
}
#Run script here..
But I can't see any way to force the popup to close at the end of your script (tried Remove-Job -Force and even Stop-Process conhost -Force but neither seemed to work).
As others have said though, the better option would be write status to the PowerShell window. You might want to look at the Write-Progress cmdlet which you can use to disply a progress bar over a running script.

Get powershell to control Internet Explorer and switch between 2 webpages

I want to have Internet Explorer to switch between 2 different webpages in an endless loop . The webpages is local files, that has to be viewed for 10 seconds before changing to next webpage. My code so far:
$ie = New-Object -Comobject 'InternetExplorer.Application'
$ie.Visible = $true
function IEWeb {
$ie.navigate(file://D:\web\index.html)
Start-sleep 10
$ie.navigate(file://D:\web\index2.html)
Start-sleep 10
}
while($true) {
IEWeb
}
Everything is working until the second webpage has to be loaded.
Then I get an error message:
Object is disconnected from it's clients. (Exception from HRESULT:0x800010108 (RPC_E_DISCONNECTED))
I have tried with global vars but still the same.
Can anyone give Me a hint of what I'm missing?
I wasn't able to produce your issue, but how about this?
function Navigate-Rotate {
[CmdletBinding()]
Param (
[Parameter(Mandatory = $true)]
[string[]]$Url
,
[Parameter()]
[int]$SleepSeconds = 10
)
Process {
$ie = New-Object -Comobject 'InternetExplorer.Application'
$ie.Visible = $true
while ($ie.Visible) {
foreach ($uri in $Url) {
if ($ie.Visible) {$ie.navigate2($uri)}
Start-Sleep -Seconds $SleepSeconds
if ($ie.Visible) {$ie.Stop()} #just incase anything's running which may interfere
}
}
}
}
#Navigate-Rotate 'https://stackexchange.com','https://google.com'
Navigate-Rotate 'file:///D:/web/index.html', 'file:///D:/web/index2.html'
Notes
I keep checking the value of $ie.Visible. Should a user exit IE this ensures that my code won't try to use any of $ie's methods. NB: This value doesn't get set to false once closed; rather it ceases to exist; but that evaluates as falsey, so has the same effect. There is a potential race condition, but it's minor / can't be avoided (or I don't know how to avoid it).
I use navigate2 instead of navigate since this method's a bit more flexible / there's no drawback.
I use $ie.Stop() after loading each page to ensure that IE's not busy with other tasks when I try to navigate away from the page. That should stop anything in the page from being able to block our attempt to navigate elsewhere.
Other Notes
The change from a function to a cmdlet doesn't make much difference; it's just my preferred approach.
Passing in a list of URLs to the function rather than hardcoding the 2 URLs means I can change the URLs easily (e.g. by reading in from a file), and I'm not restricted to 2 files/sites.
Details of the methods and properties available in IE are listed here: https://learn.microsoft.com/en-us/previous-versions/windows/internet-explorer/ie-developer/platform-apis/aa752084(v=vs.85)
Apologies that I can't explain why you're seeing the issue you are; I just hope that this amended version avoids the issue you're seeing, and a few other potential issues you've not yet seen.

How to stop a powershell block after a timeout for powershell script

I want to invoke the command
$myArray = Get-Wmiobject -Class Win32-printer
But, on rare occasions, this command sometimes never fails nor succeed, returning the list of available printer.
I would like to assign $myArray only if the invocation takes less than a few seconds, lets say 5. Otherwise, I want to display a dialog telling that printers are unavailable at this moment.
How to start a powershell block with a timeout?
You can use a job for that:
$job = Start-Job { Get-Wmiobject -Class Win32-printer }
$job | Wait-Job -Timeout 5
if ($job.State -eq 'Running') {
# Job is still running, cancel it
$job.StopJob()
} else {
# Job completed normally, get the results
$myArray = $job | Receive-Job
}
I would say to create your own customized WMI queries using type casting and the native .NET providers. This way the work is still being done in the same opened console and you have physical control on the time outs.
I basically had the same frustration as you did. I would be querying servers all day until I hit the one that had a broken WMI. After so much, I started researching how to create my own WMI function to get around this. That was my solution to the problem. Learned a lot along the way.
Here is an article to help you along your way.
http://stevenmurawski.com/powershell/2009/01/dealing-with-wmi-timeouts/
Just to add to the above- powershell also has a built in stopwatch diagnostic for timeout functionality.
https://technet.microsoft.com/en-us/magazine/2009.03.heyscriptingguy.aspx

PowerShell - Upload Test and reduce the speed

It´s easy to find and or create a script to test the upload for a web based environment (For example SharePoint).
But how can I specify/reduce the upload speed?
I want to simulate big and slow uploads and wan´t to check if a upload reached a timeout from a infrastructure component.
I wan´t to copy this script to different locations in my infrastructure to check which component had the timeout problem.
(Changing the nework speed in the infrastructure is not a option)
The reason for this test is because a few users can only use slow internet connections (satellite, isdn, modem etc.) but had to upload big files.
How can I create a PowerShell script to upload a file with a very reduced speed?
Thank you in advance!
You should look after NetAdapter at MSDN: https://technet.microsoft.com/en-us/library/jj130867(v=wps.630).aspx
For example
[cmdletbinding()]
param(
[int]$ThrottleLimit = 5
)
foreach -parallel -throttlelimit $ThrottleLimit ($n in 1..5){
"Working on $n"
"{0:hh}:{0:mm}:{0:ss}" -f (Get-Date)
}
}
I hope you will find the right solution with my answer ;-)

Creating batch jobs in PowerShell

Imagine a DOS style .cmd file which is used to launch interdependent windowed applications in the right order.
Example:
1) Launch a server application by calling an exe with parameters.
2) Wait for the server to become initialized (or a fixed amount of time).
3) Launch client application by calling an exe with parameters.
What is the simplest way of accomplishing this kind of batch job in PowerShell?
Remember that PowerShell can access .Net objects. The Start-Sleep as suggested by Blair Conrad can be replaced by a call to WaitForInputIdle of the server process so you know when the server is ready before starting the client.
$sp = get-process server-application
$sp.WaitForInputIdle()
You could also use Process.Start to start the process and have it return the exact Process. Then you don't need the get-process.
$sp = [diagnostics.process]::start("server-application", "params")
$sp.WaitForInputIdle()
$cp = [diagnostics.process]::start("client-application", "params")
#Lars Truijens suggested
Remember that PowerShell can access
.Net objects. The Start-Sleep as
suggested by Blair Conrad can be
replaced by a call to WaitForInputIdle
of the server process so you know when
the server is ready before starting
the client.
This is more elegant than sleeping for a fixed (or supplied via parameter) amount of time. However,
WaitForInputIdle
applies only to processes with a user
interface and, therefore, a message
loop.
so this may not work, depending on the characteristics of launch-server-application. However, as Lars pointed out to me, the question referred to a windowed application (which I missed when I read the question), so his solution is probably best.
To wait 10 seconds between launching the applications, try
launch-server-application serverparam1 serverparam2 ...
Start-Sleep -s 10
launch-client-application clientparam1 clientparam2 clientparam3 ...
If you want to create a script and have the arguments passed in, create a file called runlinkedapps.ps1 (or whatever) with these contents:
launch-server-application $args[0] $args[1]
Start-Sleep -s 10
launch-client-application $args[2] $args[3] $args[4]
Or however you choose to distribute the server and client parameters on the line you use to run runlinkedapps.ps1. If you want, you could even pass in the delay here, instead of hardcoding 10.
Remember, your .ps1 file need to be on your Path, or you'll have to specify its location when you run it. (Oh, and I've assumed that launch-server-application and launch-client-application are on your Path - if not, you'll need to specify the full path to them as well.)