Set your Powershell console window icon (at runtime)
In my last post I mentioned that I use the title of a console window to help track what codebase/component I’m working with. Another way we organize some of these windows is via the window icon (we happen to have color names for our various codebases, so it works pretty well). Today my co-worker Scott mentioned that if there was a way to programmatically set the icon of the console window, he’d think about switching to Powershell. We normally use different shortcuts to assign a custom icon. I couldn’t pass up the challenge!
Having the .NET framework at your disposal in Powershell is amazing. Add to that the ability to do P/Invoke calls and, while it’s a little more advanced than an average administrator will use, your scripts are really quite unlimited. Thanks to Lee Holmes for the Invoke-Win32 function.
Here is Set-ConsoleIcon.ps1 for your enjoyment! (You can download it here.)
1: ##############################################################################
2: ##
3: ## Script: Set-ConsoleIcon.ps1
4: ## By: Aaron Lerch
5: ## Website: www.aaronlerch.com/blog
6: ##
7: ## Set the icon of the current console window to the specified icon
8: ##
9: ## Usage: Set-ConsoleIcon [string]
10: ##
11: ## ie:
12: ##
13: ## PS:1 > Set-ConsoleIcon "C:Iconsspecial_powershell_icon.ico"
14: ##
15: ##############################################################################
16:
17: param(
18: [string] $iconFile
19: )
20:
21: $WM_SETICON = 0x80
22: $ICON_SMALL = 0
23:
24: function Main
25: {
26: [System.Reflection.Assembly]::LoadWithPartialName("System.Drawing") | out-null
27:
28: # Verify the file exists
29: if ([System.IO.File]::Exists($iconFile) -eq $TRUE)
30: {
31: $icon = new-object System.Drawing.Icon($iconFile)
32:
33: if ($icon -ne $null)
34: {
35: $consoleHandle = GetConsoleWindow
36: SendMessage $consoleHandle $WM_SETICON $ICON_SMALL $icon.Handle | out-null
37: }
38: }
39: else
40: {
41: Write-Host "Icon file not found"
42: }
43: }
44:
45:
46: ## Invoke a Win32 P/Invoke call.
47: ## From: Lee Holmes
48: ## http://www.leeholmes.com/blog/GetTheOwnerOfAProcessInPowerShellPInvokeAndRefOutParameters.aspx
49: function Invoke-Win32([string] $dllName, [Type] $returnType,
50: [string] $methodName, [Type[]] $parameterTypes, [Object[]] $parameters)
51: {
52: ## Begin to build the dynamic assembly
53: $domain = [AppDomain]::CurrentDomain
54: $name = New-Object Reflection.AssemblyName 'PInvokeAssembly'
55: $assembly = $domain.DefineDynamicAssembly($name, 'Run')
56: $module = $assembly.DefineDynamicModule('PInvokeModule')
57: $type = $module.DefineType('PInvokeType', "Public,BeforeFieldInit")
58:
59: ## Go through all of the parameters passed to us. As we do this,
60: ## we clone the user's inputs into another array that we will use for
61: ## the P/Invoke call.
62: $inputParameters = @()
63: $refParameters = @()
64:
65: for($counter = 1; $counter -le $parameterTypes.Length; $counter++)
66: {
67: ## If an item is a PSReference, then the user
68: ## wants an [out] parameter.
69: if($parameterTypes[$counter - 1] -eq [Ref])
70: {
71: ## Remember which parameters are used for [Out] parameters
72: $refParameters += $counter
73:
74: ## On the cloned array, we replace the PSReference type with the
75: ## .Net reference type that represents the value of the PSReference,
76: ## and the value with the value held by the PSReference.
77: $parameterTypes[$counter - 1] =
78: $parameters[$counter - 1].Value.GetType().MakeByRefType()
79: $inputParameters += $parameters[$counter - 1].Value
80: }
81: else
82: {
83: ## Otherwise, just add their actual parameter to the
84: ## input array.
85: $inputParameters += $parameters[$counter - 1]
86: }
87: }
88:
89: ## Define the actual P/Invoke method, adding the [Out]
90: ## attribute for any parameters that were originally [Ref]
91: ## parameters.
92: $method = $type.DefineMethod($methodName, 'Public,HideBySig,Static,PinvokeImpl',
93: $returnType, $parameterTypes)
94: foreach($refParameter in $refParameters)
95: {
96: $method.DefineParameter($refParameter, "Out", $null)
97: }
98:
99: ## Apply the P/Invoke constructor
100: $ctor = [Runtime.InteropServices.DllImportAttribute].GetConstructor([string])
101: $attr = New-Object Reflection.Emit.CustomAttributeBuilder $ctor, $dllName
102: $method.SetCustomAttribute($attr)
103:
104: ## Create the temporary type, and invoke the method.
105: $realType = $type.CreateType()
106: $realType.InvokeMember($methodName, 'Public,Static,InvokeMethod', $null, $null,
107: $inputParameters)
108:
109: ## Finally, go through all of the reference parameters, and update the
110: ## values of the PSReference objects that the user passed in.
111: foreach($refParameter in $refParameters)
112: {
113: $parameters[$refParameter - 1].Value = $inputParameters[$refParameter - 1]
114: }
115: }
116:
117: function SendMessage([IntPtr] $hWnd, [Int32] $message, [Int32] $wParam, [Int32] $lParam)
118: {
119: $parameterTypes = [IntPtr], [Int32], [Int32], [Int32]
120: $parameters = $hWnd, $message, $wParam, $lParam
121:
122: Invoke-Win32 "user32.dll" ([Int32]) "SendMessage" $parameterTypes $parameters
123: }
124:
125: function GetConsoleWindow()
126: {
127: Invoke-Win32 "kernel32" ([IntPtr]) "GetConsoleWindow"
128: }
129:
130: . Main
August 22nd, 2007 at 11:29 pm
Guess I have to use Powershell now.
August 22nd, 2007 at 11:29 pm
Done – I had forgotten about that site, thanks!
August 22nd, 2007 at 11:29 pm
This looks like a script that a lot of people would find useful.
You could really help the community by submitting it to the ScriptCenter script repository. That is the first place a lot of people will go to when looking for a script to do something.
You can submit it by using the links at: http://www.microsoft.com/technet/scriptcenter/csc/default.mspx
Thanks!
Jeffrey Snover [MSFT]
Windows PowerShell/MMC Architect
Visit the Windows PowerShell Team blog at: http://blogs.msdn.com/PowerShell
Visit the Windows PowerShell ScriptCenter at: http://www.microsoft.com/technet/scriptcenter/hubs/msh.mspx