From ce7d14b227863dca7318da6cf09fc670f803b1dc Mon Sep 17 00:00:00 2001 From: "Mr.k" Date: Mon, 8 Jul 2024 23:14:10 +0300 Subject: [PATCH] Fix Icon Support for Shortcut Creation (#2242) * Make 'ConvertTo-Icon' Private Function Parameter more stricter * Fix 'Invoke-WPFShortcut' Public Function Icon Support for the created shortcuts * Replace Tabs with Spaces to follow the conventions * Add new optional parameter to 'ConvertTo-Icon' and Improve the documentation for the function * Update the Description for 'ConvertTo-Icon' Function * Update some Documentation Wording in 'ConvertTo-Icon' Function * Change behavior of one case from throwing an exception to doing an early return Co-authored-by: Martin Wiethan <47688561+Marterich@users.noreply.github.com> * Update Examples for 'ConvertTo-Icon' Functions & Add a new Example, as well as some other changes Besides the updated documentation for 'ConvertTo-Icon' Function, the icon file path has changed from '$env:TEMP\cttlogo.ico' into '$env:LOCALAPPDATA\winutil\cttlogo.ico', and add edge-case of Folder not being found for the Icon File in 'ConvertTo-Icon' Code. --------- Co-authored-by: Martin Wiethan <47688561+Marterich@users.noreply.github.com> --- functions/private/ConvertTo-Icon.ps1 | 82 ++++++++++++++++++++++--- functions/public/Invoke-WPFShortcut.ps1 | 11 +++- 2 files changed, 82 insertions(+), 11 deletions(-) diff --git a/functions/private/ConvertTo-Icon.ps1 b/functions/private/ConvertTo-Icon.ps1 index 5ed7f5e7..44f4d6be 100644 --- a/functions/private/ConvertTo-Icon.ps1 +++ b/functions/private/ConvertTo-Icon.ps1 @@ -2,26 +2,92 @@ function ConvertTo-Icon { <# .DESCRIPTION - This function will convert PNG to ICO file + This function will convert BMP, GIF, EXIF, JPG, PNG and TIFF to ICO file + + .PARAMETER bitmapPath + The file path to bitmap image to make '.ico' file out of. + Supported file types according to Microsoft Documentation is the following: + BMP, GIF, EXIF, JPG, PNG and TIFF. + + .PARAMETER iconPath + The file path to write the new '.ico' resource. + + .PARAMETER overrideIconFile + An optional boolean Parameter that makes the function overrides + the Icon File Path if the file exists. Defaults to $true. .EXAMPLE - ConvertTo-Icon -bitmapPath "$env:TEMP\cttlogo.png" -iconPath $iconPath + try { + ConvertTo-Icon -bitmapPath "$env:TEMP\cttlogo.png" -iconPath "$env:TEMP\cttlogo.ico" + } catch [System.IO.FileNotFoundException] { + # Handle the thrown exception here... + } + + This Example makes a '.ico' file at "$env:TEMP\cttlogo.ico" File Path using the bitmap file + found in "$env:TEMP\cttlogo.png", the function overrides the '.ico' File if it's found. + this function will throw a FileNotFound Exception at the event of not finding the provided bitmap File Path. + + .EXAMPLE + try { + ConvertTo-Icon "$env:TEMP\cttlogo.png" "$env:TEMP\cttlogo.ico" + } catch [System.IO.FileNotFoundException] { + # Handle the thrown exception here... + } + + This Example is the same as Example 1, but uses Positional Parameters instead. + + .EXAMPLE + if (Test-Path "$env:TEMP\cttlogo.png") { + ConvertTo-Icon -bitmapPath "$env:TEMP\cttlogo.png" -iconPath "$env:TEMP\cttlogo.ico" + } + + This Example is same as Example 1, but checks if the bitmap File exists before calling 'ConvertTo-Icon' Function. + This's the recommended way of using this function, as it doesn't require any try-catch blocks. + + .EXAMPLE + try { + ConvertTo-Icon -bitmapPath "$env:TEMP\cttlogo.png" -iconPath "$env:TEMP\cttlogo.ico" -overrideIconFile $false + } catch [System.IO.FileNotFoundException] { + # Handle the thrown exception here... + } + + This Example make use of '-overrideIconFile' Optional Parameter, the default for this paramter is $true. + By doing '-overrideIconFile $false', the 'ConvertTo-Icon' function will raise an exception that needs to be catched throw a 'catch' Code Block, + otherwise it'll crash the running PowerShell instance/process. + #> - param( [Parameter(Mandatory=$true)] - $bitmapPath, - $iconPath = "$env:temp\newicon.ico" + param( + [Parameter(Mandatory=$true, position=0)] + [string]$bitmapPath, + [Parameter(Mandatory=$true, position=1)] + [string]$iconPath, + [Parameter(position=2)] + [bool]$overrideIconFile = $true ) Add-Type -AssemblyName System.Drawing if (Test-Path $bitmapPath) { + if ((Test-Path $iconPath) -AND ($overrideIconFile -eq $false)) { + Write-Host "[ConvertTo-Icon] Icon File is found at '$iconPath', and the 'overrideIconFile' Parameter is set to '$overrideIconFile'. Skipping the bitmap to icon convertion..." -ForegroundColor Yellow + return + } + + # Load bitmap file into memory, and make an Icon version out of it $b = [System.Drawing.Bitmap]::FromFile($bitmapPath) $icon = [System.Drawing.Icon]::FromHandle($b.GetHicon()) + + # Create the folder for the new icon file if it doesn't exists + $iconFolder = (New-Object System.IO.FileInfo($iconPath)).Directory.FullName + [System.IO.Directory]::CreateDirectory($iconFolder) | Out-Null + + # Write the Icon File and do some cleaning-up $file = New-Object System.IO.FileStream($iconPath, 'OpenOrCreate') $icon.Save($file) $file.Close() $icon.Dispose() - #explorer "/SELECT,$iconpath" } - else { Write-Warning "$BitmapPath does not exist" } -} \ No newline at end of file + else { + throw [System.IO.FileNotFoundException] "[ConvertTo-Icon] The provided bitmap File Path is not found at '$bitmapPath'." + } +} diff --git a/functions/public/Invoke-WPFShortcut.ps1 b/functions/public/Invoke-WPFShortcut.ps1 index f8ae04f9..8a26b82a 100644 --- a/functions/public/Invoke-WPFShortcut.ps1 +++ b/functions/public/Invoke-WPFShortcut.ps1 @@ -27,8 +27,10 @@ function Invoke-WPFShortcut { $ArgumentsToSourceExe = "$powershell '$IRM'" $DestinationName = "WinUtil.lnk" + Invoke-WebRequest -Uri "https://christitus.com/images/logo-full.png" -OutFile "$env:TEMP\cttlogo.png" + if (Test-Path -Path "$env:TEMP\cttlogo.png") { - $iconPath = "$env:SystempRoot\cttlogo.ico" + $iconPath = "$env:LOCALAPPDATA\winutil\cttlogo.ico" ConvertTo-Icon -bitmapPath "$env:TEMP\cttlogo.png" -iconPath $iconPath } } @@ -40,20 +42,23 @@ function Invoke-WPFShortcut { $FileBrowser.Filter = "Shortcut Files (*.lnk)|*.lnk" $FileBrowser.FileName = $DestinationName - # Do an Early Return if The Save Shortcut operation was cancel by User's Input. + # Do an Early Return if the Save Operation was canceled by User's Input. $FileBrowserResult = $FileBrowser.ShowDialog() $DialogResultEnum = New-Object System.Windows.Forms.DialogResult if (-not ($FileBrowserResult -eq $DialogResultEnum::OK)) { return } + # Prepare the Shortcut paramter $WshShell = New-Object -comObject WScript.Shell $Shortcut = $WshShell.CreateShortcut($FileBrowser.FileName) $Shortcut.TargetPath = $SourceExe $Shortcut.Arguments = $ArgumentsToSourceExe - if ($null -ne $iconPath) { + if ($iconPath -ne $null) { $shortcut.IconLocation = $iconPath } + + # Save the Shortcut to disk $Shortcut.Save() if ($RunAsAdmin -eq $true) {