如何在Windows 8.1中获取MessageBox图标

时间:2014-06-17 06:59:04

标签: c# icons windows-8.1 messagebox

我想获取MessageBoxIcons,当用户看到MessageBox时会显示该消息。之前我为此目的使用了SystemIcons,但现在它似乎返回的图标与MessageBox上的图标不同。

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

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

1 个答案:

答案 0 :(得分:7)

<强>更新

您应该使用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 = 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();

结果如下:The retrieved icon

note

  

如果此函数返回 hIcon 成员中的图标句柄    SHSTOCKICONINFO 由psii指向的结构,您负责   

时,用 DestroyIcon 释放图标。


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

原始回答:

非常有趣的是,SystemIcons中显示的图标与MessageBoxesAsteriskInformationQuestion上显示的图标不同。对话框上的图标看起来很 flatter

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();
    }
}

代码运行后,名为PictureBox的{​​{1}}将显示与pictureBox3相同的图像(如图中右侧所示)。

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


此处参考的是所有代码(它是一个WinForms应用程序,表单有三个MessageBox和一个PicturBoxes,它们的名称可以从代码中扣除...):< / p>

Timer