Monday, April 29, 2013

Retrospectively timing long-running operations in PowerShell

Sometimes I run an operation that takes longer than I expect to execute, but once it is finished, the only way to see how long it did take is to run it again in Measure-Command or use some other timing mechanism. This PowerShell prompt preserves your existing prompt (for example PoshGit) and tacks on an execution time for each and every command you run.
C:\Demo [master]> Start-Sleep 3
00:00:03.0048920
C:\Demo [master]> Start-Sleep 5
00:00:04.9974939
C:\Demo [master]>
00:00:00.0004274
C:\Demo [master]>

Installation (Quick Start)

To install the prompt, run the following PowerShell command:

iex (New-Object Net.Webclient).DownloadString('https://gist.github.com/nzbart/5481021/raw/3-Installer.ps1')

That's it.

Overview

A PowerShell prompt that times how long each operation takes and displays the result at the command prompt. Measure-Command is great, but only useful if you had the presence of mind to run your long-running command within it.

A balloon will pop up in the notification area telling you of any tasks that take longer than 30 seconds to complete.

An example of what the command line looks like (playing nicely with PoshGit):

C:\Demo [master]> Start-Sleep 3
00:00:03.0048920
C:\Demo [master]> Start-Sleep 5
00:00:04.9974939
C:\Demo [master]>
00:00:00.0004274
C:\Demo [master]>

Known Issues

  • Autocompletion of path names etc. causes the PowerShell host to fire a Busy event, which prematurely sets of the timer, inflating the next timing measurement.
  • The notification icon is not disposed of when the PowerShell instance shuts down.
<#
.SYNOPSIS
Starts a timer that automatically times how long operations take and displays elapsed time at the command prompt.
A notification balloon pops up if a task takes longer than 30 seconds.
.NOTES
Author: Bart Joy
#>
function Start-TimerPrompt()
{
$ErrorActionPreference = 'Stop'
if(!$global:prompt_timer) {
$global:prompt_old = get-content function:\prompt
$className = 'StateChanged'
Add-Type -IgnoreWarnings @"
public class $className {
public System.Diagnostics.Stopwatch Timer { get; set; }
public $className(System.Management.Automation.Runspaces.Runspace runspace) {
runspace.AvailabilityChanged += (o,a) => {
if(runspace.RunspaceAvailability == System.Management.Automation.Runspaces.RunspaceAvailability.Busy && Timer == null)
{
Timer = System.Diagnostics.Stopwatch.StartNew();
}
};
}
}
"@
$global:prompt_timer = New-Object $className $Host.Runspace
}
function global:Prompt()
{
if($global:prompt_timer) {
$timer = $global:prompt_timer.Timer
if($timer) {
$elapsed = $timer.Elapsed
Write-Host $elapsed
if($timer.ElapsedMilliseconds -gt 30 * 1000) {
Show-BalloonTip -Title "PowerShell Task Completed" -MessageType Info -Message "A long running task has completed." -Duration 10000
}
$global:prompt_timer.Timer = $null
}
}
& $global:prompt_old
}
}
function Show-BalloonTip(
[parameter(Mandatory=$true)][string]$Title,
[parameter(Mandatory=$true)][string]$Message,
[string][ValidateSet('None', 'Info', 'Warning', 'Error')]$MessageType = 'Info',
[int]$DurationInMilliseconds = 10000
)
{
$balloon = $global:notification_baloon
if(!$balloon) {
Add-Type -AssemblyName System.Drawing
Add-Type -AssemblyName System.Windows.Forms
$icon = [System.Drawing.Icon]::ExtractAssociatedIcon((Get-Process -Id $PID).Path)
$balloon = New-Object System.Windows.Forms.NotifyIcon
$balloon.Icon = $icon
$balloon.Visible = $true
$global:notification_baloon = $balloon
}
$balloon.BalloonTipIcon = $MessageType
$balloon.BalloonTipText = $Message
$balloon.BalloonTipTitle = $Title
$balloon.ShowBalloonTip($DurationInMilliseconds)
}
<#
.SYNOPSIS
This script is only intended for easy installation via Github Gist. It is not required for normal operation of the tool.
See 1-Installation.markdown for installation instructions.
#>
$profileDir = Split-Path -Parent $profile
$moduleFile = Join-Path $profileDir 'TimerPrompt.psm1'
(new-object net.webclient).DownloadFile('https://gist.github.com/nzbart/5481021/raw/2-TimerPrompt.psm1', $moduleFile)
$command = 'ipmo $PSScriptRoot\TimerPrompt.psm1'
$content = cat $profile -ErrorAction SilentlyContinue
if($content -notcontains $command) {
$content += "`r`n`r`n$command`r`nStart-TimerPrompt`r`n"
$content | Set-Content -Path $profile -Encoding UTF8
}
& $profile
Write-Host 'TimerPrompt successfully installed.'
Write-Host
view raw 3-Installer.ps1 hosted with ❤ by GitHub

Thursday, April 25, 2013

Old SysInternals source code

The source code for SysInternals tools is no longer published, although it was for some of the tools before Microsoft purchased them.

The source code can be downloaded for those tools at the Internet Archive.

Monday, April 22, 2013

Launch NUnit GUI with multiple assemblies

The NUnit GUI does not support loading multiple assemblies from the command line, so this PowerShell function creates an NUnit project file that can be specified as a command line argument. Just provide a file name for the new project file (must end in .nunit or NUnit will barf) and an array of assembly files to load.

As a bonus, this project file will load your assemblies in multiple process mode, which means that any associated assembly configuration files will be correctly loaded.

function WriteNUnitProjectFile(
[string][parameter(mandatory)]$projectFilePath,
[string[]][parameter(mandatory)]$assemblyPaths)
{
$xmlWriter = New-Object System.Xml.XmlTextWriter $projectFilePath, $null
try {
$xmlWriter.Formatting = "Indented"
$xmlWriter.Indentation = "4"
$xmlWriter.WriteStartDocument()
$xmlWriter.WriteStartElement("NUnitProject")
$xmlWriter.WriteStartElement("Settings")
$xmlWriter.WriteAttributeString("activeconfig", 'Default')
$xmlWriter.WriteAttributeString("processModel", 'Multiple')
$xmlWriter.WriteEndElement()
$xmlWriter.WriteStartElement("Config")
$xmlWriter.WriteAttributeString("name", 'Default')
$xmlWriter.WriteAttributeString("binpathtype", 'Auto')
$assemblyPaths | % {
$xmlWriter.WriteStartElement("assembly")
$xmlWriter.WriteAttributeString("path", $_)
$xmlWriter.WriteEndElement()
}
$xmlWriter.WriteEndElement()
$xmlWriter.WriteEndElement()
$xmlWriter.WriteEndDocument()
}
finally {
$xmlWriter.Dispose();
}
}
view raw gistfile1.ps1 hosted with ❤ by GitHub
 
W3Counter Web Stats