Проблемы с потоками при использовании библиотеки WPF в Java-приложении?
Я пытался связать существующий API C# WPF с приложением Java.
До сих пор я успешно использовал jni4net для генерации прокси для взаимодействия между Java и кодом.NET.
Эта интеграция вызвала проблемы с многопоточностью STA при отображении пользовательского интерфейса WPF:
System.InvalidOperationException: The calling thread must be STA, because many UI components require this.
at System.Windows.Input.InputManager..ctor()
at System.Windows.Input.InputManager.GetCurrentInputManagerImpl()
at System.Windows.Input.KeyboardNavigation..ctor()
at System.Windows.FrameworkElement.FrameworkServices..ctor()
at System.Windows.FrameworkElement.EnsureFrameworkServices()
at System.Windows.FrameworkElement..ctor()
at System.Windows.Controls.Control..ctor()
at System.Windows.Window..ctor()
Это было преодолено с помощью следующего шаблона для загрузки пользовательских интерфейсов WPF с использованием ShowDialog:
Thread thread = new Thread(new ParameterizedThreadStart(ParameterizedMethodName));
thread.SetApartmentState(ApartmentState.STA);
thread.Start(parameter);
thread.Join();
Теперь, однако, я испытываю исключения, подобные следующим, при использовании пользовательского интерфейса WPF щелчок мыши или нажатие клавиши может вызвать следующее (этот пример был от щелчка мыши):
System.InvalidOperationException: The calling thread cannot access this object because a different thread owns it.
at System.Windows.Threading.Dispatcher.VerifyAccess()
at System.Windows.DependencyObject.GetValue(DependencyProperty dp)
at System.Windows.Input.InputBinding.get_Command()
at System.Windows.Input.InputBindingCollection.FindMatch(Object targetElement, InputEventArgs inputEventArgs)
at System.Windows.Input.CommandManager.TranslateInput(IInputElement targetElement, InputEventArgs inputEventArgs)
at System.Windows.UIElement.OnMouseDownThunk(Object sender, MouseButtonEventArgs e)
at System.Windows.Input.MouseButtonEventArgs.InvokeEventHandler(Delegate genericHandler, Object genericTarget)
at System.Windows.RoutedEventArgs.InvokeHandler(Delegate handler, Object target)
at System.Windows.RoutedEventHandlerInfo.InvokeHandler(Object target, RoutedEventArgs routedEventArgs)
at System.Windows.EventRoute.InvokeHandlersImpl(Object source, RoutedEventArgs args, Boolean reRaised)
at System.Windows.UIElement.RaiseEventImpl(DependencyObject sender, RoutedEventArgs args)
at System.Windows.UIElement.RaiseTrustedEvent(RoutedEventArgs args)
at System.Windows.UIElement.RaiseEvent(RoutedEventArgs args, Boolean trusted)
at System.Windows.Input.InputManager.ProcessStagingArea()
at System.Windows.Input.InputManager.ProcessInput(InputEventArgs input)
at System.Windows.Input.InputProviderSite.ReportInput(InputReport inputReport)
at System.Windows.Interop.HwndMouseInputProvider.ReportInput(IntPtr hwnd, InputMode mode, Int32 timestamp, RawMouseActions actions, Int32 x, Int32 y, Int32 wheel)
at System.Windows.Interop.HwndMouseInputProvider.FilterMessage(IntPtr hwnd, WindowMessage msg, IntPtr wParam, IntPtr lParam, Boolean& handled)
at System.Windows.Interop.HwndSource.InputFilterMessage(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean& handled)
at MS.Win32.HwndWrapper.WndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean& handled)
at MS.Win32.HwndSubclass.DispatcherCallbackOperation(Object o)
at System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate callback, Object args, Int32 numArgs)
at MS.Internal.Threading.ExceptionFilterHelper.TryCatchWhen(Object source, Delegate method, Object args, Int32 numArgs, Delegate catchHandler)
at System.Windows.Threading.Dispatcher.InvokeImpl(DispatcherPriority priority, TimeSpan timeout, Delegate method, Object args, Int32 numArgs)
at MS.Win32.HwndSubclass.SubclassWndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam)
at MS.Win32.UnsafeNativeMethods.DispatchMessage(MSG& msg)
at System.Windows.Threading.Dispatcher.PushFrameImpl(DispatcherFrame frame)
at System.Windows.Threading.Dispatcher.PushFrame(DispatcherFrame frame)
at System.Windows.Window.ShowHelper(Object booleanBox)
at System.Windows.Window.Show()
at System.Windows.Window.ShowDialog()
В настоящее время я не уверен, как решить эту проблему и определить причину проблемы из трассировки стека.
Любая помощь / совет с благодарностью.
1 ответ
Я не могу дать много советов относительно решения, но я могу рассказать вам о проблеме. Весь ваш код WPF должен выполняться в одном потоке (предположительно тот, который вы создаете). Вы получаете ошибку, потому что что-то пыталось получить доступ к элементу управления WPF из другого потока.
Допустим, у вас есть вызов API, который показывает диалог, а ваши вызовы Java-кода MyApi.ShowDialog
, Ваш API ShowDialog
метод не может просто вызвать MyDialog.ShowDialog()
потому что этот вызов компонента пользовательского интерфейса будет исходить из потока Java. Вместо этого ваш API должен быть достаточно умным, чтобы перенаправить эти вызовы в соответствующий поток WPF (UI).
Так что это должно сделать что-то вроде:
if(!CheckAccess())
MyDialog.Dispatcher.BeginInvoke(DeleageToShowDialog);
К сожалению, это, вероятно, означает, что вам придется проделать большую работу над своим API, потому что он, вероятно, не учитывает проблемы с потоками, подобные этой.