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

如何在IIS应用中启动AppPool停止后仍不退出的独立进程

IIS托管应用启动的独立更新进程随AppPool下线异常退出问题

问题场景

  • 自研.exe更新程序用于更新IIS站点下运行的主应用,当前AppPool运行身份为LocalService或指定自定义用户。
  • 非IIS托管场景下更新全流程运行正常;部署为IIS应用后,更新程序总会在IIS AppPool下线后数毫秒无异常终止,代码外层全局try/catch未捕获到任何异常信息。
  • 任务管理器中可观察到更新程序拥有独立PID。

相关实现代码

更新程序启动逻辑

public bool ExecuteCommand(string domain, string userName, SecureString password, string extractedFolderPath, string arguments)
{
    Arguments.CheckNonEmptyString(extractedFolderPath, nameof(extractedFolderPath));
    if (Directory.Exists(extractedFolderPath) && File.Exists(Path.Combine(extractedFolderPath, UPDATER_EXE_FILENAME)))
    {
        Logger.Info($"Executing command with {UPDATER_EXE_FILENAME} and arguments {arguments} in working directory {extractedFolderPath}");
        var startInfo = new ProcessStartInfo()
        {
            UseShellExecute = true,
            RedirectStandardOutput = false,
            FileName = Path.Combine(extractedFolderPath, UPDATER_EXE_FILENAME),
            Arguments = arguments,
            WorkingDirectory = extractedFolderPath,
            //UserName = userName,
            //Domain = domain,
            //Password = password
        };

        Process process = Process.Start(startInfo);
        Logger.Info($"Startet update process. [PID: {process.Id}]");
        return process != null;
    }
    else
        return false;
}

AppPool停止逻辑

Logger.Debug($"Process with id '{processId.Value}' will be shut down!");
Process.GetProcessById(processId.Value).Close();
if (ProcessExists(processId.Value))
    Process.GetProcessById(processId.Value).Kill();

现场运行日志

Updater-2022-06-28 12:10:16-Debug: Process with id '14868' still running!
   at Updater.IISHelper.StopApplication(ApplicationPool pool, Nullable`1 processId)
   at Updater.Program.Main(String[] args)
Updater-2022-06-28 12:10:16-Debug: Process with id '14868' will be shut down!
   at Updater.IISHelper.StopApplication(ApplicationPool pool, Nullable`1 processId)
   at Updater.Program.Main(String[] args)
Updater-2022-06-28 12:10:16-Info: Backup will be created shortly at path: 'F:\path\Temp\software_update\App_V1.0.1\backup'
   at Updater.Program.CreateBackup(String backupPath, String applicationPath)
   at Updater.Program.Main(String[] args)
Updater-2022-06-28 12:10:16-Debug: Created directory at path 'F:\path\Temp\software_update\App_V1.0.1\backup\wwwroot\assets\i18n'!
   at Updater.Program.CopyContent(String sourcePath, String targetPath, List`1 ignoreFolders, List`1 ignoreFiles)
   at Updater.Program.CreateBackup(String backupPath, String applicationPath)
   at Updater.Program.Main(String[] args)
   
  ...
  ...
  ...

Updater-2022-06-28 12:10:16-Debug: Created directory at path 'F:\path\Temp\software_update\App_V1.0.1\backup\wwwroot\assets\images'!
   at Updater.Program.CopyContent(String sourcePath, String targetPath, List`1 ignoreFolders, List`1 ignoreFiles)
   at Updater.Program.CreateBackup(String backupPath, String applicationPath)
   at Updater.Program.Main(String[] args)
Updater-2022-06-28 12:10:16-Debug: Created directory at path 'F:\path\Temp\software_update\App_V1.0.1\backup\wwwroot\assets\Logs'!
   at Updater.Program.CopyContent(String sourcePath, String targetPath, List`1 ignoreFolders, List`1 ignoreFiles)
   at Updater.Program.CreateBackup(String backupPath, String applicationPath)
   at Updater.Program.Main(String[] args)
Updater-2022-06-28 12:10:16-Debug: Created directory at path 'F:\path\Temp\software_update\App_V1.0.1\backup\wwwroot\assets\svg'!
   at Updater.Program.CopyContent(String sourcePath, String targetPath, List`1 ignoreFolders, List`1 ignoreFiles)
   at Updater.Program.CreateBackup(String backupPath, String applicationPath)
   at Updater.Program.Main(String[] args)
Updater-2022-06-28 12:10:16-Debug: Created directory at path 'F:\path\Temp\software_update\App_V1.0.1\backup\brgData\transfer\persist-Brd-2-Skr\persist-Mqtt-Brd-2-Skr'!
   at Updater.Program.CopyContent(String sourcePath, String targetPath, List`1 ignoreFolders, List`1 ignoreFiles)
   at Updater.Program.CreateBackup(String backupPath, String applicationPath)
   at Updater.Program.Main(String[] args)

问题根因说明

  1. 进程绑定关系结论:哪怕更新程序拥有独立PID、通过UseShellExecute = true方式启动,它依然和启动它的主应用所属AppPool存在强绑定关联。
  2. 无异常退出原因:IIS会为每个应用程序池创建独立的Windows Job对象,默认开启JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE标志,所有由该AppPool进程拉起的子进程(包含Shell启动的独立进程)都会被自动纳入这个Job对象的管控范围。当AppPool下线时,对应的Job对象句柄会被关闭,Windows内核会直接终止该Job关联的所有进程。这个终止动作发生在内核层,不会走用户态进程的异常分发流程,因此用户代码中配置的所有异常捕获逻辑都无法感知,也不会留下异常日志。

方案验证

后续调整为通过Windows服务监听更新事件、再触发更新程序执行更新的方案可稳定运行,本质是因为此时更新程序的启动源脱离了IIS AppPool的Job对象管控范围,不会随AppPool生命周期被连带终止,和更新逻辑本身无关。
如果不希望引入Windows服务,也可以通过Windows API主动将新启动的更新进程从当前AppPool所属Job对象中移除,同样可以避免进程被连带终止。

内容的提问来源于stack exchange,提问作者Timo K.

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 13:27:22