如何在窗口上设置HICON(带有多个尺寸的.ICO文件)?

5
我想定义一个Win32窗口的应用程序图标,例如通过调用SetClassLong并传递一个指向图标的句柄来设置GCL_HICON(请参阅MSDN上的SetClassLong Function)。
这很有效,但我还没有想出如何加载图标文件(ICO文件),以保留所有可用大小(例如16x16、32x32、48x48和完整大小的PNG图标)。当我通过LoadImage将图标文件加载到内存中以获取HICON时,我必须指定我想要的大小(请参见我的回答相关问题)。
我的ICO文件包含一个小型图像,应该用作窗口图标(标题栏左上角),并已被设计得非常清晰,但也包含更大的变体,应该显示在Alt-Tab对话框中,但是...
  1. 加载16x16图标可以在标题栏中显示正确的图标,但是当我使用Alt-Tab时,会显示一个丑陋的拉伸版本。在任务栏中显示的图标也不太好看。

  2. 加载48x48图标可以在Alt-Tab时显示漂亮的图标,但是在标题栏中显示的图标模糊不清,因为它是48x48图标的缩小版本。

有没有办法告诉Windows我的应用程序有多个大小的图标?有没有我错过的明显API?

5个回答

6

.ICO文件中包含多个图像,但HICON只是其中的一个。如果使用LR_DEFAULTSIZE,则可能会出现某些神奇的行为,保留到.ico文件的链接并使用其中适当的图像,但我怀疑这点。

如果这样还不行,那就没有办法了。

HICON hicon = LoadImage(NULL, "filename.ico", IMAGE_ICON, 
                        0, 0, LR_DEFAULTSIZE | LR_LOADFROMFILE);

一些背景知识。

当一个应用程序的资源中包含.ico文件时,该文件被打开并且文件中的每个图像都成为一个单独的资源。文件头被修改并成为ICON资源。因此,当LoadIcon/LoadImage传递给它一个ICON资源的资源ID时,实际上是传递了其他资源的目录。它选择符合请求的图像在那个时间点上并将其转换为HICON。实际执行此操作的函数称为LookupIconIdFromDirectory

这就是为什么当您为HICON GetIconInfo时,您只会得到一个单独的ICONINFO结构。

typedef struct _ICONINFO {
    BOOL fIcon;
    DWORD xHotspot;
    DWORD yHotspot;
    HBITMAP hbmMask;
    HBITMAP hbmColor;
} ICONINFO;

对于LR_DEFAULTSIZE,MSDN表示:“如果资源包含多个图像,则函数使用第一个图像的大小”。 - Anders
@Anders:是的。但是,.NET图标似乎会跟踪.ico源,并且当您要求它从旧图标制作新图标时,它将回到源而不仅仅是拉伸。我认为这对于非托管图标也可能是正确的,但我没有确凿的知识。 - John Knoeller
1
感谢您的想法和解释。然而,LR_DEFAULTSIZE并没有实现我们梦寐以求的神奇行为。我想我将不得不使用GCL_HICON和GCL_HICONSM;我会通过评论Anders的回复来报告结果。 - Pierre Arnaud
嗨@PierreArnaud,你能分享一下你上次评论的发现吗?我有一个ico容器,正在尝试使用SetClassLongPtr来应用正确的大小到大和小。 - Noitidart
请参见http://stackoverflow.com/questions/2266479/setclasslonghwnd-gcl-hicon-hicon-cannot-replace-winforms-form-icon - Pierre Arnaud

3

GCL_HICON设置“大”图标,GCL_HICONSM设置小图标(大小通常为32x32和16x16,但应使用GetSystemMetrics与SM_CXICON和SM_CXSMICON查找实际大小(对于大图标,您还可以仅传递LR_DEFAULTSIZE到LoadImage并使用0大小))


目前,我无法在我的WinForms应用程序上按预期使用SetClassLong。默认的WinForms图标获胜;当我按Alt-Tab时,我可以短暂地看到我的图标,但是随后默认的WinForms图标会覆盖我的图标。很奇怪。如果我自己无法解决这个问题,我将在Stack Overflow上发布另一个问题。 - Pierre Arnaud
另一个问题在这里,如果你想知道的话:http://stackoverflow.com/questions/2266479 - Pierre Arnaud

1
如果.ico文件不包含256x256的完整大小PNG图标,当我简单地写入以下内容时,Windows似乎很满意:
var assembly = typeof (Xyz).Assembly;
var stream   = assembly.GetManifestResourceStream ("Foo.Resources.Form.ico");
var icon     = new System.Drawing.Icon (stream);

form.Icon = icon;

有了这个设置,并且将名为Form.ico的图标放置在我的程序集的Resources文件夹中,该程序集以Foo作为其默认命名空间,Windows将使用32x32版本的图像用于任务栏和使用Alt-Tab进行任务切换,16x16版本用于窗口标题。因此,在使用WinForms的Form.Icon时要小心PNG图标...

0

这个问题似乎在现代版本的Visual Studio / Windows中已经被解决了。如果我通过LoadIcon加载,并使用相同的多尺寸.ico文件在窗口类中设置小图标和普通图标,Shell似乎能够找到并在Alt-Tab /任务栏/标题栏/小/中/大甚至是资源管理器中的巨型图标中加载适当的图标。


0

HICON 是一个单一大小的图标,但它也可以包含源信息,以便知道从哪里加载。它必须具有这样的信息,以便 DrawIconEx 调用(在标题栏/任务栏/alt-tab 对话框中)可以选择更合适的图标而不是调整大小。您可以通过调用 GetIconInfoEx 来检查。

引用 Raymond Chen 的话:

如果您通过调用 LoadIcon 创建图标,则窗口管理器会从指定的模块中加载指定的图标,但它还记得图标来自何处。(这个讨论同样适用于光标,但我只谈论图标,以避免尴尬。)当您向 CopyImage 函数传递 LR_COPYFROMRESOURCE 标志时,窗口管理器会返回到原始图标源以创建您请求的副本,而不是盲目地拉伸您传入的图标像素。

请记住,ICO文件不仅代表一个图标,而是一组图标(称为“图标组”),每个图标具有不同的大小或颜色深度。意图是图标组中的每个图标都描绘相同的基础图像,只是针对特定设置进行了调整。(现在,请注意,没有强制执行此意图。如果您将16×16图像设为微笑脸,将32×32图像设为狗叫声,那么这就是您的问题。)例如,单个ICO文件可能包含16×16图像、32×32图像和48×48图像。如果有人要求某个尺寸的图标,则使用相应的图像。另一方面,如果有人要求24×24图像,则窗口管理器将取出32×32图像并将其拉伸到必要的大小。

您可以使用GetIconInfoEx函数(适用于Windows Vista)恢复此“隐藏的源信息”。如果按序号加载图标,则szResName为空字符串,并将序号放置在wResID成员中。如果按名称加载图标,则wResID为零,szResName包含资源名称。

我们已经知道,图标和光标知道它们来自何处,窗口管理器在您要求更改图标大小时使用此信息。但并非所有图标都携带此信息,只有通过传递 HINSTANCE 和资源名称创建的图标才能携带。

如果您想销毁与图标关联的模块和资源信息,可以利用这一点。例如,CreateIconIndirect 函数从原始位图信息创建图标,不参考 HINSTANCE 或资源名称。这允许您在运行时创建图标,但也允许您创建一个“丢弃”额外信息的图标。

HICON CopyIconWithoutResourceInfo(HICON hicoSrc)
{
  ICONINFO ii;
  HICON hico = NULL;
  if (GetIconInfo(hicoSrc, &ii)) {
    hico = CreateIconIndirect(&ii);
    if (ii.hbmMask) DeleteObject(ii.hbmMask);
    if (ii.hbmColor) DeleteObject(ii.hbmColor);
  }
  return hico;
}

现在,丢弃这些信息是一种绝望的举动,因为它会阻止窗口管理器在调整图标大小时使用原始资源信息,导致图标被拉伸变形。

你甚至可能会错误地丢弃这些信息。例如,如果你的程序被要求生成一个图标,最好使用像LoadImage这样的函数加载图标,因为它记录了额外的信息;如果调用者决定调整图标大小,可以使用CopyImage函数来保持完全的保真度。另一方面,如果你使用像ExtractIconCreateIconFromResource这样的函数,那么它将没有额外的信息,任何图标拉伸都会变得非常丑陋。

https://devblogs.microsoft.com/oldnewthing/20080820-00/?p=21173 https://devblogs.microsoft.com/oldnewthing/20080822-00/?p=21133


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接