WcaUtil is a static library of convenience functions for writing custom actions in native C++. One of the more useful functions is WcaLog, which writes messages into the Windows Installer log. The first argument to WcaLog is the level of the message:

  • LOGMSG_TRACEONLY: Written to the log only in debug builds for debugging custom actions.
  • LOGMSG_VERBOSE: Written to the log only when verbose logging is enabled.
  • LOGMSG_STANDARD: Always written to the log.

WcaLog considers verbose logging enabled whenever any of the following is true:

  • LOGVERBOSE property: There’s a property in your package named LOGVERBOSE, regardless of its value.
  • MsiLogging property: There’s a property in your package named MsiLogging that contains a V character.
  • Logging policy: The logging policy is set and contains a V character.

Otherwise, messages tagged with LOGMSG_VERBOSE will be ignored.

The second argument is a printf-style format string so there are a variable number of arguments (zero or more) after it which specify the values referred to in the format string. For example:

WcaLog(LOGMSG_VERBOSE, "App: %S found running, %d processes, setting ‘%S’ property.", wzApplication, cProcessIds, wzProperty);

Note that WcaLog uses ANSI strings for the format string and its arguments, so if you want to log a Unicode string, you need to use the %ls or %S field characters.

Posted in WiX at July 15th, 2008. 13 Comments.

When you include deferred custom actions — that somehow modify the machine — in your setup, you have two big responsibilities:

  1. Provide rollback custom actions that "undo" what the deferred CAs do so that the installation transaction is actually transactional.
  2. Test.
  3. Test.
  4. Test.

OK, so numbers 2 through 4 are kinda the same but not really: Even a simple installation (say, without patching or upgrades) has three different scenarios you need to test when you have deferred/rollback custom actions:

  1. Installation rollback.
  2. Repair rollback.
  3. Uninstallation rollback.
  4. All of the above.

The right behavior for each kind of rollback is usually the opposite action. Rolling back installation is uninstallation. Rolling back uninstallation is installation. Rolling back repair is usually installation. Mixing installation, repair, and uninstallation is possible if your package has user-selectable features and users go into maintenance mode to turn on and off features. And, of course, it’s always an option from the msiexec.exe command line using the ADDLOCAL/ADDSOURCE/ADDDEFAULT, REMOVE, and REINSTALL properties.

Testing rollback means testing failure

Windows Installer initiates rollback when an action fails, so to test rollback you need to cause a failure. WiX includes an easy way to trigger failure: The WixFailWhenDeferred custom action, part of WixUtilExtension, triggers a failure when it’s executed. Include it in your package by referencing WixUtilExtension (in your Votive .wixproj or via the -ext switch to the light.exe command line) and adding a CustomActionRef to your package authoring:

<CustomActionRef Id="WixFailWhenDeferred" />

WixFailWhenDeferred automatically schedules itself in InstallExecuteSequence before InstallFinalize, with a condition of:

WIXFAILWHENDEFERRED=1

The condition means that you can have one package to test all the different possible combinations of "normal" installation and rollback. Just pass the WIXFAILWHENDEFERRED=1 property value on the msiexec.exe command line to trigger rollback. For example:

msiexec /qb- /i intermediate.msi /L*vx installfail.log WIXFAILWHENDEFERRED=1
msiexec /qb- /i intermediate.msi /L*vx install.log
msiexec /qb- /fvamus intermediate.msi /L*vx repairfail.log WIXFAILWHENDEFERRED=1
msiexec /qb- /fvamus intermediate.msi /L*vx repair.log
msiexec /qb- /x intermediate.msi /L*vx uninstallfail.log WIXFAILWHENDEFERRED=1
msiexec /qb- /x intermediate.msi /L*vx uninstall.log

WixFailWhenDeferred has been in WiX v3 weekly releases since April.

Posted in WiX, Windows Installer at June 18th, 2008. 5 Comments.

Google announced the release of Google Earth 4.3 today. Given the recent release of their WiX-based setup for the Google App Engine SDK, I had to give it a shot. (It helps that my day job also deals with 3-D terrain imagery.)

When you click the link to “Download Google Earth 4.3” (and accept the EULA), you download not Google Earth but “Google Updater.exe.” Run it and it starts downloading the Google Earth installer.

Personally, I much prefer to download the actual installers for the software I use. Some of it’s purely practical: I can stick it on a network and put it on multiple computers (EULA permitting, of course) without waiting for multiple downloads. Perhaps more importantly, for the paranoid among us, is the ability to virus-scan the installers. (Google Updater requests elevation, so it has admin rights to install multiple packages.)

Google Updater also runs as a startup app, optionally showing an icon in the increasingly-unusable system tray. (Yes, I know it’s technically the “notification area,” but come on, who calls it that?) I don’t think I need 24/7 instant access to software that doesn’t get updated that often. In fact, I’m sure I don’t.

Naturally, installing a packaging system with update capabilities is a boon to many users (and Google itself, of course, which has a nicely visible entry point to suggest additional apps for download). Apple does it with iTunes and Microsoft does it with Windows Live. Luckily, Google Updater has its own entry in Add/Remove Programs so you can remove it without impact to Google Earth.

Google Earth installer

The Google Earth installer is built with InstallShield, using its support for “dynamic file linking.” (If you haven’t used it, think of running Heat or Tallow with every build.) Interestingly, it uses the CAQuietExec custom action from WiX and has wixca.dll in the Binary table.

The .msi package fails ICE validation, with errors in ICE03, ICE15, ICE34, ICE38, ICE43, ICE44, ICE57, ICE64, and ICE99, and warnings in ICE45, ICE60, ICE82, ICE86, and ICE91.

There are 36 custom actions, some of which are mildly disturbing:

  • registerFlashSOL is a deferred, no-impersonate custom action that runs an included .exe with a “-install” command-line switch. Oh joy: self-reg.
  • InstallToolBarCA is an immediate custom action that runs an installed .exe. It’s not scheduled and is only available from the UI sequence – the one that’s suppressed because Google Updater runs the installation silently.
  • SetGEUserStats is another custom action run only from the suppressed – but still included – UI sequence.
  • SET_RES_READ_ONLY sets QtExecCmdLine to run the WiX CAQuietExec custom action. What is it hiding? It’s running attrib.exe to turn off the read-only attribute on a recursive set of files/directories. Doesn’t the File table let you control the read-only attribute? Yes, it does, but apparently not when you use InstallShield’s dynamic file linking to harvest a directory tree at build time. In a previous life, I used that functionality and turned off the read-only attributes in the build script before harvesting. Doing it as part of installation is bad karma on Google’s part.

Overall, it’s not a bad installer, but I hope Google cleans it up a bit before it loses the “beta” label.

Posted in Windows Installer at April 16th, 2008. 5 Comments.

I’ve had the good fortune to work on several agile software-development teams over the years. If you’ve ever hated having software you’ve developed sit on the shelf for months or years until the next release, shipping several times a year feels good. Shipping almost every month is even better. Shipping almost every week is phenomenal.

Most of the principles of the agile manifesto reinforce the idea that shipping software often is how to best deliver customer value. To ship often, you need predictability:

  • Finish all the work associated with a feature.
  • Make sure it’s of ship quality.

Done versus done-done

The waterfall model of designing, then coding, then throwing over the wall to test, then fixing bugs, then throwing over the other wall to setup–yes, of course I’m going to talk about setup–is just procrastinating. Sure, it’s more fun to start working on the next new thing, but it’s just postponing the work required to get a feature really done and ready to ship.

Setup development, like testing, is often considered one of the “taxes” of software development that is best put off as long as possible. But if you want to deliver shippable software on a rapid schedule, paying taxes late doesn’t work; you might get to April 15th and discover that you have more bugs than you can fix by midnight. Instead, you pay your taxes as you go along so there are no surprises when you get to the last day and you want to ship.

Rather than developers considering themselves “done” when they’ve written some code and debugged it, developers need to get “done done” by providing code, unit tests, acceptance tests, and setup.

WiX supports “done done” by easily integrating into the development build cycle. Every member of an agile team can author setup. That prevents the lone setup developer, who probably supports multiple product teams, from being a bottleneck.

Plus, if developers are responsible for their own setup authoring, they’re less likely to push bad design decisions onto the setup developer’s shoulders.

Keep quality high and take the weekends off

Agile methodologies bring a whole set of tools and processes to help keep quality high and ready to “ship on a dime,” like:

  • Test-driven development to produce high-quality designs and code
  • Automated acceptance testing to reduce the need for armies of testers to validate the whole system at the end

Add some setup flavor:

  • Make sure that “done” for every feature includes its setup authoring.
  • Run your automated acceptance tests from official setup packages, not from local builds.
  • Build upgrades and patches and create automated tests to ensure you can service the product.
  • Use only fully declarative custom actions–they make it easier to handle patching, upgrades, and repair.

Frequent, automated testing tells you if your code (and setup) do what they’re supposed to (or not–at least as far as the tests are concerned). Adding setup into the testing mix helps ensure that your setup is ready too.

Posted in WiX at February 13th, 2008. 3 Comments.

Reggie Burnett, creator and developer of MySQL’s Connector/NET ADO.NET provider, announced an alpha release of the MySQL Connector/NET. Among the new features:

Perfmon counters are now created using Wix code instead of a managed custom action. This seems to help the problem where some installation were failing becausing of being unable to register the counters.

Great job! Now, about the two remaining managed custom actions and the H2Reg CAs that don’t have rollback…

Posted in Windows Installer at February 11th, 2008. No Comments.

I was working on my current double-secret project at work and got a requirement that I knew would need a custom action. The requirement is to add entries to a configuration file without overwriting existing entries.

The configuration file (scenery.cfg, for FlightSim fans) is actually in .ini format. Naturally, I looked into using the standard IniFile table but it wasn’t sufficient for my needs. Scenery.cfg has entries that point to scenery data; each entry is numbered to control the priority of the scenery in rendering.

So, for example, given the following tail entries:

[Area.114]
Title=Propeller Objects
Local=Scenery\Props
Layer=114
Active=TRUE
Required=TRUE

[Area.115]
Title=Addon Scenery
Local=Addon Scenery
Layer=115
Active=TRUE
Required=FALSE

New entries would start out like this:

[Area.116]
Title=Test1
Local=MyScenery\Test1
Layer=116
Active=TRUE
Required=FALSE

Unfortunately, scenery.cfg is different among the various editions and languages of FlightSim, so the entry numbers aren’t static. (Also, users can add third-party scenery and we can’t overwrite those entries.) Though the IniFile table supports formatted strings, adding scenery entries would require string manipulation and math operators. While those might make for interesting MSI features, they don’t exist today.

So I started planning to write a deferred custom action and its matching rollback CA. Neither is too difficult — Win32 still supports API functions to add and remove .ini file entries. (Amusingly, the SDK doc groups them together with Registry Functions.)

Still, I’m a strong believer in not doing more work than necessary. (I prefer to think of it as efficiency rather than laziness.) MSI already handles the deferred and rollback aspects of modifying .ini files via the IniFile table and WriteIniValues and RemoveIniValues standard actions. If I could plug in dynamic data, I’d be able to write a simpler immediate CA rather than two more complicated deferred/rollback CAs.

Efficiency through transience

As you probably suspect by now, MSI has just such support. Immediate CAs can add temporary tables, rows, and columns to the active database. By adding temporary rows to standard tables, immediate CAs can determine at install time what data should be installed.

It’s important to note that temporary rows are…well…temporary. There’s not a whole lot of doc in the MSI SDK about temporary rows in general but one mention points out their temporary nature:

A custom action can be used to add rows to the Registry table during an installation, uninstallation, or repair transaction. These rows do not persist in the Registry table and the information is only available during the current transaction. The custom action must therefore be run in every installation, uninstallation, or repair transaction that requires the information in these additional rows. The custom action must come before the RemoveRegistryValues and WriteRegistryValues actions in the action sequence.

So that’s the recipe: An immediate CA runs, reads whatever data it needs from a custom table, using component states to decide what data should be written to temporary rows, and writes the rows (to the Registry or IniFile tables, for example). No deferred or rollback CAs are required, because MSI handles that.

WiX’s wcautil library makes this kind of CA easy to write. The WcaOpenExecuteView function executes a query and returns a view handle. WcaFetchRecord fetches the next record from the view. The WcaGetRecordInteger, WcaGetRecordString, and WcaGetRecordFormattedString functions get column values from the record. The best is WcaAddTempRecord which does all the work of adding a temporary row. It’s a fairly straightforward function but as no other CA in WiX uses it, here’s an example:

hr = WcaAddTempRecord(&hIniTableView, &hIniColumns,
// the table
L”IniFile”,
// the column number of the key we want “uniquified”
1,
// the number of columns we’re adding
8,
// primary key
L”AcesSceneryConfig”,
// FileName — always scenery.cfg
L”scenery.cfg”,
// DirProperty — set by AppSearch in extension .wixlib
L”ACESSCENERYCFGDIR”,
// Section — [Area.<n>]
pwzArea,
// Key
L”Title”,
// Value
wzTitle,
// Action
msidbIniFileActionAddLine,
// Component_
wzComponent);

The first two parameters are pointers to MSIHANDLEs. You initialize them to NULL and the first time you call WcaAddTempRecord, it initializes them. The first handle is to a view on the table you’re inserting into. The second is a handle to information about the columns in the table. By passing them in and out as arguments, you can let WcaAddTempRecord initialize them once then re-use the same handles. WcaAddTempRecord doesn’t know when you’re done adding records, so you’re responsible for calling MsiCloseHandle on both handles.

The fourth parameter is named uiUniquifyColumn and is the column number of a column that must have a unique value, like the primary key of a table. Yes, somebody made uniquify a verb; as far as I can tell, Rob’s to blame. Anyway, what uiUniquifyColumn does is add a semi-random value to the value you pass in as the value for the specified column. (Remember that column numbers start at one, not zero.) That ensures the temporary rows you’re adding won’t conflict with existing rows. As the rows are temporary, there’s no harm in using non-deterministic IDs.

The other parameters are straightforward: the table name, the number of columns, and the value of each column. Note that by default the number of columns you pass in must match the number of columns in the table. If you want to pass in fewer than the actual number of columns, you need to pass in your own view of the table, via a query that selects just the columns you’re interested in.

Risks

Because the data you’re adding is processed during the install transaction, then dropped, a bug in the CA could orphan the data those rows represent during uninstallation. The easiest way to mitigate this risk is to run the immediate CA during every transaction and use the install state and action state of a component to determine whether to write the temporary rows. You can write the temporary rows only when the component state is changing (i.e., being installed, being removed, being repaired). For example:

er = ::MsiGetComponentStateW(WcaGetInstallHandle(), wzComponent, &isInstalled, &isAction);
if (WcaIsInstalling(isInstalled, isAction) || WcaIsReInstalling(isInstalled, isAction) || WcaIsUninstalling(isInstalled, isAction))
{ … }

By triggering off any change in component state, the CA supports installation, uninstallation, repair, and also is smart enough to not do anything when the component isn’t being installed. That lets you avoid adding any conditions to the CA scheduling itself.

Extra credit

Naturally, a WiX compiler extension lets users easily author data into the custom table. Also, there’s no reason you have to limit yourself to standard tables: You can just easily add temporary rows to the XmlConfig table to have the WiX XmlConfig custom action modify XML files in ways you couldn’t with just XPath and formatted properties alone.

Posted in Windows Installer at July 1st, 2007. Comments Off.

Apple’s Safari browser is now available in public beta on Windows. A little spelunking shows that it uses Windows Installer packages and that they’re built with WiX. Sadly, they didn’t use WixUI.<g>

On a more serious-but-sad note, the packages have ICE validation errors (other than the typical ones), contain VBScript custom actions, and the main Safari package uses a custom action to install the Apple Software Updater package (instead of using a chainer). Already there’s a forum report of a 2738 error with the VBScript CA. And I guess the only way to report bugs is via forum posts…? It’s not clear. The “Report Bugs to Apple” command on the Help menu seems focused on rendering problems. Too bad. After all, Setup Development Is Just Development.

Posted in WiX at June 11th, 2007. 5 Comments.

As everyone knows, script custom actions are inherently evil. A security addition to Windows Installer 4.0 in Windows Vista means that script CAs are even more likely to fail; see Heath’s blog entry on the issue and Aaron’s follow-up.

But did you know that script CAs, evil that they are, nonetheless ship in Orca, MsiVal2, and even the WiX toolset? Shocking but true. The Internal Consistency Evaluators (ICEs) are implemented as custom actions in .cub files that are MSI databases with a vastly different schema you’re used to seeing. A couple of ICEs are written in VBScript so a misregistered VBScript engine will cause those ICEs to fail. As WiX v3 runs validation by default during linking (and on-demand using the Smoke tool), failed ICEs fail your setup build.

Of 98 ICEs in the version of Darice.cub in the Windows Vista SDK, only four are written in VBScript — yet that’s all it takes to fail your build.

If you run into the problem, check out the workaround Aaron offers before you take the drastic step of disabling validation.

Posted in Windows Installer at June 7th, 2007. 9 Comments.

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.

Posted in WiX at April 29th, 2007. 1 Comment.