Posts Tagged ‘Automate’

Too Many AutoHotkey Shortcuts To Remember? There’s An App For That!

January 19th, 2013 3 comments

I love AutoHotkey (AHK). Ever since I discovered it a little over a year ago I am constantly surprised and pleased with what I am able to accomplish with it.  And there in lied my problem.  Out of the box, AHK allows you to trigger your own scripts using hotkeys.  My problem was that I had so many of these little (and some large) scripts to do so many things, that i quickly ran out of hotkeys to use that wouldn’t interfere with other application’s shortcut keys (Visual Studio anyone); also even if I had more hotkeys available, trying to remember which ones did what was a nightmare. To remedy this, I created AHK Command Picker.

AHK Command Picker is really just a little UI that allows you to quickly run your scripts; so instead of using a hotkey, you just hit the Caps Lock key to bring up the UI, and then start typing for the script that you want to run.  It provides Camel Case filtering to still make launching scripts super fast, and also shows your scripts in a list, allowing you to browse through them. It also allows to you easily pass parameters into your scripts, which can be used to change a script’s behaviour.  Here’s a screenshot showing the UI and some of the many scripts that I have:


And here’s a screenshot showing it filter the list as you type:


One of my favourite things about AHK is the community. There are so many scripts out there that before you write your own code, you should just do a quick Google search and chances are you’ll find someone who has already written the script for you, or one that is close to your needs and can be quickly modified.  I assumed this would be the case for the type of tool I was looking for.  So I searched and came across HotKeyIt and Keyword Launcher, both of which were interesting and neat, but neither provided a GUI to quickly see your list of scripts so that you could launch it quickly.  They still relied on you to remember your specific hotkeys; I wanted a list that I could browse my scripts from to easily launch them.

I love programming and learning new languages, so I thought I’d give creating the type of picker that I wanted a shot. It’s still not perfect, and I have many ideas for new features that could make it better, but using it as it is today has improved my productivity so much.  I’ve shown it to some people at my work and many of them started using it and agree.

If all you are looking for is a program to quickly launch applications or open files, then I would recommend KeyBreeze.  It’s a great little free app written in C# and is very polished (and much better than Launchy in my opinion because you can add ANY file/app to it via the context menu). But if you use AHK and want to be able to quickly launch your own scripts, definitely check out AHK Command Picker. Like myself you will probably find that you go from using AHK to launch 10 – 20 scripts, to using it to launch hundreds or even thousands of scripts, saving you time and effort constantly throughout your day.  Those things that you maybe only do once or twice a month and didn’t want to dedicate a hotkey to will start showing up in your scripts list.

So go ahead and give AHK Command Picker a try, and be sure to let me know what you think of it and what improvements you would like to see by logging it on the Codeplex site.

Force ClickOnce applications to automatically update without prompting user – Automatically update MinimumRequiredVersion using PowerShell

August 15th, 2012 2 comments

Today I was thinking about using a ClickOnce application in my build process.  The problem is, when using an installed ClickOnce application (as opposed to an online one) if an update to the ClickOnce application is published, the application prompts the user to Accept or Skip downloading and applying the new update.  This would cause a problem for my automated builds as it would end up waiting forever for a user to click Accept.  This post lead me to the answer, which is:

“If your application is an installed application, you can force updates by using the MinimumRequiredVersion attribute. If you publish your application using Visual Studio, you can set this property from the Updates Dialog.”

Just for clarification, the dialog he mentions can be found in Visual Studio in Project Properties->Publish->Updates…  Ok, great.  This will allow the prompt to be suppressed, which is also useful if you don’t want to allow users to skip updates.

There is still a problem however.  Every time I publish a new version of the tool I have to remember to go in and update the MinimumRequiredVersion.  If I forget to do this and then publish another release, the prompt will be back and will ruin my automated builds.

To get around this I created a PowerShell script that keeps the MinimumRequiredVersion up to date, and I call it from a Post-Build event.  This allows me to never have to worry about manually setting the Minimum Required Version, since it gets updated automatically after every successful build.


I’ve improved upon the powershell script below and created a NuGet package that handles all of the setup/installation for you, as described in my newer post.


Here is the powershell script:

# Script finds the current ClickOnce version in a project's .csproj file, and updates the MinimumRequiredVersion to be this same version.
# This can be used to force a ClickOnce application to update automatically without prompting the user.

[Parameter(Position=0, HelpMessage="Comma separated paths of the .csproj files to process")]

# If a path to a project file was not provided, grab all of the project files in the same directory as this script.
if (-not($projectFilePaths))
# Get the directory that this script is in.
$scriptDirectory = Split-Path $MyInvocation.MyCommand.Path -Parent

# Create comma-separated list of project file paths.
Get-Item "$scriptDirectory\*.csproj" | foreach { $projectFilePaths += "$_,"}
$projectFilePaths = $projectFilePaths.TrimEnd(',')

# Catch any unhandled exceptions, write its error message, and exit the process with a non-zero error code to indicate failure.
[string]$errorMessage = [string]$_
[int]$exitCode = 1

# If this is one of our custom exceptions, strip the error code off of the front.
if ([string]$errorMessage.SubString(0, 1) -match "\d")
$exitCode = [string]$errorMessage.SubString(0, 1)
$errorMessage = [string]$errorMessage.SubString(1)

Write-Error $errorMessage
EXIT [int]$exitCode

Function UpdateProjectsMinimumRequiredClickOnceVersion
[Parameter(Mandatory=$true, Position=0, HelpMessage="The project file (.csproj) to update.")]
if (-not([System.IO.File]::Exists($projectFilePath))) { throw "2Cannot find project file to update at the path: '$projectFilePath'" }

# Build the regular expressions to find the information we will need.
$rxMinimumRequiredVersionTag = New-Object System.Text.RegularExpressions.Regex "\<MinimumRequiredVersion\>(?<Version>.*?)\</MinimumRequiredVersion\>", SingleLine
$rxApplicationVersionTag = New-Object System.Text.RegularExpressions.Regex "\<ApplicationVersion\>(?<Version>\d+\.\d+\.\d+\.).*?\</ApplicationVersion\>", SingleLine
$rxApplicationRevisionTag = New-Object System.Text.RegularExpressions.Regex "\<ApplicationRevision\>(?<Revision>[0-9]+)\</ApplicationRevision\>", SingleLine
$rxVersionNumber = [regex] "\d+\.\d+\.\d+\.\d+"

# Read the file contents in.
$text = [System.IO.File]::ReadAllText($projectFilePath)

# Get the current Minimum Required Version, and the Version that it should be.
$oldMinimumRequiredVersion = $rxMinimumRequiredVersionTag.Match($text).Groups["Version"].Value
$majorMinorBuild = $rxApplicationVersionTag.Match($text).Groups["Version"].Value
$revision = $rxApplicationRevisionTag.Match($text).Groups["Revision"].Value
$newMinimumRequiredVersion = [string]$majorMinorBuild + $revision

# If there was a problem constructing the new version number, throw an error.
if (-not $rxVersionNumber.Match($newMinimumRequiredVersion).Success)
throw "3'$projectFilePath' does not appear to have any ClickOnce deployment settings in it."

# If we couldn't find the old Minimum Required Version, throw an error.
if (-not $rxVersionNumber.Match($oldMinimumRequiredVersion).Success)
throw "4'$projectFilePath' is not currently set to enforce a MinimumRequiredVersion. To fix this in Visual Studio go to Project Properties->Publish->Updates... and check off 'Specify a minimum required version for this application'."

# Only write to the file if it is not already up to date.
if ($newMinimumRequiredVersion -eq $oldMinimumRequiredVersion)
Write "The Minimum Required Version of '$projectFilePath' is already up-to-date on version '$newMinimumRequiredVersion'."
# Update the file contents and write them back to the file.
$text = $rxMinimumRequiredVersionTag.Replace($text, "<MinimumRequiredVersion>" + $newMinimumRequiredVersion + "</MinimumRequiredVersion>")
[System.IO.File]::WriteAllText($projectFilePath, $text)
Write "Updated Minimum Required Version of '$projectFilePath' from '$oldMinimumRequiredVersion' to '$newMinimumRequiredVersion'"

# Process each of the project files in the comma-separated list.
$projectFilePaths.Split(",") | foreach { UpdateProjectsMinimumRequiredClickOnceVersion $_ }

The script was actually very small at first, but after commenting it and adding some proper error handling it is fairly large now.

So copy-paste the powershell script text into a new file, such as “UpdateClickOnceVersion.ps1”, and add this file to your project somewhere.

The next step now is to call this script from the Post Build event, so in Visual Studio go into your ClickOnce project’s properties and go to the Build Events tab.  In the Post-build event command line put the following:

REM Update the ClickOnce MinimumRequiredVersion so that it auto-updates without prompting
PowerShell set-executionpolicy remotesigned
PowerShell "$(ProjectDir)UpdateClickOnceVersion.ps1" "$(ProjectPath)"

The first line is just a comment.  The second line may be a security concern, so you might want to remove it.  Basically by default PowerShell is not allowed to run any scripts, so trying to run our script above would result in an error.  To fix this we change the execution policy to allow remotesigned scripts to be ran.  I’m not going to pretend to understand why powershell requires this (as I just started learning it today), but that line only needs to be ran on a PC once, so if you want to remove it from here and just run it from PowerShell manually instead (if you haven’t already ran it before), feel free to do so.  I just include it here so that other developers who build this tool in the future don’t have to worry about this setting.

The third line is where we are actually calling the powershell script, passing the path to the .csproj file to update as a parameter.  I added the powershell script to my project at the root level (so it sits right beside the .csproj file), but you can put the powershell script wherever you like.  Also, you don’t even have to include it in the project if you don’t want to, but I chose to so that it is easily visible for other developers when in Visual Studio, and so that it implicitly gets added to source control.  If you want to put the script in a folder instead of in the root of the project directory feel free; just remember to properly update the path in the post-build events.

So after going through and adding all of my nice error messages to the powershell script, I realized that if there is a problem with the script Visual Studio does not forward the error message to the Output window like I hoped it would; it just spits out the text in the Post-build event window and says an error occurred; which doesn’t really tell us anything.  So if you find you are getting errors, copy-paste that third line into PowerShell, replace the macro variables for their absolute values, and run it there.  Powershell should then give you a much more informative error message.

One last comment about this process is that because the powershell script modifies the .csproj outside of visual studio, after you publish a new version and build, the script will write to that .csproj file and visual studio will give you a prompt that the project was modified outside of visual studio and will want to reload it.  You can choose to reload it (which will close all file tabs for that project), or choose to ignore it; it’s up to you.  This is the one minor annoyance I haven’t been able to find a way around, but it’s still better than having to remember to update the Minimum Required Version manually after every new version of the tool I publish.

I hope you find this post useful, and I appreciate any comments; good or bad.  Happy coding!