我们已经使用WiX有一段时间了,尽管人们对它的易用性有一些抱怨,但它运行得相当不错。我想要的是有用的建议:
设置WiX项目(布局、引用、文件模式) 将WiX集成到解决方案中,并构建/发布流程 为新的安装和升级配置安装程序 任何你想分享的好的WiX技巧
我们已经使用WiX有一段时间了,尽管人们对它的易用性有一些抱怨,但它运行得相当不错。我想要的是有用的建议:
设置WiX项目(布局、引用、文件模式) 将WiX集成到解决方案中,并构建/发布流程 为新的安装和升级配置安装程序 任何你想分享的好的WiX技巧
当前回答
编辑对话框
编辑对话框的一个很好的功能是使用版本为4.0.1.7090(或更高)的SharpDevelop。在这个工具的帮助下,一个独立的对话框(来自WiX源的wxs文件,如installdirdg .wxs)可以在设计视图中打开、预览和编辑。
其他回答
使用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
Keep variables in a separate wxi include file. Enables re-use, variables are faster to find and (if needed) allows for easier manipulation by an external tool. Define Platform variables for x86 and x64 builds <!-- Product name as you want it to appear in Add/Remove Programs--> <?if $(var.Platform) = x64 ?> <?define ProductName = "Product Name (64 bit)" ?> <?define Win64 = "yes" ?> <?define PlatformProgramFilesFolder = "ProgramFiles64Folder" ?> <?else ?> <?define ProductName = "Product Name" ?> <?define Win64 = "no" ?> <?define PlatformProgramFilesFolder = "ProgramFilesFolder" ?> <?endif ?> Store the installation location in the registry, enabling upgrades to find the correct location. For example, if a user sets custom install directory. <Property Id="INSTALLLOCATION"> <RegistrySearch Id="RegistrySearch" Type="raw" Root="HKLM" Win64="$(var.Win64)" Key="Software\Company\Product" Name="InstallLocation" /> </Property> Note: WiX guru Rob Mensching has posted an excellent blog entry which goes into more detail and fixes an edge case when properties are set from the command line. Examples using 1. 2. and 3. <?include $(sys.CURRENTDIR)\Config.wxi?> <Product ... > <Package InstallerVersion="200" InstallPrivileges="elevated" InstallScope="perMachine" Platform="$(var.Platform)" Compressed="yes" Description="$(var.ProductName)" /> and <Directory Id="TARGETDIR" Name="SourceDir"> <Directory Id="$(var.PlatformProgramFilesFolder)"> <Directory Id="INSTALLLOCATION" Name="$(var.InstallName)"> The simplest approach is always do major upgrades, since it allows both new installs and upgrades in the single MSI. UpgradeCode is fixed to a unique Guid and will never change, unless we don't want to upgrade existing product. Note: In WiX 3.5 there is a new MajorUpgrade element which makes life even easier! Creating an icon in Add/Remove Programs <Icon Id="Company.ico" SourceFile="..\Tools\Company\Images\Company.ico" /> <Property Id="ARPPRODUCTICON" Value="Company.ico" /> <Property Id="ARPHELPLINK" Value="http://www.example.com/" /> On release builds we version our installers, copying the msi file to a deployment directory. An example of this using a wixproj target called from AfterBuild target: <Target Name="CopyToDeploy" Condition="'$(Configuration)' == 'Release'"> <!-- Note we append AssemblyFileVersion, changing MSI file name only works with Major Upgrades --> <Copy SourceFiles="$(OutputPath)$(OutputName).msi" DestinationFiles="..\Deploy\Setup\$(OutputName) $(AssemblyFileVersion)_$(Platform).msi" /> </Target> Use heat to harvest files with wildcard (*) Guid. Useful if you want to reuse WXS files across multiple projects (see my answer on multiple versions of the same product). For example, this batch file automatically harvests RoboHelp output. @echo off robocopy ..\WebHelp "%TEMP%\WebHelpTemp\WebHelp" /E /NP /PURGE /XD .svn "%WIX%bin\heat" dir "%TEMP%\WebHelp" -nologo -sfrag -suid -ag -srd -dir WebHelp -out WebHelp.wxs -cg WebHelpComponent -dr INSTALLLOCATION -var var.WebDeploySourceDir There's a bit going on, robocopy is stripping out Subversion working copy metadata before harvesting; the -dr root directory reference is set to our installation location rather than default TARGETDIR; -var is used to create a variable to specify the source directory (web deployment output). Easy way to include the product version in the welcome dialog title by using Strings.wxl for localization. (Credit: saschabeaumont. Added as this great tip is hidden in a comment) <WixLocalization Culture="en-US" xmlns="http://schemas.microsoft.com/wix/2006/localization"> <String Id="WelcomeDlgTitle">{\WixUI_Font_Bigger}Welcome to the [ProductName] [ProductVersion] Setup Wizard</String> </WixLocalization> Save yourself some pain and follow Wim Coehen's advice of one component per file. This also allows you to leave out (or wild-card *) the component GUID. Rob Mensching has a neat way to quickly track down problems in MSI log files by searching for value 3. Note the comments regarding internationalization. When adding conditional features, it's more intuitive to set the default feature level to 0 (disabled) and then set the condition level to your desired value. If you set the default feature level >= 1, the condition level has to be 0 to disable it, meaning the condition logic has to be the opposite to what you'd expect, which can be confusing :) <Feature Id="NewInstallFeature" Level="0" Description="New installation feature" Absent="allow"> <Condition Level="1">NOT UPGRADEFOUND</Condition> </Feature> <Feature Id="UpgradeFeature" Level="0" Description="Upgrade feature" Absent="allow"> <Condition Level="1">UPGRADEFOUND</Condition> </Feature>
从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”(区分大小写)。
当用户按下按钮时,他/她将出现标准的选择打印机对话框,并能够从那里打印。
修复ProgressDlg,使其正确显示。
我将安装程序的字体大小从8增加到10,以使字体在高分辨率显示器上更人性化、更实用。我用这个XML魔法做到这一点:
<UI Id="MyCustomUI">
<TextStyle Id="WixUI_Font_Normal" FaceName="Tahoma" Size="10" />
<TextStyle Id="WixUI_Font_Big" FaceName="Tahoma" Size="12" />
<TextStyle Id="WixUI_Font_Bigger" FaceName="Tahoma" Size="14" />
<TextStyle Id="WixUI_Font_Title" FaceName="Tahoma" Size="12" Bold="yes" />
<Property Id="DefaultUIFont" Value="WixUI_Font_Normal" />
</UI>
但这意味着ProgressDlg将无法正常显示。这是一个显示安装进度的程序,就在最后。ActionText被剪切,因此g和j等字母的下移不显示。在后处理Javascript中,通过调整Progressdialog中各种控件的大小和位置来解决这个问题。生成MSI后运行这个脚本:
var msiOpenDatabaseModeTransact = 1;
var filespec = WScript.Arguments(0);
var installer = new ActiveXObject("WindowsInstaller.Installer");
var database = installer.OpenDatabase(filespec, msiOpenDatabaseModeTransact);
// The text on the exit dialog is too close to the title. This
// step moves the text down from Y=70 to Y=90, about one line.
sql = "UPDATE `Control` SET `Control`.`Y` = '90' " +
"WHERE `Control`.`Dialog_`='ExitDialog' AND `Control`.`Control`='Description'";
view = database.OpenView(sql);
view.Execute();
view.Close();
// The progressbar is too close to the status text on the Progress dialog.
// This step moves the progressbar down from Y=115 to Y=118, about 1/3 line.
sql = "UPDATE `Control` SET `Control`.`Y` = '118' " +
"WHERE `Control`.`Dialog_`='ProgressDlg' AND `Control`.`Control`='ProgressBar'";
view = database.OpenView(sql);
view.Execute();
view.Close();
// The StatusLabel and ActionText controls are too short on the Progress dialog,
// which means the bottom of the text is cut off. This step
// increases the height from 10 to 16.
sql = "UPDATE `Control` SET `Control`.`Height` = '16' " +
"WHERE `Control`.`Dialog_`='ProgressDlg' AND `Control`.`Control`='StatusLabel'";
view = database.OpenView(sql);
view.Execute();
view.Close();
sql = "UPDATE `Control` SET `Control`.`Height` = '16' " +
"WHERE `Control`.`Dialog_`='ProgressDlg' AND `Control`.`Control`='ActionText'";
view = database.OpenView(sql);
view.Execute();
view.Close();
database.Commit();
使用Msi诊断日志记录获取详细的故障信息 msiexec /i包。msi /l*v c:\Package.log
包的地方。Msi是您的包的名称 c:\Package.log是你想要输出日志的地方
Msi错误码
Wix介绍视频 哦,随机Wix介绍视频“Mr. Wix”Rob Mensching是“概念性的大图片”有帮助。