1

语境:

所以这是一个安装旧软件的软件包。当我说老时,想想20岁。它很大,我们依赖它,我们正在编写替代品,但在准备好之前,我们将在接下来的几年里坚持使用这个旧应用程序。

问题:

msi 将通过SCCM推送,我们的 IT 部门坚持这意味着我们不能在此 msi 包中包含任何 UI 元素(例如浏览安装路径)。

该程序必须安装在:volume + ":\" + companyabbreviation。默认路径为 [WindowsVolume]company 缩写

我们还必须生成一些空文件夹以使程序正常运行,例如“ TRANSFER

为了实现这一点,在不被 ICE 警告淹没的情况下,wix xml 看起来像这样:

<Directory Id="TARGETDIR" Name="SourceDir">
    <Directory Id="VOLUME_WindowsVolume_companyabbreviation" Name="WindowsVolume_companyabbreviation">
        <Directory Id="Directory_0_1_0" Name="subfolderA">
            <Directory Id="Directory_0_2_1" Name="subsubfolderAA" />
        </Directory>
        <Directory Id="Directory_0_1_0" Name="subfolderB" /> 
        <!-- and a lot more directories -->
        <Directory Id="Directory_0_2_6" Name="TRANSFER" />
    </Directory>
</Directory>

<!-- Component groups -->
    <ComponentGroup Id="FEATUREID_Directory_0_2_6" Directory="VOLUME_WindowsVolume_companyabbreviation">
        <Component Id="INSTALL_Directory_0_2_6" Guid="4a9cd25e-4d66-4398-af52-356a3b48c337">
            <CreateFolder Directory="Directory_0_2_6" />
            <RemoveFile Id="PurgeCacheFolderDirectory_0_2_6" Name="*.*" On="uninstall" />
            <RemoveFolder Id="Directory_0_2_6" On="uninstall" />
        </Component>
    </ComponentGroup>
<!-- more Component Groups, components, features, etc.. -->

<SetDirectory Id="VOLUME_WindowsVolume_companyabbreviation" Value="[WindowsVolume]companyabbreviation" />

这按预期工作。

由于这是一个旧应用程序(从未打包而是通过自制安装程序分发),目标机器通常在获得 msi 之前进行某种“安装”。

因此,我从旧的自制安装中创建了一个用于清理的自定义操作。这也很好用。如果尝试安装,我们只需确保它仅在以前没有人通过 msi 安装过的系统上运行。

<Binary Id="CustomActionsId" SourceFile="../customActions/CA UPDATE FROM REGISTRY.CA.dll" />
<CustomAction Id="Launch_UPDATE_FROM_REGEDIT" BinaryKey="CustomActionsId" DllEntry="UPDATE_FROM_REGISTRY" Execute="immediate" Return="check" />
<InstallExecuteSequence>
     <Custom Action="Launch_UPDATE_FROM_REGEDIT" Before="CostFinalize">NOT Installed</Custom>
</InstallExecuteSequence>

现在我们应该准备好发布了:)

除了,我们刚刚发现了我的前任?很久以前就决定,他们会告诉客户如何将程序从默认位置移动......

看,开发人员可以使用多个安装,只需通过修改一组注册表项来选择运行哪一个。

因此,基本上,开发人员确保可以将应用程序移动到另一个驱动器,例如:D:\companyabbreviation并通过注册表编辑要启动的版本。要使用哪个TRANSFER文件夹,等等,等等……

好吧,您可能会争辩说,这需要很多分区,但在公司意识到 CVS、SVN、GIT 等之前的时间里,我猜这很有意义。

后来另一位开发人员看到了这个功能,作为解决一些客户缺乏管理权限的一种方法(所以你不能在 WindowsVolume Root 上安装),并决定旧的安装程序在存在时会尊重注册表项。如果key 不存在,他会让旧的安装程序创建它,并将值设置为默认路径。

因此,多年后,现在我们的 MSI 也需要尊重这一点,如果我们的任何用户移动了应用程序......(我们已经检查过,有些人这样做......)

我们的想法是使用自定义操作来更新路径:

我们做了一个辅助函数:

    public static ActionResult getCustomActionInfo(Session session, string WhereSourceStringContains, ref List<string> data)
    {
        var CustomActionsActions = session.Database.ExecuteQuery("SELECT Action from CustomAction");
        var CustomActionsTypes = session.Database.ExecuteQuery("SELECT Type from CustomAction");
        var CustomActionsSources = session.Database.ExecuteQuery("SELECT Source from CustomAction");
        var CustomActionsTargets = session.Database.ExecuteQuery("SELECT Target from CustomAction");

        session.Log("Retrieved " + CustomActionsTargets.Count.ToString() + " custom actions");
        int index = -1;
        for (int i = 0; i < CustomActionsTargets.Count; i++)
        {
            if (CustomActionsTypes[i].ToString().Trim() == "51" && CustomActionsSources[i].ToString().Contains(WhereSourceStringContains))
            {
                index = i;
                break;
            }
        }

        if (index == -1)
        {
            session.Log("Error could not locate the " + WhereSourceStringContains + " setDirectory customAction, cannot set a different installation target..");
            return ActionResult.SkipRemainingActions;
        }
        else
        {
            session.Log("row found with index: " + index.ToString() + "\r\n\t [action] => " + CustomActionsActions[index].ToString() + "\r\n\t [type]   => " + CustomActionsTypes[index].ToString() + "\r\n\t [source] => " + CustomActionsSources[index].ToString() + "\r\n\t [target] => " + CustomActionsTargets[index].ToString());
            data.Add(index.ToString());
            data.Add(CustomActionsActions[index].ToString());
            data.Add(CustomActionsTypes[index].ToString());
            data.Add(CustomActionsSources[index].ToString());
            data.Add(CustomActionsTargets[index].ToString());
        }

        return ActionResult.Success;
    }

和:

    public static void setCustomActionTarget(Session session, string ActionData, string TargetData)
    {
        session.Database.Execute("UPDATE CustomAction SET Target='" + TargetData + "' WHERE Action='" + ActionData + "'");
    }

然后这段代码使事情发生:

[CustomAction]
public static ActionResult UPDATE_FROM_REGISTRY(Session session)
{
    var view32 = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry32);
    RegistryKey ProductKey = view32.OpenSubKey(@"secretPath\To\Applications\Key");

    /* instDir is the previous location of the folder */
    string instDir = ProductKey.GetValue("instDir ").ToString().Trim();
    session.Log("discovered a instDir  key: " + instDir );
    string BaseDir = instDir .Substring(0, DataDir.ToUpper().LastIndexOf("\\")).ToUpper();

    List<string> PRODUCT = new List<string>();
    ActionResult test = getCustomActionInfo(session, "companyabbreviation", ref PRODUCT );
    if (test != ActionResult.Success) return test;
    string BaseTarget = SABITC[4];
    session.Log("BaseTarget: " + BaseTarget + " == BaseDir: " + BaseDir.Replace(@"C:\", "[WindowsVolume]") + " ?? ");
   if (BaseTarget.ToUpper() == BaseDir.Replace(@"C:\", "[WindowsVolume]").ToUpper())
    {
        session.Log("\tdefault directory was used for SABITC, do nothing to change that :D ");
    }
    else
    {
        session.Log("\tinstall at old location: " + BaseDir);
        setCustomActionTarget(session, SABITC[1], BaseDir);  //here throws exception

        //TODO: update "target" and instdir in msi registry entries, to ensure information is not overwritten by defaults..
     }
}

显然您不允许修改会话数据库,但是我们如何才能做到这一点呢?

如果可以避免的话,我对在 wix xml 中创建属性不感兴趣。- 原因是目前 xml 是自动生成的,因为我们的开发人员(主要是工程师)不需要学习 wix,就可以维护数学代码。

如果我们必须使用 xml 中定义的属性,我们将不得不以某种方式让自定义操作知道目录节点的 ID 是什么。

假设客户决定只移动提到的 TRANSFER 文件夹:可以这样实现:

<!-- information for the custom action -->
<Property Id="TRANSFER_ID" Value="Directory_0_2_6"/>
<!-- values below can be overwritten by the custom action:
     but unfortunately also this line also sets default paths,
     and in case of errors, it makes the directory tree above unreliable
     at least for humans that read it in the future.-->
<Property Id="Directory_0_2_6" Value="C:\companyabbrevation\TRANSFER\" />

为了完成这项工作,我们必须手动修改所有这些自动生成的 wix 文件,并将“默认”路径值设置为目录树隐式设置的值.. - 或者制作一些解析 xml 的东西,找到正确的目录 ID,然后添加属性节点。

也许如果我们可以在安装时动态地将属性添加到 MSI,我们仍然可以让它工作吗?

非功能性解决方案的要求摘要:

* change installation folder using information found in registry
* do not overwrite registry information for specific keys, if they are present.
4

0 回答 0