проблема в том что он выдает окошку с ошибкой вместо окна с репортом
из за того что поток в котором он попытался показать свою окошку не 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)
как все прерасно показывается...
Здравствуйте, jyuyjiyuijyu, Вы писали:
J>но у меня есть так же много потоков которые создаются не мной а в пуле... J>а для них исключения тоже надо ловить...
Ловить-то надо. А разве принципиально показывать окно именно из того потока, в котором поймалось?
Здравствуйте, samius, Вы писали:
S>Ловить-то надо. А разве принципиально показывать окно именно из того потока, в котором поймалось?
ну как я понял хендлер вызывается в контексте потока поймавшего исключение... и если это гуевый поток то показывается репорт все хорошо... а если исключение поймал поток из пула или явно созданный бэкграунд поток то он вместо окошка c репортом показывает окошко с ошибкой что якобы я сломался из за того что вы меня вызвали не в STA потоке а в MTA...
и вот еще что странно... если бэкграунд поток делает вызов кода через Invoke в контексте гуевого потока то если в этот момент этот код который сейчас выполняется в гуевом потоке поймает исключение то снова показывается вместо окна с реполртом сообщение о том что поток MTA ... хотя мы же сейчас выполняемся в контексте гуевого потока в который нас переключил Invoke...
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 внутри бэкграунд потока... а если это необработанное исключение... надо в хендлере как то переключиться в гуевый поток и из него уже показать окошку с репортом... ?
Здравствуйте, jyuyjiyuijyu, Вы писали:
J>Здравствуйте, samius, Вы писали:
S>>Ловить-то надо. А разве принципиально показывать окно именно из того потока, в котором поймалось?
J>ну как я понял хендлер вызывается в контексте потока поймавшего исключение... и если это гуевый поток то показывается репорт все хорошо... а если исключение поймал поток из пула или явно созданный бэкграунд поток то он вместо окошка c репортом показывает окошко с ошибкой что якобы я сломался из за того что вы меня вызвали не в STA потоке а в MTA...
Все правильно. Так я спрашиваю, зачем показывать репорт именно из бэкграунд-потока?
J>и вот еще что странно... если бэкграунд поток делает вызов кода через Invoke в контексте гуевого потока то если в этот момент этот код который сейчас выполняется в гуевом потоке поймает исключение то снова показывается вместо окна с реполртом сообщение о том что поток MTA ... хотя мы же сейчас выполняемся в контексте гуевого потока в который нас переключил Invoke...
Если все так как вы описываете (хотя я немного понял), значит у вас гуевый MTA. Это действительно тот поток, что стартует в main с атрибутом STA? Если нет, то кто-нибудь устанавливает у гуевого STA?
J>вообщем какие то непонятки...
Здравствуйте, jyuyjiyuijyu, Вы писали:
J>я так понимаю он явно ловит исключение с помощью catch внутри бэкграунд потока... а если это необработанное исключение... надо в хендлере как то переключиться в гуевый поток и из него уже показать окошку с репортом... ?
J>но как это сделать из хендлера ?
Например, через SynchonizationContext, или через Invoke у Control-а.
Здравствуйте, samius, Вы писали:
S>Если все так как вы описываете (хотя я немного понял), значит у вас гуевый MTA. Это действительно тот поток, что стартует в main с атрибутом STA? Если нет, то кто-нибудь устанавливает у гуевого STA?
да это точно гуевый STA поток...
начинается он так...
[STAThreadAttribute]
int main(array<System::String ^> ^args)
но почему то если исключение ловит он то нормально показывается краш реполрт а если это код вызванный из другого потока через Invoke контрола то ловим исключение якобы гуевый поток MTA ...
сейчас я подготовлю вам солюшен максимально простой описывающий эту проблему...
там есть строчка в файле Form1.h // t->SetApartmentState(ApartmentState::STA); стоит её раскомментить как начинает нормально показываться репорт... хотя в обоих случаях исключение создается кодом выполняемым через вызов Invoke тоесть в контексте гуевого потока...
Здравствуйте, 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 потока. Все как в аптеке.
Здравствуйте, 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 внутри хендлера ?
Здравствуйте, 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 и показать репорт в нем. А вот менять аппартмент потокам из пула я бы не советовал.
создал 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()
Здравствуйте, 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 вызывающий метод в одном потоке, вызываемый работает в другом, стеки разные.
Здравствуйте, jyuyjiyuijyu, Вы писали:
J>Здравствуйте, samius, Вы писали:
J>какой же он неудобный в плане отладки этот Invoke ... вот так свалится и каллстек фиг посмотриш ...
J>а есть что то похожее на Invoke но если упадет весь каллстек был виден ?
Нет. Решения вообще только 3.
Одно — устанавливать STA текущему потоку
другое — передавать показ репорта STA потоку у которого другой стек
последнее — не использовать требовательные к аппартменту компоненты. Тут не очень уверен, но мне кажется что некий набор элементов управления WinForms может работать в MTA и не кашлять. Для проверки гипотезы достаточно склепать форму с кнопкой и показать ее из MTA потока. Если мне не изменяет склероз, это должно работать. А вот RichTextBox — уже требует STA, как мне кажется.
Скажите, зачем эти заморочки с Invoke?
Делаем статический класс в котором конкурентная очередь. В нее пробрасываем исключения.
На таймере в главном потоке ее обрабатываем. Все.
Если таймер не нравится — делаем делегат у нашего статического хранилища. Подписываемся на него, там хоть Invoke, хоть PostMessage.
Данное решение отвяжет UI от функциональной части — не надо будет прокидывать синхрозационные контексты, да и поприличней будет выглядеть.
Здравствуйте, Danchik, Вы писали:
D>Здравствуйте, samius, Вы писали:
D>[Skip]
D>Скажите, зачем эти заморочки с Invoke? D>Делаем статический класс в котором конкурентная очередь. В нее пробрасываем исключения. D>На таймере в главном потоке ее обрабатываем. Все. D>Если таймер не нравится — делаем делегат у нашего статического хранилища. Подписываемся на него, там хоть Invoke, хоть PostMessage. D>Данное решение отвяжет UI от функциональной части — не надо будет прокидывать синхрозационные контексты, да и поприличней будет выглядеть.
Решительно не понимаю, чему там приличнее выглядеть?
1. Статическим можно сделать поле, в котором будет лежать контекст синхронизации. И тогда его не надо будет прокидывать.
2. Очередь уже есть в качестве очереди оконных сообщений. А в вашу конкурентную очередь надо складывать, забирать, проверять на пустоту.
3. Таймер он ведь тоже без подписки не работает. Подписываться нужно либо на события таймера, либо на событие статического хранилища. А как вы это событие от статического хранилища будете обрабатывать? Его надо опять-таки перенаправить в GUI поток.
Чем вам все эти заморочки ближе чем Post в контекст синхронизации?
Здравствуйте, samius, Вы писали:
S>Здравствуйте, Danchik, Вы писали:
S>Решительно не понимаю, чему там приличнее выглядеть? S>1. Статическим можно сделать поле, в котором будет лежать контекст синхронизации. И тогда его не надо будет прокидывать.
Лучше специализированный инстанс класса.
S>2. Очередь уже есть в качестве очереди оконных сообщений. А в вашу конкурентную очередь надо складывать, забирать, проверять на пустоту.
И что? Прибьем себя гвоздями к Winforms?
S>3. Таймер он ведь тоже без подписки не работает. Подписываться нужно либо на события таймера, либо на событие статического хранилища. А как вы это событие от статического хранилища будете обрабатывать? Его надо опять-таки перенаправить в GUI поток.
Не понимаю проблемы, стартануть в UI потоке таймер — это смертельно? При чем таймер можна заменить на Post в событии когда Exception пришел.
S>Чем вам все эти заморочки ближе чем Post в контекст синхронизации?
В любой момент отвязаться от Winforms