【问题标题】:How do I detect the currently installed features during a MajorUpgrade using WiX Burn MBA Bundles?如何在使用 WiX Burn MBA Bundles 的 MajorUpgrade 期间检测当前安装的功能?
【发布时间】:2013-07-07 08:10:52
【问题描述】:

我正在使用 WiX 3.7 的 Burn/Managed Bootstrapper Application 功能来创建基于 MBA 的自定义安装程序。对于我的包链中的每个包,在执行 MinorUpdate 时,我可以轻松检测哪些包功能已安装,以确保在升级期间通过使用引导程序的 WiX 基类中提供的这些事件来维护这些功能选择:DetectPackageCompleteDetectMsiFeatureDetectRelatedBundleDetectRelatedMsiPackageDetectComplete

但是,在 MajorUpgrade 期间,我只看到了一种确定安装了哪些软件包的方法,但没有看到如何确定安装了哪些功能,因为 DetectMsiFeature 事件不会触发。 我尝试在产品配置上使用MigrateFeatures 标志,但这似乎不起作用(或者我没有正确使用它)。

在 WiX 中使用自定义托管引导程序应用程序执行 MajorUpgrade 时,检测/迁移现有功能的正确方法是什么?


一些文件sn-ps:

注意:如果有帮助,我可以提供包含所有代码的完整工作 VS 解决方案。

捆绑.wxs:
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi" xmlns:bal="http://schemas.microsoft.com/wix/BalExtension">
    <Bundle Name="Bootstrapper1"  Version="1.1.0.0" Manufacturer="Knights Who Say Ni" UpgradeCode="e6fbf160-d1d9-4b38-b293-94d60eae876f" Compressed="yes">    
        <BootstrapperApplicationRef Id="ManagedBootstrapperApplicationHost" >
          <Payload SourceFile="$(var.ManagedBootstrapperApplication.TargetPath)" />
          <!-- other files here -->
        </BootstrapperApplicationRef>
        <Chain>      
          <PackageGroupRef Id="NetFx40Web" />
          <MsiPackage SourceFile="$(var.SetupProject1.TargetPath)" EnableFeatureSelection="yes" Vital="yes"  Compressed="yes" />
        </Chain>
    </Bundle>
</Wix>

产品.wxs:

<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
    <Product Id="*" Name="SetupProject1" Language="1033" Codepage="1252"
           Version="1.1.0.0" Manufacturer="Knights Who Say Ni" 
           UpgradeCode="5fcd463a-3287-4fdf-bf00-d5d74baeccda">

        <Package InstallerVersion="200" Compressed="yes" InstallScope="perMachine" />
        <MajorUpgrade AllowSameVersionUpgrades="no" AllowDowngrades="no" MigrateFeatures="yes" DowngradeErrorMessage="Bring me a shrubbery!" />
        <MediaTemplate EmbedCab="yes" />

        <Feature Id="feature_one" Title="Primary Feature" Level="1">      
          <Component Id="CMP_emptyFile1" Guid="{1740AFA6-A98F-482A-B319-A153AA1BEF10}" Directory="INSTALLFOLDER">
            <File Id="file_emptyFile1" Checksum="yes" KeyPath="yes" Source="TextFile1.txt" />
          </Component>      
        </Feature>
        <Feature Id="feature_Two" Title="Optional Feature" Level="2">
          <Component Id="CMP_emptyFile2" Guid="{F0831C98-AF35-4F5E-BE9A-2F5E3ECF893C}" Directory="INSTALLFOLDER">
            <File Id="file_emptyFile2" Checksum="yes" KeyPath="yes" Source="TextFile2.txt"  />
          </Component>
        </Feature>    
    </Product>
</Wix>

CustomBootstrapper.cs

public class CustomBootstrapperApplication : BootstrapperApplication {        
    protected override void Run() {
            DetectPackageComplete += HandlePackageDetected;
            DetectMsiFeature += HandleFeatureDetected;
            DetectRelatedBundle += HandleExistingBundleDetected;
            DetectRelatedMsiPackage += HandleExistingPackageDetected;
            DetectComplete += HandleDetectComplete;
            this.Engine.Detect();
            //blocks here until DetectComplete fires...
    }

    private void HandleExistingPackageDetected(object sender, DetectRelatedMsiPackageEventArgs e) {
        Log(string.Format("Detected Related Package {2} ({1}) at version {3} which is a {0}",
            e.Operation, e.PackageId, e.ProductCode, e.Version));
    }

    private void HandleExistingBundleDetected(object sender, DetectRelatedBundleEventArgs e) {
        Log(string.Format("Detected Related {2} Bundle {0} at version {1} which is a {3}",
            e.ProductCode, e.Version, e.RelationType, e.Operation));
    }

    private void HandleFeatureDetected(object sender, DetectMsiFeatureEventArgs e) {
        Log(string.Format("Feature {0} from Package {1} detected in state {2}",
            e.FeatureId, e.PackageId, e.State));
    }

    private void HandlePackageDetected(object sender, DetectPackageCompleteEventArgs e) {
        Log(string.Format("Package {0} Detected in State {1}",
            e.PackageId, e.State));
    }

    private void HandleDetectComplete(object sender, DetectCompleteEventArgs e)
    { /* release the main thread to continue with work */ }

}

升级输出:

请注意,该软件包和两个功能都安装在 v1.0.0 中,并且在状态 Absent 中检测到。已检测到相关包,但未包含任何功能详细信息。

检测到版本 1.0.0.0 的相关升级包 {5eff0a3c-4b0d-4fd9-875f-05117c07f373),这是一个 MajorUpgrade 在当前状态下检测到包 NetFx4OWeb 检测到版本 1.0.0.0 的相关包 {540AE32D-75C0-4BF3-A72D-ADBE97FSFF3E} (SetupProject1.msi),这是一个 MajorUpgrade 在状态 Absent 中检测到来自 Package SetupProjectl.msi 的功能 feature_one 包 SetupProjecti .msi 中的功能 feature_Two 在状态 Absent 中检测到 包 SetupProject1.msi 检测到状态缺失

【问题讨论】:

    标签: c# wix burn wix3.7


    【解决方案1】:

    我将Bob Arnson's response 标记为答案,因为它为我提供了推动这一进程所需的东西,但对于遇到这篇文章的其他人,我想我会提供更多关于如何收集的细节该功能状态使用 WiX 提供的 ProductInstallation 类(在 WiX SDK 中的 Microsoft.Deployment.WindowsInstaller.dll 程序集中找到),因此无需直接调用本机 MSI API。

    这是一个可以注册到DetectRelatedMsiPackage 事件的方法示例。请注意,您需要存储所收集的信息,以便在计划阶段设置适当的状态。

    private void DetectRelatedMsiPackageHandler(object sender, DetectRelatedMsiPackageEventArgs e)
    {
        var existingPackageProductCode = e.ProductCode;
        var actionToBeAppliedToExistingPackage = e.Operation;
        var existingPackageId = e.PackageId;
        var existingPackageVersion = e.Version;
    
        Log(string.Format("Detected existing related package {0} (product: {1}) at version {2}, which will be {3}",
                          existingPackageId, existingPackageProductCode, existingPackageVersion,
                          actionToBeAppliedToExistingPackage));
    
        if (actionToBeAppliedToExistingPackage == RelatedOperation.MajorUpgrade)
        {
    
            //requires reference to WiX Toolset\SDK\Microsoft.Deployment.WindowsInstaller.dll
            var installedPackage = new Microsoft.Deployment.WindowsInstaller.ProductInstallation(existingPackageProductCode);
            if (!installedPackage.IsInstalled) {
                Log(string.Format("Migrating Package {0}, which is not installed, so marking it and it's features as Absent", existingPackageId));
                //TODO: add logic to store state so that during Plan phase can set package with package with product code = existingPackageProductCode to PackageState.Absent
            } else {
                Log(string.Format("Migrating features for MajorUpgrade of Package {0}", existingPackageId));
    
                foreach (var currentInstallFeature in installedPackage.Features) {                        
                    if (currentInstallFeature.State == InstallState.Local) {
                        Log(string.Format("Migrating feature {1} of Package {0} - marking as Present", existingPackageId, currentInstallFeature.FeatureName));
                        //TODO: add logic to store state so that during Plan phase can set package and feature states based on this info
                    } else {
                        Log(string.Format("Migrating feature {1} of Package {0} - marking as Absent", existingPackageId, currentInstallFeature.FeatureName));
                        //TODO: add logic to store state so that during Plan phase can set package and feature states based on this info
                    }
                }
            }
        }
    }
    

    【讨论】:

    • 我为此苦苦挣扎了很长时间,然后才意识到您需要在捆绑包中包含 Microsoft.Deployment.WindowsInstaller.dll 作为有效负载。在我添加之前,我什至没有收到 DetectRelatedMsi 回调,并且尝试手动调用该 dll 中的方法导致线程似乎死了,甚至没有抛出异常。
    • @DaveAndersen 非常感谢您提供的其他信息。你救了我:)
    【解决方案2】:

    DetectMsiFeature 告诉您新包的功能状态;它没有安装,所以显然这些功能没有。 DetectRelatedMsiPackage 为您提供使用(本机)MSI API 函数 MsiEnumFeatures 和 MsiGetFeatureState/MsiGetFeatureUsage 查询已安装版本的功能状态所需的数据。

    【讨论】:

    • 谢谢鲍勃!由于 WiX SDK 提供了一些托管包装器来收集该数据,并且我更愿意让其他人(即:WiX 团队中的你们)处理本机 API 编组,我添加了一个单独的答案来详细说明该路线。将您的标记为正式答案,因为您引导我走上这条路;)
    • 我很少使用 DTF,所以我不知道 DTF 等价物的名称。 :)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-10-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多