Public/Windows/Set-WindowsFolderIcon.ps1
# REFACTOR: Code quality. function Set-WindowsFolderIcon { [CmdletBinding()] param ( [Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName, ParameterSetName = "All", Position = 0)] [Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName, ParameterSetName = "Icon", Position = 0)] [Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName, ParameterSetName = "Reset", Position = 0)] [ValidateScript({ if (!(Test-Path -LiteralPath $_)) { throw [System.ArgumentException] "File or Folder does not exist." } if (Test-Path -LiteralPath $_ -PathType Leaf) { throw [System.ArgumentException] "File passed when a folder was expected." } return $true })] [String[]] $Folder, [Parameter(Mandatory, ValueFromPipelineByPropertyName, ParameterSetName = "Icon")] [ValidateScript({ if (!(Test-Path -LiteralPath $_)) { throw [System.ArgumentException] "File or Folder does not exist." } if (Test-Path -LiteralPath $_ -PathType Container) { throw [System.ArgumentException] "Folder passed when a file was expected." } if ($_ -notmatch "(\.ico)") { throw [System.ArgumentException] "The file specified must be of type .ico" } return $true })] [String] $Icon, [Parameter(Mandatory = $false, ValueFromPipelineByPropertyName, ParameterSetName = "Reset")] [Switch] $Reset ) process { foreach ($FolderToChange in $Folder) { if(!($Reset)){ # Create a temp directory to store our # desktop.ini file before moving it $TmpDir = (Join-Path ([IO.Path]::GetTempPath()) ([IO.Path]::GetRandomFileName())) mkdir $TmpDir -force >$null $TmpIniPath = "$TmpDir\desktop.ini" # Define desktop.ini content $INIContent = @( "[.ShellClassInfo]" "IconFile=$Icon" "IconIndex=0" "ConfirmFileOp=0" "" ) -join "`r`n" # Pipe desktop.ini content out into an actual file. $INIContent | Out-File $TmpIniPath -Force (Get-Item -LiteralPath $TmpIniPath).Attributes = 'Archive, System, Hidden' # Remove existing desktop.ini file if # it exists in our target folder $IniFilePath = "$FolderToChange\desktop.ini" if(Test-Path -LiteralPath $IniFilePath -PathType Leaf){ Remove-Item -LiteralPath $IniFilePath -Force } # Desktop.ini must be updated using a Shell API method # in order for the Shell/Explorer to be notified # This is the secret sauce for getting icons to display # and refresh immediately. # # FOF_SILENT 0x0004 don't display progress UI # FOF_NOCONFIRMATION 0x0010 don't display confirmation UI, assume "yes" # FOF_NOERRORUI 0x0400 don't put up error UI # $shell = New-Object -com Shell.Application $shell.NameSpace($FolderToChange).MoveHere($TmpIniPath, 0x0004 + 0x0010 + 0x0400) Request-ExplorerRefresh # Clean up and remove our temp directory Remove-Item -LiteralPath $TmpDir -Recurse -Force # Set the ReadOnly attribute on our folder so # Explorer knows to use the desktop.ini file. $FolderObject = Get-Item -LiteralPath $FolderToChange $FolderObject.Attributes = 'ReadOnly,Directory' }else{ # Reset code: # Remove desktop.ini and revert folder attributes if(Test-Path -LiteralPath "$FolderToChange\desktop.ini" -PathType Leaf){ Remove-Item -LiteralPath "$FolderToChange\desktop.ini" -Force } (Get-Item -LiteralPath $FolderToChange).Attributes = 'Directory' Request-ExplorerRefresh } } } end { # Refresh the icon cache just for good measure $cmd = 'ie4uinit.exe -show' Invoke-Expression $cmd } } |