如何在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
存在的图标与Asterisk
, Information
and Question
MessageBoxes
上显示的图标不同。 对话框上的图标看起来更平坦 。
在所有其他情况下,它们看起来完全相同,例如:如果出现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(); } }
代码运行后,名为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(); } } } } }