【问题标题】:How to properly save an HICON from an IMAGELIST as an image file如何正确地将 IMAGELIST 中的 HICON 保存为图像文件
【发布时间】:2020-04-23 09:57:07
【问题描述】:

我的目标:

HIMAGELIST 中的所有Window Icon Handle(HICON) 保存为多个图像文件(.png.tiff)。


我的问题:

在我的保存过程之后,有些图像质量很差,但有些则不然。

我只在带有子文件夹/子文件的文件夹的图像上注意到了这个问题。


我的尝试:

代码背景:

  • 我正在使用 Vanara 来帮助我处理 PInvoke 呼叫等等。
  • HIMAGELIST来自ListView,使用ListViewMessage:LVM_GETIMAGELIST
  • 此方法是 Shell 扩展的一部分(我知道,我不应该这样做)。
private void Saving()
{
    var hWnd = GetListViewHWnd(); // This is the Desktop SysListView32 HWND

    IntPtr lParam = IntPtr.Zero;
    IntPtr pHil = SendMessage(hWnd, ListViewMessage.LVM_GETIMAGELIST, 0, ref lParam);

    var sHil = new SafeHIMAGELIST(pHil); // This is the IMAGELIST of the ListView

    var imageCount = sHil.Interface.GetImageCount(); // sHil.Interface == IImageList Interface

    for (int i = 0; i < imageCount; i++)
    {
        using (var fs = File.OpenWrite(@"C:\Users\Julien\Desktop\Icons\" + i + ".tiff"))
        {
            using (SafeHICON sHIcon = sHil.Interface.GetIcon(i, IMAGELISTDRAWFLAGS.ILD_NORMAL))
            {
                var bmpS = Imaging.CreateBitmapSourceFromHIcon(
                        sHIcon.DangerousGetHandle(),
                        Int32Rect.Empty,
                        BitmapSizeOptions.FromEmptyOptions());

                BitmapEncoder enc = new TiffBitmapEncoder();
                enc.Frames.Add(BitmapFrame.Create(bmpS));
                enc.Save(fs);
            }
        }
    }

    sHil.Dispose();
}

还有:

var bmp = Bitmap.FromHicon(sHIcon.DangerousGetHandle());
bmp.Save(fs);

常见问题解答:

为什么我使用 listview imagelist 而不是 SHGetFileInfo

因为SHGetFileInfo 会给我一个HICON 这样的:

对于实际上看起来像这样的文件夹:

在你的SHGetFileInfo 中传递SHGFI_SYSICONINDEX 怎么样?

同样的,非空文件夹的图标不存储在系统图像列表中。

因为我可以用 C++ 编写我的扩展,所以我也愿意接受任何用 C++ 编写的解决方案。

编辑: 我尝试使用IImageList.Draw() 绘制那些有故障的图像,它似乎有效。很明显,问题出在我如何从HICON 创建图像。

var hdc = GetDC(notepadHWnd);
var dp = new IMAGELISTDRAWPARAMS(
    hdc,
    new RECT(73, 73, 73, 73), 12,
    COLORREF.None,
    IMAGELISTDRAWFLAGS.ILD_NORMAL);

sHil.Interface.Draw(dp);

【问题讨论】:

  • 您应该使用IShellItemImageFactory 来获取这些图标,而不是在另一个进程中为私有数据而卑躬屈膝。
  • 真是巧合,我只是使用IShellItemImageFactorySHCreateItemFromIDList 完成的。问题是,这些动态文件夹图标被视为Thumbnails 而不是Icons。 “SIIGBF_THUMBNAILONLY”。我可以将我的扩展程序扔进垃圾桶,但你知道,卑躬屈膝总是很有趣:) 我会发布一个解决方案,除非之前有人处理过。
  • @JonathanPotter IShellItemImageFactory.GetImage() 适用于Thumbnails 但不适用于带有 UAC 防护罩的"icons"。它返回一个没有图标。我知道图标覆盖,但 UAC 盾不是其中之一。

标签: c# image winapi com


【解决方案1】:

由于我很固执,所以我坚持使用桌面列表视图的IMAGELIST

我设法从中获取了图标/缩略图。通过在内存设备上下文中绘制它们。

没有更多的故障图像。

private void Saving()
{
    var hWnd = GetListViewHWnd(); // Desktop SysListView32 HWND

    IntPtr lParam = IntPtr.Zero;
    IntPtr pHil = SendMessage(hWnd, ListViewMessage.LVM_GETIMAGELIST, 0, ref lParam);

    var sHil = new SafeHIMAGELIST(pHil); // IMAGELIST of the ListView
    sHil.Interface.GetIconSize(out var cx, out var cy);

    var imageCount = sHil.Interface.GetImageCount(); // IImageList Interface
    var desktopHdc = new SafeHDC(GetDC(GetListViewHWnd()).DangerousGetHandle());
    var inMemoryHdc = CreateCompatibleDC(desktopHdc);

    for (int i = 0; i < imageCount; i++)
    {
        var inMemoryBmp = CreateCompatibleBitmap(desktopHdc, cx, cy);

        SelectObject(inMemoryHdc, inMemoryBmp);

        var ilDp = new IMAGELISTDRAWPARAMS(
            inMemoryHdc, 
            new RECT(0, 0, 0, 0), 
            i, 
            COLORREF.None,
            IMAGELISTDRAWFLAGS.ILD_NORMAL);

        sHil.Interface.Draw(ilDp);

        var bmpS = Imaging.CreateBitmapSourceFromHBitmap(
                inMemoryBmp.DangerousGetHandle(), 
                IntPtr.Zero,
                Int32Rect.Empty, 
                BitmapSizeOptions.FromEmptyOptions());

        using (var fs = File.OpenWrite(@"C:\Users\Julien\Desktop\Icons\" + i + ".png"))
        {
            BitmapEncoder enc = new PngBitmapEncoder();
            enc.Frames.Add(BitmapFrame.Create(bmpS));
            enc.Save(fs);
        }

        inMemoryBmp.Dispose();
    }

    sHil.Dispose();
    desktopHdc.Dispose();
    inMemoryHdc.Dispose();
}

但正如乔纳森·波特所说,这不是一个好主意:

例如,IShellItemImageFactorySHCreateItemFromIDList 的组合似乎更好。

【讨论】:

    猜你喜欢
    • 2022-11-04
    • 2011-01-18
    • 2022-01-07
    • 2011-03-01
    • 2010-12-21
    • 1970-01-01
    • 2018-06-12
    • 2021-10-12
    • 2019-01-04
    相关资源
    最近更新 更多