Примусово закрити програмне забезпечення MessageBox

Дозвольте мені дати вам фон.

У нас є додаток (середнього розміру), який використовує MessageBox.Show (....) у різних місцях (у сотнях).

Ці повідомлення є частиною робочого коду і використовуються для інформування, попередження або введення інформації від користувача. Заявка повинна автоматично вийти після певного часу, якщо немає активності. У нас є вимога, що під час виходу програми потрібно лише очистити дані сеансу, щоб очистити представлення даних і приховати себе, щоб в наступному запуску не потрібно було виконувати процес завантаження, який коштує дорого коштів у часі.

Все працює нормально, але за сценарію, коли на екрані з'являється якесь повідомлення, а користувач залишає машину, не відповідаючи на вікно повідомлень, а потім через відсутність активності, щоб програма виходила з системи. Проблема поля "Повідомлення" не зникне.

Як я можу закрити відкриту скриньку повідомлень, якщо така є, при приховуванні програми?

Дякую.

14
Я думав, що MessageBox.Show (...) є модальним, так як програма може відправити ключ? Ви використовуєте теми/завдання?
додано Автор Fischermaen, джерело
Може бути, надішліть ключ enter або esc? :)
додано Автор Reniuz, джерело
Дякую за відповіді. Просто для уточнення використання спеціального поля msg не є варіантом, оскільки переробка є досить великою. Надсилання ключа ESC також не є правильним, оскільки тільки активна програма отримає команду. Я використовую FIndWindow підхід, де я отримую ручку Msgbox, передаючи заголовок id і msg box. Після отримання обробника я закриваю, використовуючи наступний API win32, наприклад. SendMessage (новий HandleRef (нуль, msgbxcHandler), WM_CLOSE, IntPtr.Zero, IntPtr.Zero); SendMessage (новий HandleRef (нуль, msgbxcHandler), WM_NCDESTROY, IntPtr.Zero, IntPtr.Zero); Поки що це нормально.
додано Автор NYK, джерело

9 Відповіді

Ось фрагмент коду, заснований на UIAutomation

    /// 
/// Attempt to close modal windows if there are any. ///
 
    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
додано
Однак, я мав на увазі .net API, про який йде мова ...
додано Автор Erik Funkenbusch, джерело
Простір імен System.Windows.Automation говорить, що це для WPF. msdn.microsoft.com/en-us/library /ms747327(v=vs.110).aspx "Автоматизація інтерфейсу Microsoft - це нова концепція доступності для Microsoft Windows, доступна для всіх операційних систем, що підтримують Windows Presentation Foundation (WPF)".
додано Автор Erik Funkenbusch, джерело
Чи не UIAутомація для WPF? Питання було для WinForms
додано Автор Erik Funkenbusch, джерело
@ErikFunkenbusch - Не знаю, що ви маєте на увазі під "для WPF". UIAautomation підтримує будь-яку програму.
додано Автор Simon Mourier, джерело
Можливо, але UIAautomation не обмежується WPF, як клієнтом, так і сервером. У тій же статті також говориться: "Розробники постачальників інтерфейсу автоматизації для інших платформ, ніж WPF".
додано Автор Simon Mourier, джерело
Спасибі, мені дуже допомогло.
додано Автор digitguy, джерело

Це посилання на форумах MSDN показано, як закрити вікно повідомлення, використовуючи FindWindow та надсилаючи повідомлення WM_CLOSE . Хоча це питання було запропоновано для .NET/WindowsCE, це може вирішити вашу проблему, її варто подивитися

6
додано
Я думаю, що це вважається хорошою практикою, щоб додати по крайней мере, дуже небагато додаткової інформації про те, як пов'язана сторінка може вирішити проблему (замість того, щоб просто розмістити голу посилання). Я сподіваюся, ви не проти, що я додав короткий опис. У всякому разі, це може бути корисним ресурсом.
додано Автор MartinStettner, джерело
Добре Мартін, ти абсолютно правий. через складність часу я не зміг додати цю інформацію. однак, спасибі за редагування. :)
додано Автор Bravo, джерело

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

Я думаю, у вас є три варіанти

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

  2. Застосувати щось на зразок цього в C#, щоб закрити вікна повідомлень програмним шляхом. http://www.codeproject.com/KB/dialog/AutoCloseMessageBox.aspx

  3. Позбудьтеся з вікнами повідомлень, перериваючи робочий процес. Це, мабуть, найкраще рішення, оскільки звук закриття вікна повідомлення програмним шляхом призведе до того, що робочий процес буде продовжуватися/змінюватися, і навіть може призвести до появи іншого повідомлення, яке може виявитися небажаним. Але, очевидно, виправлення кореневої проблеми може бути кращим, але це не завжди найпростіше.

1 і 2 слід виконати з окремого потоку, тому вам потрібно буде подумати про наслідки цього, тому що показуючи, що повідомлення буде блокуватись.

3
додано
+1 для пункту 3.! Мені також не було б добре відчути, як просто «підтвердити» будь-яке відкрите вікно повідомлення, навіть не знаючи, чи помітив це користувач ...
додано Автор MartinStettner, джерело

Це моє приклад з SendKeys - перевірено і працює:

Скажімо, ми маємо фонового редактора та кнопку у формі. Після того, як кнопка натиснула - запустити працівника і показати вікно повідомлення. У робочих DoWork події засинайте 5 секунд, а потім надішліть клавішу enter, - вікно messsage box закрите.

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
}
2
додано
Це рішення працює, лише якщо користувач не змінює фокус на інший додаток. Ви не можете бути впевнені, що додаток має фокус. І Windows не дозволяє програмі програмувати фокус з іншого додатка.
додано Автор Alex, джерело
Я просто хочу зазначити, що для рішення дуже важливо перевірити, чи відкрито справжнє вікно повідомлення: клавіша введення, надіслана до будь-якої іншої частини користувальницького інтерфейсу, може мати небажані наслідки ...
додано Автор MartinStettner, джерело
Так ваша точка хороша. Його швидкий шлях, таким чином, ключ введення, очевидно, не є правильним ключем, краще використовувати есконтролю в інтерфейсі користувача, це означає скасування.
додано Автор Reniuz, джерело

Вважаючи припущення, що ви можете редагувати код, який викликає MessageBox.Show() метод, я б рекомендував не використовувати MessageBox. Замість цього просто використовуйте свою власну форму, називаючи ShowDialog() на це робити в основному те ж саме, що і клас MessageBox. Тоді ти мати екземпляр форми самої, і ви можете викликати Close() на цьому наприклад, щоб закрити його.

Хорошим прикладом є тут .

1
додано
Ой! І ми бачимо багато разів, наскільки жахливим є це, коли люди повторюють коробки повідомлень. msg показує, що ви можете мати справжнє вікно повідомлення та закрити його після затримки ...
додано Автор Joey, джерело
@ Джей: так, ви маєте рацію ... це була просто ідея, а не краща, ймовірно. Ви кажете, що msg показує, що ви можете мати справжнє вікно повідомлення та закрити його після затримки ... хто або хто є "msg"? Вибач, я не розумію, прошу вибачення
додано Автор Marco, джерело

Я думаю, найчистішим способом було б реалізувати у вас власну форму вікна повідомлення, як

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
додано
Що робити, якщо користувач натискає вкладку alt або інше поле повідомлення, яке відображається над потрібним полем повідомлення. в цьому сценарії я думаю, що він закриє небажаний.
додано Автор Vivekh, джерело

Refer to DmitryG post in "Close a MessageBox after several seconds"

Автоматично закривати 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
додано

Я використав .net 2 і два підходи з тим же трюком.

Open the MessageBox from stub-Form with MessageBox.Show(this,"message")

Коли форма не відображається або не має реального інтерфейсу.

  1. Keep the form handler and close it with:

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

    or

  2. holding the form as class parameter and using FormX.Close().

Оскільки форма є власником MessageBox, після закриття буде закрито вікно повідомлення.

1
додано

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

0
додано
var chat = new Chat();
var chat = new Chat();
642 учасників

Обсуждение вопросов по C# / .NET / .NET Core / .NET Standard / Azure Сообщества-организаторы: — @itkpi — @dncuug