如何在Windows 8.1中获取MessageBox图标

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

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

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

更新:

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

这是结果的样子: 检索到的图标

请注意 :

如果此函数返回psii指向的SHSTOCKICONINFO结构的hIcon成员中的图标句柄,则您负责在不再需要时使用DestroyIcon释放该图标。


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

原始答案:

非常有趣的是, SystemIcons存在的图标与AsteriskInformation and Question MessageBoxes上显示的图标不同。 对话框上的图标看起来更平坦

Asterix和信息的图标问题的图标

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

错误的图标

当您尝试使用SystemIcons获取图标时,您将获得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(); } } 

代码运行后,名为pictureBox3PictureBox将显示与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(); } } } } }