Принудительно закрыть MessageBox программно - PullRequest
15 голосов
/ 28 октября 2011

Позвольте мне дать вам фон.

У нас есть приложение (среднего размера), которое использует MessageBox.Show (....) в разных местах (сотнями).

Эти окна сообщений являются частью рабочего процесса и используются для информирования, предупреждения или получения ввода от пользователя. Приложение должно автоматически выходить из системы по истечении определенного времени, если нет активности. У нас есть требование, чтобы при выходе из приложения просто очищать данные сеанса, очищать представления и скрывать себя, чтобы при следующем запуске не требовалось выполнять процесс запуска, который является дорогостоящим с точки зрения времени.

Все работает нормально, но в сценарии, когда на экране есть какое-то окно сообщения, и пользователь покинул машину, не отвечая на окно сообщения, а затем из-за отсутствия активности, чтобы заставить приложение выйти из системы. Проблема в том, что окно сообщения не исчезнет.

Как закрыть открытое окно сообщения, если оно есть, при скрытии приложения?

Ответы [ 9 ]

9 голосов
/ 28 октября 2011

Вот фрагмент кода, основанный на UIAutomation (классный, но все еще не очень используемый API), который пытается закрыть все модальные окна (включая окно, открытое с помощью MessageBox) текущего процесса:

    /// <summary>
    /// Attempt to close modal windows if there are any.
    /// </summary>
    public static void CloseModalWindows()
    {
        // get the main window
        AutomationElement root = AutomationElement.FromHandle(Process.GetCurrentProcess().MainWindowHandle);
        if (root == null)
            return;

        // it should implement the Window pattern
        object pattern;
        if (!root.TryGetCurrentPattern(WindowPattern.Pattern, out pattern))
            return;

        WindowPattern window = (WindowPattern)pattern;
        if (window.Current.WindowInteractionState != WindowInteractionState.ReadyForUserInteraction)
        {
            // get sub windows
            foreach (AutomationElement element in root.FindAll(TreeScope.Children, new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Window)))
            {
                // hmmm... is it really a window?
                if (element.TryGetCurrentPattern(WindowPattern.Pattern, out pattern))
                {
                    // if it's ready, try to close it
                    WindowPattern childWindow = (WindowPattern)pattern;
                    if (childWindow.Current.WindowInteractionState == WindowInteractionState.ReadyForUserInteraction)
                    {
                        childWindow.Close();
                    }
                }
            }
        }
    }

Например, если у вас есть приложение WinForms, которое выдает всплывающее окно MessageBox при нажатии какой-либо кнопки1, вы все равно сможете закрыть приложение с помощью меню «Закрыть окно» Windows (щелкните правой кнопкой мыши на панели задач).):

    private void button1_Click(object sender, EventArgs e)
    {
        MessageBox.Show("Don't click me. I want to be closed automatically!");
    }

    protected override void WndProc(ref System.Windows.Forms.Message m)
    {
        const int WM_SYSCOMMAND = 0x0112;
        const int SC_CLOSE = 0xF060;

        if (m.Msg == WM_SYSCOMMAND) // this is sent even if a modal MessageBox is shown
        {
            if ((int)m.WParam == SC_CLOSE)
            {
                CloseModalWindows();
                Close();
            }
        }
        base.WndProc(ref m);
    }

Вы можете использовать CloseModalWindows где-то еще в вашем коде, конечно, это всего лишь пример.

7 голосов
/ 28 октября 2011

Эта ссылка на форумах MSDN показывает, как закрыть окно сообщения, используя FindWindow и отправив сообщение WM_CLOSE.Хотя вопрос был задан для .NET / WindowsCE, он может решить вашу проблему, его стоит посмотреть

3 голосов
/ 28 октября 2011

Первый вопрос: если окна сообщений используются как часть рабочего процесса, не приведет ли программно закрытие окна сообщений к изменению / продолжению потока?

Я думаю, у вас есть три варианта

  1. Создайте свою собственную версию класса сообщений, которая открывает диалоговое окно, которое выглядит как окно сообщений с дополнительными функциями, поэтому оно автоматически закрывается через определенный промежуток времени.

  2. Реализуйте что-то подобное в c # для программного закрытия окон сообщений. http://www.codeproject.com/KB/dialog/AutoCloseMessageBox.aspx

  3. Избавьтесь от окон сообщений от прерывания рабочего процесса. Это, вероятно, лучшее решение, поскольку из-за звука, которое оно закрывает окно сообщений программным способом, приведет к продолжению / изменению рабочего процесса и, возможно, даже к отображению другого окна сообщения, что может быть нежелательным. Но, очевидно, решение проблемы с корнем может быть лучшим, но не всегда самым простым.

1 и 2 необходимо будет сделать из отдельного потока, поэтому вам нужно подумать о последствиях этого, поскольку показ сообщения будет блокировать.

2 голосов
/ 28 октября 2011

Вот мой пример с SendKeys - проверено и работает:

Допустим, у нас есть фоновый работник и кнопка в форме. После нажатия кнопки - запустить рабочий и показать окно сообщения. В рабочих DoWork событие спит в течение 5 секунд, а затем отправляет ввод ключа - окно сообщения закрыто.

private void button1_Click(object sender, EventArgs e)
{
    backgroundWorker1.RunWorkerAsync();
    MessageBox.Show("Close this message!");
}

private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
    Thread.Sleep(5000);
    SendKeys.SendWait("{Enter}");//or Esc
}
1 голос
/ 06 февраля 2014

См. Сообщение DmitryG в "Закрыть окно сообщений через несколько секунд"

Автоматическое закрытие MessageBox после достижения тайм-аута

using System.Runtime.InteropServices;
using System.Threading;
using System.Windows.Forms;

    public class AutoClosingMessageBox
    {
        System.Threading.Timer _timeoutTimer;
        string _caption;
        AutoClosingMessageBox(string text, string caption, int timeout)
        {
            _caption = caption;
            _timeoutTimer = new System.Threading.Timer(OnTimerElapsed,
                null, timeout, System.Threading.Timeout.Infinite);
            MessageBox.Show(text, caption);
        }
        public static void Show(string text, string caption, int timeout)
        {
            new AutoClosingMessageBox(text, caption, timeout);
        }
        void OnTimerElapsed(object state)
        {
            IntPtr mbWnd = FindWindow(null, _caption);
            if (mbWnd != IntPtr.Zero)
                SendMessage(mbWnd, WM_CLOSE, IntPtr.Zero, IntPtr.Zero);
            _timeoutTimer.Dispose();
        }
        const int WM_CLOSE = 0x0010;
        [System.Runtime.InteropServices.DllImport("user32.dll", SetLastError = true)]
        static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
        [System.Runtime.InteropServices.DllImport("user32.dll", CharSet = System.Runtime.InteropServices.CharSet.Auto)]
        static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, IntPtr lParam);
    }

и позвоните через

AutoClosingMessageBox.Show("Content", "Title", TimeOut);
1 голос
/ 11 апреля 2013

Я использовал .net 2 и два подхода с одним и тем же приемом.

Откройте MessageBox в форме-заглушке с помощью MessageBox.Show(this,"message")

Когда форма не видна или не имеетдействительно пользовательский интерфейс.

  1. Сохраните обработчик формы и закройте его:

    static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, IntPtr lParam);
    

    или

  2. , удерживая форму какПараметр класса и использование FormX.Close().

Поскольку форма является владельцем MessageBox, ее закрытие закроет MessageBox.

1 голос
/ 28 октября 2011

Я думаю, что самым чистым способом было бы реализовать собственную форму окна сообщения, например

class MyMessageBox : Form {
  private MyMessageBox currentForm; // The currently active message box

  public static Show(....) { // same as MessageBox.Show
    // ...
  }

  public static Show(...) { // define additional overloads
  }

  public static CloseCurrent() {
    if (currentForm != null)
      currentForm.Close();
  }

  // ...
}

В некоторых из моих крупных проектов я нашел этот подход полезным и для других целей (таких как автоматическое ведение журнала сообщений об ошибках и т. Д.)

Вторая идея, которую я имею, заключается в том, чтобы использовать GetTopWindow() (или, возможно, какую-то другую функцию WIN32), чтобы получить текущее окно верхнего уровня вашего приложения и отправить ему WM_CLOSE сообщение.

1 голос
/ 28 октября 2011

Исходя из предположения, что вы можете редактировать код, который вызывает MessageBox.Show() метод, я бы рекомендовал не использовать Окно сообщения. Вместо этого просто используйте свою собственную форму, вызывая ShowDialog() на нем делать в основном то же самое, что и класс MessageBox. Затем вы есть экземпляр самой формы, и вы можете вызвать Close() на этом экземпляр, чтобы закрыть его.

Хороший пример: здесь .

0 голосов
/ 28 октября 2011

Создайте свой собственный контроль для этого и реализуйте поведение, которое вы хотели бы иметь там. В качестве опции может быть таймер для закрытия этого MessageBox.

...