自动维护

平台

客户端– Windows 8
服务器– Windows Server 2012

说明

Windows许多增值操作(包括 Windows 更新、自动磁盘碎片整理以及防病毒更新和扫描)依赖于收件箱和第三方维护活动的执行。 此外,企业经常使用网络访问保护 (NAP) 扫描等维护活动,以帮助在所有企业工作站上强制实施安全标准。

Windows维护活动旨在在后台运行,用户交互有限,对性能和能耗的影响最小。 但是,Windows 7 及更早版本中,由于 Windows 中多个维护活动的计划不确定且差异很大,性能和Windows。 当用户主动使用计算机时,维护活动运行时,用户的响应能力降低。 应用还经常要求用户更新其软件并运行后台维护,并引导用户获得多种体验,包括操作中心、控制面板、Windows 更新、任务计划程序 MMC 管理单元和第三方控件。

本自动维护是合并 Windows 中所有后台维护活动,并帮助第三方开发人员将其维护活动添加到 Windows,而不会对性能和能耗产生负面影响。 此外,自动维护使用户和企业能够控制维护活动的计划和配置。

关键问题

自动维护旨在解决以下任务中维护活动的Windows:

  • 截止时间计划
  • 资源利用率冲突
  • 节能
  • 用户的透明度

功能

自动维护提高空闲效率,并允许所有活动及时且优先地运行。 它还有助于实现统一的可见性和控制维护活动,并允许第三方开发人员将其维护活动添加到Windows而不会对性能和能耗产生负面影响。 为此,它提供完全自动模式、用户启动模式、自动停止、截止时间与通知以及企业控制。 下面介绍了每个示例。

完全自动模式

此默认模式在电脑空闲时间和计划时间启用智能计划- 无需任何用户干预即可执行和自动暂停维护活动。 用户可以设置每周或每日计划。 所有维护活动都是非交互式的,以无提示方式执行。

当系统不太可能使用时,计算机会自动从睡眠状态恢复,并遵循电源管理策略(对于笔记本电脑)默认允许仅在交流电源上唤醒。 高功率的完整系统资源用于尽快完成维护活动。 如果系统从睡眠状态恢复自动维护,则系统将请求重新进入睡眠状态。

与配置等活动相关的任何必需用户交互在执行自动维护执行。

用户启动的模式

如果用户需要为旅行做好准备、需要长时间使用电池电源,或者希望优化性能和响应能力,他们可以选择按需启动自动维护电源。 用户可以配置自动维护属性,包括自动运行计划。 他们可以查看当前执行状态自动维护,并根据需要自动维护停止运行。

自动停止

自动维护用户开始与计算机交互时,系统会自动停止当前正在运行的维护活动。 当系统恢复空闲状态时,维护活动将恢复。

备注

所有活动自动维护必须支持在 2 秒或更近的时间停止。 应通知用户活动已停止。

截止时间与通知

必须在预定义的时间范围内执行关键维护活动。 如果关键任务未能在其指定时间内运行,自动维护将在下一个可用的系统空闲机会自动开始执行。 但是,如果任务状态仍滞后于截止时间,自动维护通知用户有关活动,并提供手动运行活动自动维护。 计划进行维护的所有任务都将运行,尽管最缺少的任务优先。 此活动可能会影响系统响应和性能;因此,自动维护将通知用户关键维护活动正在执行。

Enterprise控件

EnterpriseIT 专业人员应该能够确定何时自动维护其 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 notepad.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 notepad.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>

资源