我们遇到了一个严重的问题,即在 Windows 10 32 位安装中,C# 应用程序会在随机且不频繁的时间点悄悄终止。例如,两次发生之间的时间间隔可能为一个月。有时甚至只有一天。
基本系统规格:
Microsoft Windows 10 Enterprise 2016 LTSB
Version 10.0.14393 Build 14393
32-bit
使用https://docs.microsoft.com/en-us/windows-hardware/drivers/debugger/setting-and-clearing-flags-for-silent-process-exit我们已经配置了静默进程退出监控。最后我们得到了一些示例:
The process 'APPLICATIONPATH\APPLICATIONNAME.exe' was terminated by
the process 'C:\Windows\System32\svchost.exe' with termination code 1067.
The creation time for the exiting process was 0x01d43bd8689073eb.
查看为监控设置的转储,我们得到了 svchost 的进程 ID。此服务仍在系统中运行,并显示以下服务列表:
这似乎是 Windows 的“netsvcs”列表。打开转储svchost.exe
并查看它,发现一个线程有一个有趣的调用堆栈:
ntdll.dll!_KiFastSystemCallRet@0 ()
ntdll.dll!_NtWaitForSingleObject@12 ()
ntdll.dll!RtlReportSilentProcessExit()
KERNELBASE.dll!TerminateProcess()
ubpm.dll!_UbpmpTerminateProcessCallback@12 ()
ubpm.dll!UbpmUtilsTimerCallback()
ntdll.dll!TppTimerpExecuteCallback()
ntdll.dll!TppWorkerThread()
kernel32.dll!@BaseThreadInitThunk@12 ()
ntdll.dll!__RtlUserThreadStart()
ntdll.dll!__RtlUserThreadStart@8 ()
UBPM 是统一后台进程管理器。但这怎么会终止我们的应用程序呢?为什么呢?终止代码1067
告诉我们什么呢?
以下是来自静默进程监控的日志条目:
Log Name: Application
Source: Microsoft-Windows-ProcessExitMonitor
Date: 2018-08-31 15:26:09
Event ID: 3001
Task Category: None
Level: Information
Keywords: Classic
User: SYSTEM
Computer: PC
Description:
The process 'APPLICATIONPATH\APPLICATIONNAME.exe' was terminated by the process 'C:\Windows\System32\svchost.exe' with termination code 1067. The creation time for the exiting process was 0x01d43ed2aee892ab.
Event Xml:
<Event xmlns="http://schemas.microsoft.com/win/2004/08/events/event">
<System>
<Provider Name="Microsoft-Windows-ProcessExitMonitor" Guid="{FD771D53-8492-4057-8E35-8C02813AF49B}" EventSourceName="Process Exit Monitor" />
<EventID Qualifiers="16384">3001</EventID>
<Version>0</Version>
<Level>4</Level>
<Task>0</Task>
<Opcode>0</Opcode>
<Keywords>0x80000000000000</Keywords>
<TimeCreated SystemTime="2018-08-31T13:26:09.988216500Z" />
<EventRecordID>4853</EventRecordID>
<Correlation />
<Execution ProcessID="0" ThreadID="0" />
<Channel>Application</Channel>
<Computer>PC</Computer>
<Security UserID="S-1-5-18" />
</System>
<EventData Name="EVENT_PROCESSTERMINATION_CROSSPROCESS">
<Data Name="param1">APPLICATIONPATH\APPLICATIONNAME.exe</Data>
<Data Name="param2">C:\Windows\System32\svchost.exe</Data>
<Data Name="param3">1067</Data>
<Data Name="param4">01d43ed2aee892ab</Data>
</EventData>
</Event>
注意:应用程序终止时 PC 并未关闭,事件日志中也没有任何其他迹象表明该进程终止的原因。
更新 1:这里有几个额外的细节(尝试回答评论中尽可能多的问题):
- 是的,Windows 启动时,进程(有时)通过 TaskScheduler 启动。其他时候由用户启动。不完全确定问题是否仅在通过 TaskScheduler 启动时发生。但有趣的是?Windows 会因为某种原因终止任务吗?请注意,进程退出之间的时间可能长达一个月。
- 我们有主程序的源代码,但在调试器中运行它会有问题,因为它是在客户那里运行的,但也许吧。不过,我们无法运行针对调试编译的程序。完全不能,因为性能问题。这是实时生产。
- 应用程序是一个普通的 WPF 应用程序,没有任何子进程或任何其他进程间通信。它确实使用了一些第三方设备,例如库和驱动程序。
- 我们已经设置了 appdomain 异常和应用程序异常等事件处理。但这些都没有发生。进程退出时没有任何异常发生的迹象。这是一次硬进程退出。
- 我们怀疑第三方驱动程序可能是问题根源,但原因何在?我们如何确定是否是这种情况?
更新 2:我们使用 nuget 包TaskScheduler
通过代码设置任务。请注意,我们没有设置 ExecutionTimeLimit,因此它应该是 Nothing,也就是无限的。
using (TaskService m_service = new TaskService())
{
var task = m_service.NewTask();
task.Principal.UserId = userId;
task.Principal.LogonType = TaskLogonType.InteractiveToken;
task.Principal.RunLevel = TaskRunLevel.Highest;
task.Settings.Enabled = true;
task.Settings.MultipleInstances = TaskInstancesPolicy.IgnoreNew;
task.Settings.Hidden = false;
// NOTICE: A subset of the following 4 settings will cause app to hang on Win10
//task.Settings.AllowHardTerminate = true;
//task.Settings.DisallowStartOnRemoteAppSession = false;
//task.Settings.RunOnlyIfLoggedOn = true;
var trigger = (LogonTrigger)task.Triggers.Add(new LogonTrigger());
trigger.Enabled = true;
trigger.UserId = userId;
task.Actions.Add(new ExecAction(executableFilePath, arguments: null,
workingDirectory: m_installDirectoryPath));
if (!IsAdministrator())
{
var message = "Cannot register task with your current identity's permissions level.";
m_logger.Error(message);
}
m_service.RootFolder.RegisterTaskDefinition(taskName, task, TaskCreation.Create,
userId, password: null, logonType: TaskLogonType.InteractiveToken);
}
更新3:也许上述说法是错误的,TaskScheduler
库中的默认时间似乎是3天或72小时。
//
// Summary:
// Gets or sets the amount of time that is allowed to complete the task. By default,
// a task will be stopped 72 hours after it starts to run.
//
// Remarks:
// If a task is started on demand, the ExecutionTimeLimit setting is bypassed. Therefore,
// a task that is started on demand will not be terminated if it exceeds the ExecutionTimeLimit.
[DefaultValue(typeof(TimeSpan), "3")]
public TimeSpan ExecutionTimeLimit { get; set; }
更新 4:唯一的问题是,我们观察到在进程运行超过 3 天(例如 30 天)之后,进程会静默退出,所以不确定这到底是不是这样。
更新 5:超过 3 天的时间没有被正确观察到,所以经过这一切,现在很清楚这是由于任务调度程序任务的设置不正确造成的。错误设置如下所示:
正确的设置为:
答案1
这只是一种理论,但也许因为它是从计划任务启动的,所以任务调度程序在启动后仍然对该进程具有“父”控制权?在我看来,可能存在某种情况或问题,Windows 任务调度程序正在停止该任务。也许你应该尝试使用开始命令,以便启动应用程序但允许“计划任务”完成,因此从 Windows 任务计划程序中释放对应用程序的控制。
例如 C:\Windows\System32\cmd.exe /c 启动“标题” C:\Windows\System32\notepad.exe
(或你正在运行的任何程序)
您必须运行 cmd.exe,因为启动命令是内置的。此外,如果您阅读启动命令的文档,您会注意到 title 参数是必需的。您可以将其保留为“Title”,也可以将其设置为您想要的任何内容。
答案2
我认为您在帖子中提供的信息都是事后信息。
首先,终止代码 1067 方法 “ERROR_PROCESS_ABORTED-进程意外终止”。
另一方面,我认为您提供的堆栈跟踪只是程序终止事件的处理,这是执行静默报告的线程,因此没有可添加的信息。
我认为,如果问题再次发生,我们将在将 svchost 上的捆绑服务分离为 描述在这里但是再次强调,将 svchost 设计为原因可能会产生误导。
根据我的经验,找到问题的最简单、最可靠的方法(需要完全控制产品的源代码)是编译产品进行调试并在调试器中运行它,在所有系统终止例程上设置断点。对于 C,这些是_exit
和_abort
,但可能还有其他。
如果程序在调试器中停止,您将能够通过检查调用堆栈和全局/局部变量来了解原因。
如果您不能使用调试器,我们将需要更多有关该程序的数据才能提供有意义的意见。
编辑
即使为生产进行编译,您至少也能够创建一个可以帮助调试和分析的符号文件。
您应该检查事件查看器 - 如果程序崩溃,那么其中可能有一些信息,并且用户模式转储文件。
为了更好地分析崩溃,您可以在控制面板>系统>高级系统设置>高级选项卡>启动和恢复>设置>写入调试信息中参数用户转储,将其设置为“完全内存转储”并确定。
您可以在调试器中将这些转储与符号文件一起使用。 您可以在另一台计算机上执行此操作,通常是在编译可执行文件的地方,但如果您使用的是 Visual Studio,则会出现一些问题:
- 转储和符号文件必须由同一可执行版本生成
- 分析转储时,它必须位于与生成时完全相同的文件夹中(
C:\some-path\
等等)。