You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在BackgroundWorker中调用操作RichTextBox的委托?

问题描述

我创建了一个用于日志记录的委托,RichTextBox控件可见,尝试在BackgroundWorker的DoWork事件中调用该委托但无法正常工作,运行时抛出异常:无效的跨线程操作: 从不是创建控件‘RTB_Log’的线程访问它。

对应的VB.NET代码如下:

Public Class Form1
    Public Delegate Sub WriteRtbLogDelegate(ByVal Texto As String, ByVal _Color As Color)
    
    Private Sub WriteRTBLog(ByVal Texto As String, ByVal TextColor As Color)
        If Me.InvokeRequired Then
            Dim method As WriteRtbLogDelegate = AddressOf Me.WriteRTBLog
            Me.Invoke(method, Texto, TextColor)
        Else
            If RTB_Log.Lines.Length < 1 Then
                'ultima = 0
                RTB_Log.AppendText("Inicio del LOG - " & Now.ToLongDateString & vbCrLf)
            End If
    
            Dim length As Integer = RTB_Log.Text.Length
    
            Texto = Format(Now.Day, "00").ToString & "/" & Format(Now.Month, "00").ToString & "/" & Now.Year.ToString & " - " & Format(Now.Hour, "00").ToString & ":" & Format(Now.Minute, "00").ToString & ":" & Format(Now.Second, "00").ToString &
                                  " " & Texto
            RTB_Log.AppendText(Texto + vbCrLf)
            RTB_Log.SelectionStart = length
            RTB_Log.SelectionLength = Texto.Length
            RTB_Log.SelectionColor = TextColor
            ' The following expression was wrapped in a checked-expression
            RTB_Log.[Select](RTB_Log.SelectionStart + RTB_Log.SelectionLength, 0)
            RTB_Log.SelectionColor = TextColor
            RTB_Log.SelectionLength = 0
            RTB_Log.SelectionStart = RTB_Log.TextLength
            RTB_Log.ScrollToCaret()
    
            If RTB_Log.TextLength > 1000000.0 Then
                With RTB_Log
                    .SaveFile(Application.StartupPath & "\" & Format(Now.Day, "00").ToString & "-" & Format(Now.Month, "00").ToString & "-" & Now.Year.ToString & " - " &
                              Format(Now.Hour, "00").ToString & Format(Now.Minute, "00").ToString & Format(Now.Second, "00").ToString &
                              ".log", System.Windows.Forms.RichTextBoxStreamType.RichText)
                    .Clear()
                    .Text = "Log guardado en " & Application.StartupPath & Now.ToShortDateString & ".log"
                End With
            End If
        End If
    End Sub
End Class

Private Sub BGW1_DoWork(sender As Object, e As System.ComponentModel.DoWorkEventArgs) Handles BGW1.DoWork
WriteRTBLog("Starting...", Color.Blue)
End Sub
解决方案

问题根源在于:代码中判断Me.InvokeRequired(Me指Form1),但跨线程访问的目标是RTB_Log控件。当BackgroundWorker的DoWork线程调用WriteRTBLog时,Me.InvokeRequired可能返回False(比如Form句柄未完全创建时),导致直接执行了访问RTB_Log的代码,触发跨线程异常。

以下两种修改方案均可解决问题:

方案一:针对RTB_Log控件判断InvokeRequired

将线程检查的对象从Form改为RTB_Log控件,确保针对目标控件做线程同步判断:

Private Sub WriteRTBLog(ByVal Texto As String, ByVal TextColor As Color)
    ' 改为检查RTB_Log的InvokeRequired
    If RTB_Log.InvokeRequired Then
        Dim method As WriteRtbLogDelegate = AddressOf Me.WriteRTBLog
        ' 使用RTB_Log的Invoke方法
        RTB_Log.Invoke(method, Texto, TextColor)
    Else
        ' 原有日志写入逻辑保持不变
        If RTB_Log.Lines.Length < 1 Then
            RTB_Log.AppendText("Inicio del LOG - " & Now.ToLongDateString & vbCrLf)
        End If

        Dim length As Integer = RTB_Log.Text.Length

        Texto = Format(Now.Day, "00").ToString & "/" & Format(Now.Month, "00").ToString & "/" & Now.Year.ToString & " - " & Format(Now.Hour, "00").ToString & ":" & Format(Now.Minute, "00").ToString & ":" & Format(Now.Second, "00").ToString &
                      " " & Texto
        RTB_Log.AppendText(Texto + vbCrLf)
        RTB_Log.SelectionStart = length
        RTB_Log.SelectionLength = Texto.Length
        RTB_Log.SelectionColor = TextColor
        RTB_Log.[Select](RTB_Log.SelectionStart + RTB_Log.SelectionLength, 0)
        RTB_Log.SelectionColor = TextColor
        RTB_Log.SelectionLength = 0
        RTB_Log.SelectionStart = RTB_Log.TextLength
        RTB_Log.ScrollToCaret()

        If RTB_Log.TextLength > 1000000 Then
            With RTB_Log
                .SaveFile(Application.StartupPath & "\" & Format(Now.Day, "00").ToString & "-" & Format(Now.Month, "00").ToString & "-" & Now.Year.ToString & " - " &
                          Format(Now.Hour, "00").ToString & Format(Now.Minute, "00").ToString & Format(Now.Second, "00").ToString &
                          ".log", System.Windows.Forms.RichTextBoxStreamType.RichText)
                .Clear()
                .Text = "Log guardado en " & Application.StartupPath & Now.ToShortDateString & ".log"
            End With
        End If
    End If
End Sub

方案二:使用BackgroundWorker的ProgressChanged事件(更符合BGW设计)

BackgroundWorker内置了ProgressChanged事件,该事件自动在UI线程执行,无需手动处理Invoke:

  1. 启用BGW的WorkerReportsProgress属性(可在Form加载或BGW初始化时设置):
BGW1.WorkerReportsProgress = True
  1. 订阅ProgressChanged事件,在事件处理方法中写入日志:
Private Sub BGW1_ProgressChanged(sender As Object, e As ProgressChangedEventArgs) Handles BGW1.ProgressChanged
    ' 通过UserState传递日志文本和颜色,这里用Tuple封装数据
    Dim logTuple = DirectCast(e.UserState, Tuple(Of String, Color))
    WriteRTBLog(logTuple.Item1, logTuple.Item2)
End Sub
  1. 在DoWork事件中调用ReportProgress触发UI更新:
Private Sub BGW1_DoWork(sender As Object, e As DoWorkEventArgs) Handles BGW1.DoWork
    ' 进度值可随意填写(如0),通过UserState传递日志数据
    BGW1.ReportProgress(0, Tuple.Create("Starting...", Color.Blue))
End Sub

这种方式贴合BackgroundWorker的设计模式,避免手动处理线程同步的潜在问题。


内容的提问来源于stack exchange,提问作者E_Blue

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.08 12:47:02