2

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?

Ken White
  • 123,280
  • 14
  • 225
  • 444
YorSubs
  • 3,194
  • 7
  • 37
  • 60
  • It does not unfortunately. I did read that question a few hours ago and I just don't see a working solution there (but if you can see a speciic answer in there that works, I'd be curious). – YorSubs Feb 21 '23 at 21:22
  • 1
    are you sure you looked into all answers in the link provided by @JosefZ ? I believe one of them should answer your question – Santiago Squarzon Feb 21 '23 at 22:23
  • 1
    You need to look into the answers using `ExtractIconEx`, `ExtractAssociatedIcon` is not meant to extract icons by index from a library. __The linked question has the solution to your problem__. – Santiago Squarzon Feb 22 '23 at 05:42
  • ok, I'll go and look further on `ExtractIconEx`, thanks – YorSubs Feb 22 '23 at 05:43

1 Answers1

1

[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

jacouh
  • 8,473
  • 5
  • 32
  • 43
  • Ah, I think this will work, thanks (I see it is a similar technique as **@Santiago Squarzon**, but I couldn't get his to work for me, and yours fits into what I am doing). More complex than I was hoping (just to get a folder icon!), but it's great. I'll check it all out this evening in case I have some queries before marking as the answer. – YorSubs Feb 22 '23 at 10:05