#c# #icons #windows-8.1 #messagebox
#c# #Значки #windows-8.1 #messagebox
Вопрос:
Я хочу получить значки MessageBoxIcons, которые отображаются, когда пользователю предоставляется a MessageBox
. Ранее я использовал SystemIcons для этой цели, но теперь кажется, что он возвращает значки, отличные от тех, что на MessageBox
.
Это приводит к выводу, что в Windows 8.1 SystemIcons и MessageBoxIcons разные. Я знаю, что значки берутся с помощью WinAPI MessageBox, но, похоже, я никак не могу получить сами значки.
Я хотел бы попросить способ получения этих значков.
Ответ №1:
Обновить:
Вы должны использовать 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();
Вот как будет выглядеть результат:
Пожалуйста, обратите внимание:
Если эта функция возвращает дескриптор значка в элементе hIcon структуры SHSTOCKICONINFO, на который указывает psii, вы несете ответственность за освобождение значка с помощью DestroyIcon, когда он вам больше не нужен.
я не буду удалять свой первоначальный ответ, поскольку — я думаю — он содержит полезную информацию об этой проблеме и другой способ (или обходной путь) получения этого значка.
Оригинальный ответ:
Довольно интересно, что значки, присутствующие в SystemIcons
, отличаются от тех, которые отображаются MessageBoxes
в случае Asterisk
, Information
и Question
. Значки в диалоговом окне выглядят намного более плоскими.
Во всех остальных случаях они выглядят точно так же, например: в случае Error
:
Когда вы пытаетесь получить значок, используя значок SystemIcons
, вы получаете тот, который слева на приведенных выше изображениях.
// get icon from SystemIcons
pictureBox1.Image = SystemIcons.Asterisk.ToBitmap();
Если вы попробуете немного сложнее, используя LoadIcon
метод из user32.dll , вы по-прежнему получаете тот же значок (как это видно в центре приведенных выше изображений).
[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);
Идея заключается в следующем: сначала мы отображаем a 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
вызываемый pictureBox3
будет отображать то же изображение MessageBox
, что и (как видно справа на изображении).
Я действительно надеюсь, что это поможет.
Для справки вот весь код (это приложение WinForms, форма имеет три 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();
}
}
}
}
}
Комментарии:
1. Ваше решение хорошее, но мне не помогает. Корректор неправильно исправил мой вопрос. Я не ищу обходной путь (ваш пример messagebox). Я ищу прямой способ получения значков (SystemIcons. Например, звездочка), но, как вы увидите, разные. Есть ли другой способ получить MessageBoxIcons