超赞的Visual Studio命令提示符和PowerShell图标覆盖

[原文发表地址] Awesome Visual Studio Command Prompt and PowerShell icons with Overlays

[原文发表时间]2010-08-18 17:00

一般来说,我并不属于“图标一族”。我的意思是说,我并没有收集图标,或者把系统上的图标换成自定义炫酷图标。不过,今天我注意到自己在使用Visual Studio 命令提示符(拥有正确路径和环境设置的命令提示符),还有常规PowerShell,以及PowerShell“VSVars32”设置提示符(再重申一下,就是有正确环境设置的PowerShell)。他们的图标都不尽相同。

看来是个恰好的机会来编辑一些新的图标,改变一下我的世界了。搜索,并下载了我所知道的最有趣的免费图标编辑器,叫做IcoFX。我鼓励你们去资助他们,因为他们在全世界范围内提供了一个很棒的服务。我在cmd.exe,devenv.exe和powershell.exe上用过他们的提取命令。

clip_image001

免责声明: 我想我这么做的确触犯了各种国际条例或者其他规定。当Ninjas跑进来说“你不能用我们的图标来取乐。”的时候,我会否认写过这篇博文。请支持我。我只是自寻其乐而已。并不是微软什么的官方产品,你也不能说这是官方的。你是谁!?不要再说我了。Jimmy不在这儿。别再打来了。

另外: 这是我还未触及的部分。看上去VS2010图标编辑器在alpha通道部分仍然有问题。其实我一直对整个产品感到反感,不过既然我不是那个团队中的一员,我就得花功夫去深究一些细节问题了。我倒是希望在VS中把这些全解决掉。

好了,现在我把所有的图标都加载到了IcoFX。我会把它们都编辑一下,并在所有可获方案中做出最优的图标,尽管从技术上来说我的Windows 7任务栏和桌面只需要32x32的图标。

clip_image002

稍稍编辑和调整大小。说真的,这个图标编辑器 真的是一种享受。开始做吧!

clip_image003

我把这些保存为vscommand.ico和vspowershell.ico,现在,我桌面上就有这两个很好看的图标了。

clip_image004

我把“Visual Studio命令提示符”贴到任务栏上,如下图所示:

clip_image005

我还给系统菜单做了一个小的图标,因为这是我的一贯做法。

clip_image006

好了,多棒啊。

不过,在PowerShell中,我有时候会通过运行添加在Microsoft.PowerShell_profile.ps1中的自定义PowerShell VSVars脚本来转换我的VSVars。还记得Chris Tavares里的这个脚本吗?

    1: function Get-Batchfile ($file) {
    2:     $cmd = "`"$file`" & set"
    3:     cmd /c $cmd | Foreach-Object {
    4:         $p, $v = $_.split('=')
    5:         Set-Item -path env:$p -value $v
    6:     }
    7: }
    8:   
    9: function VsVars32($version = "10.0")
   10: {
   11:     $key = "HKLM:SOFTWARE\Microsoft\VisualStudio\" + $version
   12:     $VsKey = get-ItemProperty $key
   13:     $VsInstallPath = [System.IO.Path]::GetDirectoryName($VsKey.InstallDir)
   14:     $VsToolsDir = [System.IO.Path]::GetDirectoryName($VsInstallPath)
   15:     $VsToolsDir = [System.IO.Path]::Combine($VsToolsDir, "Tools")
   16:     $BatchFile = [System.IO.Path]::Combine($VsToolsDir, "vsvars32.bat")
   17:     Get-Batchfile $BatchFile
   18:     [System.Console]::Title = "Visual Studio " + $version + " Windows Powershell"
   19:     //add a call to set-consoleicon as seen below...hm...!
   20: }

为什么不断然处之,和Aaron Lerch's为“改变Windows PowerShell控制台图标”而写的脚本合并起来呢?这样的话,当我调用“vsvars32”时,我会同时改变我PowerShell的图标。

以下是Aaron的脚本,做过一些变更,使其成为点源函数,还进行了一些修改。这能快速改变系统菜单图标,不过不会更新任务栏或者ALT-TAB。我也不确定这能否实现。

    1: ##############################################################################
    2: ## Script: Set-ConsoleIcon.ps1
    3: ## By: Aaron Lerch, tiny tiny mods by Hanselman
    4: ## Website: www.aaronlerch.com/blog 
    5: ## Set the icon of the current console window to the specified icon
    6: ## Dot-Source first, like . .\set-consoleicon.ps1
    7: ## Usage:  Set-ConsoleIcon [string]
    8: ## PS:1 > Set-ConsoleIcon "C:\Icons\special_powershell_icon.ico" 
    9: ##############################################################################
   10:  
   11: $WM_SETICON = 0x80
   12: $ICON_SMALL = 0
   13:  
   14: function Set-ConsoleIcon
   15: {
   16:     param(
   17:         [string] $iconFile
   18:     )
   19:  
   20:     [System.Reflection.Assembly]::LoadWithPartialName("System.Drawing") | out-null
   21: $iconFile
   22:     # Verify the file exists
   23:     if ([System.IO.File]::Exists($iconFile) -eq $TRUE)
   24:     {
   25:         $icon = new-object System.Drawing.Icon($iconFile) 
   26:  
   27:         if ($icon -ne $null)
   28:         {
   29:             $consoleHandle = GetConsoleWindow
   30:             SendMessage $consoleHandle $WM_SETICON $ICON_SMALL $icon.Handle 
   31:         }
   32:     }
   33:     else
   34:     {
   35:         Write-Host "Icon file not found"
   36:     }
   37: }
   38:  
   39:  
   40: ## Invoke a Win32 P/Invoke call.
   41: ## From: Lee Holmes
   42: ## https://www.leeholmes.com/blog/GetTheOwnerOfAProcessInPowerShellPInvokeAndRefOutParameters.aspx
   43: function Invoke-Win32([string] $dllName, [Type] $returnType, 
   44:    [string] $methodName, [Type[]] $parameterTypes, [Object[]] $parameters) 
   45: {
   46:    ## Begin to build the dynamic assembly
   47:    $domain = [AppDomain]::CurrentDomain
   48:    $name = New-Object Reflection.AssemblyName 'PInvokeAssembly'
   49:    $assembly = $domain.DefineDynamicAssembly($name, 'Run') 
   50:    $module = $assembly.DefineDynamicModule('PInvokeModule')
   51:    $type = $module.DefineType('PInvokeType', "Public,BeforeFieldInit")
   52:  
   53:    ## Go through all of the parameters passed to us.  As we do this, 
   54:    ## we clone the user's inputs into another array that we will use for
   55:    ## the P/Invoke call.  
   56:    $inputParameters = @()
   57:    $refParameters = @()
   58:    
   59:    for($counter = 1; $counter -le $parameterTypes.Length; $counter++) 
   60:    {
   61:       ## If an item is a PSReference, then the user 
   62:       ## wants an [out] parameter.
   63:       if($parameterTypes[$counter - 1] -eq [Ref])
   64:       {
   65:          ## Remember which parameters are used for [Out] parameters 
   66:          $refParameters += $counter
   67:  
   68:          ## On the cloned array, we replace the PSReference type with the 
   69:          ## .Net reference type that represents the value of the PSReference, 
   70:          ## and the value with the value held by the PSReference. 
   71:          $parameterTypes[$counter - 1] = 
   72:             $parameters[$counter - 1].Value.GetType().MakeByRefType()
   73:          $inputParameters += $parameters[$counter - 1].Value
   74:       }
   75:       else
   76:       {
   77:          ## Otherwise, just add their actual parameter to the
   78:          ## input array.
   79:          $inputParameters += $parameters[$counter - 1]
   80:       }
   81:    }
   82:  
   83:    ## Define the actual P/Invoke method, adding the [Out] 
   84:    ## attribute for any parameters that were originally [Ref] 
   85:    ## parameters.
   86:    $method = $type.DefineMethod($methodName, 'Public,HideBySig,Static,PinvokeImpl', 
   87:       $returnType, $parameterTypes) 
   88:    foreach($refParameter in $refParameters)
   89:    {
   90:       $method.DefineParameter($refParameter, "Out", $null)
   91:    }
   92:  
   93:    ## Apply the P/Invoke constructor
   94:    $ctor = [Runtime.InteropServices.DllImportAttribute].GetConstructor([string])
   95:    $attr = New-Object Reflection.Emit.CustomAttributeBuilder $ctor, $dllName
   96:    $method.SetCustomAttribute($attr)
   97:  
   98:    ## Create the temporary type, and invoke the method.
   99:    $realType = $type.CreateType() 
  100:    $realType.InvokeMember($methodName, 'Public,Static,InvokeMethod', $null, $null, 
  101:       $inputParameters)
  102:  
  103:    ## Finally, go through all of the reference parameters, and update the
  104:    ## values of the PSReference objects that the user passed in. 
  105:    foreach($refParameter in $refParameters)
  106:    {
  107:       $parameters[$refParameter - 1].Value = $inputParameters[$refParameter - 1]
  108:    }
  109: }
  110:  
  111: function SendMessage([IntPtr] $hWnd, [Int32] $message, [Int32] $wParam, [Int32] $lParam) 
  112: {
  113:     $parameterTypes = [IntPtr], [Int32], [Int32], [Int32]
  114:     $parameters = $hWnd, $message, $wParam, $lParam
  115:  
  116:     Invoke-Win32 "user32.dll" ([Int32]) "SendMessage" $parameterTypes $parameters 
  117: }
  118:  
  119: function GetConsoleWindow()
  120: {
  121:     Invoke-Win32 "kernel32" ([IntPtr]) "GetConsoleWindow"
  122: }

用PowerShell“类型扩展”功能来为System.Console做“.Icon”属性一定也很有趣。那样我就会运行 [System.Console]::Icon = "something.ico" 不过我把这个任务留给读者们来练习。

clip_image007

记住,我们不会只说不做。