向 ProgressBar 添加自定义操作

自定义操作可以将时间和进度信息添加到 ProgressBar 控件。 有关创建具有 ProgressBar 的操作显示对话框的详细信息,请参阅创作 ProgressBar 控件

请注意,必须向 Windows Installer 包添加两个自定义操作,以便准确地向 ProgressBar 报告时间和进度信息。 一个自定义操作必须是延迟的自定义操作。 当安装程序运行安装脚本时,此自定义操作应完成自定义安装,并将各个增量的数量发送到 ProgressBar 控件。 第二个自定义操作必须是立即执行的自定义操作,该操作会告知 ProgressBar 在安装的获取和脚本生成阶段要添加到总计数中的时钟周期数。

向 ProgressBar 添加自定义操作的步骤

  1. 确定自定义操作如何描述其进度。 例如,安装注册表项的自定义操作可能会在安装程序每次写入一个注册表项时显示进度消息并更新 ProgressBar

  2. 自定义操作每次更新都会以常量增量更改 ProgressBar 的长度。 指定或计算每个增量中的时钟周期数。 通常,ProgressBar 长度的一个时钟周期变化对应于一个字节的安装。 例如,如果安装程序在写入一个注册表项时安装大约 10000 字节,则可以指定一个增量中有 10000 个时钟周期。

  3. 指定或计算自定义操作添加到 ProgressBar 长度的时钟周期总数。 自定义操作添加的时钟周期数通常按以下方式计算:(时钟周期增量)x(项数)。 例如,如果自定义操作写入 10 个注册表项,则安装程序将安装大约 100000 个字节,因此安装程序必须将 ProgressBar 最终总长度的估计值增加 100000 个时钟周期。

    注意

    若要动态地计算该值,自定义操作必须包含一个在脚本生成期间立即执行的部分。 延迟执行自定义操作报告的时钟周期量必须等于立即执行操作添加到总时钟周期计数的时钟周期数。 如果不是这样,则 TimeRemaining 文本控件报告的剩余时间将不准确。

     

  4. 将自定义操作分为两个代码部分:在脚本生成阶段运行的部分和在安装执行阶段运行的部分。 可以使用两个文件执行此操作,也可以通过在安装程序的运行模式上设置条件来使用一个文件。 以下示例使用一个文件并检查安装状态。 根据安装程序是处于安装执行阶段还是脚本生成阶段,示例的各部分将有条件地运行。

  5. 在脚本生成期间运行的部分应将 ProgressBar 的最终总长度的估计值增加自定义操作中的总时钟周期数。 这是通过发送 ProgressAddition 进度消息来完成的。

  6. 在安装执行阶段运行的部分应设置消息文本和模板,以通知用户自定义操作正在执行的操作,并指导安装程序更新 ProgressBar 控件。 例如,通知安装程序将 ProgressBar 向前移动一个增量,并在每次更新时发送显式进度消息。 如果自定义操作正在安装某些内容,则此部分中通常会有一个循环。 每次通过此循环后,安装程序都可以安装一个引用项(如注册表项)并更新 ProgressBar 控件。

  7. 将立即执行自定义操作添加到 Windows Installer 包。 此自定义操作告知 ProgressBar 在安装的获取和脚本生成阶段要前进多少。 在以下示例中,源是通过编译示例代码创建的 DLL,目标是入口点 CAProgress。

  8. 将延迟执行自定义操作添加到 Windows Installer 程序包。 此自定义操作完成实际安装的步骤,并在安装程序运行安装脚本时通知 ProgressBar 在进度条上前进多少。 在以下示例中,源是通过编译示例代码创建的 DLL,目标是入口点 CAProgress。

  9. InstallExecuteSequence 表中将这两个自定义操作都安排在 InstallInitializeInstallFinalize 之间。 延迟的自定义操作应安排在立即执行自定义操作之后。 在执行脚本之前,安装程序不会运行延迟的自定义操作。

以下示例演示了如何将自定义操作添加到 ProgressBar。 这两个自定义操作的源都是通过编译示例代码创建的 DLL,这两个自定义操作的目标都是入口点 CAProgress。 此示例不会对系统进行任何实际更改,而是像安装 10 个引用项一样运行 ProgressBar,每个引用项的大小约为 10,000 字节。 每安装一个引用项,安装程序都会更新消息和 ProgressBar。

#include <windows.h>
#include <msiquery.h>
#pragma comment(lib, "msi.lib")

// Specify or calculate the number of ticks in an increment
// to the ProgressBar
const UINT iTickIncrement = 10000;
 
// Specify or calculate the total number of ticks the custom 
// action adds to the length of the ProgressBar
const UINT iNumberItems = 10;
const UINT iTotalTicks = iTickIncrement * iNumberItems;
 
UINT __stdcall CAProgress(MSIHANDLE hInstall)
{
    // Tell the installer to check the installation state and execute
    // the code needed during the rollback, acquisition, or
    // execution phases of the installation.
  
    if (MsiGetMode(hInstall,MSIRUNMODE_SCHEDULED) == TRUE)
    {
        PMSIHANDLE hActionRec = MsiCreateRecord(3);
        PMSIHANDLE hProgressRec = MsiCreateRecord(3);

        // Installer is executing the installation script. Set up a
        // record specifying appropriate templates and text for
        // messages that will inform the user about what the custom
        // action is doing. Tell the installer to use this template and 
        // text in progress messages.
 
        MsiRecordSetString(hActionRec, 1, TEXT("MyCustomAction"));
        MsiRecordSetString(hActionRec, 2, TEXT("Incrementing the Progress Bar..."));
        MsiRecordSetString(hActionRec, 3, TEXT("Incrementing tick [1] of [2]"));
        UINT iResult = MsiProcessMessage(hInstall, INSTALLMESSAGE_ACTIONSTART, hActionRec);
        if ((iResult == IDCANCEL))
            return ERROR_INSTALL_USEREXIT;
              
        // Tell the installer to use explicit progress messages.
        MsiRecordSetInteger(hProgressRec, 1, 1);
        MsiRecordSetInteger(hProgressRec, 2, 1);
        MsiRecordSetInteger(hProgressRec, 3, 0);
        iResult = MsiProcessMessage(hInstall, INSTALLMESSAGE_PROGRESS, hProgressRec);
        if ((iResult == IDCANCEL))
            return ERROR_INSTALL_USEREXIT;
              
        //Specify that an update of the progress bar's position in
        //this case means to move it forward by one increment.
        MsiRecordSetInteger(hProgressRec, 1, 2);
        MsiRecordSetInteger(hProgressRec, 2, iTickIncrement);
        MsiRecordSetInteger(hProgressRec, 3, 0);
 
        // The following loop sets up the record needed by the action
        // messages and tells the installer to send a message to update
        // the progress bar.

        MsiRecordSetInteger(hActionRec, 2, iTotalTicks);
       
        for( int i = 0; i < iTotalTicks; i+=iTickIncrement)
        {
            MsiRecordSetInteger(hActionRec, 1, i);

            iResult = MsiProcessMessage(hInstall, INSTALLMESSAGE_ACTIONDATA, hActionRec);
            if ((iResult == IDCANCEL))
                return ERROR_INSTALL_USEREXIT;
          
            iResult = MsiProcessMessage(hInstall, INSTALLMESSAGE_PROGRESS, hProgressRec);
            if ((iResult == IDCANCEL))
                return ERROR_INSTALL_USEREXIT;
   
            //A real custom action would have code here that does a part
            //of the installation. For this sample, code that installs
            //10 registry keys.
            Sleep(1000);
                    
        }
        return ERROR_SUCCESS;
    }
    else
    {
        // Installer is generating the installation script of the
        // custom action.
  
        // Tell the installer to increase the value of the final total
        // length of the progress bar by the total number of ticks in
        // the custom action.
        PMSIHANDLE hProgressRec = MsiCreateRecord(2);

         MsiRecordSetInteger(hProgressRec, 1, 3);
            MsiRecordSetInteger(hProgressRec, 2, iTotalTicks);
        UINT iResult = MsiProcessMessage(hInstall, INSTALLMESSAGE_PROGRESS, hProgressRec);
           if ((iResult == IDCANCEL))
            return ERROR_INSTALL_USEREXIT;     
        return ERROR_SUCCESS;
     }
}