Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/video/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
SetClassLong(hWnd、GCL_HICON、HICON)无法替换WinForms Form.Icon_Winforms_Winapi_Pinvoke_Icons - Fatal编程技术网

SetClassLong(hWnd、GCL_HICON、HICON)无法替换WinForms Form.Icon

SetClassLong(hWnd、GCL_HICON、HICON)无法替换WinForms Form.Icon,winforms,winapi,pinvoke,icons,Winforms,Winapi,Pinvoke,Icons,我想使用特定的ICO文件作为WinForms应用程序的图标。由于我希望能够在Alt Tab键切换时为标题栏指定一个小图标16x16和一个普通图标32x32,因此我无法使用Form.icon属性,该属性接受单个System.Drawing.icon对象,强制我使用低分辨率图标或普通图标 我发布了一篇文章,其中提出了一个非常简单的解决方案,适用于本机Win32应用程序: SetClassLong(hWnd, GCL_HICON, hIcon32x32); SetClassLong(hWnd, GCL

我想使用特定的ICO文件作为WinForms应用程序的图标。由于我希望能够在Alt Tab键切换时为标题栏指定一个小图标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。但是,这不起作用:

表单中的图标仍然是WinForms提供的默认图标。 按Alt Tab键时,我仍然可以看到WinForms默认图标。 …但是,有趣的是,当我按下Alt Tab键时,我会在很短的时间内看到我使用GCL_HICON或GCL_HICONSM定义的图标(如果我不使用GCL_HICON)。幕后似乎发生了一些事情,这迫使Windows使用WinForms默认图标绘制图标

我不知道我做错了什么,也不知道幕后发生了什么

编辑:我真的希望能够提供两个不同的图标创建的飞行,而不是绑定表单。图标到磁盘上的图标。这就是为什么我尝试使用P/Invoke代码来指定内存中的图标。

您可以使用Form.Icon。您只需要一个包含16x16和32x32像素版本的图标文件

我只是尝试了一下,使用了一个图标文件,其中包含一个32x32像素的红色圆圈和一个16x16像素的蓝色矩形。小窗口图标显示蓝色矩形,alt tab图标显示红色圆圈

完全不需要p/Invoke。

您可以使用Form.Icon。您只需要一个包含16x16和32x32像素版本的图标文件

我只是尝试了一下,使用了一个图标文件,其中包含一个32x32像素的红色圆圈和一个16x16像素的蓝色矩形。小窗口图标显示蓝色矩形,alt tab图标显示红色圆圈


根本不需要p/Invoke。

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

您正在设置与窗口类关联的图标。您可以使用SetClassLong[Ptr]函数和GCL_HICON/GCL_HICONSM索引来执行此操作,但其效果与在类注册时在结构中设置它相同。这将为该类的窗口设置默认图标

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

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

我真的希望能够提供两个不同的图标创建的飞行,而不是绑定表单。图标到磁盘上的图标。这就是为什么我尝试使用P/Invoke代码来指定内存中的图标

但这并不意味着不能设置Icon属性。您可以在这里指定图标的句柄,就像使用P/Invoke一样。您所需要的只是静态方法,它从指定的图标创建一个新的图标对象。然后将此图标对象指定给窗体的图标属性

不过,你不必这么做。如果需要,可以使用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);

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

您正在设置与窗口类关联的图标。您可以使用SetClassLong[Ptr]函数和GCL_HICON/GCL_HICONSM索引来执行此操作,但其效果与在类注册时在结构中设置它相同。这将为该类的窗口设置默认图标

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

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

我真的希望能够提供两个不同的图标创建的飞行,而不是绑定表单。图标到磁盘上的图标。这就是为什么我尝试使用P/Invoke代码来指定内存中的图标

但这并不意味着不能设置Icon属性。您可以在这里指定图标的句柄,就像使用P/Invoke一样。您所需要的只是静态方法,它从指定的图标创建一个新的图标对象。然后将此图标对象指定给窗体的图标属性

不过,你不必这么做。如果需要,可以使用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);

谢谢你的回答;我没有意识到这一点。但是在我的例子中,我不想使用图标文件作为Form.icon的源,因为我希望能够动态生成大小图标。以及表单背后的魔力。加载大小图标版本的图标不起作用,因为我不知道如何创建一个包含两种尺寸的HICON;我没有意识到这一点。但是在我的例子中,我不想使用图标文件作为Form.icon的源,因为我希望能够动态生成大小图标。还有表单背后的魔力。加载大小图标版本的图标不起作用,因为我不知道如何创建一个包含两种尺寸的HICON。这就成功了,再加上我必须在应用图标更改之前显示表单的事实。非常感谢您在回答中花了这么多时间,并做了详细的解释。@Pierre的确,这是因为WinForms在创建表单时会用自己的默认图标覆盖类默认图标。但只有当表单的Icon属性为null时才会发生这种情况。我已经向您展示了,您可以为该属性指定一个HICON值,并且可以在显示表单之前执行此操作。然后,框架应自动使用适当的图标初始化表单。该逻辑位于Icon属性的getter中。如果没有设置用户图标,并且表单不是受限窗口,它将返回form.DefaultIcon的结果,这是一种内部静态方法。结合我必须在应用图标更改之前显示表单的事实,这就成功了。非常感谢您一直以来的支持
“请输入您的答案和详细解释。@Pierre确实,这是因为WinForms在创建窗体时会使用自己的默认图标覆盖类默认图标。但只有当表单的Icon属性为null时才会发生这种情况。我已经向您展示了,您可以为该属性指定一个HICON值,并且可以在显示表单之前执行此操作。然后,框架应自动使用适当的图标初始化表单。该逻辑位于Icon属性的getter中。如果未设置用户图标,并且表单不是受限窗口,则返回form.DefaultIcon(内部静态方法)的结果。