You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在WIX中获取OriginalDatabase属性并传递给C++自定义动作计算MSI MD5

问题详情

介绍

我有一套用于MSI程序更新的代码库,更新逻辑基于MD5哈希实现:每次服务器推送更新时,代码会从数据库读取MSI的MD5值进行校验,不匹配则触发更新。但新用户首次安装时,由于用户可能修改MSI的存储路径和文件名导致哈希变动,我需要在运行时获取当前MSI的哈希值。

问题陈述

我正在使用WIX制作MSI安装包,希望从WIX中获取[Original Database]属性,传递给C++自定义动作DLL用于运行时生成MSI的MD5值。我需要在安装完成后执行该逻辑,因为数据库需要在安装完成、应用启动后才会完成初始化,因此我可以先将MD5存入临时文件,或在安装后阶段执行操作。我已查阅过相关资料,但都没有完全匹配我的需求,也没有合适的代码示例。

我目前遇到的核心问题如下:

  • 如何在WIX中获取对应路径并传递给C++自定义动作
  • 如何通过WIX将生成的MD5存入数据库,该需求优先级较低,如有解决方案也非常欢迎。

现有WIX代码

<?xml version="1.0"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi"
    xmlns:util="http://schemas.microsoft.com/wix/UtilExtension">

<Product Id="{}" UpgradeCode="{}" 
        Name="Secureone Agent" Version="1.0.0" Manufacturer="MJ Inc." Language="1033">

    <Package InstallerVersion="200" Compressed="yes" Comments="MJ Client Installer Package" Platform="x64"/>
    <Media Id="1" Cabinet="product.cab" EmbedCab="yes"/>

    <!-- Pre-Install checks -->

    <Condition Message="This application is only supported on Windows Vista, Windows Server 2008, or higher.">
        <![CDATA[Installed OR (VersionNT >= 600)]]>
    </Condition>

    <Condition Message="Setup can not be installed on x32 machine.">
      <![CDATA[Not VersionNT32]]>
    </Condition>

    
    <?define Platform=x64 ?>
    <?define BIN_PATH='.\bin' ?>
    <?if $(var.Platform) = x64 ?>
    <?define PlatformProgramFilesFolder = "ProgramFiles64Folder" ?>
    <?else ?>
    <?define PlatformProgramFilesFolder = "ProgramFilesFolder" ?>
    <?endif ?>

    
    <!-- License agreement -->
    <WixVariable Id="WixUILicenseRtf" Value=".\eula\eula-en.rtf" />

    <!--Application logo-->
    <Icon Id="Logo.ico" SourceFile=".\resources\logo.ico" />

    <!--Application logo for Control Panel-->
    <Property Id="ARPPRODUCTICON" Value="Logo.ico" />

    <!--Top Banner UI Logo-->
    <WixVariable Id="WixUIBannerBmp" Overridable="yes" Value=".\resources\TopBanner.jpg" />
    <!--Verticle Banner UI Logo-->
    <WixVariable Id="WixUIDialogBmp" Overridable="yes" Value=".\resources\BackgroundLogo.jpg" />
    
    <Directory Id="TARGETDIR" Name="SourceDir">
        <Directory Id="$(var.PlatformProgramFilesFolder)">
            <Directory Id="INSTALLDIR" Name="Secureone">
               <Component Id="ServiceConfigFile" Guid="{}">
                  <File Id="ServiceConfigFile" Source="$(var.BIN_PATH)\core.db"/>
               </Component>
               <Component Id="ServiceFile" Guid="{}">
                  <File Id="ServiceFile" Source="$(var.BIN_PATH)\lzsvc.exe"/>
<!-- Install service -->
                  <ServiceInstall Id="ServiceInstaller"
                                    Type="ownProcess"
                                    Name="LZAgentService"
                                    DisplayName="LZ Agent Service"
                                    Description="Service for LZ Agent"
                                    Start="auto"
                                    ErrorControl="normal"
                                    Account="LocalSystem">
                        <util:ServiceConfig 
                                            FirstFailureActionType="restart"
                                            SecondFailureActionType="restart"
                                            ThirdFailureActionType="none"
                                            ResetPeriodInDays="1"
                                            RestartServiceDelayInSeconds="0" />
                    </ServiceInstall>
                    <ServiceControl Id="Service_start" Start="install" Stop="both" Name="LZAgentService" Wait="no" />
                    <ServiceControl Id="Service_stop" Stop="both" Remove="uninstall" Name="LZAgentService" Wait="no" />
<!-- Install service end -->    
                </Component>
            </Directory>
        </Directory>
    </Directory>
    
    <UIRef Id="WixUI_Minimal"/>

    <Feature Id="DefaultFeature" Level="1">
        <ComponentRef Id="ServiceFile"/>
        <ComponentRef Id="ServiceConfigFile"/>
        <ComponentRef Id="ProductComponent" />
    </Feature>
    <InstallExecuteSequence>
        <Custom Action="RollbackCA" After="InstallFiles" />
        <Custom Action='UserAuth' After='InstallFiles'/>
    </InstallExecuteSequence>
</Product>

<Fragment>
    <ComponentGroup Id="ProductComponents" Directory="INSTALLDIR">
        <Component Id="ProductComponent" Guid="{B2C908AF-C09A-41D0-92D1-AC7BEAC8F68D}">
            <RemoveFile Id='lzsvc_log' On='uninstall' Name='lzsvc.log' />
            <RemoveFile Id='lzsvc.log.bak' On='uninstall' Name='lzsvc.log.bak' />
            <RemoveFile Id='lzcore_log' On='uninstall' Name='lzcore.log' />
         </Component>
    </ComponentGroup>
</Fragment>
   
<Fragment>
    <CustomAction Id='UserAuth'
                  BinaryKey='AuthBinary'
                  DllEntry='AuthenticateUser'
                  Execute='deferred'
                  Return='check'
                  Impersonate="yes"
                />
                
    <CustomAction Id='RollbackCA'
                      BinaryKey='AuthBinary'
                      DllEntry='RollbackInstall'
                      Execute='rollback'
                      Return='check'
                      Impersonate="yes"
                />

    <Binary Id='AuthBinary' SourceFile='$(var.BIN_PATH)\CustomAction.dll'/>
</Fragment>   
   
</Wix>
解决方案

1. 获取OriginalDatabase属性并传递给C++自定义动作

你当前用的是延迟执行的自定义动作,这类动作无法直接读取MSI的普通属性,需要通过CustomActionData传递参数,操作步骤如下:

  1. 新增一个即时执行的自定义动作,用于给延迟动作传递参数,添加到自定义动作的Fragment节点中:
<!-- 即时执行的参数传递动作 -->
<CustomAction Id="SetUserAuthParams" 
              Property="UserAuth" 
              Value="ORIGINALDATABASE=[OriginalDatabase];INSTALLDIR=[INSTALLDIR]" 
              Execute="immediate" />
  1. 修改安装执行序列,确保参数赋值动作在延迟动作之前执行,同时将逻辑执行节点放到安装完成后:
<InstallExecuteSequence>
    <Custom Action="RollbackCA" After="InstallFiles" />
    <Custom Action="SetUserAuthParams" Before="UserAuth" />
    <!-- 调整到InstallFinalize之后,确保安装流程完成后执行 -->
    <Custom Action='UserAuth' After='InstallFinalize'/>
</InstallExecuteSequence>
  1. 在C++自定义动作中读取传递的参数,示例代码如下:
#include <windows.h>
#include <msi.h>
#include <msiquery.h>

UINT __stdcall AuthenticateUser(MSIHANDLE hInstall)
{
    TCHAR szCustomActionData[2048] = {0};
    DWORD dwBufSize = sizeof(szCustomActionData) / sizeof(TCHAR);
    // 读取传递的CustomActionData
    UINT ret = MsiGetProperty(hInstall, TEXT("CustomActionData"), szCustomActionData, &dwBufSize);
    if (ret != ERROR_SUCCESS) return ret;

    TCHAR szMsiPath[MAX_PATH] = {0};
    TCHAR szInstallDir[MAX_PATH] = {0};
    // 解析CustomActionData中的键值对,提取ORIGINALDATABASE和INSTALLDIR的值
    // 用_tcstok按;分割,再按=拆分键和值即可
    // 拿到MSI路径后即可计算对应的MD5值
    // 计算完成后可以写入到INSTALLDIR下的临时文件,等待应用启动后读取入库

    return ERROR_SUCCESS;
}

2. 自定义动作直接写入数据库(可选)

如果需要在安装阶段直接将MD5写入数据库,你可以直接在C++自定义动作中引入对应数据库的操作SDK,连接安装目录下的core.db执行插入操作即可。注意要确保自定义动作的执行顺序在InstallFiles之后,保证数据库文件已经被释放到目标路径。


内容的提问来源于stack exchange,提问作者MadhurJ

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.23 17:06:04