Powershell: Prompt user to close running applications

Update: The PowerShell Application Deployment Toolkit provides this functionality and a lot more, including the ability to prevent users from launching applications while an installation is in progress and optionally allow the user to defer the installation X number of times, X number of days or until a deadline. It also provides a nice UI which you can customize with your own text and logo/banner. Check it out here: http://psappdeploytoolkit.codeplex.com

Recently I needed to write a PowerShell script that required that certain processes were not running. The script needed to be run on workstations, so I had to provide some user interaction to prompt the user to close the running applications.

Below is the snippet of code I used. The script checks to see if certain processes are running and prompts the user to close the applications. A simple message box with and exclamation mark and OK button is used.

MessageBox1

Update: 28/09/2011 – I have updated the script to display a balloon tip notification that the application(s) can be used again, instead of using a second message box prompt that the user would need to acknowledge. Thanks to Robert Robelo for this function: http://robertrobelo.wordpress.com/2010/03/19/balloon-tip-notifications/

Once the script has finished processing, a balloon tip notification is displayed notifying the user that the applications that were closed can now be used again.

Balloon

The message box can be customized using the parameters documented here:

http://msdn.microsoft.com/en-us/library/x83z1d9f(v=vs.85).aspx

For example, to display a message box with the Information Mark icon (64) and OK & Cancel Buttons (1), the “PromptType” parameter should be the sum of these values, i.e. 65. Note that a Cancel button won’t have any effect in the script below, since the user response is not evaluated, only the running processes are evaluated.

# Function to create a MessageBox prompt.
# Arguments: PromptText,PromptWaitTime(Seconds to Wait),PromptTitle,PromptType - See here: http://msdn.microsoft.com/en-us/library/x83z1d9f(v=vs.85).aspx
Function New-Prompt {
	Param (
	[string]$PromptText,
	[int]$PromptWaitTime,
	[string]$PromptTitle,
	[int]$PromptType
	)

	# Create a shell object and invoke a popup prompt
	$promptShell = New-Object -ComObject WScript.Shell		
	$promptAnswer = $promptShell.popup($promptText,$promptWaitTime,$promptTitle,$promptType)
}

# Function to create a balloon tip notification
Function Show-BalloonTip {
	Param(
	[Parameter(Mandatory = $true, Position = 0)]
	[ValidateNotNull()]
	[String]
	$BalloonTipText,
	[Parameter(Position = 1)]
	[String]
	$BalloonTipTitle = 'PowerShell Event Notificaton',
	[Parameter(Position = 2)]
	[ValidateSet('Error', 'Info', 'None', 'Warning')]
	[String]
	$BalloonTipIcon = 'Info',
	[Parameter(Position = 3)]
	[Int]
	$BalloonTipTime = 1000
	)
	end {
		Add-Type -AssemblyName System.Windows.Forms
		Add-Type -AssemblyName System.Drawing
		[Windows.Forms.ToolTipIcon]$BalloonTipIcon = $BalloonTipIcon
		$NotifyIcon = New-Object Windows.Forms.NotifyIcon -Property @{
			BalloonTipIcon = $BalloonTipIcon
			BalloonTipText = $BalloonTipText
			BalloonTipTitle = $BalloonTipTitle
			Icon = [Drawing.Icon]::ExtractAssociatedIcon((Get-Command powershell).Path)
			Text = -join $BalloonTipText[0..62]
			Visible = $true
		}
		switch ($BalloonTipIcon) {
			Error {[Media.SystemSounds]::Hand.Play()}
			Info {[Media.SystemSounds]::Asterisk.Play()}
			None {[Media.SystemSounds]::Beep.Play()}
			Warning {[Media.SystemSounds]::Exclamation.Play()}
		}
		$NotifyIcon.ShowBalloonTip($BalloonTipTime)		
		switch ($Host.Runspace.ApartmentState) {
			STA {
				$null = Register-ObjectEvent -InputObject $NotifyIcon -EventName BalloonTipClosed -Action { 					
					$Sender.Dispose()
					Unregister-Event $EventSubscriber.SourceIdentifier
					Remove-Job $EventSubscriber.Action
				}
			}
			default {
				continue
			}
		}
	}
}

# Function to check for running applications and prompt user to close them. 
Function Stop-RunningApplications {
	Param(
	[parameter(Mandatory = $true)]
	[string]$ProcessName # Specify process names separated by commas 
	)		
	# Split multiple processes on a comma and join with the regex operator '|' to perform "or" match against multiple applications
	$processName = $processName -split(",") -join ("|")	
	# Prompt the user as long as one of the matching processes are found running and store the processes description
	While (Get-Process | Where { $_.ProcessName -match $processName } -ErrorAction SilentlyContinue | Select Description -ExpandProperty Description | Select -Unique -OutVariable runningProcess) {	 
		$promptText = "The following application(s) must be closed before the script can proceed:`n`n" + ($runningProcess -join ",") + "`n`nPlease close the application and press OK to continue with the script."
		New-Prompt -PromptText $promptText -PromptWaitTime 0 -PromptTitle "Message from PowerShell Script" -PromptType 48		
		# Maintain one array of all the unique processes matched from every iteration of the while loop
		[array]$matchedProcess = (([array]$matchedProcess + [array]$runningProcess) | Select -Unique)
		# Make the matched processes available outside the scope of the function, so that the user can be notified later that applications can be used again
		If ($matchedProcess -ne $null) { 
			Set-Variable -Name matchedProcess -Value $MatchedProcess -Scope Script
		}		
	}
}

Stop-RunningApplications -ProcessName "iexplore,word,excel,powerpnt" 

#
# Do processing here ...
# 

# Notify user that the application(s) can be used again
If ($matchedProcess -ne $null) {
	$balloonText = "You may use " + ($matchedProcess -join ",") + " again."	
	Show-BalloonTip -BalloonTipIcon "Info" -BalloonTipText $balloonText -BalloonTipTitle "Windows PowerShell" -BalloonTipTime 1000	
}
Advertisements