如何在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传递参数,操作步骤如下:
- 新增一个即时执行的自定义动作,用于给延迟动作传递参数,添加到自定义动作的Fragment节点中:
<!-- 即时执行的参数传递动作 --> <CustomAction Id="SetUserAuthParams" Property="UserAuth" Value="ORIGINALDATABASE=[OriginalDatabase];INSTALLDIR=[INSTALLDIR]" Execute="immediate" />
- 修改安装执行序列,确保参数赋值动作在延迟动作之前执行,同时将逻辑执行节点放到安装完成后:
<InstallExecuteSequence> <Custom Action="RollbackCA" After="InstallFiles" /> <Custom Action="SetUserAuthParams" Before="UserAuth" /> <!-- 调整到InstallFinalize之后,确保安装流程完成后执行 --> <Custom Action='UserAuth' After='InstallFinalize'/> </InstallExecuteSequence>
- 在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
相关产品推荐
相关产品推荐

