SetClassLong(hWnd,GCL_HICON,hIcon)无法替换WinForms Form.Icon [英] SetClassLong(hWnd, GCL_HICON, hIcon) cannot replace WinForms Form.Icon

查看:1149
本文介绍了SetClassLong(hWnd,GCL_HICON,hIcon)无法替换WinForms Form.Icon的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想使用特定的 ICO 文件作为我的WinForms应用程序的图标。由于我希望能够为Alt-Tabbing指定标题栏的小图标(16x16)和普通图标(32x32),我无法使用 Form.Icon 属性接受一个 System.Drawing.Icon 对象,它强制我使用低分辨率图标或普通图标。



我发布了一个相关的问题提出了一个非常简单的解决方案,适用于本机Win32应用程序:

  SetClassLong(hWnd,GCL_HICON ,hIcon32x32); 
SetClassLong(hWnd,GCL_HICONSM,hIcon16x16);

尝试在表单上应用相同的技巧不起作用。我有以下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 。但这不起作用:


  1. 表单中的图标仍然是默认的WinForms提供的图标。

  2. 按下Alt-Tab时,我仍然看到WinForms默认图标。

...但是,有趣的是,当我按下Alt-Tab时,我会在非常短的时间内看到我使用 GCL_HICON 定义的图标(或 GCL_HICONSM 如果我不使用 GCL_HICON )。在幕后似乎发生了一些事情,迫使Windows使用WinForms默认图标绘制图标。



我无法弄清楚我做错了什么幕后发生了什么。



编辑:我真的希望能够提供动态创建的两个不同的图标,而不是绑定 Form.Icon 到磁盘上的图标。这就是我尝试使用P / Invoke代码在内存中指定图标的原因。

解决方案

我还没有真正验证过通过测试或查看反汇编的WinForms代码,我不确定这个答案是否能满足可靠和/或官方来源的赏金条件。但我觉得我很可靠,所以无论如何我都会试一试!



你正在设置与窗口关联的图标。您正在使用 SetClassLong [Ptr] 函数和 GCL_HICON / GCL_HICONSM indices,但它与在 WNDCLASSEX 结构。这会为该类的窗口设置默认图标。



但是,各个窗口可以设置自己的图标,覆盖其提供的默认图标类。您可以通过发送 WM_SETICON 消息,传递 ICON_BIG ICON_SMALL 作为 wParam ,图标的句柄为 lParam 。据推测,这就是WinForms正在做的事情。这就是出现默认WinForms图标而不是您正在分配的默认窗口类图标的原因,因为WinForms使用 WM_SETICON 设置其默认图标,而不是通过窗口类。关于WinForms图标的唯一默认是,如果您没有分配不同的自定义图标,它将由框架自动分配。它不适合默认的任何其他定义 - 当然也不是从Win32角度使用的那个。



Form.Icon 属性肯定使用 WM_SETICON 来修改图标,这就是它按预期工作的原因。现在,你说你不想设置Icon属性,因为


我真的希望能够提供两个不同的图标来创建苍蝇,不将Form.Icon绑定到磁盘上的图标。这就是我尝试使用P / Invoke代码在内存中指定图标的原因。


但这并不意味着你可以不要设置图标属性。您可以在此处指定图标的句柄( HICON ),就像使用P / Invoke一样。您只需要静态 图标.FromHandle 方法,它从指定的 HICON Icon 对象>。然后,您将此图标对象分配给表单的图标属性。



但是,你不必这样做。如果需要,可以使用P / Invoke:

  const int WM_SETICON = 0x80; 

enum IconType
{
ICON_BIG = 1;
ICON_SMALL = 0;
}

[DllImport(user32.dll)]
static extern IntPtr SendMessage(IntPtr hWnd,
int message,
IntPtr wParam,
IntPtr lParam);

然后,将其称为类似于您所拥有的:

  IntPtr hIcon32x32 = ...; 
IntPtr hIcon16x16 = ...;
SendMessage(this.Handle,WM_SETICON,(IntPtr)IconType.ICON_BIG,hIcon32x32);
SendMessage(this.Handle,WM_SETICON,(IntPtr)IconType.ICON_SMALL,hIcon16x16);

只有一件事你做错了:假设大图标总是32x32像素并且小图标将始终为16x16像素。至少,我假设你是从变量的名字这样做的。如果是这样,那是一个无效的假设。这些只是最常见的尺寸。并不保证它们在所有环境中都是相同的。这就是为什么在.ico文件中提供更大的图标很重要的原因;例如,48x48图标。由于您正在动态设置图标,因此Windows无法访问较大的图标以进行缩减采样,当您的32x32图标按比例放大时,您可能会得到一些非常模糊和丑陋的内容。



要检索实际大小,请调用 GetSystemMetrics 函数。 SM_CXICON SM_CYICON 标志将分别告诉您大图标的X和Y尺寸。 SM_CXSMICON SM_CYSMICON 标志将分别告诉您小图标的X和Y尺寸。

  const int SM_CXICON = 11; 
const int SM_CYICON = 12;
const int SM_CXSMICON = 49;
const int SM_CYSMICON = 50;

[DllImport(user32.dll)]
static extern int GetSystemMetrics(int smIndex);





 静态大小GetBigIconSize( )
{
int x = GetSystemMetrics(SM_CXICON);
int y = GetSystemMetrics(SM_CYICON);
返回大小(x,y);
}
静态大小GetSmallIconSize()
{
int x = GetSystemMetrics(SM_CXSMICON);
int y = GetSystemMetrics(SM_CYSMICON);
返回大小(x,y);
}


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:

  1. The icon in the form is still the default WinForms provided icon.
  2. 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.

解决方案

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 and GCL_HICON/GCL_HICONSM indices, but it has the same effect as setting it in the WNDCLASSEX 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 either ICON_BIG or ICON_SMALL as the wParam and a handle to the icon as the lParam. 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 using WM_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 uses WM_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 because

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.

But 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 static Icon.FromHandle method, which creates a new Icon object from the specified HICON. You then assign this Icon object to the form's Icon property.

You don't have to, though. You can use P/Invoke if you want:

const int WM_SETICON = 0x80;

enum IconType
{
    ICON_BIG   = 1;
    ICON_SMALL = 0;
}

[DllImport("user32.dll")]
static extern IntPtr SendMessage(IntPtr hWnd,
                                 int message,
                                 IntPtr wParam,
                                 IntPtr lParam);

Then, call it similar to what you have:

IntPtr hIcon32x32 = ...;
IntPtr hIcon16x16 = ...;
SendMessage(this.Handle, WM_SETICON, (IntPtr)IconType.ICON_BIG, hIcon32x32);
SendMessage(this.Handle, WM_SETICON, (IntPtr)IconType.ICON_SMALL, hIcon16x16);

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. The SM_CXICON and SM_CYICON flags will tell you the X and Y dimensions, respectively, of the "big" icon. The SM_CXSMICON and SM_CYSMICON flags will tell you the X and Y dimensions, respectively, of the "small" icon.

const int SM_CXICON   = 11;
const int SM_CYICON   = 12;
const int SM_CXSMICON = 49;
const int SM_CYSMICON = 50;

[DllImport("user32.dll")]
static extern int GetSystemMetrics(int smIndex);

static Size GetBigIconSize()
{
    int x = GetSystemMetrics(SM_CXICON);
    int y = GetSystemMetrics(SM_CYICON);
    return Size(x, y);
}
static Size GetSmallIconSize()
{
    int x = GetSystemMetrics(SM_CXSMICON);
    int y = GetSystemMetrics(SM_CYSMICON);
    return Size(x, y);
}

这篇关于SetClassLong(hWnd,GCL_HICON,hIcon)无法替换WinForms Form.Icon的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆