我们已经使用WiX有一段时间了,尽管人们对它的易用性有一些抱怨,但它运行得相当不错。我想要的是有用的建议:

设置WiX项目(布局、引用、文件模式) 将WiX集成到解决方案中,并构建/发布流程 为新的安装和升级配置安装程序 任何你想分享的好的WiX技巧


当前回答

使用Javascript的CustomActions,因为它们非常简单

人们说Javascript是错误的东西用于MSI CustomActions。给出的原因:难以调试,难以使其可靠。我不同意。调试它并不难,当然不比c++难。就是不一样。我发现用Javascript写CustomActions超级简单,比用c++简单多了。快得多。而且同样可靠。

只有一个缺点:Javascript CustomActions可以通过Orca提取,而C/ c++ CA则需要逆向工程。如果您认为您的安装程序魔法是受保护的知识产权,那么您将希望避免使用脚本。

如果你使用脚本, 你只需要从一些结构开始。这里有一些让你开始。


CustomAction的Javascript“样板”代码:

//
// CustomActions.js 
// 
// Template for WIX Custom Actions written in Javascript.
// 
// 
// Mon, 23 Nov 2009  10:54
// 
// ===================================================================


// http://msdn.microsoft.com/en-us/library/sfw6660x(VS.85).aspx
var Buttons = {
        OkOnly           : 0,
        OkCancel         : 1,
        AbortRetryIgnore : 2,
        YesNoCancel      : 3
};

var Icons = {
        Critical         : 16,
        Question         : 32,
        Exclamation      : 48,
        Information      : 64
};

var MsgKind = {
        Error            : 0x01000000,
        Warning          : 0x02000000,
        User             : 0x03000000,
        Log              : 0x04000000
};

// http://msdn.microsoft.com/en-us/library/aa371254(VS.85).aspx
var MsiActionStatus = {
        None             : 0,
        Ok               : 1, // success
        Cancel           : 2,
        Abort            : 3,
        Retry            : 4, // aka suspend?
        Ignore           : 5  // skip remaining actions; this is not an error.
};


function MyCustomActionInJavascript_CA() {
    try {
        LogMessage("Hello from MyCustomActionInJavascript");
        // ...do work here...
        LogMessage("Goodbye from MyCustomActionInJavascript");
    }
    catch (exc1) {
        Session.Property("CA_EXCEPTION") = exc1.message ;
        LogException(exc1);
        return MsiActionStatus.Abort;
    }
    return MsiActionStatus.Ok;
}

// Pop a message box.  also spool a message into the MSI log, if it is enabled. 
function LogException(exc) {
    var record = Session.Installer.CreateRecord(0);
    record.StringData(0) = "CustomAction: Exception: 0x" + decimalToHexString(exc.number) + " : " + exc.message;
    Session.Message(MsgKind.Error + Icons.Critical + Buttons.btnOkOnly, record);
}


// spool an informational message into the MSI log, if it is enabled. 
function LogMessage(msg) {
    var record = Session.Installer.CreateRecord(0);
    record.StringData(0) = "CustomAction:: " + msg;
    Session.Message(MsgKind.Log, record);
}


// http://msdn.microsoft.com/en-us/library/d5fk67ky(VS.85).aspx
var WindowStyle = {
    Hidden : 0,
    Minimized : 1,
    Maximized : 2
};

// http://msdn.microsoft.com/en-us/library/314cz14s(v=VS.85).aspx
var OpenMode = {
    ForReading : 1,
    ForWriting : 2,
    ForAppending : 8
};

// http://msdn.microsoft.com/en-us/library/a72y2t1c(v=VS.85).aspx
var SpecialFolders = {
    WindowsFolder : 0, 
    SystemFolder :  1, 
    TemporaryFolder : 2
};

// Run a command via cmd.exe from within the MSI
function RunCmd(command)
{
    var wshell = new ActiveXObject("WScript.Shell");
    var fso = new ActiveXObject("Scripting.FileSystemObject");
    var tmpdir = fso.GetSpecialFolder(SpecialFolders.TemporaryFolder);
    var tmpFileName = fso.BuildPath(tmpdir, fso.GetTempName());

    LogMessage("shell.Run("+command+")");

    // use cmd.exe to redirect the output
    var rc = wshell.Run("%comspec% /c " + command + "> " + tmpFileName, WindowStyle.Hidden, true);
    LogMessage("shell.Run rc = "  + rc);

    // here, optionally parse the output of the command 
    if (parseOutput) {
        var textStream = fso.OpenTextFile(tmpFileName, OpenMode.ForReading);
        while (!textStream.AtEndOfStream) {
            var oneLine = textStream.ReadLine();
            var line = ParseOneLine(oneLine);
                ...
        }
        textStream.Close();
    }

    if (deleteOutput) {
        fso.DeleteFile(tmpFileName);
    }

    return {
        rc : rc,
        outputfile : (deleteOutput) ? null : tmpFileName
    };
}

然后,像这样注册自定义动作:

<Fragment>
  <Binary Id="IisScript_CA" SourceFile="CustomActions.js" />

  <CustomAction Id="CA.MyCustomAction"
              BinaryKey="IisScript_CA"
              JScriptCall="MyCustomActionInJavascript_CA"
              Execute="immediate"
              Return="check" />
</Fragmemt>

当然,您可以为多个自定义操作插入任意数量的Javascript函数。举个例子:我使用Javascript在IIS上进行WMI查询,以获得现有网站的列表,可以安装ISAPI过滤器。然后,该列表用于填充UI序列中稍后显示的列表框。一切都很简单。

在IIS7上,IIS没有WMI提供程序,因此我使用shell.Run()方法调用appcmd.exe来执行工作。一件容易的事。

相关问题:关于Javascript CustomActions

其他回答

检查是否安装IIS:

<Property Id="IIS_MAJOR_VERSION">
    <RegistrySearch Id="CheckIISVersion" Root="HKLM" Key="SOFTWARE\Microsoft\InetStp" Name="MajorVersion" Type="raw" />
</Property>

<Condition Message="IIS must be installed">
    Installed OR IIS_MAJOR_VERSION
</Condition>

检查Vista+上是否安装了iis6元数据库兼容性:

<Property Id="IIS_METABASE_COMPAT">
    <RegistrySearch Id="CheckIISMetabase" Root="HKLM" Key="SOFTWARE\Microsoft\InetStp\Components" Name="ADSICompatibility" Type="raw" />
</Property>

<Condition Message="IIS 6 Metabase Compatibility feature must be installed">
    Installed OR ((VersionNT &lt; 600) OR IIS_METABASE_COMPAT)
</Condition>

从Wix3.0及更高版本打印EULA

1)当你编译你的wix源代码时,light.exe必须在命令行引用WixUIExtension.dll。为此使用命令行开关-ext。

2)如果当你在WixUIExtension.dll中添加引用时,你的项目编译失败,这很可能是因为对话框id的冲突,即你的项目使用了与WixUIExtension.dll中一些标准对话框相同的对话框id,给你的对话框提供不同的id。这是一个很普遍的问题。

3)你的许可对话框必须有id为“LicenseText”的ScrollableText控件。Wix在打印时搜索该控件的名称。

<Control Id="LicenseText" Type="ScrollableText" X="20" Y="60" Width="330" Height="160" Sunken="yes" TabSkip="no">
    <Text SourceFile="License.rtf" />
</Control>

以及指向自定义动作的PushButton

<Control Type="PushButton" Id="PrintButton" Width="57" Height="17" X="19" Y="244" Text="Print">
    <Publish Event="DoAction" Value="PrintEula">1</Publish>
</Control>

4)使用Id="PrintEula"定义CustomAction,如下所示:

<CustomAction Id="PrintEula" BinaryKey="WixUIWixca" DllEntry="PrintEula" Return="ignore" Execute="immediate" />

注意:BinaryKey在Wix3.0和Wix2.0中是不同的,必须准确地为“WixUIWixca”(区分大小写)。

当用户按下按钮时,他/她将出现标准的选择打印机对话框,并能够从那里打印。

这是一个很好的结构,但根据我的经验,我想知道你如何解决这些情况:

答:你的安装似乎都落在了同一个目的地。如果用户需要同时安装所有3个版本,您的进程是否允许这样做。它们能明确地说出它们正在触发的每个可执行文件的哪个版本吗?

B.如何处理TEST和/或TRAINING中存在但LIVE中还没有的新文件?

使用Heat.exe砸碎脸和造成“史诗Pwnage”痛苦的大安装

扩展Si的和 Robert-P关于热的回答。 翻译: (使用heat可以避免手动将单个文件输入到项目中,并且可以自动化构建,从而使整个过程更容易。) 详细介绍WiX 2.0热语法

For newer versions (not all that different from older versions but there are potentially annoying syntax changes....) go to the directory Heat is in from the cmd.exe and just type in heat but I have a example one right here for help with newer versions if needed. Adding the following to your Build Event in visual studio 2010. (Right Click Project->Properties ->Build Events-> Pre-Build Events) $(WIX)bin\heat.exe" dir "$(EnviromentVariable)" -cg GroupVariable -gg -scom -sreg -sfrag - srd -dr INSTALLLOCATION -var env.LogicPath -out "$(FragmentDir)\FileName.wxs -gg Generates Guids when heat is run(as in when you execute the command above) -scom Dont grab "COM files" -sreg Dont grab "Registry Files" -sfrag Dont grab "Fragments" -srd Dont grab the "root Dir" dir dir indicates you want Heat to look in a folder "$(EnviromentVariable)" The name of the variable you would add to the Preprocessor variables in the (Right click project, Go to properties) project properties->Build section where it says Define preprocessor variables (assumes visual studio 2010) Example: EnviromentVariable=C:\Project\bin\Debug;No double quotes but end with a semicolon -cg GroupVariable The ComponentGroup that will be referenced from the fragment created to the main wxs file FragmentDir The fragment directory where the output wxs fragment will be stored FileName.wxs The the name of the file Full tutorial here, So freakin helpful Part 1 Part 2

创建WIX的自定义动作,用托管代码(c#)编写,不带Votive

http://www.codeproject.com/KB/install/wixcustomaction.aspx