升级安装程序时出现错误2769

时间:2015-08-04 05:54:23

标签: windows wix windows-installer wix3.8

我创建了一个安装程序,它将调用两个自定义操作。第一个自定义操作是WRITEFILETODISK,它将根据传递给安装程序的参数创建配置文件,并将其存储在%ProgramData%\SomeFolder中。第二个自定义操作是ResidueRemove,可以清除%ProgramData%\SomeFolder中的任何残留物。

以下是我的Wix文件中自定义操作的片段:

<Binary Id="SetupCA"  SourceFile="..\..\ext_library\SetupCACPP\SetupCACPP\bin\Release\SetupCACPP.dll"/>
<CustomAction Id="WRITEFILETODISK" Execute="immediate" BinaryKey="SetupCA" DllEntry="WriteFileToDisk" />
<CustomAction Id="ResidueRemove" Execute="immediate" BinaryKey="SetupCA" DllEntry="DeleteResidue" />
<InstallExecuteSequence>
  <Custom Action="WRITEFILETODISK" After="InstallFinalize">NOT Installed</Custom>
  <Custom Action="ResidueRemove" After="InstallFinalize">REMOVE~="ALL"</Custom>
  <!-- Rob Menshing Answer @ http://stackoverflow.com/a/321874/2634612 -->
</InstallExecuteSequence>

此处添加ResidueRemove自定义操作:

    UINT __stdcall DeleteResidue(MSIHANDLE hInstall)
{
    HRESULT hr = S_OK;
    UINT er = ERROR_SUCCESS;
    SC_HANDLE sHandleLPA, sHandleLPAM;
    SERVICE_STATUS servStatusLPA, servStatusLPAM;
    SHFILEOPSTRUCT shFileCommFolder, shFileInstalledFolder;
    LPWSTR lpaCommFolder, lpaInstalledFolder;
    std::wstring wlpaCommFolder, wlpaInstalledFolder;
    std::string errorCode;
    hr = WcaInitialize(hInstall, "DeleteResidue");
    ExitOnFailure(hr, "Failed to initialize");

    //Stop the LPA and LPA Monitor Service. Then delete the residue.
    WcaLog(LOGMSG_STANDARD, "Doing Delete Residue"); // Fails after printing this log

    hr = WcaGetProperty(L"LPCOMMAPPFOLDER",&lpaCommFolder);
    ExitOnFailure(hr, "Failure in Common Folder"); // Getting the %ProgramData%\SomeFolder here

    hr = WcaGetProperty(L"INSTALLFOLDER",&lpaInstalledFolder);
    ExitOnFailure(hr, "Failure in getting Installed Folder"); // Getting Installed Folder here

    //Path should be double Terminated for SHFILEOPSTRUCT.pFrom
    wlpaCommFolder = std::wstring(lpaCommFolder);
    wlpaCommFolder.push_back(L'\0');

    wlpaInstalledFolder = std::wstring(lpaInstalledFolder);
    wlpaInstalledFolder.push_back(L'\0');

    try
    {
        sHandleLPAM = OpenSCManager(NULL,NULL, SC_MANAGER_ALL_ACCESS);
        if(sHandleLPAM == NULL)
        {
            WcaLog(LOGMSG_STANDARD, "OpenSCManager NULL Handle");
        }
        sHandleLPAM = OpenService(sHandleLPAM, L"SomeServiceOne",SERVICE_ALL_ACCESS);
        if(sHandleLPAM == NULL)
        {
            WcaLog(LOGMSG_STANDARD, "OpenService NULL Handle");
        }
        bool res = ControlService(sHandleLPAM, SERVICE_CONTROL_STOP,(LPSERVICE_STATUS) &servStatusLPAM);
        if(!res)
        {
            WcaLog(LOGMSG_STANDARD, "ControlService Cannot Stop the service");
        }
        // Free the service Handler
        CloseServiceHandle(sHandleLPAM);
    }
    catch(std::exception& e)
    {
        WcaLog(LOGMSG_STANDARD, e.what());
    }

    try
    {
        sHandleLPA = OpenSCManager(NULL,NULL, SC_MANAGER_ALL_ACCESS);
        if(sHandleLPA == NULL)
        {
            WcaLog(LOGMSG_STANDARD, "OpenSCManager NULL Handle");
        }
        sHandleLPA = OpenService(sHandleLPA, L"SomeServiceTwo",SERVICE_ALL_ACCESS);
        if(sHandleLPA == NULL)
        {
            WcaLog(LOGMSG_STANDARD, "OpenService NULL Handle");
        }
        bool res = ControlService(sHandleLPA, SERVICE_CONTROL_STOP,(LPSERVICE_STATUS) &servStatusLPA);
        if(!res)
        {
            WcaLog(LOGMSG_STANDARD, "ControlService Cannot Stop the service");
        }
        // Free the service Handler
        CloseServiceHandle(sHandleLPA);
    }
    catch(std::exception& e)
    {
        WcaLog(LOGMSG_STANDARD, e.what());
    }

    ZeroMemory(&shFileCommFolder, sizeof(SHFILEOPSTRUCT));
    shFileCommFolder.hwnd = NULL;
    shFileCommFolder.wFunc = FO_DELETE;
    shFileCommFolder.pFrom = wlpaCommFolder.c_str();
    shFileCommFolder.fFlags = FOF_ALLOWUNDO | FOF_NOCONFIRMATION | FOF_NOERRORUI;
    BOOL res = DirectoryExists(lpaCommFolder);
    if(res)
    {
        WcaLog(LOGMSG_STANDARD, "The directory exist");
        int result = SHFileOperation(&shFileCommFolder);
        if(!result)
            WcaLog(LOGMSG_STANDARD, "The directory should have deleted by now");
        else
        {
            errorCode = GetLastErrorStdStr();
            WcaLog(LOGMSG_STANDARD, "The directory could not be deleted %s", errorCode);
        }

    }       
    else
    {
        WcaLog(LOGMSG_STANDARD, "It Seems the Installed Folder is No more there");
    }

    ZeroMemory(&shFileInstalledFolder, sizeof(SHFILEOPSTRUCT));
    shFileInstalledFolder.hwnd = NULL;
    shFileInstalledFolder.wFunc = FO_DELETE;
    shFileInstalledFolder.pFrom = wlpaInstalledFolder.c_str();
    shFileInstalledFolder.fFlags = FOF_ALLOWUNDO | FOF_NOCONFIRMATION | FOF_NOERRORUI;
    res = DirectoryExists(lpaInstalledFolder);
    if(res)
    {
        WcaLog(LOGMSG_STANDARD, "The directory exist");
        int result = SHFileOperation(&shFileInstalledFolder);
        if(!result)
            WcaLog(LOGMSG_STANDARD, "The directory should have deleted by now");
        else
        {
            errorCode = GetLastErrorStdStr();
            WcaLog(LOGMSG_STANDARD, "The directory could not be deleted %s", errorCode);
        }
    }       
    else
    {
        WcaLog(LOGMSG_STANDARD, "It Seems the Installed Folder is No more there");
    }


LExit:
    er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
    return WcaFinalize(er);
}

我在日志Delete Residue之后收到错误:

DeleteResidue:  Doing Delete Residue
MSI (s) (64:A4) [10:54:51:643]: Leaked MSIHANDLE (11) of type 790541 for thread 980
MSI (s) (64:A4) [10:54:51:643]: Note: 1: 2769 2: ResidueRemove 3: 1 
MSI (s) (64:A4) [10:54:51:643]: Note: 1: 2205 2:  3: Error 
MSI (s) (64:A4) [10:54:51:643]: Note: 1: 2228 2:  3: Error 4: SELECT `Message` FROM `Error` WHERE `Error` = 2769 
DEBUG: Error 2769:  Custom Action ResidueRemove did not close 1 MSIHANDLEs.
The installer has encountered an unexpected error installing this package. This may indicate a problem with this package. The error code is 2769. The arguments are: ResidueRemove, 1, 
Action ended 10:54:51: ResidueRemove. Return value 3.
Action ended 10:54:51: INSTALL. Return value 3.

在(非英语)德语Windows中完成安装程序升级时会复制此项。经过一些研究,我发现The InstallExecuteSequence may have been authored incorrectly. Actions that change the system must be sequenced between the InstallInitialize and InstallFinalize actions. Perform package validation and check for ICE77.这是否意味着我的InstallExecuteSequence错了?第一个CA WriteFileToDisk应该在安装之后和服务启动之前运行(因为可执行文件取决于文件)。第二个CA应该删除残留,在卸载完成后停止服务。我的代码中有什么问题?任何帮助表示赞赏。

1 个答案:

答案 0 :(得分:0)

首先,您尝试使用立即模式自定义操作更改系统状态。这是不可取的,并且违反Windows安装程序的最佳做法。它可能有效或可能无效(取决于您的环境)。 我的第一个建议是将这些操作更改为延迟模式自定义操作,因为您正在更改系统的状态。

我没有看到您的整个自定义操作代码,以了解您正在使用什么来停止服务并删除名为DeleteResidue的函数中的文件夹。我真的不知道你的自定义操作在什么条件下失败并返回失败。但是,我在下面说的可能会给你提示来解决你的问题。

我想你的Id = ResidueRemove自定义操作失败的原因是因为你试图在InstallFinalize之后立即停止模式自定义操作中的服务或清理文件夹。尝试停止这些服务时,系统中可能不再存在您的服务/文件夹。在执行清理已安装产品任务的InstallFinalize之前,有许多标准操作会执行。 这些标准操作先于您的即时模式自定义操作。当您使用Id = ResidueRemove执行自定义操作时,您的所有已安装文件都不会出现。它们都将被清除。您尝试清理的文件夹可能不再存在。

为了更好地理解Windows安装程序序列和不同的自定义操作类型,我建议阅读以下文章:

http://www.installsite.org/pages/en/isnews/200108/

以下是建议的更改 - 更改CustomAction Id =&#34; WRITEFILETODISK&#34;到延迟模式自定义操作。 安排它在标准操作之后运行:&#34; InstallFiles&#34;但在&#34; StartServices&#34;之前标准行动

如果要查看Windows安装程序标准操作的执行顺序,请安装名为&#34; Orca&#34;的Windows安装程序编辑器。从Windows安装程序SDK。检查&#34; InstallExecuteSequence&#34;的内容。表。按&#34;序列&#34;对动作进行排序。柱。 在这里,您将看到标准操作&#34; InstallFiles&#34;序列号低于标准动作:&#34; StartFiles&#34;。

现在,您将大致了解可能需要对行动进行排序的位置。

- 对于自定义操作=&#34; ResidueRemove&#34;,类似地,将此标记为延迟模式自定义操作。 检查&#34; InstallExecuteSequence&#34;表格,了解您可能想要对此进行排序的位置。

清理已安装文件和文件夹的标准操作是:RemoveFiles和RemoveFolders。 同样,还有一个用于删除名为DeleteServices的服务。

以下是您将如何使用它:

<!--For each deferred action where you want to access public property values, you need to use type 51 set a property custom action and sequence them in the InstallExecute sequence-->
  <!--The name of the property in the type 51 custom action should be the name of the **deferred mode** custom action where you want to access the value-->
  <!--The value of the type 51 custom action should be the value(s) you wan to access-->
  <!--In case, you need multiple property values to be accessed, seperate the values using a delimiter such as a semicolon-->
  <CustomAction Id="SetProperty_WRITEFILETODISK" Property="WRITEFILETODISK" Value="[LPCOMMAPPFOLDER];[INSTALLFOLDER]" Execute="immediate"/>
  <CustomAction Id="WRITEFILETODISK" Execute="deferred" BinaryKey="SetupCA" DllEntry="WriteFileToDisk" />

  <CustomAction Id="SetProperty_ResidueRemove" Property="ResidueRemove" Value="[LPCOMMAPPFOLDER];[INSTALLFOLDER]" Execute="immediate" />
  <CustomAction Id="ResidueRemove" Execute="deferred" BinaryKey="SetupCA" DllEntry="DeleteResidue" />

  <Property Id="LPCOMMAPPFOLDER" Secure="yes" />
  <Property Id="IPADDRESS" Secure="yes" />
  <Property Id="SSL" Secure="yes" />

  <CustomAction Id="SetCommonFolder" Property="CheckCommonFolder" Value="[LPCOMMAPPFOLDER]" />
  <CustomAction Id="SetIPExample" Property="CheckIP" Value="[IPADDRESS]" />
  <CustomAction Id="SetSSLExample" Property="CheckSSL" Value="[SSL]" />
  <InstallExecuteSequence>
    <Custom Action="SetCommonFolder" After="ValidateProductID" />
    <Custom Action="SetIPExample" After="ValidateProductID" />
    <Custom Action="SetSSLExample" After="ValidateProductID" />

    <Custom Action="SetProperty_WRITEFILETODISK" Before="WRITEFILETODISK">NOT Installed</Custom>
    <Custom Action="WRITEFILETODISK" Before="InstallServices">NOT Installed</Custom>
    <Custom Action="SetProperty_ResidueRemove" Before="ResidueRemove">REMOVE~="ALL"</Custom>
    <Custom Action="ResidueRemove" After="RemoveFiles">REMOVE~="ALL"</Custom>
    <!-- Rob Menshing Answer @ http://stackoverflow.com/a/321874/2634612 -->
  </InstallExecuteSequence>

此外,在您的自定义操作代码中,您将访问属性&#34; CustomActionData&#34;,而不是访问属性LPCOMMAPPFOLDER,INSTALLFOLDER。自定义操作数据的值将是您在类型51自定义操作中设置的值。

此外,我认为您不需要那些SetCommonFolder等自定义操作。他们服务的目的是什么?我们有我使用的属性标签。

希望这有帮助。