Существует ли неблокирующая версия MessageBox.Show (или что-то подобное)? - PullRequest
31 голосов
/ 14 мая 2010

Обновление с большой задержкой

Я принимаю ответ MUG4N на этот вопрос и хочу ответить на некоторые критические замечания, высказанные против него.

ChrisF сказал:

... вы не можете делать вызовы пользовательского интерфейса напрямую из фоновых потоков.

Это общий оператор, и он не на 100% верен.Позвольте мне лишь указать на несколько фактов:

  1. На самом деле вы можете делать вызовы пользовательского интерфейса все, что вы хотите , если вы установите Control.CheckForIllegalCrossThreadCalls = false. «Ack!» Я слышу, как ты говоришь. «Не никогда не делайте этого!» Да, да - но почему ?Ответ: потому что иногда это приведет к повреждению памяти.

    Управляющие классы в System.Windows.Forms не записаны как поточно-ориентированные, поэтому иногда их обновление из фоновых потоков может повредить память.Но если это только иногда происходит, а не всегда , то это говорит мне о том, что это не вызов кода UI per se , а скореепотенциально небезопасная коллизия кода пользовательского интерфейса, которая может вызвать исключения .

  2. Чтобы укрепить пункт 1, рассмотрим это: «безопасный» способ вызова кода пользовательского интерфейсаиз фонового потока это сделать, используя Control.Invoke или Control.BeginInvoke, верно? Но это является вызовом пользовательского интерфейса ;это просто пользовательский интерфейс, который мы должны сделать, если мы обновляем GUI из потока, не являющегося GUI.Я имею в виду, очевидно, что это не просто вызов «любого» метода для объекта Control из внешнего потока, который вызовет хаос (если бы это было так, то мы не могли бы даже вызвать Invoke, и мызастрял бы полностью).Опять же, это потенциальная коллизия отдельных вызовов пользовательского интерфейса, которые не могут безопасно происходить одновременно, что приведет к разрушительным последствиям.

  3. Имея в виду две вышеупомянутые точки, спросите себя: почему было бы небезопасно звонитьMessageBox.Show из потока без графического интерфейса?Полностью отдельный Form создается и отображается;его свойства никоим образом не взаимодействуют с любым другим существующим объектом GUI;на самом деле, он не может быть доступен где-нибудь в любым способом , кроме одного: из вызывающего потока, который получает доступ к свойству DialogResult (и только через метод Show).возвращаемое значение).

Перемещение.Конрад Альбрехт сказал:

... учитывая утверждение, что Show () настраивает свою собственную рассылку сообщений в теме рефана Дэна (которая не была подтверждена, но которую я не могу опровергнуть)...

Это совершенно справедливо (хотя я лично высоко ценю Джареда Пара, что я не склонен к сомнению в его словах).В любом случае, взгляд на метод MessageBox.Show через Reflector показывает этот фрагмент:

Application.BeginModalMessageLoop();
try
{
    result = Win32ToDialogResult(SafeNativeMethods.MessageBox(new HandleRef(owner, zero), text, caption, type));
}
finally
{
    Application.EndModalMessageLoop();
    UnsafeNativeMethods.ThemingScope.Deactivate(userCookie);
}

Дальнейший взгляд на метод Application.BeginModalMessageLoop показывает это:

ThreadContext.FromCurrent().BeginModalMessageLoop(null);

И это ThreadContext.FromCurrent, в свою очередь:

// [Reflector shows that currentThreadContext is a ThreadStatic member. -Dan]
if (currentThreadContext == null)
{
    currentThreadContext = new Application.ThreadContext();
}
return currentThreadContext;

Я недостаточно знаю об этих низкоуровневых конструкциях Windows, чтобы полностью понять этот код, но мне кажется, что это свидетельствует оименно то, что говорил Джаред в ответе, на который я ссылался в своем старом комментарии (для любопытных читателей: Является ли MessageBox.Show () автоматически маршалировать в поток пользовательского интерфейса? ).

Итак, да.Я полностью согласен с MUG4N по этому вопросу.

(Если кто-то может убедительно доказать, что я все еще здесь ошибаюсь, пожалуйста, говорите. Хотя я чувствую, что сделал довольно хорошее объяснение, почему я верю в MUG4NПравильно, я, очевидно, не уверен на 100%.)


Оригинальный вопрос

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

MessageBox.Show("Something has occurred", "Something", MessageBoxButtons.OK);

Этот код, как мы все знаем, вызывает появление небольшого всплывающего окна только с кнопкой OK . Теперь вот что: этот код блокирует (поток пользовательского интерфейса). Но в подавляющем большинстве случаев, мне кажется, что если у вас только есть кнопка OK , блокировка не требуется. (Разве цель блокирования обычно не заключается в получении какого-либо ввода от пользователя? И если выбор пользователя only"OK", то в этом типичном случае блокирование не является довольно бессмысленным?)

Очевидно, я мог бы написать свою собственную маленькую форму, которая в основном делает то же, что делает MessageBox.Show, за исключением того, что она ничего не возвращает (нет DialogResult) и не блокирует. Но мне было просто интересно, существует ли что-то подобное, о чем я не знал.

Ответы [ 4 ]

11 голосов
/ 14 мая 2010

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

7 голосов
/ 14 мая 2010

А как насчет добавления NotifyIcon в ваше приложение и отображения всплывающей подсказки ? Недостатком является то, что уведомление исчезнет через короткое время, но, возможно, это будет лучше для ваших пользователей, если им не нужно предпринимать какие-либо действия.

Есть еще предложения по этому вопросу .

2 голосов
/ 14 мая 2010

Я бы попробовал вызвать функцию MessageBox непосредственно из Win32 API, например:

using System.Runtime.InteropServices;

[DllImport("User32.dll")]
public static extern int MessageBox(int h, string m, string c, int type);

Попробуйте использовать нулевой дескриптор и тип APPLMODAL. Это может сработать.

0 голосов
/ 30 апреля 2013

Это старый вопрос, но neverheles ...

1) Импорт IWshShell («Объектная модель хоста сценариев Windows»)

Dim wsh As New IWshRuntimeLibrary.WshShell wsh.Popup (String.Concat (Me.GetType.FullName, vbCrLf, _ Application.ExecutablePath), 0,75, «Заголовок», MessageBoxButtons.OKCancel или MessageBoxIcon.Question)

Jens ...

...