【问题标题】:Threading issues when consuming a WPF library in a java application?在 Java 应用程序中使用 WPF 库时出现线程问题?
【发布时间】:2012-08-24 14:57:09
【问题描述】:

我一直在尝试将现有的 C# WPF API 与 Java 应用程序接口。

到目前为止,我已成功使用jni4net 生成代理以在 Java 和 .NET 代码之间进行接口。

此集成在 WPF UI 显示时产生了 STA 线程问题:

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

通过使用以下模式通过 ShowDialog 加载 WPF UI 克服了这一问题:

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

但是,现在,我在使用 WPF UI 时遇到了类似于以下的异常,鼠标单击或按键可以触发以下情况(此示例来自鼠标单击):

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

我目前不确定如何解决此问题并从堆栈跟踪中找出问题的原因。

非常感谢任何帮助/建议。

【问题讨论】:

    标签: c# java wpf thread-safety jni4net


    【解决方案1】:

    我无法就解决方案提供太多建议,但我可以告诉您问题所在。您的所有 WPF 代码必须在同一个线程(大概是您正在创建的线程)上运行。您得到的错误是因为某些东西试图从不同的线程访问 WPF 控件。

    假设您有一个显示对话框的 API 调用,并且您的 java 代码调用 MyApi.ShowDialog。您的 API ShowDialog 方法不能简单地调用 MyDialog.ShowDialog(),因为对 UI 组件的调用将来自 java 线程。相反,您的 API 需要足够智能,以便将这些调用编组到适当的 WPF (UI) 线程。

    所以它应该做这样的事情:

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

    不幸的是,这可能意味着您必须在 API 上做很多工作,因为它可能无法解决此类线程问题。

    【讨论】:

    • 谢谢。我已经知道使用 CheckAccess() 并且已经为此编写了扩展方法。唯一的问题是我不知道从哪里开始测试!异常的堆栈跟踪并没有将我指向问题的方向,因为它是一个 .NET 堆栈跟踪...
    • 我认为这并不重要,在您的 WPF API 中找到任何调用 ShowDialog() 的位置,并确保它是线程安全的。您需要这样做,否则您最终会修复此特定错误,然后遇到另一个错误,然后再遇到另一个错误。
    • 即使 ShowDialog() 使用 CheckAccess() 和 BeginInvoke() 包装时,我的主 WPF UI 上仍然遇到同样的异常。
    • 很抱歉,我无法提供更多帮助,但根本问题是您必须确保对 WPF UI 的任何访问都只能从创建 UI 的线程。我敢肯定,这将是一个非常难以解决的问题,因为跨线程访问有时可能非常微妙,但异常很清楚问题是什么。
    • 感谢您的帮助,尽管我将研究此代码集成的不同方向。我没有提到的一件事是,当通过 COM 与不同的应用程序集成时,此代码可以成功运行。由于将 COM 与 Java 应用程序集成的类似问题,我只切换到 JNI4NET。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2023-03-06
    • 1970-01-01
    • 2022-11-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多