ExceptionReporter
От: jyuyjiyuijyu  
Дата: 13.07.13 04:40
Оценка: :)
Всем привет

использую компонент ExceptionReporter http://exceptionreporter.codeplex.com/

проблема в том что он выдает окошку с ошибкой вместо окна с репортом
из за того что поток в котором он попытался показать свою окошку не STA а MTA ...


ну хорошо для отдельно взятого потока (кроме потока гуя разумеется) я могу вручную
выполнить такой код

Thread^ t = gcnew Thread(gcnew ThreadStart(start));
t->SetApartmentState(ApartmentState::STA);
t->Start();


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

и что каждому потоку который родился в пуле при входе в юзер процедуру
выставлять STA ? видите ли для того что бы если произойдет исключение
ExceptionReporter смог отобразить свою окошку ?

непоняточка...

например этот код... тут в отдельном потоке происходит исключение его ловит наш фильтр
и пытается вызвать ExceptionReporter::Show на что он сообщает что я отказываюсь работать
из за того что поток MTA а не STA ... но стоит раскомментить t->SetApartmentState(ApartmentState::STA)
как все прерасно показывается...


ref struct CatchException
{
    void Show(System::Exception ^e)
    {
        ExceptionReporter^ reporter = gcnew ExceptionReporter();

        reporter->Show(e);
    }

    void UnhandledException(System::Object ^,System::UnhandledExceptionEventArgs ^e)
    {
        Show((System::Exception ^)e->ExceptionObject);

    }

    void ThreadException(System::Object ^,ThreadExceptionEventArgs ^e)
    {
        Show(e->Exception);
    }
};

void start()
{
    *(char*)0 = 'j';
}

[STAThreadAttribute]
int main(array<System::String ^> ^args)
{
    AppDomain::CurrentDomain->UnhandledException += gcnew 
        UnhandledExceptionEventHandler(gcnew CatchException, &CatchException::UnhandledException);
    
    Application::ThreadException += gcnew 
        ThreadExceptionEventHandler(gcnew CatchException, &CatchException::ThreadException);

    Thread^ t = gcnew Thread(gcnew ThreadStart(start));
    //t->SetApartmentState(ApartmentState::STA);
    t->Start();

    Threading::Thread::Sleep(1000000);

    return 0;
}



спасибо
Re: ExceptionReporter
От: samius Япония http://sams-tricks.blogspot.com
Дата: 13.07.13 08:54
Оценка:
Здравствуйте, jyuyjiyuijyu, Вы писали:

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

J>а для них исключения тоже надо ловить...

Ловить-то надо. А разве принципиально показывать окно именно из того потока, в котором поймалось?
Re[2]: ExceptionReporter
От: jyuyjiyuijyu  
Дата: 13.07.13 11:21
Оценка:
Здравствуйте, samius, Вы писали:

S>Ловить-то надо. А разве принципиально показывать окно именно из того потока, в котором поймалось?


ну как я понял хендлер вызывается в контексте потока поймавшего исключение... и если это гуевый поток то показывается репорт все хорошо... а если исключение поймал поток из пула или явно созданный бэкграунд поток то он вместо окошка c репортом показывает окошко с ошибкой что якобы я сломался из за того что вы меня вызвали не в STA потоке а в MTA...

и вот еще что странно... если бэкграунд поток делает вызов кода через Invoke в контексте гуевого потока то если в этот момент этот код который сейчас выполняется в гуевом потоке поймает исключение то снова показывается вместо окна с реполртом сообщение о том что поток MTA ... хотя мы же сейчас выполняемся в контексте гуевого потока в который нас переключил Invoke...


вообщем какие то непонятки...
Re[2]: ExceptionReporter
От: jyuyjiyuijyu  
Дата: 13.07.13 11:46
Оценка:
Здравствуйте, samius, Вы писали:

вот тут http://exceptionreporter.codeplex.com/discussions/237047 похожая проблема у человека он так же ловил исключение из бэкграунд потока и имел проблемы с отображением репорта...

на что ему ответили

Is the ExceptionReport being called on the Background thread?

I always catch the exception on the thread and show the ExceptionReporter back on the UI thread. Otherwise weird things can happen.


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

но как это сделать из хендлера ?
Re[3]: ExceptionReporter
От: samius Япония http://sams-tricks.blogspot.com
Дата: 13.07.13 13:22
Оценка:
Здравствуйте, jyuyjiyuijyu, Вы писали:

J>Здравствуйте, samius, Вы писали:


S>>Ловить-то надо. А разве принципиально показывать окно именно из того потока, в котором поймалось?


J>ну как я понял хендлер вызывается в контексте потока поймавшего исключение... и если это гуевый поток то показывается репорт все хорошо... а если исключение поймал поток из пула или явно созданный бэкграунд поток то он вместо окошка c репортом показывает окошко с ошибкой что якобы я сломался из за того что вы меня вызвали не в STA потоке а в MTA...

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

J>и вот еще что странно... если бэкграунд поток делает вызов кода через Invoke в контексте гуевого потока то если в этот момент этот код который сейчас выполняется в гуевом потоке поймает исключение то снова показывается вместо окна с реполртом сообщение о том что поток MTA ... хотя мы же сейчас выполняемся в контексте гуевого потока в который нас переключил Invoke...

Если все так как вы описываете (хотя я немного понял), значит у вас гуевый MTA. Это действительно тот поток, что стартует в main с атрибутом STA? Если нет, то кто-нибудь устанавливает у гуевого STA?

J>вообщем какие то непонятки...
Re[3]: ExceptionReporter
От: samius Япония http://sams-tricks.blogspot.com
Дата: 13.07.13 13:23
Оценка:
Здравствуйте, jyuyjiyuijyu, Вы писали:

J>я так понимаю он явно ловит исключение с помощью catch внутри бэкграунд потока... а если это необработанное исключение... надо в хендлере как то переключиться в гуевый поток и из него уже показать окошку с репортом... ?


J>но как это сделать из хендлера ?

Например, через SynchonizationContext, или через Invoke у Control-а.
Re[4]: ExceptionReporter
От: jyuyjiyuijyu  
Дата: 13.07.13 13:33
Оценка:
Здравствуйте, samius, Вы писали:

S>Если все так как вы описываете (хотя я немного понял), значит у вас гуевый MTA. Это действительно тот поток, что стартует в main с атрибутом STA? Если нет, то кто-нибудь устанавливает у гуевого STA?


да это точно гуевый STA поток...

начинается он так...

[STAThreadAttribute]
int main(array<System::String ^> ^args)



но почему то если исключение ловит он то нормально показывается краш реполрт а если это код вызванный из другого потока через Invoke контрола то ловим исключение якобы гуевый поток MTA ...

сейчас я подготовлю вам солюшен максимально простой описывающий эту проблему...
Re[4]: ExceptionReporter
От: jyuyjiyuijyu  
Дата: 13.07.13 14:24
Оценка:
Здравствуйте, samius, Вы писали:

вот создал простенький солюшен точно показывающий суть проблемы http://www.fileconvoy.com/dfl.php?id=g123045b3bee3b8d0999328514668cfe514b33e3f2

там есть строчка в файле Form1.h // t->SetApartmentState(ApartmentState::STA); стоит её раскомментить как начинает нормально показываться репорт... хотя в обоих случаях исключение создается кодом выполняемым через вызов Invoke тоесть в контексте гуевого потока...

ничего не понимаю...
Re[5]: ExceptionReporter
От: samius Япония http://sams-tricks.blogspot.com
Дата: 13.07.13 18:22
Оценка: 4 (1)
Здравствуйте, jyuyjiyuijyu, Вы писали:

J>Здравствуйте, samius, Вы писали:


J>вот создал простенький солюшен точно показывающий суть проблемы http://www.fileconvoy.com/dfl.php?id=g123045b3bee3b8d0999328514668cfe514b33e3f2


J>там есть строчка в файле Form1.h // t->SetApartmentState(ApartmentState::STA); стоит её раскомментить как начинает нормально показываться репорт... хотя в обоих случаях исключение создается кодом выполняемым через вызов Invoke тоесть в контексте гуевого потока...


J>ничего не понимаю...

А в документации к Control.Invoke есть строчка

Exceptions that are raised during the call will be propagated back to the caller.

Что означает что исключения, выброшенные в указанном Invoke-у методе, выбросятся вызывателю Invoke-а. А Invoke зовется из MTA потока. Все как в аптеке.
Re[6]: ExceptionReporter
От: jyuyjiyuijyu  
Дата: 14.07.13 00:24
Оценка:
Здравствуйте, samius, Вы писали:

S>А в документации к Control.Invoke есть строчка

S>

S>Exceptions that are raised during the call will be propagated back to the caller.

Что означает что исключения, выброшенные в указанном Invoke-у методе, выбросятся вызывателю Invoke-а. А Invoke зовется из MTA потока. Все как в аптеке.






спасибо большое а то уже начал с ума сходить...

тогда вопрос как мне из обработчика исключения переключиться в контекст гуевого потока ? неужели каким то образом протащить в класс CatchException ссылку на главную форму и дергать Invoke внутри хендлера ?
Re[7]: ExceptionReporter
От: samius Япония http://sams-tricks.blogspot.com
Дата: 14.07.13 05:01
Оценка: 4 (1)
Здравствуйте, jyuyjiyuijyu, Вы писали:

J>Здравствуйте, samius, Вы писали:


J>тогда вопрос как мне из обработчика исключения переключиться в контекст гуевого потока ? неужели каким то образом протащить в класс CatchException ссылку на главную форму и дергать Invoke внутри хендлера ?

Именно. Без того что бы обработчик знал нечто о STA потоке, в котором ему следует показывать форму репорта, никак не обойтись. Другое дело, что это может быть не форма, а нечто специальное для этой цели созданное разработчиками фреймворка. Например, SyncrhonizationContext, взятый из свойства SynchronizationContext::Current после создания элементов управления в данном потоке.

Т.е.
1. в CatchException создаем поле типа SynchronizationContext.
2. при создании формы в главном потоке проинициируется SynchronizationContext::Current, тогда его можно будет забрать и передать в CatchException. (либо создать gcnew WindowsFormsSynchronizationContext(), но создать его нужно будет именно в GUI потоке, а не при обработке исключения в фоновом).
3. CatchException::ThreadException должен сначала вызвать sychContext.Send/Post, передать ему объект исключения и делегат, который будет создавать репортер и вызывать его метод Show.

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

Тонкий момент
После закрытия главной формы вызов Invoke будет ломаться. Потому следует предусмотреть какие-то альтернативные сценарии для репорта исключений из фоновых потоков. Например, создать поток, назначить ему STA и показать репорт в нем. А вот менять аппартмент потокам из пула я бы не советовал.
Re[8]: ExceptionReporter
От: jyuyjiyuijyu  
Дата: 14.07.13 07:13
Оценка:
Здравствуйте, samius, Вы писали:

спасибо большое всё прояснили...
Re[8]: ExceptionReporter
От: jyuyjiyuijyu  
Дата: 14.07.13 08:46
Оценка: :)
Здравствуйте, samius, Вы писали:

создал WindowsFormsSynchronizationContext() в главном потоке до создания формы и передал в конструктор CatchException ссылку на него все отлично работает...

есть еще вот такой вопрос почему если мы вызываем какой то код в контексте гуевого потока из фонового посредством Invoke то callstack обрезается по Invoke "ниже" Invoke ничего не видно ?

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

   at System.Windows.Forms.Control.MarshaledInvoke(Control caller, Delegate method, Object[] args, Boolean synchronous)
   at System.Windows.Forms.Control.Invoke(Delegate method, Object[] args)
   at System.Windows.Forms.Control.Invoke(Delegate method)
   at testbug.Form1.crash_thread() in c:\documents and settings\администратор\рабочий стол\testbug\testbug\form1.h:line 107
   at System.Threading.ThreadHelper.ThreadStart_Context(Object state)
   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
   at System.Threading.ThreadHelper.ThreadStart()
Re[9]: ExceptionReporter
От: samius Япония http://sams-tricks.blogspot.com
Дата: 14.07.13 10:11
Оценка: 9 (2)
Здравствуйте, jyuyjiyuijyu, Вы писали:

J>Здравствуйте, samius, Вы писали:


J>создал WindowsFormsSynchronizationContext() в главном потоке до создания формы и передал в конструктор CatchException ссылку на него все отлично работает...

рад

J>есть еще вот такой вопрос почему если мы вызываем какой то код в контексте гуевого потока из фонового посредством Invoke то callstack обрезается по Invoke "ниже" Invoke ничего не видно ?

Все довольно просто. Callstack у каждого потока свой и связи между callstack-ами разных потоков никакой нет. Потому, у одного потока видно callstack до Invoke, а у другого (гуевого) видно от диспетчеризации сообщения.

J>вот пример каллстека потока который вызвал Invoke для выполнения кода в контексте гуевого потока...

J>callstack обрезался по Invoke хотя у меня еще выполнились две процедуры до того как сгенерировалось исключение уже в контексте гуевого потока...
все верно.
Invoke не делает "вызов метода в контексте другого потока". Invoke посылает сообщение с делегатом в цикл сообщений гуевого потока и ждет сигнала завершения вызова делегата через какой-нибудь WaitHandle. А по ощущениям Invoke действительно напоминает вызов метода, т.к. не вернет управление пока не завершится переданный метод.
Т.к. ключевое отличие от нормального вызова в том, что при нормальном вызове вызывающий и вызываемый метод выполняются одним потоком, и имеют общий стек, а при Invoke вызывающий метод в одном потоке, вызываемый работает в другом, стеки разные.
Re[10]: ExceptionReporter
От: jyuyjiyuijyu  
Дата: 14.07.13 10:25
Оценка: :)
Здравствуйте, samius, Вы писали:

какой же он неудобный в плане отладки этот Invoke ... вот так свалится и каллстек фиг посмотриш ...


а есть что то похожее на Invoke но если упадет весь каллстек был виден ?
Re[11]: ExceptionReporter
От: samius Япония http://sams-tricks.blogspot.com
Дата: 14.07.13 13:07
Оценка:
Здравствуйте, jyuyjiyuijyu, Вы писали:

J>Здравствуйте, samius, Вы писали:


J>какой же он неудобный в плане отладки этот Invoke ... вот так свалится и каллстек фиг посмотриш ...


J>а есть что то похожее на Invoke но если упадет весь каллстек был виден ?

Нет. Решения вообще только 3.
Одно — устанавливать STA текущему потоку
другое — передавать показ репорта STA потоку у которого другой стек
последнее — не использовать требовательные к аппартменту компоненты. Тут не очень уверен, но мне кажется что некий набор элементов управления WinForms может работать в MTA и не кашлять. Для проверки гипотезы достаточно склепать форму с кнопкой и показать ее из MTA потока. Если мне не изменяет склероз, это должно работать. А вот RichTextBox — уже требует STA, как мне кажется.
Re[12]: ExceptionReporter
От: Danchik Украина  
Дата: 15.07.13 13:42
Оценка:
Здравствуйте, samius, Вы писали:

[Skip]

Скажите, зачем эти заморочки с Invoke?
Делаем статический класс в котором конкурентная очередь. В нее пробрасываем исключения.
На таймере в главном потоке ее обрабатываем. Все.
Если таймер не нравится — делаем делегат у нашего статического хранилища. Подписываемся на него, там хоть Invoke, хоть PostMessage.
Данное решение отвяжет UI от функциональной части — не надо будет прокидывать синхрозационные контексты, да и поприличней будет выглядеть.
Re: ExceptionReporter
От: Danchik Украина  
Дата: 15.07.13 13:46
Оценка:
Здравствуйте, jyuyjiyuijyu, Вы писали:

J>Всем привет


J>использую компонент ExceptionReporter http://exceptionreporter.codeplex.com/


Это еще посмотрите, дабы не только диалоги показывать
http://www.gibraltarsoftware.com/
Re[13]: ExceptionReporter
От: samius Япония http://sams-tricks.blogspot.com
Дата: 15.07.13 16:14
Оценка:
Здравствуйте, Danchik, Вы писали:

D>Здравствуйте, samius, Вы писали:


D>[Skip]


D>Скажите, зачем эти заморочки с Invoke?

D>Делаем статический класс в котором конкурентная очередь. В нее пробрасываем исключения.
D>На таймере в главном потоке ее обрабатываем. Все.
D>Если таймер не нравится — делаем делегат у нашего статического хранилища. Подписываемся на него, там хоть Invoke, хоть PostMessage.
D>Данное решение отвяжет UI от функциональной части — не надо будет прокидывать синхрозационные контексты, да и поприличней будет выглядеть.
Решительно не понимаю, чему там приличнее выглядеть?
1. Статическим можно сделать поле, в котором будет лежать контекст синхронизации. И тогда его не надо будет прокидывать.
2. Очередь уже есть в качестве очереди оконных сообщений. А в вашу конкурентную очередь надо складывать, забирать, проверять на пустоту.
3. Таймер он ведь тоже без подписки не работает. Подписываться нужно либо на события таймера, либо на событие статического хранилища. А как вы это событие от статического хранилища будете обрабатывать? Его надо опять-таки перенаправить в GUI поток.
Чем вам все эти заморочки ближе чем Post в контекст синхронизации?
Re[14]: ExceptionReporter
От: Danchik Украина  
Дата: 16.07.13 17:31
Оценка:
Здравствуйте, samius, Вы писали:

S>Здравствуйте, Danchik, Вы писали:


S>Решительно не понимаю, чему там приличнее выглядеть?

S>1. Статическим можно сделать поле, в котором будет лежать контекст синхронизации. И тогда его не надо будет прокидывать.
Лучше специализированный инстанс класса.

S>2. Очередь уже есть в качестве очереди оконных сообщений. А в вашу конкурентную очередь надо складывать, забирать, проверять на пустоту.

И что? Прибьем себя гвоздями к Winforms?

S>3. Таймер он ведь тоже без подписки не работает. Подписываться нужно либо на события таймера, либо на событие статического хранилища. А как вы это событие от статического хранилища будете обрабатывать? Его надо опять-таки перенаправить в GUI поток.

Не понимаю проблемы, стартануть в UI потоке таймер — это смертельно? При чем таймер можна заменить на Post в событии когда Exception пришел.

S>Чем вам все эти заморочки ближе чем Post в контекст синхронизации?

В любой момент отвязаться от Winforms
Re[15]: ExceptionReporter
От: samius Япония http://sams-tricks.blogspot.com
Дата: 16.07.13 17:50
Оценка:
Здравствуйте, Danchik, Вы писали:

D>Здравствуйте, samius, Вы писали:


S>>Решительно не понимаю, чему там приличнее выглядеть?

S>>1. Статическим можно сделать поле, в котором будет лежать контекст синхронизации. И тогда его не надо будет прокидывать.
D>Лучше специализированный инстанс класса.
Вот вам специализированный инстанс класса SynchronizationContext. Чем он плох?

S>>2. Очередь уже есть в качестве очереди оконных сообщений. А в вашу конкурентную очередь надо складывать, забирать, проверять на пустоту.

D>И что? Прибьем себя гвоздями к Winforms?
Нет, SynchronizationContext работает не только с WinForms, а с WPF еще. И никто не мешает написать SC для работы с чем угодно, хоть с TCL.

S>>3. Таймер он ведь тоже без подписки не работает. Подписываться нужно либо на события таймера, либо на событие статического хранилища. А как вы это событие от статического хранилища будете обрабатывать? Его надо опять-таки перенаправить в GUI поток.

D>Не понимаю проблемы, стартануть в UI потоке таймер — это смертельно? При чем таймер можна заменить на Post в событии когда Exception пришел.
А вы не боитесь таймером прибить себя гвоздями к Winforms?

S>>Чем вам все эти заморочки ближе чем Post в контекст синхронизации?

D>В любой момент отвязаться от Winforms
т.е. заюзать еще один компонент Winforms что бы в любой момент отвязаться от Winforms? Занятно.
Re[16]: ExceptionReporter
От: Danchik Украина  
Дата: 16.07.13 17:59
Оценка:
Здравствуйте, samius, Вы писали:

S>Здравствуйте, Danchik, Вы писали:


D>>Здравствуйте, samius, Вы писали:


S>>>Решительно не понимаю, чему там приличнее выглядеть?

S>>>1. Статическим можно сделать поле, в котором будет лежать контекст синхронизации. И тогда его не надо будет прокидывать.
D>>Лучше специализированный инстанс класса.
S>Вот вам специализированный инстанс класса SynchronizationContext. Чем он плох?

S>>>2. Очередь уже есть в качестве очереди оконных сообщений. А в вашу конкурентную очередь надо складывать, забирать, проверять на пустоту.

D>>И что? Прибьем себя гвоздями к Winforms?
S>Нет, SynchronizationContext работает не только с WinForms, а с WPF еще. И никто не мешает написать SC для работы с чем угодно, хоть с TCL.

S>>>3. Таймер он ведь тоже без подписки не работает. Подписываться нужно либо на события таймера, либо на событие статического хранилища. А как вы это событие от статического хранилища будете обрабатывать? Его надо опять-таки перенаправить в GUI поток.

D>>Не понимаю проблемы, стартануть в UI потоке таймер — это смертельно? При чем таймер можна заменить на Post в событии когда Exception пришел.
S>А вы не боитесь таймером прибить себя гвоздями к Winforms?
Таймер и стартуем в Winforms, тоесть там где он необходим, а сам класс об этом совсем не должен знать

S>>>Чем вам все эти заморочки ближе чем Post в контекст синхронизации?

D>>В любой момент отвязаться от Winforms
S>т.е. заюзать еще один компонент Winforms что бы в любой момент отвязаться от Winforms? Занятно.
Нда...
Re[17]: ExceptionReporter
От: samius Япония http://sams-tricks.blogspot.com
Дата: 16.07.13 18:10
Оценка:
Здравствуйте, Danchik, Вы писали:

D>Здравствуйте, samius, Вы писали:


S>>А вы не боитесь таймером прибить себя гвоздями к Winforms?

D>Таймер и стартуем в Winforms, тоесть там где он необходим, а сам класс об этом совсем не должен знать
Скажем так, для решения задачи автора ни таймер, ни очередь не являются не то что бы необходимыми сущностями, они являются несколько посторонними сущностями. А SynchronizationContext просто предназначен для этого. И он не привязан к Winforms.

S>>>>Чем вам все эти заморочки ближе чем Post в контекст синхронизации?

D>>>В любой момент отвязаться от Winforms
S>>т.е. заюзать еще один компонент Winforms что бы в любой момент отвязаться от Winforms? Занятно.
D>Нда...
Да, это то что вы предлагаете. Причем, внятных преимуществ перед SynchronizationContext вы пока не предъявили.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.