我正在使用ExtractIconEx从shell32.dll中按索引提取图标,如下所示:

https://stackoverflow.com/a/62504226/9420881

然后,我将输出转换为ImageSource,如下所示:

ImageSource imageSource = Imaging.CreateBitmapSourceFromHIcon(
                hIconLarge,
                Int32Rect.Empty,
                BitmapSizeOptions.FromEmptyOptions());

通过查看shell32.dll,我可以看到索引0有一个256x256的图标,但它是PNG而不是BMP:

enter image description here

返回的图标不是256x256的图标,我假设它是64x64的BMP:

enter image description here

如何获得索引0(或任何其他索引)的256x256 PNG图标(如果有的话)?

推荐答案

下面是一个实用程序类,它公开了二进制文件(注意:它不能读取.ico文件)的所有图标句柄(所有大小、所有 colored颜色 计数)的列表.

它是基于ICO Format specification的.

Example 1:按索引加载图标并按大小 Select :

// load all icons handles with index 0 from a dll
var icons0 = IconInfo.LoadIconsFromBinary(@"c:\windows\system32\shell32.dll", 0);

// get the "jumbo" (256x256) one
var icon0_256 = icons0.First(i => i.WithIcon(i => i.Size.Width == 256));

// dispose loaded icons
icons0.ForEach(i => i.Dispose());

Example 2:加载二进制文件的所有图标

// load all icons handles from a dll
var icons = IconInfo.LoadIconsFromBinary(@"c:\windows\system32\shell32.dll");

// ... do something

// dispose loaded icons
icons.ForEach(i => i.Dispose());

Example 3:按ID加载图标句柄

// load icons with id "1" from a dll (same as index 0 for shell32)
// id is the value you see when opening the .dll from Visual Studio as a binary file in the ICON list of Win32 resources
// we pass it as a negative value
var iconsId = IconInfo.LoadIconsFromBinary(@"c:\windows\system32\shell32.dll", -1);

// get the jumbo one
var iconId_256 = iconsId.First(i => i.WithIcon(i => i.Size.Width == 256));

// dispose loaded icons
iconsId.ForEach(i => i.Dispose());

enter image description here This is icon index 0, id "1"

实用程序类:

public sealed class IconInfo : IDisposable
{
    private IconInfo(int groupIndex, string groupId, int index, string id, IntPtr handle)
    {
        GroupIndex = groupIndex;
        GroupId = groupId;
        Index = index;
        Id = id;
        Handle = handle;
    }

    public IntPtr Handle { get; }
    public int Index { get; }
    public int GroupIndex { get; }
    public string Id { get; }
    public string GroupId { get; }

    public void WithIcon(Action<Icon> action)
    {
        if (action == null)
            throw new ArgumentNullException(nameof(action));

        using var icon = Icon.FromHandle(Handle);
        action(icon);
    }

    public T WithIcon<T>(Func<Icon, T> func)
    {
        if (func == null)
            throw new ArgumentNullException(nameof(func));

        using var icon = Icon.FromHandle(Handle);
        return func(icon);
    }

    public override string ToString() => Index.ToString();
    public void Dispose() => DestroyIcon(Handle);

    public static List<IconInfo> LoadIconsFromBinary(string iconFilePath, int? byIndexOrResourceId = null)
    {
        if (iconFilePath == null)
            throw new ArgumentNullException(nameof(iconFilePath));

        var list = new List<IconInfo>();
        var handle = LoadLibraryEx(iconFilePath, IntPtr.Zero, LOAD_LIBRARY_AS_DATAFILE | LOAD_LIBRARY_AS_IMAGE_RESOURCE);
        if (handle != IntPtr.Zero)
        {
            try
            {
                list = LoadIconsFromBinary(handle, byIndexOrResourceId);
            }
            finally
            {
                FreeLibrary(handle);
            }
        }
        return list;
    }

    private static List<IconInfo> LoadIconsFromBinary(IntPtr handle, int? byIndexOrResourceId)
    {
        var list = new List<IconInfo>();
        var entries = new Dictionary<ushort, GRPICONDIRENTRY>();
        var groupIndices = new Dictionary<ushort, int>();
        var groupIds = new Dictionary<ushort, string>();
        var groupIndex = 0;
        if (EnumResourceNames(handle, new IntPtr(RT_GROUP_ICON), (m, t, n, lp) =>
        {
            if (byIndexOrResourceId.HasValue && byIndexOrResourceId.Value >= 0 && byIndexOrResourceId.Value != groupIndex)
            {
                groupIndex++;
                return true;
            }

            string name;
            if (n.ToInt64() > ushort.MaxValue)
            {
                name = Marshal.PtrToStringAuto(n);
            }
            else
            {
                name = n.ToInt32().ToString(CultureInfo.InvariantCulture);
            }

            if (byIndexOrResourceId.HasValue && byIndexOrResourceId.Value < 0 && !string.Equals((-byIndexOrResourceId.Value).ToString(CultureInfo.InvariantCulture), name, StringComparison.Ordinal))
            {
                groupIndex++;
                return true;
            }

            try
            {
                ExtractIconGroupEntries(handle, n, t, groupIndex, entries, groupIndices, groupIds);
                groupIndex++;
            }
            catch
            {
                // do nothing
            }
            return true;
        }, IntPtr.Zero))
        {
            EnumResourceNames(handle, new IntPtr(RT_ICON), (m, t, n, lp) =>
            {
                var iconHandle = ExtractIcon(handle, n, t, entries);
                if (iconHandle != IntPtr.Zero)
                {
                    var info = new IconInfo(groupIndices[(ushort)n.ToInt32()], groupIds[(ushort)n.ToInt32()], n.ToInt32() - 1, n.ToString(), iconHandle);
                    list.Add(info);
                }
                return true;
            }, IntPtr.Zero);
        }
        return list;
    }

    private static void ExtractIconGroupEntries(IntPtr module, IntPtr name, IntPtr type, int index, Dictionary<ushort, GRPICONDIRENTRY> entries, Dictionary<ushort, int> groupIndices, Dictionary<ushort, string> groupIds)
    {
        var handle = FindResource(module, name, type);
        if (handle == IntPtr.Zero)
            return;

        var size = SizeofResource(module, handle);
        if (size == 0)
            return;

        var resource = LoadResource(module, handle);
        if (resource == IntPtr.Zero)
            return;

        var ptr = LockResource(resource);
        if (ptr == IntPtr.Zero)
            return;

        // GRPICONDIR
        ptr += 2; // idReserved;
        var idtype = Marshal.ReadInt16(ptr);
        if (idtype != 1)  // idType, 1 for ICO
            return;

        var elementSize = Marshal.SizeOf<GRPICONDIRENTRY>();

        ptr += 2;
        var count = Marshal.ReadInt16(ptr);
        ptr += 2;
        for (var i = 0; i < count; i++)
        {
            var entry = Marshal.PtrToStructure<GRPICONDIRENTRY>(ptr);
            ptr += elementSize;
            entries[entry.nId] = entry;

            // is it a string or an id?
            groupIndices[entry.nId] = index;
            if (name.ToInt64() > ushort.MaxValue)
            {
                var id = Marshal.PtrToStringAuto(name);
                groupIds[entry.nId] = id;
            }
            else
            {
                groupIds[entry.nId] = "#" + name.ToInt32();
            }
        }
    }

    private static IntPtr ExtractIcon(IntPtr module, IntPtr name, IntPtr type, Dictionary<ushort, GRPICONDIRENTRY> entries)
    {
        if (!entries.TryGetValue((ushort)name.ToInt32(), out _))
            return IntPtr.Zero;

        var hres = FindResource(module, name, type);
        if (hres == IntPtr.Zero)
            return IntPtr.Zero;

        var size = SizeofResource(module, hres);
        if (size == 0)
            return IntPtr.Zero;

        var res = LoadResource(module, hres);
        if (res == IntPtr.Zero)
            return IntPtr.Zero;

        var ptr = LockResource(res);
        if (ptr == IntPtr.Zero)
            return IntPtr.Zero;

        return CreateIconFromResourceEx(ptr, size, true, 0x30000, 0, 0, 0);
    }

    private delegate bool EnumResNameProc(IntPtr hModule, IntPtr lpszType, IntPtr lpszName, IntPtr lParam);

    [DllImport("kernel32", CharSet = CharSet.Auto, SetLastError = true)]
    private static extern bool EnumResourceNames(IntPtr hModule, IntPtr lpszType, EnumResNameProc lpEnumFunc, IntPtr lParam);

    [DllImport("kernel32", CharSet = CharSet.Unicode)]
    private static extern IntPtr FindResource(IntPtr hModule, IntPtr lpName, IntPtr lpType);

    [DllImport("kernel32")]
    private static extern int SizeofResource(IntPtr hModule, IntPtr hResInfo);

    [DllImport("kernel32")]
    private static extern IntPtr LoadResource(IntPtr hModule, IntPtr hResInfo);

    [DllImport("user32")]
    private static extern IntPtr CreateIconFromResourceEx(IntPtr presbits, int dwResSize, bool fIcon, int dwVer, int cxDesired, int cyDesired, int flags);

    [DllImport("user32")]
    private static extern bool DestroyIcon(IntPtr handle);

    [DllImport("kernel32", CharSet = CharSet.Unicode)]
    private static extern IntPtr LockResource(IntPtr hResData);

    [DllImport("kernel32", CharSet = CharSet.Unicode)]
    private static extern IntPtr LoadLibraryEx(string lpFileName, IntPtr hFile, int dwFlags);

    [DllImport("kernel32")]
    private static extern bool FreeLibrary(IntPtr hModule);

    private const int LOAD_LIBRARY_AS_DATAFILE = 0x2;
    private const int LOAD_LIBRARY_AS_IMAGE_RESOURCE = 0x20;
    private const int RT_ICON = 3;
    private const int RT_GROUP_ICON = RT_ICON + 11;

    [StructLayout(LayoutKind.Sequential, Pack = 1)]
    private struct GRPICONDIRENTRY
    {
        public byte bWidth;
        public byte bHeight;
        public byte bColorCount;
        public byte bReserved;
        public short wPlanes;
        public short wBitCount;
        public int dwBytesInRes;
        public ushort nId;
    }
}

Csharp相关问答推荐

为什么我不能更改尚未设置的模拟对象属性的值?

Microsoft. SQLServer. Types(106.1000.6)在try 从用户定义的类型检索值时引发异常

C++/C#HostFXR通过std::tuple传递参数

==和Tuple对象的相等<>

此反射有什么问题.是否发送值转换委托?

碰撞检测与盒碰撞器,其isTrigger on问题

try 在Blazor项目中生成html

Azure Function应用(. NET 8)不将信息记录到应用洞察

在调整大小的控件上绘制

异步实体框架核心查询引发InvalidOperation异常

当索引和外键是不同的数据类型时,如何设置导航属性?

为什么此名称不再被识别?名称不存在于当前上下文中?

如何在C#中正确类型化带有泛型的嵌套类

在等待OnGetAsync时打开Razor Page显示微调器

如何对特定异常使用Polly重试机制?

如何在C# WinForm控件中使用Windows 10/11的黑暗主题?

处理方法内部过滤与外部过滤

如何在.NET8中使用Blazor Web App(WebAssembly)托管服务器端控制器?

测试单个对象是否与Func<;T,bool>;匹配

Avalonia MVVM数据模板