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
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.