SetClassLong(hWnd, GCL_HICON, hIcon) 无法替换 WinForms Form.Icon
我想使用特定的 ICO 文件作为 WinForms 应用程序的图标。由于我希望能够在 Alt-Tabbing 时为标题栏指定一个小图标 (16x16) 和一个普通图标 (32x32),因此我无法使用接受单个 < 的 Form.Icon
属性。 code>System.Drawing.Icon 对象,这迫使我使用低分辨率图标或普通图标。
我发布了一个相关问题想出了一个非常简单的解决方案,适用于本机 Win32 应用程序:
SetClassLong(hWnd, GCL_HICON, hIcon32x32);
SetClassLong(hWnd, GCL_HICONSM, hIcon16x16);
尝试在 Form
上应用相同的技巧是行不通的。我有以下 P/Invoke 定义:
[DllImport ("User32.dll")]
extern static int SetClassLong(System.IntPtr hWnd, int index, int value);
const int GCL_HICON = -14;
const int GCL_HICONSM = -34;
然后我只需调用:
System.IntPtr hIcon32x32 = ...;
System.IntPtr hIcon16x16 = ...;
SetClassLong(this.Handle, GCL_HICON, hIcon32x32.ToInt32());
SetClassLong(this.Handle, GCL_HICONSM, hIcon16x16.ToInt32());
并且从不调用 Form.Icon
。然而,这不起作用:
- 表单中的图标仍然是 WinForms 提供的默认图标。
- 当按 Alt-Tab 时,我仍然看到 WinForms 默认图标。
...但是,有趣的是,当我按 Alt-Tab 时,我会在非常短的时间内看到我使用 GCL_HICON
定义的图标(或者 GCL_HICONSM
如果我不要使用GCL_HICON
)。幕后似乎发生了一些事情,迫使 Windows 使用 WinForms 默认图标绘制图标。
我不明白我做错了什么以及幕后发生了什么。
编辑:我真的希望能够提供动态创建的两个不同的图标,而不是将 Form.Icon
绑定到磁盘上的图标。这就是为什么我尝试使用 P/Invoke 代码来指定内存中的图标。
I'd like to use a specific ICO
file as my icon with a WinForms application. Since I want to be able to specify a small icon (16x16) for the title bar and a normal icon (32x32) when Alt-Tabbing, I cannot use the Form.Icon
property which accepts a single System.Drawing.Icon
object, which forces me to use either the low res icon, or the normal icon.
I posted a related question which came up with a very simple solution which works fine for native Win32 applications:
SetClassLong(hWnd, GCL_HICON, hIcon32x32);
SetClassLong(hWnd, GCL_HICONSM, hIcon16x16);
Trying to apply the same trick on a Form
does not work. I have following P/Invoke definitions:
[DllImport ("User32.dll")]
extern static int SetClassLong(System.IntPtr hWnd, int index, int value);
const int GCL_HICON = -14;
const int GCL_HICONSM = -34;
and I then simply call:
System.IntPtr hIcon32x32 = ...;
System.IntPtr hIcon16x16 = ...;
SetClassLong(this.Handle, GCL_HICON, hIcon32x32.ToInt32());
SetClassLong(this.Handle, GCL_HICONSM, hIcon16x16.ToInt32());
and never call Form.Icon
. This does not work, however:
- The icon in the form is still the default WinForms provided icon.
- When pressing Alt-Tab, I still see the WinForms default icon.
...but, what's interesting, is that when I press Alt-Tab, I see for a very very short moment the icon I defined using GCL_HICON
(or GCL_HICONSM
if I do not use GCL_HICON
). Something seems to be happening behind the scenes, which forces Windows to paint the icon using the WinForms default icon.
I can't figure out what I've done wrong and what is going on behind the scenes.
edited: I really want to be able to provide two different icons created on the fly, not bind the Form.Icon
to an icon on disk. That's why I am trying to use the P/Invoke code to specify the icons in memory.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
我实际上还没有通过测试或查看反汇编的 WinForms 代码来验证这一点,所以我不确定这个答案是否满足“可信和/或官方来源”的赏金条件。但我认为我非常可信,所以无论如何我都会尝试一下!
您正在设置与窗口类关联的图标。您可以使用
SetClassLong[Ptr]
函数和GCL_HICON
/GCL_HICONSM
索引来完成此操作,但它与在WNDCLASSEX
结构位于班级注册时间。这会设置该类窗口的默认图标。但是,各个窗口可以设置自己的图标,覆盖其类提供的默认图标。您可以通过发送
WM_SETICON
消息,传递ICON_BIG
或ICON_SMALL
作为wParam
以及指向图标作为lParam
。据推测,这就是 WinForms 正在做的事情。这就是为什么显示“默认”WinForms 图标而不是您分配的默认窗口类图标的原因,因为 WinForms 使用 WM_SETICON 设置其默认图标,而不是通过窗口类。关于 WinForms 图标的唯一“默认”是,如果您不分配不同的自定义图标,它会由框架自动分配。它不符合“默认”的任何其他定义——当然不符合从 Win32 角度可能使用的定义。Form.Icon
属性肯定使用WM_SETICON
来修改图标,这就是它按预期工作的原因。现在,您说您不想设置 Icon 属性,因为但这并不意味着您无法设置
Icon
属性。您可以在此处指定图标的句柄 (HICON
),就像使用 P/Invoke 时一样。您所需要的只是静态Icon.FromHandle< /code>
方法,该方法根据指定的
HICON
创建一个新的Icon
对象。然后,将此Icon
对象分配给表单的Icon
属性。但你不必这样做。如果需要,您可以使用 P/Invoke:
然后,像您所拥有的那样调用它:
只有一件事您做错了:假设“大”图标始终为 32x32 像素,而“小”图标始终为 32x32 像素为 16x16 像素。至少,我假设您是根据变量的名称来执行此操作的。如果是这样,那就是一个无效的假设。这些只是最常见的尺寸。不保证它们在所有环境中都相同。这就是为什么在 .ico 文件中提供更大的图标很重要;例如,48x48 图标。由于您动态设置图标,Windows 将无法访问较大的图标来进行缩减采样,并且当您的 32x32 图标放大时,您可能会得到一些非常模糊和丑陋的东西。
要检索实际尺寸,请调用
GetSystemMetrics
函数。SM_CXICON
和SM_CYICON
标志将分别告诉您“大”图标的 X 和 Y 尺寸。SM_CXSMICON
和SM_CYSMICON
标志将分别告诉您“小”图标的 X 和 Y 尺寸。I haven't actually verified this by testing it or looking at the disassembled WinForms code, so I'm not sure if this answer will satisfy the bounty condition of "credible and/or official sources". But I think I'm pretty [in]credible, so I'll give it a shot anyway!
You're setting the icon associated with the window class. You're doing it with the
SetClassLong[Ptr]
function andGCL_HICON
/GCL_HICONSM
indices, but it has the same effect as setting it in theWNDCLASSEX
structure at the time that the class is registered. This sets the default icon for windows of that class.However, individual windows can set their own icons, overriding the default icon provided by their class. You do this by sending the
WM_SETICON
message, passing eitherICON_BIG
orICON_SMALL
as thewParam
and a handle to the icon as thelParam
. Presumably, this is what WinForms is doing. That's why the "default" WinForms icon is appearing instead of the default window class icon you're assigning, because WinForms is setting its default icon usingWM_SETICON
, not via the window class. The only thing "default" about the WinForms icon is that it's assigned automatically by the framework if you don't assign a different custom icon. It doesn't fit any other definition of "default"—certainly not one that might be used from a Win32 perspective.The
Form.Icon
property definitely usesWM_SETICON
to modify the icon, that's why it is working as expected. Now, you say you don't want to set the Icon property becauseBut that doesn't mean you can't set the
Icon
property. You can specify a handle to an icon (HICON
) here, just as well as you can if you use P/Invoke. All you need is the staticIcon.FromHandle
method, which creates a newIcon
object from the specifiedHICON
. You then assign thisIcon
object to the form'sIcon
property.You don't have to, though. You can use P/Invoke if you want:
Then, call it similar to what you have:
Only one thing you're doing wrong: assuming that the "big" icon will always be 32x32 pixels and that the "small" icon will always be 16x16 pixels. At least, I'm assuming that you're doing this from the names of the variables. If so, that's an invalid assumption. Those are only the most common sizes. They are not guaranteed to be the same in all environments. This is why it's important to provide larger icons in your .ico file; for example, a 48x48 icon. Since you're setting the icons dynamically, Windows won't have access to a larger icon to downsample and you might end up with something really blurry and ugly when your 32x32 icon is scaled up.
To retrieve the actual sizes, call the
GetSystemMetrics
function. TheSM_CXICON
andSM_CYICON
flags will tell you the X and Y dimensions, respectively, of the "big" icon. TheSM_CXSMICON
andSM_CYSMICON
flags will tell you the X and Y dimensions, respectively, of the "small" icon.您可以使用
Form.Icon
。您只需要一个包含 16x16 和 32x32 像素版本图标的图标文件。我刚刚尝试过,使用包含 32x32 像素红色圆圈和 16x16 蓝色矩形的单个图标文件。小窗口图标显示蓝色矩形,alt-tab 图标显示红色圆圈。
根本不需要 P/Invoke。
You can use
Form.Icon
. You just need a single icon file that contains 16x16 and 32x32 pixel versions of your icon.I just tried it, using a single icon file that contains a 32x32 pixel red circle and a 16x16 blue rectangle. The small windows icon shows the blue rectangle, the alt-tab icon shows a red circle.
No need for P/Invoke at all.