Search code examples
powershelldlliconssystem.drawing

how to get a specific icon from an icon library?


I have this snippet of code to extract an icon from shell32.dll.

$iconPath = "$env:SystemRoot\system32\shell32.dll"  # Path to the shell32.dll library
$iconIndex = 4  # Index of the folder icon in the library
$icon = [System.Drawing.Icon]::ExtractAssociatedIcon($iconPath)
$icon = [System.Drawing.Icon]::new($icon, 16, 16)

This partially works in that it fetches an icon from shell32.dll, but as you can see, $icon does not reference $iconIndex anywhere, and so I do not get icon '4'. I have tried putting $iconIndex as a 2nd argument to ExtractAssociatedIcon but that generates an error.

How can I get $icon to reference $iconIndex = 4 from the shell32.dll library?


Solution

  • [System.Drawing.Icon]::ExtractAssociatedIcon() is not solution to extract a given number of icon in shell32.dll, but only associated one.

    But shell32.dll provides istself a method ([Shell32_Extract]::ExtractIconEx()) to do so, this solution is adapted one of Shell32_Extract:

    
    add-type -typeDefinition '
    
    using System;
    using System.Runtime.InteropServices;
    
    public class Shell32_Extract {
    
      [DllImport(
         "Shell32.dll",
          EntryPoint        = "ExtractIconExW",
          CharSet           =  CharSet.Unicode,
          ExactSpelling     =  true,
          CallingConvention =  CallingConvention.StdCall)
      ]
    
       public static extern int ExtractIconEx(
          string lpszFile          , // Name of the .exe or .dll that contains the icon
          int    iconIndex         , // zero based index of first icon to extract. If iconIndex == 0 and and phiconSmall == null and phiconSmall = null, the number of icons is returnd
          out    IntPtr phiconLarge,
          out    IntPtr phiconSmall,
          int    nIcons
      );
    
    }
    ';
    #
    $iconPath = "$env:SystemRoot\system32\shell32.dll"  # Path to the shell32.dll library
    $iconIndex = 4  # Index of the folder icon in the library
    #$icon = [System.Drawing.Icon]::ExtractAssociatedIcon($iconPath)
    #$icon = [System.Drawing.Icon]::new($icon, 16, 16)
    #$path = (Get-Location).Path + "\a1.ico"
    #$icon.ToBitMap().save($path)
    
    [System.IntPtr] $phiconSmall = 0
    [System.IntPtr] $phiconLarge = 0
    
    $nofImages = [Shell32_Extract]::ExtractIconEx($iconPath, -1, [ref] $phiconLarge, [ref] $phiconSmall, 0)
    
    $nofIconsExtracted = [Shell32_Extract]::ExtractIconEx($iconPath, $iconIndex, [ref] $phiconLarge, [ref] $phiconSmall, 1)
    
    if ($nofIconsExtracted -ne 2) {
        Write-Error "iconsExtracted = $nofIconsExtracted"
    }
    
    $iconSmall = [System.Drawing.Icon]::FromHandle($phiconSmall);
    $iconLarge = [System.Drawing.Icon]::FromHandle($phiconLarge);
    
    $bmpSmall = $iconSmall.ToBitmap()
    $bmpLarge = $iconLarge.ToBitmap()
    
    #
    #  System.Drawing.Image.Save(), without specifying an encoder, stores
    #  the bitmap in png format.
    #
    $bmpLarge.Save("$(get-location)\small-$iconIndex.png");
    $bmpLarge.Save("$(get-location)\large-$iconIndex.png");
    
    #
    #  Use System.Drawing.Imaging.ImageFormat to specify a
    #  different format:
    #
    
    $bmpSmall.Save("$(get-location)\small-$iconIndex.bmp", [System.Drawing.Imaging.ImageFormat]::Bmp );
    $bmpLarge.Save("$(get-location)\large-$iconIndex.bmp", [System.Drawing.Imaging.ImageFormat]::Bmp );
       
    $bmpSmall.Save("$(get-location)\small-$iconIndex.jpg", [System.Drawing.Imaging.ImageFormat]::Jpeg);
    $bmpLarge.Save("$(get-location)\large-$iconIndex.jpg", [System.Drawing.Imaging.ImageFormat]::Jpeg);
    
    

    This generates images like this:

    enter image description here