Исключение при использовании вторичного насоса сообщений интерфейса для заставки - PullRequest
2 голосов
/ 18 июля 2010

Я столкнулся со странной проблемой при отображении всплывающей формы, которая вызывает InvalidAsynchronousStateException .

Прежде всего, вот код для Main {} где я запускаю всплывающую форму:

[STAThread]
static void Main(string[] args)
{
    Application.EnableVisualStyles();
    Application.SetCompatibleTextRenderingDefault(false);

    Thread splash = new Thread(new ThreadStart(ShowSplash));
    splash.Start();

     Application.Run(new MainForm());
}

static void ShowSplash()
{
    using (SplashForm splash = new SplashForm())
    {
        Application.Run(splash);
    }
}

Я использую .NET2.0 с Win XP.

Во время некоторых испытаний, когда приложение оставалось работающим в течение нескольких часов, я заметилчто число исключений иногда увеличивается на один или два.(Числа, полученные PerfMon, просматривая счетчик «# of Exceps Thrown».) Эти исключения, похоже, улавливаются и проглатываются средой выполнения, потому что они не пульсируют и не приводят к ошибкам в самом приложении.По крайней мере, я ничего не могу определить.

Я обнаружил, что исключение выдается, когда система запускает событие UserPreferenceChanged.Обнаружив это, я могу сгенерировать исключение по желанию, изменив фоновое изображение или заставку и т. Д.

Я не подписываюсь на это событие самостоятельно нигде в коде, но я понимаю (благодаря силеGoogle), что все элементы управления и формы верхнего уровня подписываются на это событие автоматически.

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

Теперь, если я остановлю запуск потока формы-всплеска, исключение исчезнет.Запустите поток, он возвращается.Итак, похоже, что что-то не отменяет подписку на событие, и, возможно, это вызывает последующее исключение?

Интересно, что если я заменил свою всплывающую форму на стандартную, из коробки «Форма», проблема все равно останется:

static void ShowSplash()
{
    using (Form splash = new Form())
    {
        Application.Run(splash);
    }
}

Во время отображения этой формы любые события UserPreferenceChanged не вызывают никаких исключений.Как только форма закрывается и поток завершается, генерируются исключения.

Дальнейшие исследования привели меня к этой статье Microsoft , в которой содержится следующий комментарий:

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

Хмм, виновные, как видно из их внешнего вида.Обратите внимание, что мое приложение не зависает и не делает ничего плохого.

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

Для меня это выглядит как форма или насос сообщений, запущенный Application.Run не очищает должным образом, когда он завершается.

Есть мысли?

Ответы [ 2 ]

2 голосов
/ 18 июля 2010

Да, вы столкнулись с классом SystemEvents.Этот класс создает скрытое окно, которое слушает системные события.В частности, событие UserPreferenceChanged, многие элементы управления используют это событие, чтобы знать, когда им нужно перекраситься, потому что системные цвета были изменены.

Проблема в том, что код инициализации, который создает окно, очень чувствителен к состоянию квартирыпотока, который его вызывает.Что в вашем случае неверно, вы не вызывали Thread.SetApartmentState () для переключения на STA.Это очень важно для потоков, которые отображают пользовательский интерфейс.

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

Я думаю, что вызов Thread.SetApartmentState () должен решить вашу проблему.Не уверен на 100%, что эти потоки пользовательского интерфейса очень сложно проанализировать, и я еще не понял это неправильно.Обратите внимание, что .NET уже имеет очень надежную поддержку для заставок.Это определенно получает детали, как это правильно.

1 голос
/ 18 июля 2010

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

Один из способов избежатьИсключением является не закрытие заставки, а просто ее скрытие.Примерно так:

public partial class SplashForm : Form
{
  public SplashForm()
  {
    InitializeComponent();
  }

  // Not shown here, this is wired to the FormClosing event!!!
  private void SplashForm_FormClosing(object sender, FormClosingEventArgs e)
  {      
    e.Cancel = true;
    this.Hide();
  }
}

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

[STAThread]  
static void Main(string[] args)  
{  
    Application.EnableVisualStyles();  
    Application.SetCompatibleTextRenderingDefault(false);  

    Thread splash = new Thread(new ThreadStart(ShowSplash));          
    splash.IsBackground = true;
    splash.Start();  

     Application.Run(new MainForm());  
}  

static void ShowSplash()  
{  
    using (SplashForm splash = new SplashForm())  
    {  
        Application.Run(splash);  
    }  
}
...