.net - 如何在 BackgroundWorker 中访问 COM 对象?

标签 .net wpf multithreading com sta

我有一个 WPF 应用程序,在主窗体中,允许用户选择几个文件(Excel),然后单击一个按钮进行数据提取并将它们上传到数据库。一切正常。

现在我想实现一个繁忙的指标。

所以我所做的是,声明一个 BackgroundWorker 线程并将我的数据库上传(这需要时间)作为后台线程。当线程启动和完成时,相应地设置繁忙指示器。问题在我的上传过程中,我访问剪贴板以打印一些消息。所以我遇到了以下明显的错误。

"Current thread must be set to single thread apartment (STA) mode before OLE calls can be made."

BackgroundWorker 默认为 MTA。那么克服这个问题的最佳方法是什么?

代码:
Public WithEvents BgWorker As BackgroundWorker = New BackgroundWorker()

 Private Sub MainWindow_Loaded() Handles Me.Loaded
    AddHandler BgWorker.DoWork, AddressOf ExtractData
    AddHandler BgWorker.RunWorkerCompleted, AddressOf BgWorker_RunWorkerCompleted        
End Sub

Private Sub btnExtract_Click(sender As Object, e As RoutedEventArgs)

    .....

    Try
        .....

       Me.busyIndicator.IsBusy = True
       BgWorker.RunWorkerAsync(Me.cmbFormats.SelectedItem.ToString.Trim())

    Catch ex As Exception
        Utility.Message.ErrorMessage(ex)
    End Try

End Sub

完成事件:
Private Sub BgWorker_RunWorkerCompleted(ByVal sender As Object, ByVal e As RunWorkerCompletedEventArgs)
    busyIndicator.IsBusy = False
End Sub

做工作:
Private Sub ExtractData(sender As Object, e As DoWorkEventArgs)
    Dim exformat As IExtractor = New FormatFactory().CreateInstance(e.Argument.ToString())
    If (exformat.FeedToDb(filename)) Then
        Utility.Message.SuccessMessage("Successfully Extracted to database")
    Else
    End If
 End Sub

Utility.Message.SuccessMessage :
 Public Shared Sub SuccessMessage(msg As String)
    Dim M As New Text.StringBuilder
    M.AppendLine()
    M.AppendLine(msg)
    M.AppendLine()
    Clipboard.Clear() 'problem with MTA
    Clipboard.SetText(M.ToString)
    MsgBox(M.ToString, MsgBoxStyle.Information, "FF IT")
End Sub

最佳答案

The thing is I have more of these calls inside other methods that are called from inside of FeedToDb



开始告诉你这是一个多么可怕的想法是相当困难的。试图专注于最严重的问题:
  • 使用 BackgroundWorker 的目的是运行需要时间执行的代码。它不会卡住您的用户界面,用户可以(希望)做其他有用的事情。如果没有你的程序,那么他可以,比如说,检查他的 Facebook 页面或启动纸牌。但是通过强制他点击消息框流的确定按钮,这完全被破坏了。你把你的用户变成了你程序的奴隶,他很快就会厌倦这种处理。
  • MsgBox() 显示一个对话框。一个对话框需要一个所有者,另一个窗口位于它的顶部。 MsgBox 是方便的功能,您不必明确谁是所有者。它自行排序,选择当前处于事件状态的窗口。但是有一个大问题,你在另一个线程上显示它。当 MsgBox 去寻找所有者时,它不会找到。 Windows 由特定线程拥有,而您的 BackgroundWorker 线程不拥有任何线程。因此它将作为所有者退回到桌面窗口。如果用户实际上继续与您的应用程序中的其他窗口进行交互,那将是一个问题,当您使用工作线程时应该是可能的。现在,在尝试显示自身的消息框窗口和用户正在使用的窗口之间发生了冲突。消息框窗口将丢失。它将结束 后面 前景窗口。用户永远看不到它,甚至不知道它的存在。在他看来你的程序卡住了,他不知道如何解冻它,他永远不会考虑最小化他正在使用的窗口。
  • Clipboard.SetText() 方法调用已经强烈暗示您了解你有一个 UI 实现问题。您已经发现您强制您的用户(或您自己)通过窥视孔查看。提供用户可能想要保留的重要信息,但除了他可以制作到剪贴板的一次性副本之外,没有给他任何选项来保留它。实际上不起作用,他将无法足够快地按Ctrl + V。如果你让他有机会稍后查看信息,这总是会工作得更好。 ListBox 是一种更好的方法。或者 RichTextBox,非常适合作为显示日志信息的一种方式。

  • 好吧,够了,您可能正在寻找快速修复。从 SuccessMessage() 方法中消除刺痛,知道它很尴尬并且在从工作线程中使用时无法正常工作:
    Public Shared Sub SuccessMessage(msg As String)
        If System.Threading.Thread.CurrentThread.GetApartmentState <> Threading.ApartmentState.STA Then Return
        '' etc...
    End Sub
    

    关于.net - 如何在 BackgroundWorker 中访问 COM 对象?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23531371/

    相关文章:

    c# - RichTextBox 的自动化支持 - 无法使用 White'sUI 获取文本

    c# - Type.GetProperties 方法

    c# - 如何在按钮上单击弹出窗口,该按钮位于 uwp 中另一个弹出窗口中的位置

    wpf - 为什么 ItemsControl 项之间有细线(1 像素?)?

    c# - 如何在 WPF 中获取折叠控件的宽度/高度?

    java - 多线程中的单例 - 各种场景

    c# - 加载资源: 403 forbidden with . js优化失败

    c# - 使用 File.Copy 时如何实现超时?

    c# - 同时执行两个独立的方法

    java - 在测试中再现 SimpleDateFormat 非线程安全