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

.NET6下动态添加FileTarget后NLog RichTextBoxTarget触发空引用异常

.NET 6 WinForms迁移中NLog RichTextBoxTarget空引用异常问题

环境与配置

团队正在将Windows Form应用从.NET Framework 4.6.2迁移至.NET 6,使用NLog(集成Microsoft.Extensions.Logging)实现日志写入文件及主窗体RichTextBox,依赖包版本:

  • NLog.Extensions.Logging 5.3.2
  • NLog.Windows.Forms 5.2.0

NLog配置文件:

<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" autoReload="true" throwExceptions="false" internalLogFile="${basedir}/LogFiles/InternalLog.txt" internalLogLevel="Info">
  <extensions>
    <add assembly="NLog.Windows.Forms"/>
  </extensions>
  <targets>
    <default-wrapper xsi:type="AsyncWrapper" timeToSleepBetweenBatches="500" />
    <target name="file" xsi:type="File" layout="${longdate}|${level}|${message}|${exception}" fileName="${basedir}/LogFiles/file.txt" archiveAboveSize="1048576" archiveEvery="Day" maxArchiveFiles="21" archiveFileName="${basedir}/LogFiles/Errorlog/file.{####}.txt" archiveNumbering="Sequence" encoding="utf-8"/>
    <target name="trace" xsi:type="Trace" layout="${longdate}|${level}|${message}|${exception}" />
    <target name="richTextBox" xsi:type="RichTextBox" layout="${message}${exception}" controlName="rtbStatus" formName="BaseForm" allowAccessoryFormCreation="false" messageRetention="All" maxLines="10000"/>
  </targets>
  <rules>
    <logger name="ErrorLog" minlevel="Info" writeTo="file,trace,richTextBox"/>
  </rules>
</nlog>

动态创建FileTarget代码

为用户配置的通信应用动态生成调试模拟文件,代码实现:

public NLogSimFileLogger(string category, string name)
{
    try
    {
        this._TargetName                        = category + "-" + name;
        FileTarget target                       = new FileTarget();
        target.Layout                           = "${message}";
        target.ReplaceFileContentsOnEachWrite   = false;
        target.ArchiveAboveSize                 = 1048576;
        target.ArchiveEvery                     = FileArchivePeriod.Day;
        target.MaxArchiveFiles                  = 21;
        target.ArchiveFileName                  = "${basedir}/LogFiles/Sims/" + category + "/" + name + "{####}.txt";
        target.ArchiveNumbering                 = ArchiveNumberingMode.Sequence;
        target.FileName                         = "${basedir}/LogFiles/Sims/" + category + "/" + name + ".sim";
        target.KeepFileOpen                     = true;
        target.ConcurrentWrites                 = true;
        target.Name                             = this._TargetName;

        LogManager.Configuration.AddRule(LogLevel.Debug, LogLevel.Fatal, target, this._TargetName);
        LogManager.ReconfigExistingLoggers();

        this._Logger = LoggerFactory.Create(configure => configure.AddNLog(LogManager.Configuration)).CreateLogger(this._TargetName);
    }
    catch(Exception ex)
    {
        // 异常处理逻辑
    }
}

异常信息

创建动态FileTarget时,RichTextBoxTarget的SendTheMessageToRichTextBox方法触发NullReferenceException,异常栈:

System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation.
 ---&gt; System.NullReferenceException: Object reference not set to an instance of an object.
   at NLog.Windows.Forms.RichTextBoxTarget.SendTheMessageToRichTextBox(String logMessage, RichTextBoxRowColoringRule rule, LogEventInfo logEvent)
   --- End of inner exception stack trace ---
   at System.RuntimeMethodHandle.InvokeMethod(Object target, Span`1&amp; arguments, Signature sig, Boolean constructor, Boolean wrapExceptions)
   at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
   at System.Delegate.DynamicInvokeImpl(Object[] args)
   at System.Delegate.DynamicInvoke(Object[] args)
   at System.Windows.Forms.Control.InvokeMarshaledCallbackDo(ThreadMethodEntry tme)
   at System.Windows.Forms.Control.InvokeMarshaledCallbackHelper(Object obj)
   at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state)
--- End of stack trace from previous location ---
   at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state)
   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
   at System.Windows.Forms.Control.InvokeMarshaledCallback(ThreadMethodEntry tme)
   at System.Windows.Forms.Control.InvokeMarshaledCallbacks()
   at System.Windows.Forms.Control.WndProc(Message&amp; m)
   at System.Windows.Forms.TextBoxBase.WndProc(Message&amp; m)
   at System.Windows.Forms.RichTextBox.WndProc(Message&amp; m)
   at System.Windows.Forms.Control.ControlNativeWindow.OnMessage(Message&amp; m)
   at System.Windows.Forms.Control.ControlNativeWindow.WndProc(Message&amp; m)
   at System.Windows.Forms.NativeWindow.Callback(IntPtr hWnd, WM msg, IntPtr wparam, IntPtr lparam)
   at Interop.User32.DispatchMessageW(MSG&amp; msg)
   at System.Windows.Forms.Application.ComponentManager.Interop.Mso.IMsoComponentManager.FPushMessageLoop(UIntPtr dwComponentID, msoloop uReason, Void* pvLoopData)
   at System.Windows.Forms.Application.ThreadContext.RunMessageLoopInner(msoloop reason, ApplicationContext context)
   at System.Windows.Forms.Application.ThreadContext.RunMessageLoop(msoloop reason, ApplicationContext context)
   at System.Windows.Forms.Application.Run(Form mainForm)
   at MyLibrary.SingleInstance..ctor(Form mainForm, SingleInstanceTypes type) in C:\SandBox\Libraries\UI\Lear.UI\SingleInstance.cs:line 104
   at MyApp.Program.Main(String[] args) in C:\Sandbox\LPS3Services\Projects\MessageRouter\LPS.MessageRouter.Monitor\Program.cs:line 38

NLog内部日志

2023-07-28 11:56:40.0448 Info Message Template Auto Format enabled
2023-07-28 11:56:40.0564 Info Loading assembly name: NLog.Windows.Forms
2023-07-28 11:56:40.0767 Info NLog.Windows.Forms, Version=5.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c. File version: 5.2.0.532. Product version: 5.2.0+2d57aed1aadb6fc0d0ffaa67674ba6ad00baf4cd. GlobalAssemblyCache: False
2023-07-28 11:56:40.1461 Info Registered target NLog.Targets.Wrappers.AsyncTargetWrapper(Name=file)
2023-07-28 11:56:40.1461 Info Registered target NLog.Targets.Wrappers.AsyncTargetWrapper(Name=trace)
2023-07-28 11:56:40.1461 Error Forbidden usage of RetentionStrategy (All) when MaxLines is not set
2023-07-28 11:56:40.1461 Info Registered target NLog.Targets.Wrappers.AsyncTargetWrapper(Name=richTextBox)
2023-07-28 11:56:40.1849 Info Validating config: TargetNames=file, trace, richTextBox, ConfigItems=30, FilePath=.\MyApp\bin\Debug\net6.0-windows\NLog.config
2023-07-28 11:56:40.2046 Info Form BaseForm not found, waiting for ReInitializeAllTextboxes.
2023-07-28 11:56:40.2113 Info Configuration initialized.
2023-07-28 11:56:40.2113 Info NLog, Version=5.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c. File version: 5.2.2.1914. Product version: 5.2.2+51cc02a2dd780a9a01eb6747de52071d78e2f506. GlobalAssemblyCache: False
2023-07-28 11:56:40.5062 Info Executing ReInitializeAllTextboxes for Form MyApp.MainForm, Text: MainForm
2023-07-28 11:56:40.5062 Info Attaching target richTextBox_wrapped to textbox BaseForm.rtbStatus
2023-07-28 11:56:42.6583 Info Registered target NLog.Targets.FileTarget(Name=OPC-TagLog)
2023-07-28 11:56:42.6583 Info Validating config: TargetNames=file, trace, richTextBox, OPC-TagLog, ConfigItems=38, FilePath=.\MyApp\bin\Debug\net6.0-windows\NLog.config
2023-07-28 11:56:42.6583 Info Closing old configuration.
2023-07-28 11:56:42.7235 Info Validating config: TargetNames=file, trace, richTextBox, OPC-TagLog, ConfigItems=38, FilePath=.\MyApp\bin\Debug\net6.0-windows\NLog.config
2023-07-28 11:56:42.7235 Info Attaching target richTextBox_wrapped to textbox BaseForm.rtbStatus
2023-07-28 11:56:42.8612 Info Registered target NLog.Targets.FileTarget(Name=AS400-AS400 Resolution Connection Data Source0)
2023-07-28 11:56:42.8612 Info Validating config: Targets=5, ConfigItems=46, FilePath=.\MyApp\bin\Debug\net6.0-windows\NLog.config
2023-07-28 11:56:42.8612 Info Closing old configuration.
2023-07-28 11:56:42.8889 Info Validating config: Targets=5, ConfigItems=46, FilePath=.\MyApp\bin\Debug\net6.0-windows\NLog.config
2023-07-28 11:56:42.8889 Info Attaching target richTextBox_wrapped to textbox BaseForm.rtbStatus

已尝试的解决方案

  • 用LogManager.SuspendLogging包裹FileTarget创建逻辑
  • 移除LogManager.ReconfigExistingLoggers()调用

问题仅在.NET6中出现,.NET Framework 4.6.2版本无此异常,推测是RichTextBoxTarget重新初始化时,其他线程尝试写入日志导致空引用。

推荐解决方案

1. 安全更新NLog配置

避免直接修改全局配置实例,使用配置副本更新,减少对已有Target的影响:

public NLogSimFileLogger(string category, string name)
{
    try
    {
        this._TargetName = category + "-" + name;
        FileTarget target = new FileTarget
        {
            Layout = "${message}",
            ReplaceFileContentsOnEachWrite = false,
            ArchiveAboveSize = 1048576,
            ArchiveEvery = FileArchivePeriod.Day,
            MaxArchiveFiles = 21,
            ArchiveFileName = "${basedir}/LogFiles/Sims/" + category + "/" + name + "{####}.txt",
            ArchiveNumbering = ArchiveNumberingMode.Sequence,
            FileName = "${basedir}/LogFiles/Sims/" + category + "/" + name + ".sim",
            KeepFileOpen = true,
            ConcurrentWrites = true,
            Name = this._TargetName
        };

        // 创建配置副本,避免修改原配置时的线程冲突
        var configCopy = LogManager.Configuration.Copy();
        configCopy.AddRule(LogLevel.Debug, LogLevel.Fatal, target, this._TargetName);
        
        // 替换全局配置,自动触发重新配置
        LogManager.Configuration = configCopy;

        // 复用已有的ILoggerFactory,避免重复初始化
        this._Logger = LoggerFactory.Create(configure => configure.AddNLog()).CreateLogger(this._TargetName);
    }
    catch(Exception ex)
    {
        // 异常处理逻辑
    }
}

2. 强制重新绑定RichTextBoxTarget

配置更新后,手动触发RichTextBoxTarget的控件重新绑定,确保引用有效:

LogManager.Configuration = configCopy;
// 强制重新初始化所有文本框目标
RichTextBoxTarget.ReInitializeAllTextboxes();

3. 升级NLog.Windows.Forms版本

检查NLog.Windows.Forms的最新版本,官方可能已修复.NET6下的线程安全问题,升级至最新稳定版。

4. 修正配置警告

内部日志中的Forbidden usage of RetentionStrategy (All) when MaxLines is not set警告,确认配置中maxLines属性正确生效,可尝试显式设置messageRetention="Limit"并配合maxLines,避免配置加载异常:

<target name="richTextBox" xsi:type="RichTextBox" layout="${message}${exception}" controlName="rtbStatus" formName="BaseForm" allowAccessoryFormCreation="false" messageRetention="Limit" maxLines="10000"/>

5. 确保日志写入的线程安全

对于RichTextBoxTarget,确保所有日志写入操作在UI线程上下文执行,或者配置AsyncWrapper时设置overflowAction="Discard",避免重新初始化时队列积压未处理消息导致空引用。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.14 23:59:49