I am trying to prevent users from shutting down the computer in certain situations. I am displaying a confirm message to do that. This is how my script looks like:
$sysevent = [microsoft.win32.systemevents]
Register-ObjectEvent -InputObject $sysevent -EventName "SessionEnding" -Action $OnShutdown -SourceIdentifier "ExecuteOnShutdown"
$OnShutdown =
{
Write-Host -ForeGround Green $event.SourceEventArgs.Reason
$OUTPUT= [System.Windows.Forms.MessageBox]::Show("Do you really want to shutdown the computer?." , "confirm" , 4)
Write-Host $OUTPUT
}
This works fine but i dont know how do i suspend the shutdown command till user clicks "yes" or "no". Is there a way to prevent the system shutdown and wait for the user to click "yes" or "no" and then shutdown the server based on the answer?
In your event handler scriptblock there are a number of automatic variables defined one of which is $EventArgs. In this case there will be a Cancel property on this object you can set to $true but the docs warn:
When set to true, this property requests that the session continue to
run. It provides no guarantee that the session will not end.
There is also another variable defined in this context - $Sender. Execute man about_automatic_variables for more info.
Consider deploying your script via group policy or a local policy shutdown / logoff script which should prevent shutdown until your condition is met. You might need to wrap your messagebox call in conditional sleep loop (which is what I did for something similar in VBScript years ago!), maybe not.
If you choose to use this method, you may also want to include a preferred default selection for your messagebox (perhaps after a specified timeout period has elapsed?); a user may not hang around to see your mesaagebox as it will be drawn after the interactive desktop has unloaded.
Here's a link to a Technet article about how to Use Startup, Shutdown, Logon, and Logoff Scripts.
I'm not sure if this answers your question as this won't prevent the shutdown, it just stops it until your condition is met.
Related
I am writing a autologin script in Powershell. With main purpose of doing autologon with keystrokes on remote clients in our environment after installation, with the desired AD and password entered.
Works fine on my i9. But most people using Tablets and Elitebooks so using
Thread Sleep
Works bad since i would need to have custom timing on Every hardware, or very high default numbers for lower end clients using my script
Is there any way adding an "wait for row above to completed" Before continuation to next.
I don't have enough on your current code to produce a more accurate answer but the idea, in all cases, remains the same.
You should periodically wake up the thread to check whether or not the machine is in the state you want it in and from there, you either go back to sleep or exit the loop and continue.
The delay is up to you but you want to find a sweet spot to have great performance and reactivity.
Example (based on your description)
$IsLoggedIn = $false
while (! $IsLoggedIn) {
$IsLoggedIn = 'Custom Logic returning $true if the user is logged in'
if ($IsLoggedIn) { break }
Start-Sleep -Milliseconds 100
}
You just need to figure out the thing you want to use as the check to validate the computer is in the correct state you need it in before proceeding further.
Is there a possibility to wait for an process to quit, without it needs to running?
I know there is the keyword WaitForExit, but to use this the process needs to run.
My second question is, if there is a possibility to use an else-Statement in an while loop.
Tried it already, but it always said that there isnt an function called else.
Do Until
Do {
Sleep 5
} Until (Get-Process iexplore);
Will wait until iexplore is found
While
While (Get-Process iexplore) {
Sleep 5
}
Will wait until iexplore is no longer running
Else after while
You cannot use an else statement after a while loop.
It needs to come after an if.
if there is a possibility to use an else-Statement in an while loop.
If you mean something like:
while (cond) {
} else {
}
?
Then NO. (how would the content of the else block be any different to code immediately following the while block?)
Is there a possibility to wait for an process to quit,
Yes. There are different ways of doing this, depending on the nature of the target process. Is it one created by the same script? Is the same session? A service? Or just an arbitrary process?
Does it seems to fits your needs (1st question) ? http://technet.microsoft.com/library/hh849813.aspx
You can use wait-process cmdlet.
Check link for details http://ss64.com/ps/wait-process.html
Example: wait-process -name notepad.exe
I need to create a component in a larger pipeline that starts vpn service and waits for a connection to be established before proceeding. I'd like to do this with Powershell if possible. I imagine the logic flow being something like this, but the multithreading aspect is vexing me.
create an event log handler
start a service
wait for a specific event log entry
exit
PowerShell v2:
Register-WmiEvent -Query "Select * from __InstanceCreationEvent Where TargetInstance ISA 'Win32_NTLogEvent'" -Action { [console]::beep() }
The script in the action block runs every time there is an event written to the eveng log. Expect a lot of beeps :)
Short version: I think I need help with properly using events in PowerShell that are invoked as a result of a Windows Message to get rid of a balloon tooltip's icon.
Long Version:
I have a long-running PowerShell command (a build) that I would like to be notified when it completes via a balloon tooltip in the system tray/notification area.
I was able to create a Write-BalloonTip script (below) that does roughly what I want. The only problem is that, as sometimes happens with tray icons, the tray icon doesn't disappear until I mouse over it. By re-using the same global variable to represent the NotifyIcon, I'm able to re-use this script and keep it so that only one system tray icon remains (until I mouse over it). This still feels like a hack. I tried to add an event handler so that it'd be notified on the BalloonTipClosed event and then dispose of it there. In the event handler, I tried all three techniques I've seen suggested for getting rid of the lingering icon to no avail.
The annoying part is that a simple .Dispose seems to work on subsequent calls of the script, leading me to think that the event script block isn't being called at all.
I've verified that BalloonTipClosed gets called after the tip fades away in a separate WinForms app.
Am I missing something basic? Any help is much appreciated. Thanks!
Here's the code for "Write-BalloonTip.ps1":
param
(
$text,
$title = "",
$icon = "Info",
$timeout=15000
)
[System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms") | out-null
[System.Reflection.Assembly]::LoadWithPartialName("System.Drawing") | out-null
if ($global:writeBalloonTipIcon)
{
# This gets rid of the previous one
$global:writeBalloonTipIcon.Dispose()
}
$global:writeBalloonTipIcon = new-object System.Windows.Forms.NotifyIcon
$global:writeBalloonTipIcon.Icon = [System.Drawing.SystemIcons]::Information
# FIXME: This *should* cleanup the icon after it's done, but it doesn't seem to work
$global:writeBalloonTipIcon.add_BalloonTipClosed(
{
# this *should* work, but it's not. What am I missing?
$global:writeBalloonTipIcon.Icon = $null;
$global:writeBalloonTipIcon.Visible = $false;
$global:writeBalloonTipIcon.Dispose();
});
$global:writeBalloonTipIcon.Visible = $true;
$global:writeBalloonTipIcon.ShowBalloonTip($timeout, $title, $text, $icon);
I think you need to execute this code in an STA thread. PowerShell (v2 shown here) executes in an MTA thread by default:
PS U:\> [System.Threading.Thread]::CurrentThread
ManagedThreadId : 5
ExecutionContext : System.Threading.ExecutionContext
Priority : Normal
IsAlive : True
IsThreadPoolThread : False
IsBackground : False
ThreadState : Running
ApartmentState : MTA
CurrentUICulture : en-US
CurrentCulture : en-US
Name : Pipeline Execution Thread
I would recommend using the Register-ObjectEvent to subscribe to the BalloonTipClosed event. This came up recently in another SO post. Check it out.
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.)