I have got a problem.
1.The first situation
Process has already started.
And I want to do something after the current process is completed.
For example
$process=[System.Diagnostics.Process]::GetProcessesByName("notepad")
And
Register-ObjectEvent $process Exited -Action {}
But in this case, it's not working
How Can I Register Event "Exited"?
2. The Second situation
The Process has not yet been started.
How I can to wait for start process?)
First part is answered by Mathias R. Jessen with [System.Diagnostics.Process]::GetProcessesByName("notepad") | foreach {Register-ObjectEvent $_ Exited -Action { ... } }
For second part, the solution is below. Credit goes here PDQ.com - Register-ObjectEvent
You need to use WQL queries to monitor for process start event, then set up a query watcher that will fire an event in response of some process getting started, then listen with Register-ObjectEvent on that watcher. Code-copy-paste:
$Query = New-Object System.Management.WqlEventQuery "__InstanceCreationEvent", (New-Object TimeSpan 0,0,1), "TargetInstance isa 'Win32_Process'"
$ProcessWatcher = New-Object System.Management.ManagementEventWatcher $query
$Action = { New-Event "PowerShell.ProcessCreated" -Sender $Sender -EventArguments $EventArgs.NewEvent.TargetInstance }
register-objectEvent -InputObject $ProcessWatcher -EventName "EventArrived" -Action $Action
Related
I'm writing a PowerShell script in Visual Studio Code which is intended to run a function for every tick of a timer. Here is the code:
$timer = New-Object Timers.Timer
$timer.Interval = 1000
$timer.Enabled = $true
Register-ObjectEvent -InputObject $timer -EventName Elapsed -Action {Write-Host "Tick"}
The problem is that I don't seem to be able to stop the script using Ctrl+C which is what I would normally do, or indeed anything other than actually killing the terminal using the 'dustbin' icon. It just continues printing 'Tick'!
What's going on here? How would I stop this script gracefully?
When you subscribe an action to an event using Register-ObjectEvent, it can be unregistered using the Unregister-Event cmdlet:
$timer = New-Object Timers.Timer
$timer.Interval = 1000
$timer.Enabled = $true
Register-ObjectEvent -SourceIdentifier MyElapsedTick -InputObject $timer -EventName Elapsed -Action {Write-Host "Tick"}
<# ... #>
Unregister-Event -SourceIdentifier MyElapsedTick
To enumerate existing subscribers (for when you forget to specify a SourceIdenfitier for example), use Get-EventSubscriber:
# This will unregister all event subscribers
Get-EventSubscriber |Unregister-Event
$timer = [System.Timers.Timer]::new()
$timer.Interval = 5000
$timer.AutoReset = $false
$timer.Enabled = $true
1..20 | % {
if (!$timer.Enabled){
exit
}
write-host $_, $timer.Enabled
Start-Sleep -Milliseconds 1000
}
I have a remote PowerShell session from machine A to B. Requirement is to do a continuous check inside B. On a specific condition, I have to execute something and stop the timer. For that, I created a timer function. But that timer function is not starting.
The code that I have written is:
$timer = new-object timers.timer
$action = {
write-host "inside loop.."
# Condition to check comes here.
}
$timer.Enabled = $true
$timer.Interval = 3000 #3 seconds
Register-ObjectEvent -InputObject $timer -EventName elapsed –SourceIdentifier thetimer -Action $action
$timer.start()
I expect the output to be
inside loop..
inside loop..
inside loop..
but the actual output is:
Id----------------1
Name--------------thetimer
PSJobTypeName----------
State-------------NotStarted
HasMoreData-------False
I don't understand why the following code generates error messages. Powershell seems difficult to learn.
$fsw = New-Object IO.FileSystemWatcher ...
$Action = {Param($option)
if ($option -eq "Copy")
{Write-Host "Copy was specified"}
}
Register-ObjectEvent $fsw Created -SourceIdentifier FileCreated -Action $Action
Register-ObjectEvent $fsw Changed -SourceIdentifier FileChanged -Action $Action -ArgumentList Copy
(This is an updated version of question How to provide options to script blocks?)
When in doubt, read the documentation. The Register-ObjectEvent cmdlet does not have a parameter -ArgumentList, so getting an error when trying to call the cmdlet with a parameter that it doesn't have is hardly surprising. And as I already told you in my comment to your previous question, passing arguments to an event action the way you're trying to do does not make much sense in the first place.
If your actions are so similar that defining different action script blocks would yield too much boilerplate you can easily discriminate by event inside the scriptblock, since that information is passed to the action automatically (via the variable $Event).
$Action = {
$name = $Event.SourceEventArgs.Name
$type = $Event.SourceEventArgs.ChangeType
switch ($type) {
'Created' { Write-Host "file ${file} was created" }
'Changed' { Write-Host "file ${file} was changed" }
}
}
I'm trying to create a PowerShell script that would download posh-git and then install it automatically. I'd like to have the download happen asynchronously, preferably with the option to have a callback once the download has finished and/or be able to wait for it to complete (but executing code until you want to wait).
I created a function that takes the URL for download, the path where to save it and the callback function. Unfortunately the callback function never seems to be called:
function DownloadFileAsync([string] $url, [string] $destination, [ScriptBlock] $action = $null)
{
$web_client = New-Object System.Net.WebClient
if ($action)
{
Register-ObjectEvent -InputObject $web_client -EventName DownloadFileCompleted -Action $action | Out-Null
}
$web_client.DownloadFileAsync($url, $destination)
}
$download_finished =
{
# This is never reached
echo "Download finished"
}
DownloadFileAsync "https://github.com/dahlbyk/posh-git/archive/master.zip" "C:/posh-git-master.zip" $download_finished
How can I fix the callback never being called? Is there also a way to implement a way to wait, later on in the code, for the download to complete?
Output from event action is not printed on PowerShell host, but captured and saved in job, returned by Register-ObjectEvent:
$web_client = New-Object System.Net.WebClient
$Job = Register-ObjectEvent -InputObject $web_client -EventName DownloadStringCompleted -Action {
Write-Host 'Download completed'
$EventArgs.Result
}
$web_client.DownloadStringAsync('http://stackoverflow.com/q/39185082')
Now when download completed, you can use Receive-Job $Job to receive job results.
If you want to print something on PowerShell host, then you need to use Write-Host or Out-Host cmdlets.
I am displaying a form that runs another script after a button is clicked. I need to check for the state of the script completion so that I can update the text of the button.
$ButtonEmailInfo.Add_Click({
$ButtonEmailInfo.Text = "Sending info"
$ButtonEmailInfo.Enabled = $false
$Form.Refresh()
Write-Host("Running start-job")
$global:SendClicked = $true
$global:SJob = Start-Job -filepath ($path + "\Send-Info.ps1")
$ButtonEmailInfo.Text = "Info sent"
$Form.Refresh()
})
The problem with the above code is that the button text is set to "Info sent" before the called script Send-Info.ps1 has completed.
I can test $global:SJob.Finished too see if the script has completed but I'm not sure how you do this in a form. Is there the equivalent of an update() method that gets checked periodically?
Thanks,
Rich
You can use Register-ObjectEvent to handle the job and perform an action when it has completed. You would add the following right after you begin the job.
Edit You will need to setup a Timer object to update the Window and allow the thread to handle the Event output from Register-ObjectEvent.
$global:SJob = Start-Job -filepath ($path + "\Send-Info.ps1")
Register-ObjectEvent -InputObject $Global:SJob -EventName StateChanged -SourceIdentifier JobWatcher -Action {
#Job completed actions go here
Write-Host "Job Completed!"
# $Event.Sender is the actual job object that you can either remove or retrieve data from
#Perform cleanup of event subscriber
Unregister-Event -SourceIdentifier $Event.SourceIdentifier
Remove-Job -Name $Event.SourceIdentifier -Force
}
Here is an example that you can run to see it in action without using it in a form that notifies when completed and then proceeds to remove the job and the event subscription.
$SJob = Start-Job {start-sleep -seconds 10} -Name TESTJOB
Register-ObjectEvent -InputObject $SJob -EventName StateChanged -SourceIdentifier JobWatcher -Action {
#Job completed actions go here
Write-Host "Job $($Event.Sender.Name) Completed!"
#Remove job
Remove-Job $Event.Sender
#Perform cleanup of event subscriber and its job
Unregister-Event -SourceIdentifier $Event.SourceIdentifier
Remove-Job -Name $Event.SourceIdentifier -Force
}
Edit You will need to setup a Timer object to update the Window and allow the thread to handle the Event output from Register-ObjectEvent. Add the following lines to your UI to create the timer as the window loads and a timer stop when it closes.
$Window.Add_Loaded({
##Configure a timer to refresh window##
#Create Timer object
$Script:timer = new-object System.Windows.Threading.DispatcherTimer
#Fire off every 5 seconds
$timer.Interval = [TimeSpan]"0:0:1.00"
#Add event per tick
$timer.Add_Tick({
[Windows.Input.InputEventHandler]{ $Script:Window.UpdateLayout() }
})
#Start timer
$timer.Start()
If (-NOT $timer.IsEnabled) {
$Window.Close()
}
})
$Window.Add_Closed({
$Script:timer.Stop()
})