New WiX feature: Setting package installation scope

For flexibility at deployment time, MSI uses the value of the ALLUSERS property in determining the type of an installation—whether it’s per-machine or per-user. That lets a network administrator easily control whether the package’s shortcuts (for example) show up for all users or just one. There’s also the it-looks-good-at-first idea of ALLUSERS=2, which theoretically lets a package adapt to either an admin or non-admin user.

Problem is, in the era of UAC, a genuine per-user package wouldn’t require a prompt for elevation. Unfortunately, most “per-user” packages still need admin rights, because they still install to per-machine locations, like ProgramFilesFolder on the file system and HKEY_LOCAL_MACHINE in the registry.

To avoid lots of mysteriously failing packages, MSI 4.0 and later require packages to opt in as not requiring UAC elevation prompts. In WiX, that’s expressed by setting the Package/@InstallPrivileges attribute to limited. Confusingly, the default for ALLUSERS is null, which means to install the package per-user, so to get a per-machine package, you have to remember to instead set ALLUSERS to 1.

It’s easy enough to forget that, so I added the InstallScope attribute to the Package element to centralize the per-machine/per-user choice:

perMachine
Set this value to declare that the package is a per-machine installation and requires elevated privileges to install. Sets the ALLUSERS property to 1.
perUser
Set this value to declare that the package is a per-user installation and does not require elevated privileges to install. Sets the package’s InstallPrivileges attribute to “limited.”

This attribute doesn’t deprecate InstallPrivileges and you can use both (though it’s redundant to do so). If you specify perMachine, you must remove any Property element that sets ALLUSERS.

New WiX feature: Internet shortcuts

A common request on wix-users is how to create shortcuts to a Web site. MSI’s Shortcut table and CreateShortcuts action don’t support targets that point to URLs like http://www.joyofsetup.com/. A shortcut’s target can point only to a feature (for advertised shortcuts) or a file or directory (for unadvertised shortcuts).

You can fake a shortcut to a URL by using the IniFile table to create a .url file. It’s a bit of a hack, given that the format of .url files isn’t explicitly documented and is therefore subject to change. (Granted, it’s not likely to change, because that would break a lot of existing applications.)

I needed URL shortcuts for Train Simulator so I wrote a custom action to create them. I then added authoring support to the WixUtilExtension compiler extension. With reviews from Heath and Peter, I checked in the work today. You’ll see it in the next weekly build of WiX v3.

The authoring is similar to, but simpler than, a standard shortcut. Here’s the attribute schema reference from WiX.chm:

Name Type Description Required
Id String Unique identifier in your installation package for this Internet shortcut. Yes
Directory String Identifier of the directory where the shortcut should be created. Yes
Name String The name of the shortcut file, which is visible to the user. (The .lnk extension is added automatically and by default, is not shown to the user.) Yes
Target String URL that should be opened when the user selects the shortcut. Windows opens the URL in the appropriate handler for the protocol specified in the URL. Note that this is a formatted field, so you can use [#fileId] syntax to refer to a file being installed (using the file: protocol). Yes

Here’s what a simple http shortcut looks like:

<util:InternetShortcut
Id=”Home”
Directory=”DesktopFolder”
Name=”Joy of Setup”
Target=”http://joyofsetup.com” />

You can also create shortcuts to resources using non-http protocols:

<util:InternetShortcut
Id=”ARP”
Directory=”ProgramMenuFolder”
Name=”ARP”
Target=”file://[%WINDIR]\Help\addremov.chm” />

Internet shortcuts are needed, among other reasons, for Vista’s Game Explorer. More on that in a future entry.

New WiX feature: DefaultCompressionLevel

Working with multigigabyte setups has taught me that compressing cabinets can be very time-consuming. Of course, it can also be vital — Flight Simulator X barely fit on two DVDs; without high compression, it would have required a third disc.

As I work on the WiX tooling and authoring for upcoming products, I knew I needed to support multiple compression levels: no/low compression for developer builds, “normal” MSZip compression for daily builds, and high compression for milestone builds.

By default, a Media element with no CompressionLevel attribute gets MSZip compression, which offers a nice compression-to-time ratio. A typical approach to control compression level outside the authoring is to pass in a preprocessor variable via the MSBuild project and use its value in Media/@CompressionLevel. That works but is kind of clunky.

So I decided to address the clunkiness by adding support to override the default MSZip compression level. Here are the two steps to take advantage of this feature:

  1. Author Media elements with a Cabinet attribute but without a CompressionLevel attribute.
  2. Pass -dcl:level on the Light command line, where level is none, low, medium, high, or mszip.
    –or–
    Declare the DefaultCompressionLevel property in your MSBuild project file, with a value of none, low, medium, high, or mszip.

There are no changes in behavior with existing authoring or build scripts. If you omit Media/@CompressionLevel and don’t use -dcl or DefaultCompressionLevel, you’ll continue to get MSZip compression.

This change will appear in the next weekly release of WiX v3.

New system information custom actions coming soon to WiX v3

The Windows Home Server folks have an SDK for extending the server with what they call “add-ins.” Their deployment sample is written with WiX. I love to see that, so I’m looking into what setup functionality would make a useful WHS extension for WiX. Right now, it doesn’t look like there’s much required — the requirements of a WHS add-in installer are pretty minimal over and above a good installer in general.

One thing they mention is detecting WHS if your add-in requires it. It’s not one of the standard Operating System Properties that MSI provides (yet, at least). The WHS SDK recommends calling the GetVersionEx API function. Clearly, it’s time for a custom action: Enter WixQueryOsInfo.

I wanted to follow the same model as MSI; that is, set a property if the OS running the package is of a particular edition or supports a particular feature. That lets you use them in conditions for features or components that only make sense when the user is running a supported OS or configuration.

As I’d be writing a CA to call GetVersionEx, I looked at what else it could detect that MSI didn’t already support. There were several, though sometimes the MSI naming convention didn’t match how GetVersionEx named them. So I decided to set properties for everything GetVersionEx offered using its naming convention. That way, it’s easy to mentally translate from the GetVersionEx doc to the properties WixQueryOsInfo sets.

Then, while I was in there, I added another CA to do something similar: WixQueryOsDirs sets properties for the “special folders” Windows defines for system and user directories. MSI already supports a long list of System Folder Properties but there are extra ones that Windows supports. For WixQueryOsDirs, I didn’t duplicate the ones MSI already supports.

Using WixQueryOsInfo and WixQueryOsDirs

The CAs are part of WixUtilExtension so you need to link with the “-ext WixUtilExtension” switch. Your WiX source must reference the CAs via their properties, to cause the linker to pull in the DLL and sequencing:

<Product …>
<PropertyRef Id=”WIX_SUITE_SINGLEUSERTS” />
<PropertyRef Id=”WIX_DIR_COMMON_DOCUMENTS” />
</Product>

Note that WixQueryOsInfo and WixQueryOsDirs both always set all the properties that apply, regardless of which you specify via PropertyRef. The PropertyRef just pulls in the CA, the DLL in the Binary table, and InstallExecuteSequence and InstallUISequence scheduling.

Look for OSInfo in the next weekly release of WiX v3.

Here’s the documentation page that’s part of WiX.chm:

OSInfo custom actions

The WixQueryOsInfo and WixQueryOsDirs custom actions in wixca (part of WixUtilExtension) set properties over and above the MSI set for OS product/suite detection and standard directories. Here’s a complete list:

WixQueryOsInfo properties

WIX_SUITE_BACKOFFICE

Equivalent to the OSVERSIONINFOEX VER_SUITE_BACKOFFICE flag.

WIX_SUITE_BLADE

Equivalent to the OSVERSIONINFOEX VER_SUITE_BLADE flag.

WIX_SUITE_COMMUNICATIONS

Equivalent to the OSVERSIONINFOEX VER_SUITE_COMMUNICATIONS flag.

WIX_SUITE_COMPUTE_SERVER

Equivalent to the OSVERSIONINFOEX VER_SUITE_COMPUTE_SERVER flag.

WIX_SUITE_DATACENTER

Equivalent to the OSVERSIONINFOEX VER_SUITE_DATACENTER flag.

WIX_SUITE_EMBEDDEDNT

Equivalent to the OSVERSIONINFOEX VER_SUITE_EMBEDDEDNT flag.

WIX_SUITE_EMBEDDED_RESTRICTED

Equivalent to the OSVERSIONINFOEX VER_SUITE_EMBEDDED_RESTRICTED flag.

WIX_SUITE_ENTERPRISE

Equivalent to the OSVERSIONINFOEX VER_SUITE_ENTERPRISE flag.

WIX_SUITE_MEDIACENTER

Equivalent to the GetSystemMetrics SM_SERVERR2 flag.

WIX_SUITE_PERSONAL

Equivalent to the OSVERSIONINFOEX VER_SUITE_PERSONAL flag.

WIX_SUITE_SECURITY_APPLIANCE

Equivalent to the OSVERSIONINFOEX VER_SUITE_SECURITY_APPLIANCE flag.

WIX_SUITE_SERVERR2

Equivalent to the GetSystemMetrics SM_SERVERR2 flag.

WIX_SUITE_SINGLEUSERTS

Equivalent to the OSVERSIONINFOEX VER_SUITE_SINGLEUSERTS flag.

WIX_SUITE_SMALLBUSINESS

Equivalent to the OSVERSIONINFOEX VER_SUITE_SMALLBUSINESS flag.

WIX_SUITE_SMALLBUSINESS_RESTRICTED

Equivalent to the OSVERSIONINFOEX VER_SUITE_SMALLBUSINESS_RESTRICTED flag.

WIX_SUITE_STARTER

Equivalent to the GetSystemMetrics SM_STARTER flag.

WIX_SUITE_STORAGE_SERVER

Equivalent to the OSVERSIONINFOEX VER_SUITE_STORAGE_SERVER flag.

WIX_SUITE_TABLETPC

Equivalent to the GetSystemMetrics SM_TABLETPC flag.

WIX_SUITE_TERMINAL

Equivalent to the OSVERSIONINFOEX VER_SUITE_TERMINAL flag.

WIX_SUITE_WH_SERVER

Windows Home Server. Equivalent to the OSVERSIONINFOEX VER_SUITE_WH_SERVER flag.

WixQueryOsDirs properties

WIX_DIR_ADMINTOOLS

Per-user administrative tools directory. Equivalent to the SHGetFolderPath CSIDL_ADMINTOOLS flag.

WIX_DIR_COMMON_ADMINTOOLS

All-users administrative tools directory. Equivalent to the SHGetFolderPath CSIDL_COMMON_ADMINTOOLS flag.

WIX_DIR_COMMON_DOCUMENTS

All-users documents directory. Equivalent to the SHGetFolderPath CSIDL_COMMON_DOCUMENTS flag.

WIX_DIR_COOKIES

Per-user Internet Explorer cookies directory. Equivalent to the SHGetFolderPath CSIDL_COOKIES flag.

WIX_DIR_HISTORY

Per-user Internet Explorer history directory. Equivalent to the SHGetFolderPath CSIDL_HISTORY flag.

WIX_DIR_INTERNET_CACHE

Per-user Internet Explorer cache directory. Equivalent to the SHGetFolderPath CSIDL_INTERNET_CACHE flag.

WIX_DIR_PERSONAL

Per-user documents directory. Equivalent to the SHGetFolderPath CSIDL_PERSONAL flag.

To use the WixQueryOsInfo and WixQueryOsDirs custom actions, add PropertyRef elements for the properties above you want to be set and add WixUtilExtension to your link options (a.k.a. the Light command line). For example:

<PropertyRef Id="WIX_SUITE_SINGLEUSERTS" />
<PropertyRef Id="WIX_DIR_COMMON_DOCUMENTS" />

WixUtilExtension automatically schedules the custom actions as needed after the AppSearch standard action.