WiX Wednesday Part 1.2: Running More Than One CAQuietExec Custom Action

Update: edited final example thanks to comments (changed first “after” to “before”)

Sometimes it is necessary to run a command line action from WiX in order to complete the configuration of the deployment. And though there is an extension for IIS called the WebAppPool Element, the features available do not completely cover all the functionality that you may want to configure in the installer. For example, you can set how often you want the app recycled in minutes, but you can’t define when you want it done (say at 5am). Defining the app to recycle every 1440 minutes isn’t the same, as you’re constricted to installing at the time you want the 24 hour refresh to occur.

Fortunately we can leverage the use of running command line via a custom action in our installer. You need a reference to WiXUtilExtension to do this, which helpfully is not written anywhere explicitly on the SourceForge page. The documentation for WiX shows us how to write a custom action to run the Quiet Execution CustomAction (qtexec custom action) that can run arbitrary command lines.

So now we can use the app.cmd.exe to configure IIS exactly how we like, once every 24 hours at 5am (keep an eye out for square bracket formatting, it will cause an ICE03 error).


<Property Id="QtExecCmdLine" Value="&quot;[WindowsFolder]system32\inetsrv\APPCMD.EXE&quot; set AppPool /apppool.name:ServiceHostAppPool /recycling.periodicRestart.time:00:00:00"/>
<CustomAction Id="QtExec" BinaryKey="wixca" DllEntry="CAQuietExec" Execute="immediate" Return="check"/>
<Binary Id="wixca" src="wixca.dll"/>

<InstallExecuteSequence>
<Custom Action="QtExec" After="InstallFinalize"/>
</InstallExecuteSequence>

But so far this command will only set the refresh in minutes to 0, which means switching it off totally. Now we need to write another command to set it to 5am. You’d think that writing something like what I have below would work:


<Property Id="QtExecCmdLine" Value="&quot;[WindowsFolder]system32\inetsrv\APPCMD.EXE&quot; set AppPool /apppool.name:ServiceHostAppPool /+recycling.periodicRestart.schedule.[\[]value='05:00:00'[\]]" />
<CustomAction Id="QtExec" BinaryKey="wixca" DllEntry="CAQuietExec" Execute="immediate" Return="check"/>
<Binary Id="wixca" src="wixca.dll"/>

<InstallExecuteSequence>
<Custom Action="QtExec" After="InstallFinalize"/>
</InstallExecuteSequence>

however you get all manner of errors:

confusedBuild

Consulting the documentation, it states that:

If you want to run more than one command line in the immediate sequence then you’ll need schedule QtExec multiple times and set the QtExecCmdLine property (using a type 51 custom action) right before you want each of them executed.

That’s not very clear….What you need to do to get this to work is to write a CustomAction with an unique Id, a unique Property, an execute time of “immediate” and your command. Then write another CustomAction with the ID set to the Property of your previously written CustomAction to link the two together.


<CustomAction
Id="RecyclingAppPoolsRestart_Cmd"
Property="RecyclingAppPoolsRestart"
Execute="immediate"
Value="&quot;[WindowsFolder]system32\inetsrv\APPCMD.EXE&quot; set AppPool /apppool.name:DefaultAppPool /recycling.periodicRestart.time:00:00:00" />

<CustomAction
Id="RecyclingAppPoolsSchedule_Cmd"
Property="RecyclingAppPoolsSchedule"
Execute="immediate"
Value="&quot;[WindowsFolder]system32\inetsrv\APPCMD.EXE&quot; set AppPool /apppool.name:DefaultAppPool/+recycling.periodicRestart.schedule.[\[]value='05:00:00'[\]]" />

<CustomAction
Id="RecyclingAppPoolsRestart"
BinaryKey="WixCA"
DllEntry="CAQuietExec"
Execute="deferred"
Return="check"
Impersonate="no"/>

<CustomAction
Id="RecyclingAppPoolsSchedule"
BinaryKey="WixCA"
DllEntry="CAQuietExec"
Execute="deferred"
Return="check"
Impersonate="no"/>

<InstallExecuteSequence>
<Custom Action="RecyclingAppPoolsRestart_Cmd" Before="InstallFinalize">NOT REMOVE</Custom>
<Custom Action="RecyclingAppPoolsSchedule_Cmd" After="RecyclingAppPoolsRestart_Cmd">NOT REMOVE</Custom>
<Custom Action="RecyclingAppPoolsRestart" After="RecyclingAppPoolsSchedule_Cmd">NOT REMOVE</Custom>
<Custom Action="RecyclingAppPoolsSchedule" After="RecyclingAppPoolsRestart">NOT REMOVE</Custom>
</InstallExecuteSequence>

The InstallSequence is then the commands followed by the referenced CustomActions.

Summary

Now we’re beginning to see why people complain that the documentation for WiX is not great. Maybe I’m being too harsh there, however we can see that there is no reference to the fact that we need to add a reference to WiXUtilExtension to get this to work and we’ve got a confusing explanation with no example! Plus when compared to running cmdline in MSBuild, it seems unnecessary convoluted process to get two lines of cmdline executed. And how much use is the WebAppPool element when it fails to adequately cover the work that we need done. But I cannot deny that it is possible to run app.cmd and leverage all the functionality that is available. Even then however, there were some things that happened in the MSI that did not occur when running the commands manually through cmdline: after every “:” I left a space when running manually, This is fine when running like this, but through when running the Installer it bombed out because the custom command did not like the space, something I was not aware of until deployment. So be warned that your executes will not be validated when compiling the WiX project.

Sources

  1. Download WiX from CodePlex here.
  2. As I said, I don’t think the manual for WiX is all that great. It feels like it’s been written by an expert in WiX for an expert in WiX. But I guess at least there is a manual.
  3. There’s also a very comprehensive tutorial written about WiX that explains it in far greater detail than I have here. It also provides plenty of examples. Such as in the advanced sections there is a tutorial on creating your own custom UI.
  4. I’ve probably short-changed msiexec. Aside from running msiexec /? in the cmdline, there’s plenty of examples on the TechNet site.
  5. There’s even a book available! I’ve not read it, however the reviews on amazon.com seem positive. For such an esoteric piece of software, and at a reasonable price it’s worth a go? Maybe I’ll buy it and review it at the end of this series….

Author: Richie Lee

Full time computer guy, part time runner. Full time Dad, part time blogger. Pokémon Nut. Writer of fractured sentences. Maker of the best damn macaroni cheese you've ever tasted.

6 thoughts on “WiX Wednesday Part 1.2: Running More Than One CAQuietExec Custom Action”

  1. Your sample code produces following linker errors:

    ICE77: RecyclingAppPoolsRestart is a in-script custom action. It must be sequenced in between the InstallInitialize action and the InstallFinalize action in the InstallExecuteSequence table.

    ICE77: RecyclingAppPoolsSchedule is a in-script custom action. It must be sequenced in between the InstallInitialize action and the InstallFinalize action in the InstallExecuteSequence table.

      1. You might want to try the “after” to “before” in the first custom action, or change the “installfinalise” to “installinitialise”. The “installexecutesequence” table shows the order in which actions are executed.

        Anyway will definitely get back to you tomorrow once I’ve looked over my installers.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s