自动维护
平台
客户端 - Windows 8
服务器 - Windows Server 2012
说明
Windows 依赖于执行收件箱和第三方维护活动来增加大部分价值,包括Windows 更新、自动磁盘碎片整理以及防病毒更新和扫描。 此外,企业经常使用网络访问保护 (NAP) 扫描等维护活动来帮助在所有企业工作站上强制实施安全标准。
Windows 中的维护活动设计为在后台运行,用户交互有限,对性能和能源效率的影响最小。 但是,在 Windows 7 及更早版本中,由于 Windows 中多个维护活动的计划不确定且差异很大,性能和能源效率仍然受到影响。 当用户主动使用计算机时,当维护活动运行时,对用户的响应能力会降低。 应用还经常要求用户更新其软件并运行后台维护,并将用户定向到多个体验,包括操作中心、控制面板、Windows 更新、任务计划程序 MMC 管理单元和第三方控件。
自动维护的目标是合并 Windows 中的所有后台维护活动,并帮助第三方开发人员将其维护活动添加到 Windows,而不会对性能和能源效率产生负面影响。 此外,自动维护使用户和企业能够控制维护活动的计划和配置。
关键问题
自动维护旨在解决 Windows 中维护活动的以下问题:
- 截止时间计划
- 资源利用率冲突
- 节能
- 用户的透明度
功能
自动维护可提高空闲效率,并允许所有活动及时按优先级运行。 它还有助于实现对维护活动的统一可见性和控制,并允许第三方开发人员将其维护活动添加到 Windows,而不会对性能和能源效率产生负面影响。 为此,它提供完全自动模式、用户启动模式、自动停止、截止时间和通知以及企业控制。 下面分别介绍了这些内容。
完全自动模式
此默认模式支持在电脑空闲时间和计划时间进行智能计划 -- 无需任何用户干预即可执行和自动暂停维护活动。 用户可以设置每周或每日计划。 所有维护活动都是非交互式的,并且以无提示方式执行。
当系统不太可能处于使用状态时,计算机会自动从睡眠状态恢复,并遵循电源管理策略(在笔记本电脑的情况下,该策略默认仅在使用交流电源时允许唤醒)。 大功率的完整系统资源用于尽快完成维护活动。 如果系统已从睡眠状态恢复为自动维护,则请求它返回到睡眠状态。
与活动(例如配置)相关的任何必需的用户交互都在自动维护执行之外执行。
用户发起的模式
如果用户需要为旅行做好准备,希望长时间使用电池电量,或者希望优化性能和响应能力,可以选择按需启动自动维护。 用户可以配置自动维护属性,包括自动运行计划。 他们可以查看自动维护执行的当前状态,并可以根据需要停止自动维护。
自动停止
如果用户开始与计算机交互,自动维护会自动停止当前运行的维护活动。 当系统恢复为空闲状态时,维护活动将恢复。
注意
自动维护中的所有活动都必须支持在 2 秒或更短时间内停止。 应通知用户活动已停止。
截止时间和通知
关键维护活动必须在预定义的时间范围内执行。 如果关键任务无法在指定时间内运行,则自动维护将在下一个可用的系统空闲机会时自动开始执行。 但是,如果任务状态仍然落后于截止时间,则自动维护将通知用户有关活动的信息,并提供手动运行自动维护的选项。 计划维护的所有任务都将运行,但最饥饿的任务优先。 此活动可能会影响系统响应能力和性能;因此,自动维护将通知用户关键维护活动正在执行。
企业控制
企业 IT 专业人员应能够确定自动维护何时在其 Windows 系统上执行,通过标准化管理界面强制实施该计划,并检索有关自动维护执行尝试状态的事件数据。 此外,IT 专业人员应能够通过标准管理界面远程调用特定的自动维护活动。 每次执行自动维护时,都会运行状态报告,包括由于用户手动暂停活动而无法执行自动维护时的通知。 IT 专业人员应考虑将登录脚本移动到自动维护,以帮助加快用户的登录体验。
创建自动维护任务
本部分详细介绍开发人员如何使用 XML 或 C 语言的任务定义创建任务。 请记住,维护活动不应启动任何需要用户交互的用户界面,因为自动维护是完全无提示的,并在用户不在时运行。 事实上,如果用户在自动维护期间与计算机交互,则进程中的任何任务都将结束,直到下一个空闲期。
使用 XML
任务计划程序包括一个内置的命令行工具(schtasks.exe),该工具可以导入 XML 格式的任务定义。 任务定义的架构记录在 中 https://msdn.microsoft.com/library/aa383609(v=VS.85).aspx。 下面是 XML 中定义的自动维护任务的示例。
<?xml version="1.0" encoding="UTF-16"?>
<Task version="1.4" xmlns="http://schemas.microsoft.com/windows/2004/02/mit/task">
<RegistrationInfo>
<Date>2011-07-01T11:34:31</Date>
<Author>IT Deptartment</Author>
</RegistrationInfo>
<Principals>
<Principal id="Author">
<RunLevel>LeastPrivilege</RunLevel>
<GroupId>NT AUTHORITY\SYSTEM</GroupId>
</Principal>
</Principals>
<Settings>
<MultipleInstancesPolicy>IgnoreNew</MultipleInstancesPolicy>
<DisallowStartIfOnBatteries>true</DisallowStartIfOnBatteries>
<StopIfGoingOnBatteries>true</StopIfGoingOnBatteries>
<AllowHardTerminate>true</AllowHardTerminate>
<StartWhenAvailable>false</StartWhenAvailable>
<RunOnlyIfNetworkAvailable>false</RunOnlyIfNetworkAvailable>
<MaintenanceSettings>
<Period>P2D</Period>
<Deadline>P14D</Deadline>
</MaintenanceSettings>
<AllowStartOnDemand>true</AllowStartOnDemand>
<Enabled>true</Enabled>
<Hidden>false</Hidden>
<RunOnlyIfIdle>false</RunOnlyIfIdle>
<DisallowStartOnRemoteAppSession>false</DisallowStartOnRemoteAppSession>
<UseUnifiedSchedulingEngine>true</UseUnifiedSchedulingEngine>
<WakeToRun>false</WakeToRun>
<ExecutionTimeLimit>P3D</ExecutionTimeLimit>
<Priority>7</Priority>
</Settings>
<Actions Context="Author">
<Exec>
<Command>cmd</Command>
<Arguments>/c timeout -t 60</Arguments>
</Exec>
</Actions>
</Task>
若要在 Windows 计算机上保存任务,请将上述 XML 保存为文本文件,并使用此命令行:
Schtasks.exe /create /tn <task name> /xml <text file name>
使用 C
也可以使用 C 代码创建自动维护任务。 下面是可用于配置任务的自动维护设置的代码示例:
/********************************************************************
This sample creates a maintenance task to start cmd window during maintenance opportunities with periodicity of 2 days and deadline 0f 14 days.
********************************************************************/
#define _WIN32_DCOM
#include <windows.h>
#include <iostream>
#include <stdio.h>
#include <comdef.h>
#include <wincred.h>
// Include the task header file.
#include <taskschd.h>
//#pragma comment(lib, "taskschd.lib")
//#pragma comment(lib, "comsupp.lib")
int __cdecl
MainteanceTask( )
{
// ------------------------------------------------------
// Initialize COM.
HRESULT hr;
// ------------------------------------------------------
// Create a name for the task.
LPCWSTR wszTaskName = L"MaintenanceTask";
ITaskService *pService = NULL;
ITaskFolder *pRootFolder = NULL;
ITaskDefinition *pTask = NULL;
ITaskSettings *pSettings = NULL;
IRegistrationInfo *pRegInfo= NULL;
IPrincipal *pPrincipal = NULL;
ITaskSettings3 *pSettings3 = NULL;
IMaintenanceSettings* pMaintenanceSettings = NULL;
IActionCollection *pActionCollection = NULL;
IAction *pAction = NULL;
IExecAction *pExecAction = NULL;
IRegisteredTask *pRegisteredTask = NULL;
wprintf(L"\nCreate Maintenance Task %ws", wszTaskName );
hr = CoInitializeEx( NULL, COINIT_MULTITHREADED);
if( FAILED(hr) )
{
wprintf(L"\nCoInitializeEx failed: %x", hr );
return 1;
}
// Set general COM security levels.
hr = CoInitializeSecurity( NULL,
-1,
NULL,
NULL,
RPC_C_AUTHN_LEVEL_PKT_PRIVACY,
RPC_C_IMP_LEVEL_IMPERSONATE,
NULL,
0,
NULL);
if( FAILED(hr) )
{
wprintf(L"\nCoInitializeSecurity failed: %x", hr );
goto CleanUp;
}
// ------------------------------------------------------
// Create an instance of the Task Service.
hr = CoCreateInstance( CLSID_TaskScheduler,
NULL,
CLSCTX_INPROC_SERVER,
IID_ITaskService,
(void**)&pService );
if (FAILED(hr))
{
wprintf(L"\nFailed to create an instance of ITaskService: %x", hr);
goto CleanUp;
}
// Connect to the task service.
hr = pService->Connect(_variant_t(), _variant_t(), _variant_t(), _variant_t());
if( FAILED(hr) )
{
wprintf(L"\nITaskService::Connect failed: %x", hr );
goto CleanUp;
}
// ------------------------------------------------------
// Get the pointer to the root task folder. This folder will hold the
// new task that is registered.
hr = pService->GetFolder( _bstr_t( L"\\") , &pRootFolder );
if( FAILED(hr) )
{
wprintf(L"\nCannot get Root folder pointer: %x", hr );
goto CleanUp;
}
// If the same task exists, remove it.
( void ) pRootFolder->DeleteTask( _bstr_t(wszTaskName), 0 );
// Create the task definition object to create the task.
hr = pService->NewTask( 0, &pTask );
if (FAILED(hr))
{
wprintf(L"\nFailed to CoCreate an instance of the TaskService class: %x", hr);
goto CleanUp;
}
// ------------------------------------------------------
// Get the registration info for setting the identification.
hr = pTask->get_RegistrationInfo( &pRegInfo );
if( FAILED(hr) )
{
wprintf(L"\nCannot get identification pointer: %x", hr );
goto CleanUp;
}
hr = pRegInfo->put_Author( _bstr_t(L"Author Name") );
if( FAILED(hr) )
{
wprintf(L"\nCannot put identification info: %x", hr );
goto CleanUp;
}
// The task needs to grant explicit FRFX to LOCAL SERVICE (A;;FRFX;;;LS)
hr = pRegInfo->put_SecurityDescriptor( _variant_t(L"D:P(A;;FA;;;BA)(A;;FA;;;SY)(A;;FRFX;;;LS)") );
if( FAILED(hr) )
{
wprintf(L"\nCannot put security descriptor: %x", hr );
goto CleanUp;
}
// ------------------------------------------------------
// Create the principal for the task - these credentials
// are overwritten with the credentials passed to RegisterTaskDefinition
hr = pTask->get_Principal( &pPrincipal );
if( FAILED(hr) )
{
wprintf(L"\nCannot get principal pointer: %x", hr );
goto CleanUp;
}
// Set up principal logon type to interactive logon
hr = pPrincipal->put_LogonType( TASK_LOGON_INTERACTIVE_TOKEN );
if( FAILED(hr) )
{
wprintf(L"\nCannot put principal info: %x", hr );
goto CleanUp;
}
// ------------------------------------------------------
// Create the settings for the task
hr = pTask->get_Settings( &pSettings );
if( FAILED(hr) )
{
wprintf(L"\nCannot get settings pointer: %x", hr );
goto CleanUp;
}
hr = pSettings->QueryInterface( __uuidof(ITaskSettings3), (void**) &pSettings3 );
if( FAILED(hr) )
{
wprintf(L"\nCannot query ITaskSettings3 interface: %x", hr );
goto CleanUp;
}
hr = pSettings3->put_UseUnifiedSchedulingEngine( VARIANT_TRUE );
if( FAILED(hr) )
{
wprintf(L"\nCannot put_UseUnifiedSchedulingEngine: %x", hr );
goto CleanUp;
}
hr = pSettings3->CreateMaintenanceSettings( &pMaintenanceSettings );
if( FAILED(hr) )
{
wprintf(L"\nCannot CreateMaintenanceSettings: %x", hr );
goto CleanUp;
}
hr = pMaintenanceSettings->put_Period ( _bstr_t(L"P2D") );
if( FAILED(hr) )
{
wprintf(L"\nCannot put_Period: %x", hr );
goto CleanUp;
}
hr = pMaintenanceSettings->put_Deadline ( _bstr_t(L"P14D") );
if( FAILED(hr) )
{
wprintf(L"\nCannot put_Period: %x", hr );
goto CleanUp;
}
// ------------------------------------------------------
// Add an action to the task. This task will execute cmd.exe.
// Get the task action collection pointer.
hr = pTask->get_Actions( &pActionCollection );
if( FAILED(hr) )
{
wprintf(L"\nCannot get Task collection pointer: %x", hr );
goto CleanUp;
}
// Create the action, specifying that it is an executable action.
hr = pActionCollection->Create( TASK_ACTION_EXEC, &pAction );
if( FAILED(hr) )
{
wprintf(L"\nCannot create the action: %x", hr );
goto CleanUp;
}
// QI for the executable task pointer.
hr = pAction->QueryInterface( IID_IExecAction, (void**) &pExecAction );
if( FAILED(hr) )
{
wprintf(L"\nQueryInterface call failed for IExecAction: %x", hr );
goto CleanUp;
}
// Set the path of the executable to cmd.exe.
hr = pExecAction->put_Path( _bstr_t(L"cmd") );
if( FAILED(hr) )
{
wprintf(L"\nCannot put action path: %x", hr );
goto CleanUp;
}
// ------------------------------------------------------
// Save the task in the root folder.
hr = pRootFolder->RegisterTaskDefinition(
_bstr_t(wszTaskName),
pTask,
TASK_CREATE_OR_UPDATE,
_variant_t(),
_variant_t(),
TASK_LOGON_INTERACTIVE_TOKEN,
_variant_t(L""),
&pRegisteredTask);
if( FAILED(hr) )
{
wprintf(L"\nError saving the Task : %x", hr );
goto CleanUp;
}
wprintf(L"\nSuccess!\n----------------------------------" );
CleanUp:
if ( pService != NULL ) pService->Release();
if ( pRootFolder != NULL ) pRootFolder->Release();
if ( pTask != NULL ) pTask->Release();
if ( pSettings != NULL ) pSettings->Release();
if ( pRegInfo != NULL ) pRegInfo->Release();
if ( pPrincipal != NULL ) pPrincipal->Release();
if ( pSettings3 != NULL ) pSettings3->Release();
if ( pMaintenanceSettings != NULL ) pMaintenanceSettings->Release();
if ( pActionCollection != NULL ) pActionCollection->Release();
if ( pAction != NULL ) pAction->Release();
if ( pExecAction != NULL ) pExecAction->Release();
if ( pRegisteredTask != NULL ) pRegisteredTask->Release();
CoUninitialize();
return SUCCEEDED ( hr ) ? 0 : 1;
}
验证任务
验证任务是否已成功创建并作为维护的一部分运行。
验证任务创建
使用此命令行将任务定义导出到文件,并确保任务定义符合预期:
Schtasks.exe /Query /tn<task name> /xml <text file name>
验证任务执行
运行此命令行以启动任务,并验证任务计划程序 UI (taskschd.msc) 是否显示任务已运行:
Schtasks.exe /Run /tn<task name>
资源