以编程方式构建MSI

时间:2010-05-24 17:28:19

标签: c# visual-studio-2008 installation

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

有人能指出我可能会有所帮助的任何文件,或者让我知道我可能从哪个开始这样的事情?

4 个答案:

答案 0 :(得分:5)

我一直在使用WixSharp(WiX#),原因只是您描述的原因。我发现XML驱动的安装程序太丑陋而且不直观,即编辑XML文件作为构建安装程序的一种方式。我挖掘了基于C#的WiX#作为一种更平易近人的选择。在我的例子中,我需要生成一个MSI来在服务器上安装.NET DLL(不在GAC中),然后将其注册到COM Interop。

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

对于OP描述的安装类型,"功能"的MSI / WiX概念。会看到应用,例如,用户可以选中一个框来安装或省略某些功能(A"功能"可以是一组文件/程序,一组注册表条目等)

在WiX#安装程序中,您在C#安装程序代码的顶部声明了一个"功能"以及#34; ID",例如二进制文件,docs,registryEntries等,然后在声明其他安装程序组件时引用该功能ID。 (参见" AllInOne" Wix#代码在Wix#install附带的" Samples"文件夹中),

        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(二进制文件,...),并将这些组件链接到功能ID"二进制文件"。

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

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

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

虽然WiX有办法查看目标系统并确定要安装的内容,但我还没有看到Wix#示例看似直观,可能除了使用自定义操作(Wix#支持)。使安装程序的行为适应目标系统将使用自定义操作来设置属性,如Wix#sample," Conditional Installation"在Wix#deployment中。

                //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#installer中返回此代码的链接:

                 new Dir(@"%Desktop%",
                    new ExeFileShortcut("MyApp", "[INSTALLDIR]MyApp.exe", "")
                    {
                        Condition = new Condition("INSTALLDESKTOPSHORTCUT=\"yes\"") //property based condition
                    }),

请注意"条件"在花括号中设置为测试属性" INSTALLDESKTOPSHORTCUT"对于值"是",它被设置为自定义操作的结果。自定义操作的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命令行。

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

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

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

我记得跑步时遇到了一些如何获取Wix#C#代码以在.wxs语句中以编程方式包含XML,然后编译.wxs以生成msi的示例。如果我再次找到Wix#例子,我会更新这个答案。

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

代码的作用是将委托分配给Compiler.WixSourceSaved事件,并且当" Compiler.BuildMsi"" Compiler.BuildMsi"在Wix#代码中调用方法。在读取了创建的.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();
    }

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

Compiler.BuildWxs(Project)

这实际上做的是重新激活WixSourceSaved事件,然后调用我的PostProcessXMLOutput委托,该委托重新生成.wxs文件的新的,内容相同的副本。这次,wxs文件不会自动删除。生成的wxs文件也将具有比构建中的相应MSI文件更晚的时间戳。

答案 1 :(得分:4)

我会查看WiX。它有一个相当陡峭的学习曲线,但它会产生msi作为项目构建周期的一部分。

答案 2 :(得分:3)

查看WixSharp - 一组C#库,允许您用C#代码表示安装。然后将其转换为WiX(XML)文件,然后编译并链接以创建标准MSI(Windows Installer)文件。

答案 3 :(得分:2)

如果您真的想在代码中执行此操作,则需要查看Windows Installer API。但是,Wix构建了一个很好的托管工具集,可以使用XML语言更轻松地创建MSI。他们还在托管包装器中包装了许多Windows Installer API,但为了获得Windows Installer的全部功能,您需要查看API文档。