Skip to content

Instantly share code, notes, and snippets.

@tresf
Last active August 18, 2024 17:51
Show Gist options
  • Save tresf/4e19e15ad38354af6732ee701c990102 to your computer and use it in GitHub Desktop.
Save tresf/4e19e15ad38354af6732ee701c990102 to your computer and use it in GitHub Desktop.
PowerShell QZ Tray Session Stealer (for Windows fast user switching)

PowerShell QZ Tray Session Stealer

Custom session stealer for Windows fast user switching

For a pure Batch solution, see here instead: https://gist.github.com/tresf/ff91bd29f1f6846c13bf6f8bc255ab55

Problem

  • QZ Tray is a singleton application, so only one instance can run at a time.
  • Windows fast-user switching feature leaves applications running when a new user signs onto a shared workstation
  • QZ Tray can "steal" the instance, however this is retroactive and places a burden on the user

Solution

  • A solution is to detect that a user switch has occured and start QZ Tray on the new desktop automatically

Limitations

  • This should ONLY be run on shared workstations with one user at a time. Terminal Server environments should instead leverage a dedicated print-server solution.
  • Company Branded builds must replace "QZ Tray", "qz-tray" and "qz" in each file with the name of your respective application.

Steps

  1. Copy start_if_active.ps1 to C:\Program Files\QZ Tray\
  2. Import Steal QZ Tray At Login.xml to Task Scheduler
    • If the new entry does not immediately appear after saving, Action -> Refresh
  3. Test fast-user switching
<#
.SYNOPSIS
Starts .\qz-tray.exe but only if the user is actively logged in
#>
$hidden = $True
if($host.Name -match 'consolehost') {
$hidden = $False
}
if($hidden) {
# Attempt to actually hide this window
$ShowWindow = Add-Type -Name ShowWindow -Member '[DllImport("user32.dll")] public static extern bool ShowWindow(int handle, int state);' -NameSpace Native
$ShowWindow::ShowWindow(([System.Diagnostics.Process]::GetCurrentProcess() | Get-Process).MainWindowHandle, 0)
}
$activeUser = Get-CimInstance –ClassName Win32_ComputerSystem | Select-Object -ExpandProperty UserName -First 1
$currentUser = [System.Security.Principal.WindowsIdentity]::GetCurrent().Name
$qz = "$(split-path -parent $MyInvocation.MyCommand.Definition)\qz-tray.exe"
$message = "[$($MyInvocation.MyCommand.Name)] $(Get-Date -f "yyyy-MM-dd HH:mm:ss")"
$foregroundColor = "yellow"
if ( $activeUser -ieq $currentUser ) {
$foregroundColor = "green"
$message += " SUCCESS: `"$currentUser`" is active, starting $qz"
Start-Process $qz -ArgumentList "--steal"
} else {
$message += " SKIPPED: `"$currentUser`" is NOT active, skipping $qz"
}
$message >> "$env:APPDATA\qz\task-scheduler.log"
Write-Host -ForegroundColor $foregroundColor $message
Write-Host
if(-Not $hidden) {
pause
}
<?xml version="1.0" encoding="UTF-16"?>
<Task version="1.2" xmlns="http://schemas.microsoft.com/windows/2004/02/mit/task">
<RegistrationInfo>
<Date>2022-12-13T12:29:01.6615572</Date>
<Author>tres@qz.io</Author>
<URI>\Steal QZ Tray At Login</URI>
</RegistrationInfo>
<Triggers>
<EventTrigger>
<Enabled>true</Enabled>
<Subscription>&lt;QueryList&gt;&lt;Query Id="0" Path="Security"&gt;&lt;Select Path="Security"&gt;*[EventData[ (Data[@Name='LogonType']=2) and (Data[@Name='ElevatedToken']='%%1843')]] and *[System[(EventID=4624)]]&lt;/Select&gt;&lt;/Query&gt;&lt;/QueryList&gt;</Subscription>
</EventTrigger>
</Triggers>
<Principals>
<Principal id="Author">
<GroupId>S-1-5-32-545</GroupId>
<RunLevel>LeastPrivilege</RunLevel>
</Principal>
</Principals>
<Settings>
<MultipleInstancesPolicy>StopExisting</MultipleInstancesPolicy>
<DisallowStartIfOnBatteries>false</DisallowStartIfOnBatteries>
<StopIfGoingOnBatteries>false</StopIfGoingOnBatteries>
<AllowHardTerminate>true</AllowHardTerminate>
<StartWhenAvailable>false</StartWhenAvailable>
<RunOnlyIfNetworkAvailable>false</RunOnlyIfNetworkAvailable>
<IdleSettings>
<StopOnIdleEnd>false</StopOnIdleEnd>
<RestartOnIdle>false</RestartOnIdle>
</IdleSettings>
<AllowStartOnDemand>true</AllowStartOnDemand>
<Enabled>true</Enabled>
<Hidden>false</Hidden>
<RunOnlyIfIdle>false</RunOnlyIfIdle>
<WakeToRun>false</WakeToRun>
<ExecutionTimeLimit>PT0S</ExecutionTimeLimit>
<Priority>7</Priority>
</Settings>
<Actions Context="Author">
<Exec>
<Command>powershell.exe</Command>
<Arguments>-WindowStyle Hidden -File start_if_active.ps1</Arguments>
<WorkingDirectory>%PROGRAMFILES%\QZ Tray\</WorkingDirectory>
</Exec>
</Actions>
</Task>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment