0

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.

Paul
  • 4,009
  • 1
  • 17
  • 15

1 Answers1

0

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.

CodingGorilla
  • 19,612
  • 4
  • 45
  • 65
  • Thanks. I am already aware of using CheckAccess() and have already written an extension method for this. Only problem is I do not know where to start testing for this! The stack trace from the exception does not point me in the direction of the issue as it is a .NET stack trace... – Paul Aug 24 '12 at 15:08
  • I don't think it really matters, find any place in your WPF API that calls `ShowDialog()` and make sure it's thread safe. You would need to do that or you'll end up fixing this particular error, and then running into another one, and another one, and another one. – CodingGorilla Aug 24 '12 at 15:10
  • I'm still experiencing the same exception on my main WPF UI even when the ShowDialog() is wrapped with CheckAccess() and BeginInvoke(). – Paul Aug 24 '12 at 15:39
  • I'm sorry I can't be of more help, but the underlying problem is that you **have to** make sure than any access to the WPF UI happens only from the thread on which the UI was created. This will be a really hard problem to troubleshoot I'm sure, because cross thread access can sometime be very subtle, but the exception is very clear as to what the problem is. – CodingGorilla Aug 24 '12 at 16:13
  • Thanks for your help, although I am going to investigate a different direction for the integration of this code. One thing I did not mention was that this code worked successfully when integrated via COM with a different application. I only switched to JNI4NET due to similar issues with integrating COM with the Java application. – Paul Aug 28 '12 at 10:21