c# – 如何在Windows 8.1中获取MessageBox图标

我想获得MessageBoxIcons,当用户看到MessageBox时会显示它.之前我为此目的使用了SystemIcons,但现在看来它返回的图标与MessageBox上的图标不同.

这导致了在Windows 8.1中SystemIcons和MessageBoxIcons不同的结论.我知道使用WinApi MessageBox获取图标,但我似乎无法以任何方式获取图标.

我想问一种检索这些图标的方法.

解决方法:

更新:

您应该使用SHGetStockIconInfo功能.

要在C#中执行此操作,您必须定义一些枚举和结构(有关更多信息,请参阅this excellent page):

public enum SHSTOCKICONID : uint
{
    //...
    SIID_INFO = 79,
    //...
}

[Flags]
public enum SHGSI : uint
{
    SHGSI_ICONLOCATION = 0,
    SHGSI_ICON = 0x000000100,
    SHGSI_SYSICONINDEX = 0x000004000,
    SHGSI_LINKOVERLAY = 0x0000

If this function returns an icon handle in the hIcon member of the SHSTOCKICONINFO structure pointed to by psii, you are responsible for freeing the icon with DestroyIcon when you no longer need it.

, SHGSI_SELECTED = 0x000010000, SHGSI_LARGEICON = 0x000000000, SHGSI_SMALLICON = 0x000000001, SHGSI_SHELLICONSIZE = 0x000000004 } [StructLayoutAttribute(LayoutKind.Sequential, CharSet = CharSet.Unicode)] public struct SHSTOCKICONINFO { public UInt32 cbSize; public IntPtr hIcon; public Int32 iSysIconIndex; public Int32 iIcon; [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260/*MAX_PATH*/)] public string szPath; } [DllImport("Shell32.dll", SetLastError = false)] public static extern Int32 SHGetStockIconInfo(SHSTOCKICONID siid, SHGSI uFlags, ref SHSTOCKICONINFO psii);

之后,您可以轻松获得所需的图标:

 SHSTOCKICONINFO sii = new SHSTOCKICONINFO();
 sii.cbSize = (UInt32)Marshal.SizeOf(typeof(SHSTOCKICONINFO));

 Marshal.ThrowExceptionForHR(SHGetStockIconInfo(SHSTOCKICONID.SIID_INFO,
         SHGSI.SHGSI_ICON ,
         ref sii));
 pictureBox1.Image = Icon.FromHandle(sii.hIcon).ToBitmap();

这是结果的样子:

note

If this function returns an icon handle in the hIcon member of the
SHSTOCKICONINFO structure pointed to by psii, you are responsible for
freeing the icon with DestroyIcon when you no longer need it.

我不会删除我的原始答案,因为 – 我认为 – 它包含有关此问题的有用信息,以及检索此图标的另一种方法(或解决方法).

原始答案:

非常有趣的是,SystemIcons中存在的图标与Asterisk,Information and Question中MessageBox上显示的图标不同.对话框上的图标看起来更平坦.

在所有其他情况下,它们看起来完全相同,例如:出现错误:

当您尝试使用SystemIcons获取图标时,您将获得上图中左侧的图标.

// get icon from SystemIcons
pictureBox1.Image = SystemIcons.Asterisk.ToBitmap();

如果您尝试使用user32.dll中的LoadIcon方法稍微努力一点,您仍然可以获得相同的图标(可以在上面图像的中心看到).

[DllImport("user32.dll")]
static extern IntPtr LoadIcon(IntPtr hInstance, IntPtr lpIconName);

...

public enum SystemIconIds
{
    ...
    IDI_ASTERISK = 32516,
    ...
}

...

// load icon by ID
IntPtr iconHandle = LoadIcon(IntPtr.Zero, new IntPtr((int)SystemIconIds.IDI_ASTERISK));
pictureBox2.Image = Icon.FromHandle(iconHandle).ToBitmap();

但是当你展示一个MessagBox时,你得到一个不同的(如图像上的MessageBox中所示).一个人别无选择,只能从MessageBox中获取该图标.

为此我们需要更多的DllImports:

// To be able to find the dialog window
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr FindWindow(string lpClassName, string lpWindowName);

// To be able to get the icon window handle
[DllImport("user32.dll")]
static extern IntPtr GetDlgItem(IntPtr hDlg, int nIDDlgItem);

// To be able to get a handle to the actual icon
[DllImport("user32.dll", CharSet = CharSet.Auto)]
static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, IntPtr lParam);

这个想法如下:首先我们显示一个MessageBox,然后(当它仍然显示时)我们找到它的句柄,使用该句柄我们将得到另一个句柄,现在到包含图标的静态控件.最后,我们将向该控件发送一条消息(STM_GETICON消息),该消息将返回带有图标本身的句柄.使用该句柄,我们可以创建一个Icon,我们可以在应用程序的任何地方使用它.

在代码中:

// show a `MessageBox`
MessageBox.Show("test", "test caption", MessageBoxButtons.OK, MessageBoxIcon.Asterisk);

...

var hwnd = FindWindow(null, "test caption");
if (hwnd != IntPtr.Zero)
{
    // we got the messagebox, get the icon from it
    IntPtr hIconWnd = GetDlgItem(hwnd, 20);
    if (hIconWnd != IntPtr.Zero)
    {
        var iconHandle = SendMessage(hIconWnd, 369/*STM_GETICON*/, IntPtr.Zero, IntPtr.Zero);

        pictureBox3.Image = Icon.FromHandle(iconHandle).ToBitmap();
    }
}

代码运行后,名为pictureBox3的PictureBox将显示与MessageBox相同的图像(如图中右侧所示).

我真的希望这会有所帮助.

这里是所有代码的参考(它是一个WinForms应用程序,Form有三个PicturBoxes和一个Timer,它们的名字可以从代码中扣除……):

using System;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Windows.Forms;

namespace WindowsFormsApplication1
{
    public partial class Form1 : Form
    {
        [DllImport("user32.dll")]
        static extern IntPtr LoadIcon(IntPtr hInstance, IntPtr lpIconName);

        [DllImport("user32.dll", SetLastError = true)]
        static extern IntPtr FindWindow(string lpClassName, string lpWindowName);

        [DllImport("user32.dll")]
        static extern IntPtr GetDlgItem(IntPtr hDlg, int nIDDlgItem);

        [DllImport("user32.dll", CharSet = CharSet.Auto)]
        static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, IntPtr lParam);

        public enum SystemIconIds
        {
            IDI_APPLICATION = 32512,
            IDI_HAND = 32513,
            IDI_QUESTION = 32514,
            IDI_EXCLAMATION = 32515,
            IDI_ASTERISK = 32516,
            IDI_WINLOGO = 32517,
            IDI_WARNING = IDI_EXCLAMATION,
            IDI_ERROR = IDI_HAND,
            IDI_INFORMATION = IDI_ASTERISK,
        }

        public Form1()
        {
            InitializeComponent();
            // Information, Question and Asterix differ from the icons displayed on MessageBox

            // get icon from SystemIcons
            pictureBox1.Image = SystemIcons.Asterisk.ToBitmap();
            // load icon by ID
            IntPtr iconHandle = LoadIcon(IntPtr.Zero, new IntPtr((int)SystemIconIds.IDI_ASTERISK));
            pictureBox2.Image = Icon.FromHandle(iconHandle).ToBitmap();
        }

        private void pictureBox1_Click(object sender, EventArgs e)
        {
            MessageBox.Show("test", "test caption", MessageBoxButtons.OK, MessageBoxIcon.Asterisk);
        }


        private void timer1_Tick(object sender, EventArgs e)
        {
            var hwnd = FindWindow(null, "test caption");
            if (hwnd != IntPtr.Zero)
            {
                // we got the messagebox, get the icon from it
                IntPtr hIconWnd = GetDlgItem(hwnd, 20);
                if (hIconWnd != IntPtr.Zero)
                {
                    var iconHandle = SendMessage(hIconWnd, 369/*STM_GETICON*/, IntPtr.Zero, IntPtr.Zero);
                    pictureBox3.Image = Icon.FromHandle(iconHandle).ToBitmap();
                }
            }
        }
    }
}
上一篇:c# – WPF MessageBox看起来没有样式,而WindowsForms MessageBox看起来不错


下一篇:Messagebox自定义计时关闭