Can't get Wix custom action to work in Votive/VS2010 - visual-studio

Help! I need to execute a managed custom action in my Wix 3.5 setup project and no matter what I've tried I can't get it to work.
I'm using the Votive integration in Visual Studio 2010. My Wix Product.wxs file is basically unchanged from the visual studio template except a few text changes:
<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<Product Id="666ffc07-90b2-4608-a9f0-a0cc879f2ad0" Name="Product Name" Language="1033" Version="5.5.0002" Manufacturer="TiGra Astronomy" UpgradeCode="d17a5991-b404-4095-9e93-08d2db984cfd">
<Package InstallerVersion="200" Compressed="yes" />
<Media Id="1" Cabinet="media1.cab" EmbedCab="yes" />
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="ProgramFilesFolder">
<Directory Id="INSTALLLOCATION" Name="Directory Name">
<!-- TODO: Remove the comments around this Component element and the ComponentRef below in order to add resources to this installer. -->
<!-- <Component Id="ProductComponent" Guid="3ea5ade7-9b7b-40da-9e83-13e066a000ef"> -->
<!-- TODO: Insert files, registry keys, and other resources here. -->
<!-- </Component> -->
</Directory>
</Directory>
</Directory>
<Feature Id="ProductFeature" Title="ASCOM Driver" Level="1">
<!-- TODO: Remove the comments around this ComponentRef element and the Component above in order to add resources to this installer. -->
<!-- <ComponentRef Id="ProductComponent" /> -->
<!-- Note: The following ComponentGroupRef is required to pull in generated authoring from project references. -->
<ComponentGroupRef Id="Product.Generated" />
</Feature>
</Product>
I have set a reference to my managed custom action project, set the HARVEST property to true. The project is called WIX.CustomActions and produces WIX.CustomActions.dll and WIX.CustomActions.CA.dll
I see that Wix is processing the reference during build and the WIX.CustomActions.dll assembly shows up in the Binary table in the final setup project, but the WIX.CustomActions.CA.dll does not.
I have a CustomActions.wxs that should package and invoke the custom action:
<?xml version="1.0" encoding="UTF-8" ?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<Fragment>
<Binary Id="DriverRegistrationCA" SourceFile="$(var.WIX.CustomActions.TargetDir)\$(var.WIX.CustomActions.TargetName).CA.dll" />
<CustomAction Id="RegisterDriver" BinaryKey="DriverRegistrationCA" DllEntry="RegisterAscomDriver" Execute="deferred" Return="check" />
<CustomAction Id="UnregisterDriver" BinaryKey="DriverRegistrationCA" DllEntry="UnregisterAscomDriver" Execute="immediate" Return="check" />
<InstallExecuteSequence>
<Custom Action="RegisterDriver" After="InstallFinalize" />
<Custom Action="UnregisterDriver" Before="RemoveFiles" />
</InstallExecuteSequence>
</Fragment>
</Wix>
I've looked at various 'howto' sources on the interweb and they are at best confusing, with contradictory advice. As I understand it, the WIX.CustomActions.CA.dll file is an unmanaged dll that loads the .NET framework and passes control to the 'real' managed custom action. However, the WIX.CustomActions.CA.dll does not get packaged in my MSI file. I've followed examples as best I can but I can't see what's wrong.
Please, has anyone got this working in Votive? Can you give me an actual working example?

You need a reference (e.g., CustomActionRef) from your product to the fragment; otherwise, it's discarded by the smart linker.

Following on from Bob Arnson's suggestion, I added the following two lines near the top of my Product.wxs file:
<CustomActionRef Id="RegisterDriver"/>
<CustomActionRef Id="UnregisterDriver"/>
That seems to have done the trick. Orca now shows that I have a Binary table, containing my CA dll, and a CustomAction entry in InstallExecuteSequence.
None of the examples I found on the web mentioned this requirement. I guess people were just recycling received wisdom with little or no understanding. So here is the answer, thanks to Bob!

Related

Problem with Wix uninstall using CustomAction

I've created a very simple MSI which copies some files to the ProgramFiles directory and while installing calling to custom actions found in a binary written in C#.
While installing, I can easily call any custom action I want. For example I've created an installation step where the user should enter a license, and after confirming the license it is checked against a server using logic written inside C# custom action.
But, when uninstalling, every time I add a custom action (even if it does nothing but returning Success), I get error that the installation failed.
This is how I use the uninstalling step:
<InstallExecuteSequence>
<Custom Action='TestUninstallation' After='MsiUnpublishAssemblies'>REMOVE="ALL"</Custom>
</InstallExecuteSequence>
where TestUninstallation is defined as following:
<CustomAction Id="TestUninstallation" Return="check" Execute="deferred" Impersonate="no" BinaryKey="TestCustomAction" DllEntry="Uninstall" />
The property DllEntry equals Uninstall which is a C# method which only returns Success.
After installation is completed, I'm trying to uninstall and I'm getting the UserExit dialog defined inside the AdminUISequence with the property OnExit.
Any idea what am I missing?
Debugging: Managed code is relatively easy to debug (native code is actually even easier). Here are some pointers:
Debug C# Custom Actions (Advanced Installer)
Different debugging methods / aspects
Suggestions: I think you just have a broken reference to the dll export function - in other words an erroneous dll function name / reference:
<CustomAction Id="TestUninstallation" Return="check" Execute="deferred" Impersonate="no"
BinaryKey="CustomActions" DllEntry="__ERRONEOUS FUNCTION REFERENCE__" />
Just check what the dll actually exports and match like this:
<CustomAction Id="CustomAction1" BinaryKey="CustomActions" DllEntry="CustomAction1"/>
As always the real McCoy is the check of the dll itself to see if you have the right function name (the below screen shot from this prior answer, recommended read).
This is a native code C++ dll:
This is a DTF-packaged managed code dll:
Notice that this is a native dll with the managed code stuff embedded. It yields a very different functions list, but you still have to find the function name in there that you refer to.
This is a straight-up managed code dll (no native wrapping):
And finally: this is the straight-up managed code DLL without being wrapped in a native dll shell.
Un-Uninstallable Setup: When a custom action crashes or fails during uninstallation, you will have problems getting rid of the installation (it just rolls-back and you are stuck with it installed). There are several fixes or workarounds.
The overall fix - in my view - is to not fail custom actions on uninstall, or at least condition them so you can force an uninstall by setting a property via the command line:
Set in MSI property table: SUPPRESSERROR = 0. Then - when needed - on the command line set:
msiexec.exe /x {PRODUCT-GUID} SUPPRESSERROR="1"
Inside the MSI you condition the uninstall custom action with:
REMOVE="ALL" AND SUPPRESSERROR="0"
Now the custom action will not run if SUPPRESSERROR is anything but 0.
There is an older answer with several further options: I screwed up, how can I uninstall my program? (courtesy of Wim Coenen, with me messing up his answer with more suggestions).
Boilerplate: For quick use, let me just dump a boilerplate ad-hoc custom action test project here. This assumes a C# managed code custom action project called "CustomAction1" in the same Visual Studio solution and a reference added to it in your WiX source - like you already have obviously (this is for later when we have all forgotten what the problem was and need to test again):
<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<Product Id="*" Name="WiXCustomActionsTesting" Language="1033" Version="1.0.0.0"
Manufacturer="test" UpgradeCode="PUT-GUID-HERE">
<Package InstallerVersion="200" Compressed="yes" InstallScope="perMachine" />
<UIRef Id="WixUI_Mondo" />
<Property Id="SUPPRESSERROR" Value="0" Secure="yes" />
<MajorUpgrade DowngradeErrorMessage="A newer version of [ProductName] is already installed." />
<MediaTemplate EmbedCab="yes" />
<Feature Id="ProductFeature" Title="WiXCustomActionsTesting" Level="1">
<ComponentGroupRef Id="ProductComponents" />
</Feature>
<!--BEGIN CUSTOM ACTION SECTION-->
<Binary Id="CustomActions" SourceFile="$(var.CustomAction1.TargetDir)\$(var.CustomAction1.TargetName).CA.dll" />
<CustomAction Id="TestUninstallation" Return="check" Execute="deferred" Impersonate="no" BinaryKey="CustomActions" DllEntry="CustomAction1" />
<InstallUISequence></InstallUISequence>
<InstallExecuteSequence>
<Custom Action='TestUninstallation' After='InstallInitialize'></Custom>
</InstallExecuteSequence>
<!--END CUSTOM ACTION SECTION-->
</Product>
<Fragment>
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="ProgramFilesFolder">
<Directory Id="INSTALLFOLDER" Name="WiXCustomActionsTesting" />
</Directory>
</Directory>
</Fragment>
<Fragment>
<ComponentGroup Id="ProductComponents" Directory="INSTALLFOLDER">
<Component>
<File Source="C:\Projects\MySetup\MyApp.exe">
</File>
</Component>
</ComponentGroup>
</Fragment>
</Wix>
Create WiX project
Copy paste the code, set a new Upgrade GUID
Create CustomAction project, default name
Add reference to custom action project from wix project
Add reference to WiXUIExtension.dll
Adjust path to file in component
Compile

Add user before running custom action in Wix

We have an install script in Wix, which contains Fragments, components and some custom actions:
<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi" xmlns:util='http://schemas.microsoft.com/wix/UtilExtension' >
<Product Id="*" Name="Installation" Language="1033" Version="1.0.0.0">
<Package InstallerVersion="200" Compressed="yes" InstallScope="perMachine">
</Package>
<MajorUpgrade DowngradeErrorMessage="A newer version of [ProductName] is already installed." />
<Media Id="1" Cabinet="cab1.cab" EmbedCab="yes" />
<Feature Id="ProductFeature" Title="MyInstallation" Level="1">
<ComponentGroupRef Id="ProductComponents" />
<ComponentGroupRef Id="MyComponents" />
</Feature>
<util:Group Id="Users" Name="Users"/>
<CustomAction Id="InstallMyService"
Directory="INSTALLFOLDER"
ExeCommand="[INSTALLFOLDER]bin\my-service.bat install"
Execute="deferred"
Impersonate="no"
Return="check"/>
<CustomAction Id="SetEnvironmentVariable" BinaryKey="ActionLib" DllEntry="SetEnvironmentVariableForNewUser" />
<Binary Id='ActionLib' SourceFile='..\InstallerActionLibrary\bin\Release\InstallerActionLibrary.CA.dll' />
<CustomAction Id="StartMyService"
Directory="INSTALLFOLDER"
ExeCommand="[INSTALLFOLDER]bin\my-service.bat start"
Execute="deferred"
Impersonate="no"
Return="asyncWait"/>
<InstallExecuteSequence>
<Custom Action="InstallMyService" After="InstallFiles"/>
<Custom Action="SetEnvironmentVariable" After="InstallMyService"/>
<Custom Action="StartMyService" After="SetEnvironmentVariable"/>
</InstallExecuteSequence>
</Product>
<Fragment>
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="CommonAppDataFolder">
<Directory Id="Company" Name="Company">
<Directory Id="App" Name="Product">
<Directory Id="INSTALLFOLDER" Name="Service" />
</Directory>
</Directory>
</Directory>
</Directory>
</Fragment>
<Fragment>
<ComponentGroup Id="ProductComponents" Directory="INSTALLFOLDER">
<Component Id="NewUser" Guid="{12345678-ABCD-1234-ABCD-987654321FED}">
<CreateFolder />
<util:User Id="CIUSER" CreateUser="yes" UpdateIfExists="no" Name="SERVICEUSER" PasswordNeverExpires="yes" Password="********">
<util:GroupRef Id="Users" />
</util:User>
</Component>
</ComponentGroup>
</Fragment>
</Wix>
However, one of the Custom Actions, SetEnvironmentVariable, needs to be run after the user has been set up, in the fragment at the bottom of the file. And this does not happen. SetEnvironmentVariable fails because it cannot find the user.
The sequence, given in InstallExecuteSequence begins after InstallFiles has occurred. I have tried to find a more appropriate place to start the sequence, using the list given here. I have tried PublishProduct and the result is the same.
Is there any way to get the user added before the custom action runs?
The suggestion from #zett42 was a good one. However, it still did not solve the problem.
The solution was the incorrect use of Execute="deferred". The execution of all the custom actions should have been set as:
Execute="commit"
From the Wix Documentation:
commit
Indicates that the custom action will run after successful completion of the installation script (at the end of the installation).
Changed the Execute type to commit and it worked.
I have no direct answer, but one that should help you to figure it out by yourself:
Open the MSI package using a tool like Orca (included in Win SDK) or InstEd (my personal preference).
Navigate to the InstallExecuteSequence table.
Lookup the record of the WiX custom action that creates the user and copy the Action identifier of that record.
Use that identifier for the After attribute of the element <Custom Action="SetEnvironmentVariable"/>.
I agree with #zett42.
There are some wix standard actions which should be checked in msi editor,
if you need to know when exactly they are being called. We normally don`t get much documentation for such actions.
Check if your msi has SchedSecureObjects_x64 or SchedSecureObjects custom action in msi. Schedule SetEnvironmentVariable custom action after that action.

Migrate Wix Project to version 4.0

I recently ugraded Wix to Version 4.0.
After updating the namespaces Visual Studio (2015) won't recognize these new ones.
<?xml version="1.0" encoding="utf-8"?>
<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
<Product Id="*" Name="_any_name" Language="1033" Version="1.0.0.0" Manufacturer="Anyone" UpgradeCode="8c568038-54cf-43ff-aa2c-581f4dd0aea0" Codepage="1252">
<Package InstallerVersion="200" Compressed="yes" InstallScope="perMachine" />
<MajorUpgrade DowngradeErrorMessage="A newer version of [ProductName] is already installed." />
<MediaTemplate EmbedCab="yes" />
<Feature Id="ProductFeature" Title="_any_title" Level="1">
<ComponentGroupRef Id="group_ProductComponents" />
</Feature>
<Property Id="pro_SetupExe" Value="INSTALLFOLDER" />
<CustomAction Id="ca_LaunchSetupExe" Property="pro_SetupExe" ExeCommand="/FORCE_HIDE_FIRST_RUN /UNATTENDED_INSTALL /AUTOACCEPT_ALL /FORCE_CLOSE_WHEN_DONE /ON_REBOOT_MESSAGE:”NO”" Execute="commit" />
<CustomAction Id="ca_SetSetupPath" Property="pro_SetupExe" Value="[INSTALLFOLDER]x64ATIDriver\setup.exe" />
<InstallExecuteSequence>
<Custom Action="ca_SetSetupPath" Before="ca_LaunchSetupExe" />
<Custom Action="ca_LaunchSetupExe" Before="InstallFinalize" />
</InstallExecuteSequence>
</Product>
<Fragment>
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="TempFolder">
<Directory Id="INSTALLFOLDER" />
</Directory>
</Directory>
</Fragment>
</Wix>
The Wix element has an incorrect namespace of 'http://wixtoolset.org/schemas/v4/wxs'. Please make the Wix element look like the following: Wix xmlns = "http://schemas.microsoft.com/wix/2006/wi"
I founde these: Migrate Wix Project to v4.0 instructions
But I don't get what this means:
Fix: Explicitly set absent Id attributes on File element to the Name attribute or filename from the Source attribute.
So currently I just have the kind of "crashed" project and can't build.
I'd really appreciate some help.
regards Muffex
The namespace error makes it sound like the project is still being built by WiX v3. Also, that migrate to v4 page that you found is for the WixCop tool which automates everything you found there. It's in the bin directory of the WiX installation directory.

Wix installed app (and shortcut) shows admin symbol

After successfully creating and testing an application I've also manually created the installer for this app using Wix instead the ClickOnce provided by VS.
Anyway, the installation is successful, places all the registry keys in correct locations, same for files where they need to be, and the shortcuts (and all is cleaned up afterwards).
The issue is not critical, I'm just really picky :D
On the main exe file that the Wix setup is installing, and on the shortcuts that points to this, they have the little blue and yellow admin shield on the bottom right of the icons. The application does not require admin permissions to work properly, nor does the application actually bring up the UAC or run as admin anyway (unless explicitly done through right-click > Run as admin).
The question is how do I prevent the shield from being applied to the application and shortcut icons?
<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi"
xmlns:netfx="http://schemas.microsoft.com/wix/NetFxExtension">
<?include "Macros.wxi" ?>
<!-- Installation Settings -->
<Product Id="*"
Name="$(var.NameApp)"
Language="1033"
Version="1.0.0.0"
Manufacturer="$(var.NameCompany)"
UpgradeCode="$(var.GUID_Upgrade)">
<Package InstallerVersion="200"
Compressed="yes"
InstallScope="perMachine"
Comments="Windows Installer Package"/>
<Media Id="1"
Cabinet="product.cab"
EmbedCab="yes"/>
<MajorUpgrade DowngradeErrorMessage="A newer version of this software is already installed" />
<!-- .NET Framework Check -->
<PropertyRef Id="NETFRAMEWORK40CLIENT" />
<Condition Message="This application requires .NET Framework 4.0. Please install the .NET Framework then try again">
<![CDATA[Installed OR NETFRAMEWORK40CLIENT]]>
</Condition>
<!-- Installation files, folders, reg-keys, shortcuts, etc -->
<Directory Id="TARGETDIR" Name="SourceDir">
<!-- Program Files Folder -->
<Directory Id="ProgramFilesFolder">
<!-- Company Application Folder -->
<Directory Id="INSTALLDIR" Name="$(var.NameCompany)">
<!-- Main Application Files -->
<Component Id="CmpAppMain" Guid="$(var.GUID_CmpAppMain)">
<File Id="FileAppMainEXE" Source="$(var.PathExe)" Vital="yes" />
<RegistryKey Root="HKLM"
Key="SOFTWARE\$(var.NameCompany)\$(var.NameApp)">
<RegistryValue Name="installed"
Type="integer"
Value="1"
KeyPath="yes" />
</RegistryKey>
</Component>
<!-- Common DLLs for multiple apps -->
<Component Id="CmpAppLibs" Guid="$(var.GUID_CmpAppLibs)">
<File Id="FileDeviceDLL" Source="$(var.PathLibDevice)" Vital="yes" />
<File Id="FileUtilDLL" Source="$(var.PathLibUtil)" Vital="yes"/>
<RemoveFile Id="FileClrDevice" Directory="INSTALLDIR" Name="Comms.log" On="uninstall"/>
<RegistryKey Root="HKLM"
Key="SOFTWARE\$(var.NameCompany)">
<RegistryValue Name="Lib Path"
Type="string"
Value="[INSTALLDIR]" />
<RegistryValue Name="Lib Ver"
Type="string"
Value="1.0.0"
KeyPath="yes" />
</RegistryKey>
</Component>
<!-- Common Resource Files -->
<Directory Id="FolderResource" Name="rsc">
<Component Id="CmpAppRsc" Guid="$(var.GUID_CmpAppRscs)">
<File Id="RscOilDb" Source="$(var.PathRscOil)" Vital="no" KeyPath="yes"/>
</Component>
</Directory>
</Directory>
<!-- END - Company Application Folder -->
</Directory>
<!-- END - Program Files Folder -->
<!-- Start Menu Folder -->
<Directory Id="ProgramMenuFolder">
<!-- Start Menu Company Folder -->
<Directory Id="ProgramMenuCompany" Name="$(var.NameCompany)">
<Component Id="CmpLnks" Guid="$(var.GUID_CmpLnks)">
<Shortcut Id="LnkStartMenu"
Name="$(var.NameApp)"
Description="$(var.NameApp)"
Target="[INSTALLDIR]$(var.NameExe)"
WorkingDirectory="INSTALLDIR">
<Icon Id="IconApp" SourceFile="$(var.PathRscIco)" />
</Shortcut>
<RegistryKey Root="HKCU"
Key="SOFTWARE\$(var.NameCompany)">
<RegistryValue Name="Lnk"
Type="integer"
Value="1"
KeyPath="yes" />
</RegistryKey>
<RemoveFolder Id="RemoveStartLnk" Directory="ProgramMenuCompany" On="uninstall" />
</Component>
</Directory>
<!-- END - Start Menu Company Folder -->
</Directory>
<!-- END - Start Menu Programs Folder -->
</Directory>
<!-- END - TARTGETDIR -->
<Feature Id="FeatCore" Title="Core Application Files" Level="1">
<ComponentRef Id="CmpAppMain" />
<ComponentRef Id="CmpAppLibs" />
<ComponentRef Id="CmpAppRsc" />
</Feature>
<Feature Id="FeatLnks" Title="Start Menu Shortcut" Level="1">
<ComponentRef Id="CmpLnks" />
</Feature>
</Product>
</Wix>
Does the app have a manifest at all? I'm wondering if it's got a
highestavailable or asInvoker setting that means that it might
sometimes elevate, and I'm assuming from what you said that it doesn't
have a requiresAdministrator setting there.
A manifest is nearly always embedded in the exe itself, that's what
needs verifying. I'm guessing that the exe is being built with an
embedded manifest. No need to include it in the install.
Thanks for the info. The issue was with the manifest which wasn't being generated in the first place due to ClickOnce settings, and then once I've generated or made my own manifest for the project it's also not being embedded into the executable.
ClickOnce publishing places it within the installation directory with the installer it generates for you. Because I didn't want to use click once (and I assumed after reading about the manifest it would be embedded in exe) my app didn't have a manifest...
The only thing I'm curious about now is why the default behavior is to ask for admin rights (I thought that would be the worst thing to do by default).
Anyway... Thanks for the help

Using WiX to create an IIS virtual directory

I'd ask this on the WiX mailing list, but it seems to be down.
I have an application which is both a desktop app and a web app which runs locally. I've created a couple of basic WiX installers, but haven't yet used the IIS extension to create a virtual directory under IIS. I haven't been able to find a simple example of how to do this. All I need to do is create the virtual directory, set its port, and point it at a real directory which I'm creating with the rest of the installer.
A bonus would be enabling IIS on the machine if it's not already enabled, but I'm guessing that's not possible, and isn't a dealbreaker for me anyway.
If it matters, this installer will only be run on Vista machines.
Since the article mentioned by David seems lost, here is an example. This also creates an application in the virtual directory.
<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi"
xmlns:iis="http://schemas.microsoft.com/wix/IIsExtension">
<Product Id="6f2b2358-YOUR-GUID-HERE-aa394e0a73a2" Name="WixProject" Language="1033" Version="1.0.0.0" Manufacturer="WixProject" UpgradeCode="225aa7b2-YOUR-GUID-HERE-110ef084dd72">
<Package InstallerVersion="200" Compressed="yes" />
<Media Id="1" Cabinet="media1.cab" EmbedCab="yes" />
<!-- Reference existing site at port 8080 -->
<iis:WebSite Id="My.Site" Description="My Site">
<iis:WebAddress Id="My.Web.Address" Port="8080"/>
</iis:WebSite>
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="ProgramFilesFolder">
<Directory Id="INSTALLLOCATION" Name="WixProject">
<Component Id="IIS.Component" Guid="{6FAD9EC7-YOUR-GUID-HERE-C8AF5F6F707F}" KeyPath="yes">
<iis:WebVirtualDir Id="My.VirtualDir" Alias="foo" Directory="INSTALLLOCATION" WebSite="My.Site">
<iis:WebApplication Id="My.Application1" Name="Web Application 1"/>
</iis:WebVirtualDir>
</Component>
</Directory>
</Directory>
</Directory>
<Feature Id="ProductFeature" Title="WixProject" Level="1">
<ComponentRef Id="IIS.Component" />
</Feature>
</Product>
</Wix>
Use iis:WebVirtualDir and iis:WebApplication from http://schemas.microsoft.com/wix/IIsExtension namespace.
I had a similar question earlier and I found the following article quite useful: Wix 3.0 Creating IIS Virtual Directory
Digging in the Google cache (which I think is now been purged by Google) I think the following is the code to the missing blog entry David Pokluda included in his answer. I had to do some reformatting to get this into SO, sorry if it's ugly.
<?xml version="1.0" encoding="UTF-8"?>
<!--
IMPORTANT
1. need to add the schema iis.xsd to the property window
2. add the following iis namespace
3. add the Visual Studio reference WixIIsExtenion
-->
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi"
xmlns:iis="http://schemas.microsoft.com/wix/IIsExtension">
<Product Id="7b523f47-YOUR-GUID-HERE-fea6be516471"
Name="Vince Wix 3 Web Service"
Language="1033"
Version="1.0.0.0"
Manufacturer="Vince LLC"
UpgradeCode="0a8c10df-YOUR-GUID-HERE-50b9ecdb0a41">
<Package InstallerVersion="200" Compressed="yes" />
<Media Id="1" Cabinet="WebAppWixProject.cab" EmbedCab="yes" />
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="ProgramFilesFolder">
<Directory Id="WebApplicationFolder" Name="MyWebApp">
<Component Id="ProductComponent" Guid="80b0ee2a-YOUR-GUID-HERE-33a23eb0588e">
<File Id="Default.aspx" Name="Default.aspx" Source="..\MyWebApp\Default.aspx" DiskId="1" />
<File Id="Default.aspx.cs" Name="Default.aspx.cs" Source="..\MyWebApp\Default.aspx.cs" DiskId="1"/>
<iis:WebVirtualDir Id="MyWebApp" Alias="MyWebApp" Directory="WebApplicationFolder" WebSite="DefaultWebSite">
<iis:WebApplication Id="TestWebApplication" Name="Test" />
</iis:WebVirtualDir>
</Component>
</Directory>
</Directory>
</Directory>
<!--
IMPORTANT
Add a virtual directory to an existing web site
If put it inside the Component, a new Web Site will be created and uninstall will remove it
-->
<iis:WebSite Id='DefaultWebSite' Description='Default Web Site' Directory='WebApplicationFolder'>
<iis:WebAddress Id="AllUnassigned" Port="80" />
</iis:WebSite>
<Feature Id="ProductFeature" Title="Vince Wix 3 Web Service" Level="1">
<ComponentRef Id="ProductComponent" />
</Feature>
</Product>
</Wix>
<!--
IMPORTANT
To get rid of light.exe location error, do the following on the Linker Tab:
Set culture to: en-US
Supress Schema Validation in the Advanced Button
-->
I'm not familiar with WiX, but both IIS 6 and 7 can be managed using WMI (Windows Management Instrumentation) objects. I've used both PowerShell and C# to create websites, virtual directories, permissions etc on IIS. You should be able to get your hands on these objects from most scripting environments.
The above snippets use the iis:WebAddress in an improper way. You need to add IP="*" if you want this to work with all websites that match the name and the port. The above example fails during the install if there is an ip address assigned to the website in IIS (wix CA will not find it in general)
Rant: wix is terrible for many reasons, in my opinion and this is a good example. If the attribute is missing it will only work for websites with the default IP - how unintuitive is this. Wix should at least emit a waring for a missing IP element. Furthermore the default IP (localhost) is represented as * in IIS metabase, at the same time in the wix file * means all websites (not only *). So it is really confusing and not intuitive at all.

Resources