如何在Windows 8.1中获取MessageBox图标

5
我想获取MessageBox中显示的MessageBoxIcons。以前我使用SystemIcons来实现这一目的,但现在似乎返回的图标与MessageBox上的不同。
由此可以得出结论,在Windows 8.1中,SystemIcons和MessageBoxIcons是不同的。我知道可以使用WinApi MessageBox来获取图标,但无论如何都无法获得图标本身。
我想请问有没有一种检索这些图标的方法。
2个回答

14
更新:
你应该使用SHGetStockIconInfo函数。
要在C#中实现这个功能,你需要定义一些枚举和结构体(参考此优秀页面获取更多信息)。
public enum SHSTOCKICONID : uint
{
    //...
    SIID_INFO = 79,
    //...
}

[Flags]
public enum SHGSI : uint
{
    SHGSI_ICONLOCATION = 0,
    SHGSI_ICON = 0x000000100,
    SHGSI_SYSICONINDEX = 0x000004000,
    SHGSI_LINKOVERLAY = 0x000008000,
    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

如果此函数在psii指向的SHSTOCKICONINFO结构体的hIcon成员中返回一个图标句柄,您需要使用DestroyIcon释放该图标。


我不会删除我的原始答案,因为我认为它包含有关此问题的有用信息,以及检索此图标的另一种方式(或解决方法)。
原始答案:
有趣的是,在Asterisk、Information和Question这些情况下,SystemIcons中存在的图标与MessageBoxes中显示的图标不同。对话框上的图标看起来更平坦。

Icons for Asterix and Information Icons for Question

在其他所有情况下,它们看起来完全相同,例如:在Error的情况下:

Icon for Error

当您尝试使用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应用程序,窗体有三个和一个,它们的名称可以从代码中推断出来...):
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();
                }
            }
        }
    }
}

你的决定很好,但是对我没有帮助。Concealer错误地更正了我的问题。我不是在寻找解决方法(比如你提供的消息框示例)。我正在寻找获取图标(例如SystemIcons.Asterisk)的直接方法,但它们可能不同。是否有其他方法可以获得MessageBoxIcons? - user3747383

0

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