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

Fody织入:是否需同时包含引用项目的DLL?

排查合并EXE后Topshelf+Quartz业务逻辑未执行的问题

我之前做类似的单EXE Windows服务时也踩过这个坑,结合你的描述,核心问题大概率出在嵌入式程序集加载异常或者Quartz调度未正确初始化上,给你几个具体的排查和解决方向:

1. 确认Costura的程序集加载逻辑生效

虽然DotPeek显示DLL已经嵌入,但Costura的自动加载有时候会因为程序集查找逻辑的问题失效,尤其是Topshelf和Quartz这类框架级库:

  • 检查FodyWeavers.xml的配置,确保没有遗漏关键设置:
    <Weavers>
      <Costura>
        <IncludedAssemblies>
          Topshelf
          Quartz
          core
        </IncludedAssemblies>
        <!-- 可选:如果加载异常,尝试关闭压缩 -->
        <DisableCompression>true</DisableCompression>
      </Costura>
    </Weavers>
    
  • 可以在Program.cs的最开头手动触发Costura的加载逻辑(虽然官方说自动,但极端情况可能需要):
    static Program()
    {
        // 强制初始化Costura的嵌入式程序集加载器
        CosturaUtility.Initialize();
    }
    

2. 修复Quartz的Job类型加载问题

合并EXE后,Quartz经常会因为无法找到Job类型导致调度不执行,这是因为默认的类型查找可能找不到嵌入式程序集里的类型:

  • 注册Job时使用完全限定类型名代替直接传递typeof(YourJob),比如:
    var jobType = Type.GetType("YourCoreNamespace.YourBusinessJob, core");
    if (jobType == null)
    {
        Console.WriteLine($"Failed to find job type: YourCoreNamespace.YourBusinessJob");
        throw new InvalidOperationException("Job type not found");
    }
    var jobDetail = JobBuilder.Create(jobType)
                              .WithIdentity("BusinessJob")
                              .Build();
    
  • 在服务启动的Start()方法里添加异常捕获,不要静默吞掉Quartz初始化的错误:
    public void Start()
    {
        try
        {
            _scheduler = await _schedulerFactory.GetScheduler();
            await _scheduler.Start();
            Console.WriteLine("Quartz scheduler started successfully, jobs registered");
        }
        catch (Exception ex)
        {
            Console.WriteLine($"Scheduler startup failed: {ex.Message}\n{ex.StackTrace}");
            // 抛出异常让Topshelf感知到启动失败,避免服务假启动
            throw;
        }
    }
    

3. 验证Topshelf的服务启动流程

确保Topshelf的配置正确绑定了服务的启动/停止逻辑,没有遗漏关键步骤:

  • 检查Topshelf的Host配置,确认WhenStarted确实调用了你的业务服务启动方法:
    HostFactory.Run(x =>
    {
        x.Service<YourServiceWrapper>(s =>
        {
            s.ConstructUsing(_ => new YourServiceWrapper());
            s.WhenStarted(async service => await service.StartAsync());
            s.WhenStopped(async service => await service.StopAsync());
        });
        x.RunAsLocalSystem();
        x.SetServiceName("YourBusinessService");
        // 启用控制台日志,方便排查启动问题
        x.UseConsoleLogger();
    });
    
  • 启用Topshelf的控制台日志,这样运行EXE时能看到更多启动过程的细节,比如是否真的进入了服务的Start()方法。

4. 调试合并后的EXE

如果以上方法都没解决,直接调试合并后的EXE:

  • 打开Visual Studio,选择「调试」→「附加到进程」,找到你的EXE进程附加进去,在业务代码和Quartz初始化代码里加断点,看是否能命中。
  • 在代码里添加更多的Console.WriteLine()输出,标记关键执行节点(比如"进入服务Start方法"、"开始初始化Quartz"等),运行EXE时观察输出,确定程序卡在了哪一步。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.19 08:30:00