2016-04-07 3 views
1

Я работаю с функциями ExcelDna и async. Если в коде async: d есть исключение, я хочу показать причудливое окно ошибки WPF. Моя проблема в том, что я получаю сообщение об ошибке «Вызывающий поток должен быть STA, потому что для этого требуется множество компонентов пользовательского интерфейса». Как я могу это решить?ExcelDna: Async: вызывающий поток должен быть STA

[ExcelFunction(Description = "", Category = "")] 
    public static async Task<object> /*string*/ Foo(CancellationToken ct) 
    { 
     try 
     { 
      return await Task.Run(async() => 
      { 
       await Task.Delay(1000, ct); 
       throw new Exception("BOO"); 
       return "HelloWorld"; 
      }, ct2.Token); 
     } 
     catch (Exception e) 
     { 
      return ShowWpfErrorWindowThatRequiresSTA(e); 
     } 
    } 
+0

Возможный дубликат [«Вызывающий поток должен быть STA, потому что многие компоненты пользовательского интерфейса требуют эту ошибку при создании всплывающего окна WPF в потоке] (http://stackoverflow.com/questions/2657212/the-calling -thread-must-be-sta-why-many-ui-components-require-this-error) – CSharpie

+0

Вы должны отобразить окно в потоке пользовательского интерфейса. Используйте Application.Current.Dispatcher.BeginInvoke(). –

+0

Возможный дубликат [Вызывающий поток должен быть STA, потому что многие компоненты пользовательского интерфейса требуют этого в WPF] (http://stackoverflow.com/questions/4183622/the-calling-thread-must-be-sta-because-many- ui-components-require-this-in-wpf) –

ответ

3

При работе функции Excel отсутствует SynchronizationContext.Current, поэтому механизм async/await запускает код после await (включая обработчик catch) в потоке ThreadPool. Это не тот контекст, где вы можете прямо показать свою форму WPF.

Установка DispatcherSynchronizationContext, соответствующий диспетчеру, запущенному в основном потоке (или другом потоке), будет работать, но вы должны сделать это для каждого вызова UDF. Каким-то образом собственный путь кода через Excel теряет контекст вызова .NET в основном потоке, поэтому SynchronizationContext теряется.

Лучше, вероятно, предположить, что обработчик улова работает в потоке ThreadPool и вызывает вызов от обработчика catch, чтобы вернуть вас к основному потоку, использующему вашу форму Диспетчер и WPF.

Вы можете посмотреть, как Excel-DNA реализует окно (LogForms) LogDisplay. (https://github.com/Excel-DNA/ExcelDna/blob/master/Source/ExcelDna.Integration/LogDisplay.cs). Вы можете позвонить по номеру LogDisplay.WriteLine(...) из любого потока, и он будет выполнять _syncContext.Post, чтобы запустить «Показать» в главной теме.

Механизм async/await C# работает не так хорошо с Excel с тех пор, как сгенерированные/управляемые переходы и независимо от того, что делает Excel внутри, испортил контекст потока, который должен протекать между продолжениями. Даже на стороне .NET неясно, как управляется потоковый контекст между AppDomains (различные надстройки Excel). Поэтому лучше не полагаться на среду выполнения .NET, способную передавать любой вид контекста через управляемые/нативные переходы.

+0

Работает! Я создаю статический WindowsFormsSynchronizationContext (как представляется, работает и для WPF?) В моем AutoOpen, который я использую внутри HandleError. –

+0

Если вы только что установили его в AutoOpen, вы не можете полагаться на него, придерживаясь ... – Govert

+0

Как вы это понимаете? Как бы вы это сделали? –

2

Многие плагины офиса есть проблема, где SynchronizationContext.Current является null и асинхронные продолжениями выполняются на пуле потоков. Я проверил бы значение SynchronizationContext.Current перед первым await.

У меня был некоторый успех в создании WinFormsSynchronizationContext и установка этого на потоке до первого await. Однако установка WPF-контекста будет более сложной.

Смежные вопросы

 Смежные вопросы