Skip to content

Instantly share code, notes, and snippets.

@santisq
Last active October 18, 2025 23:35
Show Gist options
  • Select an option

  • Save santisq/9d070d44271a04f4a15b966ff34f4d8c to your computer and use it in GitHub Desktop.

Select an option

Save santisq/9d070d44271a04f4a15b966ff34f4d8c to your computer and use it in GitHub Desktop.
Add-Type -AssemblyName System.Drawing
$refAssemblies = @(
[Drawing.Icon].Assembly.Location
if ($IsCoreCLR) {
[psobject].Assembly.Location
$pwshLocation = Split-Path -Path ([psobject].Assembly.Location) -Parent
$pwshRefAssemblyPattern = [IO.Path]::Combine($pwshLocation, 'ref', '*.dll')
(Get-Item -Path $pwshRefAssemblyPattern).FullName
}
)
Add-Type @'
using System;
using System.ComponentModel;
using System.Runtime.InteropServices;
using System.Drawing;
using System.IO;
using System.Management.Automation;
using System.Linq;
namespace ExtractIconEx
{
[Cmdlet(VerbsLifecycle.Invoke, "ExtractIconEx")]
[OutputType(typeof(FileInfo))]
public sealed class InvokeExtractIconEx : PSCmdlet
{
[DllImport("shell32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
private static extern uint ExtractIconExW(
[In] string lpszFile,
[In] int nIconIndex,
out IntPtr phiconLarge,
out IntPtr phiconSmall,
uint nIcons);
[DllImport("shell32.dll", CharSet = CharSet.Unicode)]
private static extern IntPtr ExtractIconW(
[In] IntPtr hInst,
[In] string lpszExeFileName,
int nIconIndex);
[DllImport("user32.dll")]
private static extern bool DestroyIcon(IntPtr hIcon);
[Parameter(Mandatory = true, Position = 0)]
public string Source { get; set; }
[Parameter(Position = 1)]
[ValidateNotNullOrEmpty]
public string Destination { get; set; }
[Parameter(Position = 2)]
public int[] Index { get; set; }
protected override void EndProcessing()
{
Destination = string.IsNullOrWhiteSpace(Destination)
? SessionState.Path.CurrentFileSystemLocation.Path
: GetUnresolvedProviderPathFromPSPath(Destination);
if (Index == null)
{
Index = Enumerable
.Range(0, GetIconCount())
.ToArray();
}
foreach (int index in Index)
{
try
{
WriteObject(
ExtractIcon(index),
enumerateCollection: true);
}
catch (Win32Exception exception)
{
WriteError(new ErrorRecord(
exception, "IconExtractError", ErrorCategory.NotSpecified, null));
}
}
}
private int GetIconCount()
{
IntPtr nullPtr;
uint result = ExtractIconExW(Source, -1, out nullPtr, out nullPtr, 0);
if (result == 0)
{
throw new Win32Exception();
}
return (int)result;
}
private FileInfo[] ExtractIcon(int iconIndex)
{
FileInfo[] outFiles;
IntPtr largeIconHandle = IntPtr.Zero;
IntPtr smallIconHandle = IntPtr.Zero;
try
{
uint result = ExtractIconExW(
lpszFile: Source,
nIconIndex: iconIndex,
phiconLarge: out largeIconHandle,
phiconSmall: out smallIconHandle,
nIcons: 1);
if (result == uint.MaxValue)
{
throw new Win32Exception();
}
using (Icon largeIcon = Icon.FromHandle(largeIconHandle))
using (Icon smallIcon = Icon.FromHandle(smallIconHandle))
{
outFiles = new[]
{
new FileInfo(GetPath("largeIcon", iconIndex)),
new FileInfo(GetPath("smallIcon", iconIndex))
};
largeIcon.ToBitmap().Save(outFiles[0].FullName);
smallIcon.ToBitmap().Save(outFiles[1].FullName);
}
}
finally
{
DestroyIcon(largeIconHandle);
DestroyIcon(smallIconHandle);
}
return outFiles;
}
private string GetPath(string name, int iconIndex)
{
return Path.Combine(
Destination,
string.Format("{0}-{1}-{2}.bmp", Source, name, iconIndex));
}
}
}
'@ -PassThru -ReferencedAssemblies $refAssemblies | Import-Module -Assembly { $_.Assembly }

Uses ExtractIconExW function to extract icons from libraries. This function should be compatible with both, Windows PowerShell 5.1 and PowerShell 7+. It will extract both icons, large and small, from a given index (-InconIndex). The function outputs 2 FileInfo instances pointing to the created icons given in -DestinationFolder. If no destination folder is provided, the icons will be extracted to the PowerShell current directory ($pwd).

# Extracts `shell32.dll` icon index 0 to PWD
Invoke-ExtractIconEx shell32.dll -Index 0

# Extracts `user32.dll` icon index 1 and 2 to a specified path
Invoke-ExtractIconEx user32.dll .\pathto\destinationFolder -Index 1, 2

# Extracts ALL icons from `shell32.dll` to PWD
Invoke-ExtractIconEx shell32.dll
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment