如何将 IMAGELIST 中的 HICON 正确保存为图像文件
How to properly save an HICON from an IMAGELIST as an image file
我的目标:
将 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);
常见问题解答:
为什么我使用列表视图图像列表而不是 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);
因为我很固执,所以我坚持使用桌面列表视图的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();
}
但正如 Jonathan Potter 所说不是一个好主意:
例如 IShellItemImageFactory
和 SHCreateItemFromIDList
的组合似乎更好。
我的目标:
将 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);
常见问题解答:
为什么我使用列表视图图像列表而不是 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);
因为我很固执,所以我坚持使用桌面列表视图的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();
}
但正如 Jonathan Potter 所说不是一个好主意:
例如 IShellItemImageFactory
和 SHCreateItemFromIDList
的组合似乎更好。