Home > Blockchain >  How to use SHGetStockIconInfo in Powershell?
How to use SHGetStockIconInfo in Powershell?

Time:11-01

I'm trying to pull folder icons for a listview in a winform. I'm using [Drawing.Icon]::ExtractAssociatedIcon for file icons, but it doesn't work on folders. I found this question here on stack which said to use SHGetStockIconInfo, but I can't wrap my head around how to load this function and use it in Powershell (I have no C programming experience). I managed to pull this from the linked question, but I'm stuck:

Add-Type -NameSpace WinAPI -Name DefaultIcons -MemberDefinition @'

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
    public struct SHSTOCKICONINFO {
        public uint cbSize;
        public IntPtr hIcon;
        public int iSysIconIndex;
        public int iIcon;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
        public string szPath;
    }

    [DllImport("shell32.dll")]
    public static extern int SHGetStockIconInfo(uint siid, uint uFlags, ref SHSTOCKICONINFO psii);

    [DllImport("user32.dll")]
    public static extern bool DestroyIcon(IntPtr handle);

    private const uint SHSIID_FOLDER = 0x3;
    private const uint SHGSI_ICON = 0x100;
    private const uint SHGSI_LARGEICON = 0x0;
    private const uint SHGSI_SMALLICON = 0x1;
'@

This runs without error, but that's as far as I can get and I don't fully understand what's going on. I know I need to call the function SHGetStockIconInfo with three parameters:

  1. uint to identify what icon I want
  2. uint flag for the size of icon I want
  3. a reference to a SHSTOCKICONINFO structure

The question I got all this from includes a GetStockIcon function definition, but I keep getting errors trying to include it saying The type or namespace name 'Icon' could not be found (are you missing a using directive or an assembly reference?)

CodePudding user response:

With a few adjustments I got the code from that C# question to work under both PowerShell 5.1 and 7.2.6.

$refAsm = if( $PSVersionTable.PSVersion.Major -le 5 ) {'System.Drawing'} else {'System.Drawing.Common'}

Add-Type -ReferencedAssemblies $refAsm -TypeDefinition @'
using System;
using System.Runtime.InteropServices;
using System.Drawing;

public static class DefaultIcons
{
    public static Icon GetStockIcon(uint type, uint size)
    {
        var info = new SHSTOCKICONINFO();
        info.cbSize = (uint)Marshal.SizeOf(info);

        SHGetStockIconInfo(type, SHGSI_ICON | size, ref info);

        var icon = (Icon)Icon.FromHandle(info.hIcon).Clone(); // Get a copy that does not use the original handle
        DestroyIcon(info.hIcon); // Clean up native icon to prevent resource leak

        return icon;
    }

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
    public struct SHSTOCKICONINFO
    {
        public uint cbSize;
        public IntPtr hIcon;
        public int iSysIconIndex;
        public int iIcon;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
        public string szPath;
    }

    [DllImport("shell32.dll")]
    public static extern int SHGetStockIconInfo(uint siid, uint uFlags, ref SHSTOCKICONINFO psii);

    [DllImport("user32.dll")]
    public static extern bool DestroyIcon(IntPtr handle);

    public const uint SHSIID_FOLDER = 0x3;
    public const uint SHGSI_ICON = 0x100;
    public const uint SHGSI_LARGEICON = 0x0;
    public const uint SHGSI_SMALLICON = 0x1;
}
'@

Use it like this:

$folderIconLarge = [DefaultIcons]::GetStockIcon( [DefaultIcons]::SHSIID_FOLDER, [DefaultIcons]::SHGSI_LARGEICON )
$folderIconSmall = [DefaultIcons]::GetStockIcon( [DefaultIcons]::SHSIID_FOLDER, [DefaultIcons]::SHGSI_SMALLICON )
  • Related