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

.NET Windows服务调用本地API时出现空引用异常求助

.NET Windows服务调用本地API空引用异常问题修复

问题描述

创建了一个.NET Windows服务,尝试调用本地API(http://localhost:7113/Home/SincronizarDatos),但无法验证API返回值并写入Txt日志,运行时抛出NullReferenceException。相同逻辑用计划任务执行时能正常获取预期返回值。日志显示RestClient、RestRequest实例被错误判定为创建失败,最终在执行response.Content.ToString()时触发空引用异常。

相关代码

Program.cs

namespace MyNewService
{
    internal static class Program
    {
        static void Main()
        {
            ServiceBase[] ServicesToRun;
            ServicesToRun = new ServiceBase[]
            {
                new Service1()
            };
            ServiceBase.Run(ServicesToRun);
        }
    }
}

MyNewService.cs

namespace MyNewService
{
    public partial class Service1: ServiceBase
    {
        int tiempo = 5;// Settings1.Default.Tiempo;
        public Thread worker = null;
        public Service1()
        {
            InitializeComponent();
        }

        protected override void OnStart(string[] args)
        {
            try
            {
                AgregaLog("Inicio el servicio", "OnStart");
                ThreadStart star = new ThreadStart(working);
                worker = new Thread(star);
                worker.Start();
            }
            catch (Exception ex)
            {
                AgregaLog("Ocurrio un problema:" + ex.Message, "OnStart");
                throw;
            }
        }

        protected override void OnStop()
        {
            try
            {
                if (worker != null && worker.IsAlive)
                {
                    worker.Abort();
                }
            }
            catch (Exception ex)
            {
                AgregaLog("Ocurrio un problema:" + ex.Message, "OnStop");
                throw;
            }
        }
        public void working()
        {
            int i = 0;
            while (true)
            {
                AgregaLog("Ejecutando linea " + i, "working");
                i++;
                ejecutarWS();
                Thread.Sleep(tiempo * 60 * 100);
            }

        }
        public void ejecutarWS()
        {
            try
            {
                AgregaLog("ejecutar WS", "ejecutarWS");
                var client = new RestClient();
                if (client != null)
                {
                    AgregaLog("RestClient fallo", "ejecutarWS");
                }
                var request = new RestRequest("http://localhost:7113/Home/SincronizarDatos");
                request.Method = Method.Post;
                if (request != null)
                {
                    AgregaLog("request fallo", "ejecutarWS");
                }
                var response = client.Execute(request);
                if (response != null)
                {
                    AgregaLog("response fallo", "ejecutarWS");
                }
                
                AgregaLog("resultado" + response.Content.ToString(), "ejecutarWS");
            }
            catch (Exception ex)
            {
                AgregaLog("Ocurrio el problema:" + ex+"\n", "OnStop");
                throw;
            }

        }

        public void AgregaLog(string Valor, string Metodo)
        {
            StreamWriter log;
            FileStream fileStream = null;
            DirectoryInfo logDirInfo = null;
            FileInfo logFileInfo;

            string logFilePath = "C:\\Logs_Servicio_TankFarm_Users\\";
            try
            {
                if (!Directory.Exists(logFilePath))
                {
                    Directory.CreateDirectory(logFilePath);
                }
            }
            catch (Exception ex)
            {
                // handle them here
            }
            logFilePath = logFilePath + "Log-" + System.DateTime.Today.ToString("[dd-MM-yyyy HH:mm]") + "." + "txt";
            logFileInfo = new FileInfo(logFilePath);
            logDirInfo = new DirectoryInfo(logFileInfo.DirectoryName);
            if (!logDirInfo.Exists) logDirInfo.Create();
            if (!logFileInfo.Exists)
            {
                fileStream = logFileInfo.Create();
            }
            else
            {
                fileStream = new FileStream(logFilePath, FileMode.Append);
            }
            log = new StreamWriter(fileStream);
            log.WriteLine(System.DateTime.Today.ToString("[dd-MM-yyyy HH:mm]") + "-" + "METODO: " + Metodo + "-- Desc: " + Valor);
            log.Close();
        }        
    }
}

日志内容

[17-04-2023 00:00]-METODO: OnStart-- Desc: Inicio el servicio
[17-04-2023 00:00]-METODO: working-- Desc: Ejecutando linea 0
[17-04-2023 00:00]-METODO: ejecutarWS-- Desc: ejecutar WS
[17-04-2023 00:00]-METODO: ejecutarWS-- Desc: RestClient fallo
[17-04-2023 00:00]-METODO: ejecutarWS-- Desc: request fallo
[17-04-2023 00:00]-METODO: ejecutarWS-- Desc: response fallo
[17-04-2023 00:00]-METODO: OnStop-- Desc: Ocurrio el problema:System.NullReferenceException: Referencia a objeto no establecida como instancia de un objeto.
   en MyNewService.Service1.ejecutarWS() en C:\Users\leona\source\repos\MyNewService\MyNewService\MyNewService .cs:línea 104

问题根源分析

  1. 日志判断逻辑完全颠倒:代码中if (client != null)实际是判断实例创建成功,却错误记录为"RestClient fallo",导致日志信息误导排查方向。
  2. 空引用直接调用ToString():response.Content可能为null(比如请求失败、API无返回内容),直接调用ToString()触发空引用异常。
  3. Windows服务权限环境差异:服务默认以Local System账户运行,可能无法访问运行在用户会话的localhost API,而计划任务以当前用户身份执行,权限足够。
  4. 线程终止方式不规范:Thread.Abort()会强制终止线程,可能导致资源泄漏或不稳定,属于不推荐的做法。
  5. 日志方法存在缺陷:使用DateTime.Today只能记录日期,时间固定为00:00;文件流未用using语句自动释放,存在资源泄漏风险。

修复方案与代码优化

1. 修正日志判断逻辑

将实例是否为null的判断条件反转,确保日志记录准确:

var client = new RestClient();
if (client == null) // 反转判断条件
{
    AgregaLog("RestClient fallo", "ejecutarWS");
    return; // 实例创建失败时直接退出方法
}
var request = new RestRequest("http://localhost:7113/Home/SincronizarDatos");
request.Method = Method.Post;
if (request == null)
{
    AgregaLog("request fallo", "ejecutarWS");
    return;
}
var response = client.Execute(request);
if (response == null)
{
    AgregaLog("response fallo", "ejecutarWS");
    return;
}

2. 处理response.Content空值

在调用ToString()前先检查是否为null:

string resultContent = response.Content ?? "API无返回内容";
AgregaLog("resultado: " + resultContent, "ejecutarWS");

3. 调整Windows服务运行账户

  • 打开服务管理器,找到你的服务,右键选择「属性」
  • 切换到「登录」选项卡,选择「此账户」,输入有API访问权限的用户账户和密码
  • 重启服务,确保服务能访问localhost的API

4. 用CancellationToken优雅终止线程

替换Thread.Abort(),使用取消令牌实现优雅终止:

private CancellationTokenSource _cts;

protected override void OnStart(string[] args)
{
    try
    {
        AgregaLog("Inicio el servicio", "OnStart");
        _cts = new CancellationTokenSource();
        Task.Run(() => working(_cts.Token), _cts.Token);
    }
    catch (Exception ex)
    {
        AgregaLog("Ocurrio un problema:" + ex.Message, "OnStart");
        throw;
    }
}

protected override void OnStop()
{
    try
    {
        _cts?.Cancel();
        AgregaLog("Servicio detenido", "OnStop");
    }
    catch (Exception ex)
    {
        AgregaLog("Ocurrio un problema:" + ex.Message, "OnStop");
        throw;
    }
}

public void working(CancellationToken token)
{
    int i = 0;
    while (!token.IsCancellationRequested)
    {
        AgregaLog("Ejecutando linea " + i, "working");
        i++;
        ejecutarWS();
        // 等待时响应取消请求
        token.WaitHandle.WaitOne(tiempo * 60 * 1000); // 修正Sleep时间(原代码少打一个0)
    }
}

5. 优化日志方法

使用DateTime.Now记录准确时间,用using自动释放资源:

public void AgregaLog(string valor, string metodo)
{
    string logFilePath = "C:\\Logs_Servicio_TankFarm_Users\\";
    try
    {
        Directory.CreateDirectory(logFilePath); // 无需判断,CreateDirectory已处理存在的情况
        logFilePath = Path.Combine(logFilePath, $"Log-{DateTime.Now:dd-MM-yyyy}.txt");
        
        using (var writer = new StreamWriter(logFilePath, true))
        {
            writer.WriteLine($"{DateTime.Now:[dd-MM-yyyy HH:mm:ss]}-METODO: {metodo}-- Desc: {valor}");
        }
    }
    catch (Exception ex)
    {
        // 可考虑将异常写入系统事件日志
        System.Diagnostics.EventLog.WriteEntry("MyNewService", $"日志写入失败: {ex.Message}", System.Diagnostics.EventLogEntryType.Error);
    }
}

内容的提问来源于stack exchange,提问作者Leonardo Carrasco Velazquez

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.24 19:47:00