Threading issues when consuming a WPF library in a java application?

769 Views Asked by At

I have been attempting to interface an existing C# WPF API with a Java application.

So far I have successfully used jni4net to generate proxies to interface between Java and .NET code.

This integration produced STA threading issues with the WPF UI's being displayed:

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()

This was overcome by using the following pattern for loading the WPF UI's using ShowDialog:

Thread thread = new Thread(new ParameterizedThreadStart(ParameterizedMethodName));
thread.SetApartmentState(ApartmentState.STA);
thread.Start(parameter);
thread.Join();

Now however, I am experiencing exceptions similar to following whilst using the WPF UI's, a mouse click or key press can trigger the following (this example was from a mouse click):

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()

I am currently unsure of how to resolve this issue and identify the cause of the problem from the stack trace.

Any help/advise is greatly appreciated.

1

There are 1 best solutions below

5
On BEST ANSWER

I can't offer much advice as to a solution, but I can tell you the problem. All of your WPF code must run on the same thread (presumably the one you're creating). The error you're getting is because something attempted to access a WPF control from a different thread.

So let's say you have an API call that shows a dialog, and your java code calls MyApi.ShowDialog. Your API ShowDialog method can't simply call MyDialog.ShowDialog() because that call to the UI component will come from the java thread. Instead your API needs to be smart enough to marshal those calls over to the appropriate WPF (UI) thread.

So it should do something like:

if(!CheckAccess())
    MyDialog.Dispatcher.BeginInvoke(DeleageToShowDialog);

Unfortunately, this probably means you're going to have to do a lot of work on your API because it probably doesn't account for threading issues like this.