Scott Hanselman

Awesome Visual Studio Command Prompt and PowerShell icons with Overlays

August 19, '10 Comments [10] Posted in Musings | PowerShell | VS2010
Sponsored By

I'm not usually one of the "icon people." By that I mean, I don't collect icons, or change all the icons on my system to custom fancypants icons. But, I noticed today that I was using the Command Prompt alongside a Visual Studio Command Prompt (which is just a command prompt with the right path and environment set) as well as regular PowerShell as well as a PowerShell prompt with "VSVars32" set (again, just PowerShell with the right environment setup). However, their icons all look the same.

Seemed like a quick opportunity to edit a few icons and change my world. I went over and downloaded the most lovely Free Icon Editor I know of, IcoFX. I encourage you to donate to them because they are doing the world a wonderful service. I used their Extract command on cmd.exe, as well as devenv.exe and powershell.exe.

 Extract Icon

Disclaimer: I'm sure I'm breaking all sorts of international law or something by doing this. When the ninjas burst in and say "you can't use our icons for fun" I will likely deny having written this post. Back me up on this. I did this for me. This is not official Microsoft anything and you can't say it is. Who are you!? Stop calling me! Jimmy no live here! You no call back!

Aside: Here's the part I'm bummed about. It seems that the VS2010 icon editor is still stupid about alpha channels. I'm actually scandalized about the whole thing, but since I don't work on that team, I'll need to dig in to get more details. I would have liked to have done this all in VS.

OK, so now I've got all my icons loaded in IcoFX. I will edit them all and make a nice icon in one of the many resolutions that are available, even though technically I suppose for my use I just need 32x32 icons for the Windows 7 Taskbar and/or my desktop.

IcoFX

A little editing and resizing...seriously, this Icon Editor is a joy. Go now!

IcoFX (3)

I saved these as vscommand.ico and vspowershell.ico and now I have these two nice icons on my desktop.

image

Now I pin the "Visual Studio Command Prompt" to the Taskbar, and it looks like this:

image

I even did a little one for the system menu, 'cause that's how I roll.

image

OK, so that's lovely.

However, when I'm in PowerShell, I'll sometimes switch my VSVars on by running the custom PowerShell VSVars script that I put in my Microsoft.PowerShell_profile.ps1. Remember this one from Chris Tavares?

function Get-Batchfile ($file) {
$cmd = "`"$file`" & set"
cmd /c $cmd | Foreach-Object {
$p, $v = $_.split('=')
Set-Item -path env:$p -value $v
}
}

function VsVars32($version = "10.0")
{
$key = "HKLM:SOFTWARE\Microsoft\VisualStudio\" + $version
$VsKey = get-ItemProperty $key
$VsInstallPath = [System.IO.Path]::GetDirectoryName($VsKey.InstallDir)
$VsToolsDir = [System.IO.Path]::GetDirectoryName($VsInstallPath)
$VsToolsDir = [System.IO.Path]::Combine($VsToolsDir, "Tools")
$BatchFile = [System.IO.Path]::Combine($VsToolsDir, "vsvars32.bat")
Get-Batchfile $BatchFile
[System.Console]::Title = "Visual Studio " + $version + " Windows Powershell"
//add a call to set-consoleicon as seen below...hm...!
}

Why not go completely over the top and combine this with Aaron Lerch's script for "Changing the Windows PowerShell Console Icon"? This way, when I call "vsvars32" I'll also change the Icon for my PowerShell. Crazy.

Here's Aaron's script with a few changes to make it a dot-sourced function and a couple typo fixes. This changes the system menu icon on the fly, but doesn't refresh the taskbar or ALT-TAB yet. Not sure if that's possible?

##############################################################################
## Script: Set-ConsoleIcon.ps1
## By: Aaron Lerch, tiny tiny mods by Hanselman
## Website: www.aaronlerch.com/blog
## Set the icon of the current console window to the specified icon
## Dot-Source first, like . .\set-consoleicon.ps1
## Usage: Set-ConsoleIcon [string]
## PS:1 > Set-ConsoleIcon "C:\Icons\special_powershell_icon.ico"
##############################################################################

$WM_SETICON = 0x80
$ICON_SMALL = 0

function Set-ConsoleIcon
{
param(
[string] $iconFile
)

[System.Reflection.Assembly]::LoadWithPartialName("System.Drawing") | out-null
$iconFile
# Verify the file exists
if ([System.IO.File]::Exists($iconFile) -eq $TRUE)
{
$icon = new-object System.Drawing.Icon($iconFile)

if ($icon -ne $null)
{
$consoleHandle = GetConsoleWindow
SendMessage $consoleHandle $WM_SETICON $ICON_SMALL $icon.Handle
}
}
else
{
Write-Host "Icon file not found"
}
}


## Invoke a Win32 P/Invoke call.
## From: Lee Holmes
## http://www.leeholmes.com/blog/GetTheOwnerOfAProcessInPowerShellPInvokeAndRefOutParameters.aspx
function Invoke-Win32([string] $dllName, [Type] $returnType,
[string] $methodName, [Type[]] $parameterTypes, [Object[]] $parameters)
{
## Begin to build the dynamic assembly
$domain = [AppDomain]::CurrentDomain
$name = New-Object Reflection.AssemblyName 'PInvokeAssembly'
$assembly = $domain.DefineDynamicAssembly($name, 'Run')
$module = $assembly.DefineDynamicModule('PInvokeModule')
$type = $module.DefineType('PInvokeType', "Public,BeforeFieldInit")

## Go through all of the parameters passed to us. As we do this,
## we clone the user's inputs into another array that we will use for
## the P/Invoke call.
$inputParameters = @()
$refParameters = @()

for($counter = 1; $counter -le $parameterTypes.Length; $counter++)
{
## If an item is a PSReference, then the user
## wants an [out] parameter.
if($parameterTypes[$counter - 1] -eq [Ref])
{
## Remember which parameters are used for [Out] parameters
$refParameters += $counter

## On the cloned array, we replace the PSReference type with the
## .Net reference type that represents the value of the PSReference,
## and the value with the value held by the PSReference.
$parameterTypes[$counter - 1] =
$parameters[$counter - 1].Value.GetType().MakeByRefType()
$inputParameters += $parameters[$counter - 1].Value
}
else
{
## Otherwise, just add their actual parameter to the
## input array.
$inputParameters += $parameters[$counter - 1]
}
}

## Define the actual P/Invoke method, adding the [Out]
## attribute for any parameters that were originally [Ref]
## parameters.
$method = $type.DefineMethod($methodName, 'Public,HideBySig,Static,PinvokeImpl',
$returnType, $parameterTypes)
foreach($refParameter in $refParameters)
{
$method.DefineParameter($refParameter, "Out", $null)
}

## Apply the P/Invoke constructor
$ctor = [Runtime.InteropServices.DllImportAttribute].GetConstructor([string])
$attr = New-Object Reflection.Emit.CustomAttributeBuilder $ctor, $dllName
$method.SetCustomAttribute($attr)

## Create the temporary type, and invoke the method.
$realType = $type.CreateType()
$realType.InvokeMember($methodName, 'Public,Static,InvokeMethod', $null, $null,
$inputParameters)

## Finally, go through all of the reference parameters, and update the
## values of the PSReference objects that the user passed in.
foreach($refParameter in $refParameters)
{
$parameters[$refParameter - 1].Value = $inputParameters[$refParameter - 1]
}
}

function SendMessage([IntPtr] $hWnd, [Int32] $message, [Int32] $wParam, [Int32] $lParam)
{
$parameterTypes = [IntPtr], [Int32], [Int32], [Int32]
$parameters = $hWnd, $message, $wParam, $lParam

Invoke-Win32 "user32.dll" ([Int32]) "SendMessage" $parameterTypes $parameters
}

function GetConsoleWindow()
{
Invoke-Win32 "kernel32" ([IntPtr]) "GetConsoleWindow"
}

It might also be interesting to make a ".Icon" property on System.Console using PowerShell's "Type Extensions" abilities. That way I could do[System.Console]::Icon = "something.ico", but I'll leave that as an exercise for the reader.

Magical Visual Studio Command Prompt Icons

Remember, we never spoke.

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.

facebook twitter subscribe
About   Newsletter
Sponsored By
Hosting By
Dedicated Windows Server Hosting by ORCS Web
Thursday, August 19, 2010 12:34:36 AM UTC
Your posts are always a joy to read!! Thanks Scott!!
Thursday, August 19, 2010 4:21:30 AM UTC
Good info & good lolz.
The Dawg
Thursday, August 19, 2010 6:14:17 AM UTC
Scott it's because of you that I love Microsoft! :o)
Thursday, August 19, 2010 7:44:06 AM UTC
Just a note for other readers:
On x64, if it throws an error here:
> $VsInstallPath = [System.IO.Path]::GetDirectoryName($VsKey.InstallDir)

Try change registry path to this one:
$key = "HKLM:SOFTWARE\Wow6432Node\Microsoft\VisualStudio\" + $version
Thursday, August 19, 2010 8:20:07 AM UTC
Nice. I got my command prompt icons looking cool now .:)
Thursday, August 19, 2010 1:31:56 PM UTC
Great written, got a laugh or two ;-)

There is no spoon...!
Thursday, August 19, 2010 7:44:36 PM UTC
Wonder if broadcasting a WM_SETTINGCHANGE message would refresh the taskbar and ALT-TAB icons:
To send the WM_SETTINGCHANGE message to all top-level windows, use the SendMessageTimeout function with the hwnd parameter set to HWND_BROADCAST.
http://msdn.microsoft.com/en-us/library/ms725497(VS.85).aspx
Friday, August 20, 2010 3:16:23 PM UTC
As a developer/designer, I couldn't live without my favorite icon creator/editor, Axialis Icon Workshop. It's not free, but it is worth every penny (to me, at least). It also integrates with VS 2010 to replace its brain-dead icon editor.
Saturday, August 21, 2010 3:33:03 PM UTC
Hi Scott,
I am a keen follower of your blog, but i have this requirement where i need to customize the boot screen of windows, but is there any .net API that can do this, which i can set during installation.

Thanks
Lookman
Tuesday, August 24, 2010 12:33:46 PM UTC
To make the VsVars32 script work on x86 and x64:

if ([intptr]::size -eq 8)
{
$key = "HKLM:SOFTWARE\Wow6432Node\Microsoft\VisualStudio\" + $version
}
else
{
$key = "HKLM:SOFTWARE\Microsoft\VisualStudio\" + $version
}


http://wiki.poshcode.org/FAQ/Tips_and_Tricks/Understanding_64Bit
Richard
Comments are closed.

Disclaimer: The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.