c# - 以编程方式构建 MSI

标签 c# visual-studio-2008 installation

我想创建一个基于多个参数创建 MSI 的 C# 程序。例如,根据用户设置,将包含某些文件或设置运行时参数。

任何人都可以指出任何可能有帮助的文档,或者给我一个想法,我可以从哪里开始这样的事情?

最佳答案

出于您描述的原因,我一直在使用 WixSharp (WiX#)。我发现 XML 驱动的安装程序太丑陋且不直观,无法处理,即将编辑 XML 文件作为构建安装程序的一种方式。我深入研究了基于 C# 的 WiX# 作为更平易近人的替代方案。就我而言,我需要生成 MSI 以在服务器上安装 .NET dll(不在 GAC 中),然后将其注册到 COM Interop。

有关 Wix# 的 CodePlex 主页的更多信息:http://wixsharp.codeplex.com/

对于 OP 描述的安装类型,“功能”的 MSI/WiX 概念似乎适用,例如,用户可以选中一个框来安装或省略某些功能(“功能”可以是一组文件/程序、一组注册表项等)

在 WiX# 安装程序中,您在 C# 安装程序代码的顶部声明一个带有“ID”的“功能”,例如二进制文件、文档、registryEntries 等,然后在声明其他安装程序组件时引用该功能 ID。 (请参阅 Wix# 安装附带的“示例”文件夹中的“AllInOne”Wix# 代码),

        Feature binaries = new Feature("MyApp Binaries");
        Feature docs = new Feature("MyApp Documentation");
                    ...
                    new File(binaries, @"AppFiles\MyApp.exe",
                        new FileShortcut(binaries, "MyApp", @"%ProgramMenu%\My Company\My Product"),
                        new FileShortcut(binaries, "MyApp", @"%Desktop%")),

请注意功能 ID“二进制文件”如何是声明 File 和 FileShortcut 组件的第一个参数,即“new File(binaries, ...) 并链接这些组件以包含在功能 ID“二进制文件”中。

此外,MSI 安装程序允许您在命令行上指定功能 ID,例如,

msiexec/i install.msi ADDLOCAL=二进制文件

另见此帖:WIX: How to Select Features From Command Line

虽然 WiX 有查看目标系统并确定要安装什么的方法,但我还没有看到看起来很直观的 Wix# 示例,除了使用自定义操作(Wix# 支持)的潜在异常(exception)。调整安装程序行为的一种方法目标系统将使用自定义操作来设置属性,如 Wix# 示例中的 Wix# 部署中的“条件安装”。
                //setting property to be used in install condition
                new Property("INSTALLDESKTOPSHORTCUT", "no"),
                new ManagedAction(@"MyAction", Return.ignore, When.Before, Step.LaunchConditions, Condition.NOT_Installed, Sequence.InstallUISequence));

在 Wix# 安装程序的前面链接回此代码:
                 new Dir(@"%Desktop%",
                    new ExeFileShortcut("MyApp", "[INSTALLDIR]MyApp.exe", "")
                    {
                        Condition = new Condition("INSTALLDESKTOPSHORTCUT=\"yes\"") //property based condition
                    }),

请注意,大括号中的“条件”设置为测试属性“INSTALLDESKTOPSHORTCUT”的值“yes”,该值设置为自定义操作的结果。自定义操作的 C# 代码类似于下面的示例。
public class CustomActions
{
    [CustomAction]
    public static ActionResult MyAction(Session session)
    {
        if (DialogResult.Yes == MessageBox.Show("Do you want to install desktop shortcut", "Installation", MessageBoxButtons.YesNo))
            session["INSTALLDESKTOPSHORTCUT"] = "yes";

        return ActionResult.Success;
    }
}

对于 C# 技能集,另一种方法是使用执行以下操作的 C# 程序前端 msi 安装程序:
  • 查看目标系统用户设置,确定哪些功能适合在该系统上安装。
  • 为所选功能组装和格式化 msiexec 命令行和功能参数。
  • 执行 MSIExec 命令行。

  • 可能还有其他“更简洁”的方法可以做到这一点,例如,涉及自定义操作等。但是,来自 C# 技能集,这种方法是平易近人的,可以帮助 Windows Installer/WiX 学习曲线。我发现,当我在 Wix# 安装程序上工作时,它帮助我逐步学习了一些 WiX 和 Windows 安装程序。我经常寻找如何在 WiX 中执行某个部署任务,然后将 Wix 方式转换为 Wix#,让 Wix# 在它生成的 .wxs 文件中生成所需的 XML。

    在我无法弄清楚如何让 Wix# 执行特定部署任务的情况下,我通常可以使用简单的方法,即在 Wix# 生成的 .wxs 文件中包含 WiX XML 语句,例如,通过手动编辑.wxs 文件。

    例如,WiX 和 Wix#,提供不是“支持”目的地之一的特定驱动器和路径的方式并不直观。我发现将这个 xml 片段添加到从 Wix# 生成的 .wxs 文件中,然后手动运行 Candle 和 Light,这对我有用。

    我记得运行过一些示例,这些示例说明如何让 Wix# C# 代码以编程方式将 XML 包含在 .wxs 语句中,然后编译 .wxs 以生成 msi。如果我再次找到 Wix# 示例,我将更新此答案。

    更新我确实找到了示例代码...在 Wix# 随附的 Wix# 示例文件夹中的 Wix# 代码示例中有几个示例。但是……它们并没有按照我希望的方式工作,并且涉及到我还不熟悉的其他 Wix# 类和对象。我想将 Wix# 输出作为 XML 文本处理,使用“标准”XML C# 工具集,而不是 Wix# 特定的类。因此,我尝试解析 .wxs 文档 XML,但未能成功使用 XPath 导航到我想要插入其他 WiX XML 语句的位置。 (也许我会发布一个 SO 问题以寻求帮助)。但是,我确实通过使用 WiX XML 的文本替换来实现我想要的,将其视为文本字符串。以下是我成功工作的内容。

    代码所做的是为 Compiler.WixSourceSaved 事件分配一个委托(delegate),并且该事件作为在 Wix# 代码中调用“Compiler.BuildMsi”方法时发生的事件的一部分而被触发。在读取创建的 .wxs 文件后(但在从 wxs 构建 MSI 文件之前),我更新文件中的文本以包含我想要包含的 XML 行。

    底层的 Wix# 事件序列然后继续并将修改后的 wxs 构建到最终的 MSI 中。
     // ...
    internal class Script
        static string myWIX_SET_DIRECTORY_STATEMENT = "    <SetDirectory Id=\"INSTALLDIR\" Value=\"D:\\Program Files\\DOL\\WA.DOL.HQSYS.ExecECL\" />";
        static string myWIX_INSERT_AFTER_TEXT = "    <InstallExecuteSequence >";
        public static void Main()
        {
        // ... (your other Wix# code goes here...)
            // Hook an event to Wix# save of .wxs file to post-process the .wxs
            Compiler.WixSourceSaved += PostProcessWxsXMLOutput;
    
            // Trigger the MSI file build
            Compiler.BuildMsi(project);
        }
    
        /// <summary>
        /// Post-process the Wix .wxs file before compiling it into an MSI
        /// </summary>
        /// <param name="wxsFileName"></param>
        private static void PostProcessWxsXMLOutput(string wxsFileName)
        {
            StreamReader sr = new StreamReader(wxsFileName);
            string myWixDocument = sr.ReadToEnd();
            sr.Close();
            string myProcessedWixDocument = WiXHelpers.InsertFragmentInWiXDocument(myWixDocument, myWIX_INSERT_AFTER_TEXT, myWIX_SET_DIRECTORY_STATEMENT);
            StreamWriter sw = new StreamWriter(wxsFileName);
            sw.Write(myProcessedWixDocument);
            sw.Close();
        }
    

    注意: .wxs 文件在 BuildMsi 完成后被删除。要强制保存生成的 .wxs 文件,您需要在 Wix# 代码的 Compiler.BuildMsi 行之后添加一行,如下所示:
    Compiler.BuildWxs(Project)
    

    这实际上是重新触发 WixSourceSaved 事件,然后调用我的 PostProcessXMLOutput 委托(delegate),重新生成 .wxs 文件的内容相同的新副本。这一次,wxs 文件不会被自动删除。生成的 wxs 文件的时间戳也将晚于构建中的相应 MSI 文件。

    关于c# - 以编程方式构建 MSI,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2898905/

    相关文章:

    .net - WiX 安装程序应始终以管理员身份运行

    c# - Encoding.UTF8.GetString 和 Encoding.UTF8.GetBytes 不是彼此相反的原因是什么?

    c# - 缺少枚举的最后一项

    windows - VB 打开浏览器窗口窗体应用程序

    windows - Inno Setup 中 AppData\LocalLow 的常量?

    python - 如何在 Windows 上导入 python26.zip

    c# - 通过套接字发送视频帧

    c# - 使用反射通过对象递归并打印树

    c# - 如何使用 c# 在 asp.net 中的数据库中在我的网页中显示我的 docx 文件数据?

    visual-studio-2008 - Visual Studio 中的深色主题使代码难以在电子邮件和其他笔记中阅读