I have the below PowerShell code to validate if a scheduled task exists and to check its state.
$SchTsk = Get-ScheduledTask | Select Name, State | ? {$_.Name -eq $SchTask}
If ($SchTsk -ne $Null)
{
Write-Host "SchTask $SchTask exists"
If ($SchTsk.State -eq 3)
{
Write-Host "SchTask State: READY!"
}
}
The code works fine on Windows Server 2008 but does not work on Windows Server 2003. In 2003 I get an error:
New-Object : Cannot load COM type Schedule.Service.
From what I've read it seems that the Schedule.Service COM object does not exist on Server 2003.
So...is there a work-around for this issue to validate a scheduled task and its state on Server 2003?
The following is a sample PowerShell script that reads from the COM object mentioned above and outputs some Task Schedule Information:
#Connecting to COM Object
$schedService = New-Object -ComObject Schedule.Service
$schedService.Connect($Computer)
# Get Scheduled Tasks on Root Folder (Task Scheduler Library)
$folder = $SchedService.GetFolder("")
$tasks = $folder.GetTasks("")
# Output Task Details
$tasks | % {
"-" * 40
"Task " + $_.Name + ":"
"-" * 40
$_.Definition.Actions
}
I have PowerShell scripts running on Win2008 and Win2003, and found the command "schtasks" to be good enough for looking up information about scheduled tasks. This isn't a powershell command, but it's a standard function in Windows, and is compatible with Windows 2003 and 2008.
$scheduledTasks = schtasks /query /v /fo csv | ConvertFrom-Csv
#this will return an array of scheduled tasks with all info available for the task
To check if a scheduled task is ready on 2003, you'll need to make sure "Scheduled Task State" is "Enabled", and Status is blank.
On 2008 and above, Status will return enabled, disabled, running, etc.
If all you want to do is gather the basic properties of a task so you know it's name state and next run time you can use schtasks with the following methods:
function New-TaskInfo()
{
param ($TaskPath, $TaskName, $NextRunTime, $Status);
$task = new-object PSObject;
$task | add-member -type NoteProperty -Name Path-Value $TaskPath;
$task | add-member -type NoteProperty -Name Name -Value $TaskName;
$task | add-member -type NoteProperty -Name NextRunTime -Value $NextRunTime;
$task | add-member -type NoteProperty -Name Status -Value $Status;
return $task;
}
function Get-ScheduledTaskInfo
{
$tasks = #();
$queryOutput = schtasks /QUERY /FO CSV
foreach($line in $queryOutput)
{
$columns = $line.Split(',');
$taskPath = $columns[0].Replace('"', '');
if($taskPath -eq "TaskName")
{
#Ignore headder lines
continue;
}
$taskName = [System.IO.Path]::GetFileName($taskPath);
$nextRunTime = $columns[1].Replace('"', '');
$status = $columns[2].Replace('"', '');
$task = New-TaskInfo -TaskPath $taskPath -TaskName $taskName -NextRunTime $nextRunTime -Status $status;
Write-Host -ForegroundColor Green "Add Task $task";
$tasks += $task;
}
return $tasks;
}
If you then want to perform an action for a specific task you can use schtasks directly specifying data stored in the objects collected earlier.
$task = Get-ScheduledTaskInfo | Where-Object {$_.Name -eq 'TaskName'}
if($task.Status -eq 'Ready')
{
Write-Host -ForegroundColor Green "Task" $task.Name "Is" $task.Status;
#End the target task
schtasks /END /TN $task.Path;
}
You'll find all the informations you need about the missing COM object in Working with scheduled tasks from Windows PowerShell.
Using Windows Server 2012 you can use Scheduled Tasks Cmdlets in Windows PowerShell.
#ameer deen.. The code you have given returns the tasks at the root level only. Windows 2008 onwards several modifications are made to task manager and one of them is folder structure. To list all the tasks in side sub folders as well, we need to query the subfolders and iterate through them.
You can find the code sample for querying all(including the ones in subfolders) scheduled tasks at http://techibee.com/powershell/powershell-get-scheduled-tasks-list-from-windows-72008-computers/1647
Related
I'm trying to get one master PowerShell script to run all of the others while waiting 30-60 seconds to ensure that the tasks are completed. Everything else I tried wouldn't stop/wait for the first script and its processes to complete before going through all the others at the same time and would cause a restart automatically.
Main script, run as admin:
$LogStart = 'Log '
$LogDate = Get-Date -Format "dd-MM-yyy-hh-mm-ss"
$FileName = $LogStart + $LogDate + '.txt.'
$scriptList = #(
'C:\Scripts\1-OneDriveUninstall.ps1'
'C:\Scripts\2-ComputerRename.ps1'
);
Start-Transcript -Path "C:\Scripts\$FileName"
foreach ($script in $scriptList) {
Start-Process -FilePath "$PSHOME\powershell.exe" -ArgumentList "-Command '& $script'"
Write-Output "The $script is running."
Start-Sleep -Seconds 30
}
Write-Output "Scripts have completed. Computer will restart in 10 seconds."
Start-Sleep -Seconds 10
Stop-Transcript
C:\Scripts\3-Restart.ps1
1-OneDriveUninstall.ps1:
Set-ItemProperty -Path REGISTRY::HKEY_LOCAL_MACHINE\Software\Microsoft\windows\CurrentVersion\Policies\System -Name ConsentPromptBehaviorAdmin -Value 0
taskkill /f /im OneDrive.exe
C:\Windows\SysWOW64\OneDriveSetup.exe /uninstall
2-ComputerRename.ps1:
$computername = Get-Content env:computername
$servicetag = Get-WmiObject Win32_Bios |
Select-Object -ExpandProperty SerialNumber
if ($computername -ne $servicetag) {
Write-Host "Renaming computer to $servicetag..."
Rename-Computer -NewName $servicetag
} else {
Write-Host "Computer name is already set to service tag."
}
The log file shows:
Transcript started, output file is C:\Scripts\Log 13-09-2019-04-28-47.txt.
The C:\Scripts\1-OneDriveUninstall.ps1 is running.
The C:\Scripts\2-ComputerRename.ps1 is running.
Scripts have completed. Computer will restart in 10 seconds.
Windows PowerShell transcript end
End time: 20190913162957
They aren't running correctly at all though. They run fine individually but not when put into one master script.
PowerShell can run PowerShell scripts from other PowerShell scripts directly. The only time you need Start-Process for that is when you want to run the called script with elevated privileges (which isn't necessary here, since your parent script is already running elevated).
This should suffice:
foreach ($script in $scriptList) {
& $script
}
The above code will run the scripts sequentially (i.e. start the next script only after the previous one terminated). If you want to run the scripts in parallel, the canonical way is to use background jobs:
$jobs = foreach ($script in $scriptList) {
Start-Job -ScriptBlock { & $using:script }
}
$jobs | Wait-Job | Receive-Job
I'm trying to execute a scheduled task with a different user, as stated in this article
I opened powershell in elevated mode (Windows 10 - 1809), and executed the following lines:
$a = New-ScheduledTaskAction -Execute notepad.exe
Register-ScheduledTask -TaskName 'TestTask' -Action $a
$svc = New-Object -ComObject 'Schedule.Service'
$svc.Connect()
$user = 'NT SERVICE\TrustedInstaller'
$folder = $svc.GetFolder('\')
$task = $folder.GetTask('TestTask')
$task.RunEx($null, 0, 0, $user)
And the output is like this,
No error, no notepad.exe created in background either.
What was wrong here?
Your code does work, but requires Windows 10 Version 1903 (that it doesn't work in earlier versions may be a bug):
It seems that the problem is specific to the NT SERVICE\TrustedInstaller user account; the IRegisteredTask::RunEx help offers no pointers.
By contrast, running as NT AUTHORITY\SYSTEM, the local system account, works fine on older versions too, including on Windows 7.
Note that even when the code does work, the Notepad instance launches invisibly, because it runs in the hidden session (with session ID 0) in which services and scheduled tasks (by default) run; to find all processes running in this session, you can use Get-Process | ? SessionId -eq 0; you can also find such hidden processes in Task Manager.
Test code (should also work on Windows 7) that can be used to experiment with different target programs and user accounts:
Note:
The code must be run from an ELEVATED ("Run As Administrator") session.
In my experiments I wasn't able to launch the task with the local system account visibly, in the current user's session.
$ok = $false
try {
# Create a scheduled task that launches $targetExe, for testing.
$taskName = "__TestTask"
$targetExe = 'notepad.exe'
$haveCmdlets = [bool] (Get-Command -ErrorAction SilentlyContinue New-ScheduledTaskAction)
# If creating the task fails, we'll catch it below on trying to retrieve the task.
if ($haveCmdlets) {
$action = New-ScheduledTaskAction -Execute $targetExe
$null = Register-ScheduledTask -Force -TaskName $taskName -Action $action
}
else { # before Windows 10: use schtasks.exe
schtasks.exe /create /tn $taskName /sc once /st 00:00 /tr $targetExe /f *>$null
}
$ErrorActionPreference = 'Stop'
# Connect to the Task Scheduler via COM Automation.
$svc = New-Object -ComObject 'Schedule.Service'
$svc.Connect()
# Get the task to run.
$folder = $svc.GetFolder('\') #'#
$task = $folder.GetTask($taskName)
# Specify the user account in which the task should run:
# W10 v1903 and above only:
$user = 'NT SERVICE\TrustedInstaller'
# NOTE: 'NT AUTHORITY\SYSTEM' should work on older systems too.
# Run the task, which outputs an object representing the running task instance.
# Note: If the task *itself* can be kicked off, but $targetExe cannot be launched, NO error occurs.
$taskInstance = $task.RunEx(
$null, # parameters to pass (Variant type)
0, # 0 == TASK_RUN_NO_FLAGS; use 1 == TASK_RUN_AS_SELF to override the configured user and run as the current one.
0, # 0 == default session ID; even when combined with 4 == TASK_RUN_USE_SESSION ID, targeting a different session doesn't seem to work.
$user # user name (use $null for the user configured with the task)
)
# Show the properties of the newly created task instance.
Write-Host "-- Launched task: "
$taskInstance | Format-Table *
# Give $targetExe some time to start up.
Start-Sleep 2
# List all $targetExe processes along with what user account launched them.
# To limit output to processes running in the hidden "services" session (session ID 0)
# use:
# Get-Process $targetExe | Where-Object SessionId -eq 0
Write-Host "-- $targetExe processes: "
Get-Process ([IO.Path]::GetFileNameWithoutExtension($targetExe)) -IncludeUserName |
Format-Table Id, Name, Username, SessionId
$ok = $true
} finally {
# Clean up.
$svc = $null
if ($haveCmdlets) {
Unregister-ScheduledTask -ErrorAction SilentlyContinue $taskName -Confirm:$false
}
else {
$null = schtasks /delete /tn $taskName /f
}
if ($ok) {
Write-Warning "A launched $targetExe process that doesn't exit by itself will continue to run. If safe to do, kill all existing instances with:`n Stop-Process -Force -ErrorAction SilentlyContinue -Name $([IO.Path]::GetFileNameWithoutExtension($targetExe))"
}
}
The above should yield something like the following:
-- Launched task:
Name InstanceGuid Path State CurrentAction EnginePID
---- ------------ ---- ----- ------------- ---------
__TestTask {E6112AC9-7616-46A1-9514-6344EC445485} \__TestTask 4 notepad.exe 4372
-- notepad.exe processes:
Id Name UserName SessionId
-- ---- -------- ---------
4372 notepad NT AUTHORITY\SYSTEM 0
Note that even though it is NT AUTHORITY\SYSTEM - not the requested NT SERVICE\TrustedInstaller account - that is the effective user name, only when started as NT SERVICE\TrustedInstaller will the process have the requisite security token to act as the trusted installer.
If you want notepad running as the system user in session 1 so that it's visible on the desktop, you can run notepad with sysinternals psexec from an elevated prompt:
psexec -si notepad
Thought I would share this quick function I made for myself, feel free to adapt it and improve it according to your needs.
Sometimes you want to run commands as the logged on user of a remote computer.
As you know, some commands show output for the user who runs it and if you run the same command with Invoke-Command, it won't return the user's information, but yours). Get-Printer is an example amongst many others.
There is no easy, quick way of running commands as the logged on user natively without any third-party apps like PsExec or others so I made this quick function that uses VBS, PS1 and Scheduled Task to make it happen.
It runs completly silently for the user (thanks to the VBS) and the output is shown in your console. Please note it assumes the remote computer has a C:\TEMP.
Created in a Windows 10, powershell v 5.1.17763.503 environement.
I don't pretend it's final and perfect, it's the simplest way I found to do what is needed and I just wanted to share it with you guys as it can be very useful!
Check the comments for explanation of the code and feel free to use it as you wish. Please share your version as I'm curious to see people improve it. A good idea would be to make it support multiple computers, but as I said it's a quick function I did I don't have too much time to put into refining it.
That being said, I had no problems using it multiple times as is :)
*Output returned is in form of a string, if you want to have a proper object, add '| ConvertFrom-String' and play with it :)
PLEASE NOTE: The surefire way of grabbing the username of who is currently logged on is via QWINSTA (since Win32_ComputerSystem - Username is only reliable if a user is logged on LOCALLY, it won't be right if a user is using RDP/RemoteDesktop). So this is what I used to grab the username, however, please note that in our french environement the name of the username property in QWINSTA is "UTILISATEUR",so you have to change that to your needs (english or other language) for it to work. If I remember correctly, it's "USERNAME" in english.
On this line:
$LoggedOnUser = (qwinsta /SERVER:$ComputerName) -replace '\s{2,22}', ',' | ConvertFrom-Csv | Where-Object {$_ -like "*Acti*"} | Select-Object -ExpandProperty UTILISATEUR
See code in the answer below.
function RunAsUser {
Param ($ComputerName,$Scriptblock)
#Check that computer is reachable
Write-host "Checking that $ComputerName is online..."
if (!(Test-Connection $ComputerName -Count 1 -Quiet)) {
Write-Host "$ComputerName is offline" -ForegroundColor Red
break
}
#Check that PsRemoting works (test Invoke-Command and if it doesn't work, do 'Enable-PsRemoting' via WMI method).
#*You might have the adjust this one to suit your environement.
#Where I work, WMI is always working, so when PsRemoting isn't, I enable it via WMI first.
Write-host "Checking that PsRemoting is enabled on $ComputerName"
if (!(invoke-command $ComputerName { "test" } -ErrorAction SilentlyContinue)) {
Invoke-WmiMethod -ComputerName $ComputerName -Path win32_process -Name create -ArgumentList "powershell.exe -command Enable-PSRemoting -SkipNetworkProfileCheck -Force" | Out-Null
do {
Start-Sleep -Milliseconds 200
} until (invoke-command $ComputerName { "test" } -ErrorAction SilentlyContinue)
}
#Check that a user is logged on the computer
Write-host "Checking that a user is logged on to $ComputerName..."
$LoggedOnUser = (qwinsta /SERVER:$ComputerName) -replace '\s{2,22}', ',' | ConvertFrom-Csv | Where-Object {$_ -like "*Acti*"} | Select-Object -ExpandProperty UTILISATEUR
if (!($LoggedOnUser) ) {
Write-Host "No user is logged on to $ComputerName" -ForegroundColor Red
break
}
#Creates a VBS file that will run the scriptblock completly silently (prevents the user from seeing a flashing powershell window)
#"
Dim wshell, PowerShellResult
set wshell = CreateObject("WScript.Shell")
Const WindowStyle = 0
Const WaitOnReturn = True
For Each strArg In WScript.Arguments
arg = arg & " " & strArg
Next 'strArg
PowerShellResult = wshell.run ("PowerShell " & arg & "; exit $LASTEXITCODE", WindowStyle, WaitOnReturn)
WScript.Quit(PowerShellResult)
"# | out-file "\\$ComputerName\C$\TEMP\RAU.vbs" -Encoding ascii -force
#Creates a script file from the specified '-Scriptblock' parameter which will be ran as the logged on user by the scheduled task created below.
#Adds 'Start-Transcript and Stop-Transcript' for logging the output.
$Scriptblock = "Start-Transcript C:\TEMP\RAU.log -force" + $Scriptblock + "Stop-Transcript"
$Scriptblock | out-file "\\$ComputerName\C$\TEMP\RAU.ps1" -Encoding utf8 -force
#On the remote computer, create a scheduled task that runs the .ps1 script silently in the user's context (with the help of the vbs)
Write-host "Running task on $ComputerName..."
Invoke-Command -ComputerName $ComputerName -ArgumentList $LoggedOnUser -ScriptBlock {
param($loggedOnUser)
$SchTaskParameters = #{
TaskName = "RAU"
Description = "-"
Action = (New-ScheduledTaskAction -Execute "wscript.exe" -Argument "C:\temp\RAU.vbs C:\temp\RAU.ps1")
Settings = (New-ScheduledTaskSettingsSet -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries -StartWhenAvailable -DontStopOnIdleEnd)
RunLevel = "Highest"
User = $LoggedOnUser
Force = $true
}
#Register and Start the task
Register-ScheduledTask #SchTaskParameters | Out-Null
Start-ScheduledTask -TaskName "RAU"
#Wait until the task finishes before continuing
do {
Write-host "Waiting for task to finish..."
$ScheduledTaskState = Get-ScheduledTask -TaskName "RAU" | Select-Object -ExpandProperty state
start-sleep 1
} until ( $ScheduledTaskState -eq "Ready" )
#Delete the task
Unregister-ScheduledTask -TaskName "RAU" -Confirm:$false
}
Write-host "Task completed on $ComputerName"
#Grab the output of the script from the transcript and remove the header (first 19) and footer (last 5)
$RawOutput = Get-Content "\\$ComputerName\C$\temp\RAU.log" | Select-Object -Skip 19
$FinalOutput = $RawOutput[0..($RawOutput.length-5)]
#Shows output
return $FinalOutput
#Delete the output file and script files
Remove-Item "\\$ComputerName\C$\temp\RAU.log" -force
Remove-Item "\\$ComputerName\C$\temp\RAU.vbs" -force
Remove-Item "\\$ComputerName\C$\temp\RAU.ps1" -force
}
#____________________________________________________
#Example command
#Note: Sometimes Start-Transcript doesn't show the output for a certain command, so if you run into empty output, add: ' | out-host' or '| out-default' at the end of the command not showing output.
$Results = RunAsUser -ComputerName COMP123 -Scriptblock {
get-printer | Select-Object name,drivername,portname | Out-host
}
$Results
#If needed, you can turn the output (which is a string for the moment) to a proper powershell object with ' | ConvertFrom-String'
I've written a PowerShell scrip that iterates through a large number of IIS W3C logfiles and inserts the values into a MSSQL database.
Set-Variable -Name "UnprocessedDir" -Value "X:\files" -Description "Folder for unprocessed log files" Scope Script
Set-Variable -Name "InputObject" -Value (New-Object -comObject MSUtil.LogQuery.IISW3CInputFormat) -Description "Log Parser input COM object" -Scope Script
Set-Variable -Name "OutputObject" -Value (New-Object -comObject MSUtil.LogQuery.SQLOutputFormat) -Description "Log Parser output COM object" -Scope Script
$OutputObject.clearTable = $false
$OutputObject.createTable = $false
$OutputObject.database = "Database_Name"
$OutputObject.driver = "SQL Server"
$OutputObject.dsn = "DSN_Name"
$OutputObject.fixColNames = $true
$OutputObject.ignoreIdCols = $true
$OutputObject.ignoreMinWarns = $true
$OutputObject.maxStrFieldLen = 511
$OutputObject.oConnString = $null
$OutputObject.password = $null
$OutputObject.server = "sqlserver.domain.com\INSTANCENAME"
$OutputObject.transactionRowCount = 5000
$OutputObject.username = $null
Set-Variable -Name "IISLogs" -Value #(Get-ChildItem -Path $UnprocessedDir -Recurse -File) -Description "Array of files to be imported into SQL" -Scope Script
Set-Variable -Name "LPComObj" -Value (New-Object -com MSUtil.LogQuery) -Description "COM Object used to import Log Parser records into MSSQL" -Scope Script
Write-Output "$(Get-ISOTimeStamp) Beginning SQL import. $($IISLogs.Count) Files to be imported"
$IISLogs | ForEach-Object { $loop = 0 } {
Set-Variable -Name "SubDir" -Value $(($_.FullName).Split('\')[-2]) -Description "Subdirectory where log file is located" -Scope Script
Set-Variable -Name "LogType" -Value $(($_.FullName).Split('\')[-3]) -Description "Type of log being imported" -Scope Script
Set-Variable -Name "ServerName" -Value $(($_.FullName).Split('\')[-4]) -Description "ServerName of file being imported" -Scope Script
Set-Variable -Name "LPQuery" -Description "Query to use in Log Parser" -Scope Script -Value #"
SELECT
-- FIELDS LogFilename,LogRow,date,time,c-ip,cs-username,s-sitename,s-computername,s-ip,s-port,cs-method,cs-uri-stem,cs-uri-query,sc-status,sc-substatus,sc-win32-status,sc-bytes,cs-bytes,time-taken,cs-version,cs-host,cs(User-Agent),cs(Cookie),cs(Referer),s-event,s-process-type,s-user-time,s-kernel-time,s-page-faults,s-total-procs,s-active-procs,s-stopped-procs
-- STANDARD FIELDS date,time,s-ip,cs-method,cs-uri-stem,cs-uri-query,s-port,cs-username,c-ip,cs(User-Agent),cs(Referer),sc-status,sc-substatus,sc-win32-status,time-taken
'$($ServerName)' as [servername],
'$($_.Name)' as [filename],
LogRow AS [row],
'$($LogType)' as [logtype],
TO_TIMESTAMP(date,time) AS [timestamp],
[s-ip],
[cs-method],
[cs-uri-stem],
[cs-uri-query],
TO_INT([s-port]),
[cs-username],
[c-ip],
[cs(User-Agent)],
[cs(Referer)],
TO_INT([sc-status]),
TO_INT([sc-substatus]),
TO_INT([sc-win32-status]),
TO_INT([time-taken]),
0 AS lock
INTO IIS_W3C
FROM '$($_.FullName)'
"#
Set-Variable -Name "LPResult" -Value ($LPComObj.ExecuteBatch($LPQuery, $InputObject, $OutputObject)) -Description "IIS Log File imported into SQL" -Scope Script
If ($LPResult -eq $false)
{
Write-Output "$(Get-ISOTimeStamp) Data imported from `"$($_.FullName)`""
Set-Variable -Name "loop" -Value ($loop + 1) -Description "Increase loop iteration Count" -Scope Script
}
Else
{
Write-Output "$(Get-ISOTimeStamp) Log Parser returned errors importing `"$($_.FullName)`""
Throw "$(Get-ISOTimeStamp) Log Parser returned errors importing `"$($_.FullName)`""
}
}
The number of logs I'm importing is tens of thousands; the code above works spectacularly for a few hundred files, but after a few hours, it crashes. From what I can tell, it looks like every iteration of the ForEach-Object loop creates a new SQL TCP connection which is not terminated at the end of the loop.
I've tried creating the $LPComObj both within and outside of the loop. I've tried Remove-Variable. I've tried some generic commands like $LPComObj.Close(), .Remove(), .Quit(), etc. The MSUtil.LogQuery method itself does not seem to contain any methods to close the SQL TCP connection, and as the script is running, I can see more and more TCP connections piling up. I tried a few things using [System.Runtime.Interopservices.Marshal]:: to release/remove the COM object, but none of them closed the TCP connection. Even closing the PowerShell session doesn't kill the connection.
The only way I was able to do this is by hunting down and finding the dllhost.exe" process that was using the ports and killing it. But from within the script, there isn't a clean way to get the PID of the offending dllhost.exe process. (Trying to kludge some variant of Get-Process | Stop-Process might work, but would add a lot of time to the execution of the script.)
What other ways might I be able to work around this problem?
I have a problem in running the below Powershell script in console.
function startMonitor {
$null = Register-ObjectEvent -InputObject ([Microsoft.Win32.SystemEvents]) -EventName "SessionSwitch" -Action {
switch($event.SourceEventArgs.Reason) {
'SessionLock'
{
---------do something----
}
'SessionUnlock'
{
--------do something----
}
}
}
}
startMonitor
When I run this in powershell ISE it works fine and output is as expected. When the session is locked or unlocked, output is generated perfectly.
But I want to run this as a script that starts up during logon.
I put this script in the startup folder as
powershell.exe -noexit -windowstyle hidden "E:\sources\lock.ps1"
The script runs fine. But, it does not generate the output (the other functions in this code generates output properly, except this function). When I try the command (without the switch -windowstyle):
Get-EventSubscriber
Shows that event is registered.
How do I run this script using the powershell.exe -noexit?
I am unable to use task scheduler for this purpose because of limitations in my environment.
In order to run in background you have to keep it within a loop and check for events, test with this little example...
[System.Reflection.Assembly]::LoadWithPartialName("System.Diagnostics")
# ------ init ------
function Start-mySession {
$null = Register-ObjectEvent -InputObject ([Microsoft.Win32.SystemEvents]) -EventName "SessionSwitch" -Action {
add-Type -AssemblyName System.Speech
$synthesizer = New-Object -TypeName System.Speech.Synthesis.SpeechSynthesizer
switch($event.SourceEventArgs.Reason) {
'SessionLock' { $synthesizer.Speak("Lock!") }
'SessionUnlock' { $synthesizer.Speak("Unlock!") }
}
}
}
# ------ stop ------
function End-mySession {
$events = Get-EventSubscriber | Where-Object { $_.SourceObject -eq [Microsoft.Win32.SystemEvents] }
$jobs = $events | Select-Object -ExpandProperty Action
$events | Unregister-Event
$jobs | Remove-Job
}
# ===== keep process in alive for 10 hours =====
[TimeSpan]$GLOBAL:timeout = new-timespan -Hours 10
$GLOBAL:sw = [diagnostics.stopwatch]::StartNew()
while ($GLOBAL:sw.elapsed -lt $GLOBAL:timeout){
[Array]$GLOBAL:XX_DEBUG_INFO += "DEBUG: initialize debug!"
# Loop
Start-mySession
start-sleep -seconds 10
#.. do something, or check results
End-mySession
}
# teardown after timeout
Write-Output "INFO: $( get-date -format "yyyy-MM-dd:HH:mm:ss" ) : $env:USERNAME : Timeout (logout/timout)"
End-mySession