I'm using the project reference feature of WiX to harvest a project automatically using Heat. This is particularly useful since the WiX installer is being built both locally and on a TFS2010 build server, and when it's built on the build server the output is redirected to a different location meaning that if I don't automatically harvest the projects, it gets very messy trying to reference the correct location for recently compiled items.
I have the following WiX "code" to install and start the service:
<ServiceInstall Id="MyService"
Type="ownProcess"
Vital="yes"
Name="MyServiceName"
DisplayName="My Service Display Name"
Description="My Service Description"
Start="auto"
Account="[SERVICEACCOUNT]"
Password="[SERVICEPASSWORD]"
ErrorControl="ignore"
Interactive="no" />
<ServiceControl Id="StartService" Name="MyServiceName" Start="install" Wait="no" />
<ServiceControl Id="StopService" Name="MyServiceName" Stop="both" Wait="yes" Remove="uninstall" />
So far, so good... I get a problem when the installer tries to install and start the Windows Services however saying "Service 'MyServiceName'(MyServiceName) failed to start. Verify that you have sufficient privileges to start system services". If I choose the "Ignore" button, the installation completes "successfully", but when I check the services installed on my machine, the new service isn't listed.
From my investigations online, I believe that the problem is that the service isn't actually getting installed correctly because I need to set the KeyPath to the executable that should be run as the service, but since I am harvesting the files using Heat, I can't find a way to do this... unless I create a custom action which will install the service for me allowing me to specify the executable name once all the files have been installed... but that doesn't sound like it should be the right solution...
Does anyone have any advice or have they encountered the same problem and come up with a solution?
Thanks
UPDATE 07/10/10: In my WiX script, I have the following:
<Directory Id="INSTALLLOCATION" Name="Dolphin Transfer Service Server" ComponentGuidGenerationSeed="AF89976D-CD66-4b94-911B-1D27F969BC14">
<Component Id="ServiceComponent" Guid="F55415F7-803C-4a83-A677-C0F882699374">
<ServiceInstall Id="DolphinTransferService" Type="ownProcess"...
and the target directory for my harvested files is the INSTALLLOCATION directory.
Looking at the msi using Orca, I can see my ServiceComponent and all the generated components for each harvested file. Looking in the File table, there are no files associated with this component (since they have a component generated for each file...). Looking in the ServiceInstall table, the component that it is trying to install is ServiceComponent.
So I think that I need to somehow get the ServiceInstall element to be inside the component that is generated for the service exe so that it installs this component as a service and not the empty "ServiceComponent" component? But since this component is generated at build time by heat I've not managed to make any further progress...
The output of heat is a WXS authoring with one file per component. This is the default behavior and can't be changed using standard heat switches. This was done to natively follow the component rules.
If a component contains a single file, this file is automatically a KeyPath. Hence, if you don't transform the output of heat and keep to the rule "one component - one file", this must not be the reason of the error you get.
I would suggest investigating the verbose log and see if it contains more detailed description of the failure you face with.
Related
Can WiX be set to automatically include all generated satellite assemblies?
The goal is to have a single English language MSI that installs an application with localized strings available for ~10 languages.
I found this existing SO quetsion:
How do I include Satellite Assemblies(Localized Resources) in an MSI built with WiX?
However that solution suggests that new component and directory definitions needs to be manually added for each culture variant.
Is that the only way, or can WiX somehow automatically learn about each language from the Visual Studio project definitions?
(Running VS2010 and WiX 3.8)
You can use HarvestDirectory task to automatically collect files to be included in installer. You just need to point it to folder with your satellite assemblies and on each build of installer - target folder will be rescanned and file list will be regenerated.
For example:
1) Place in .wixproj harvest task inside beforedBuild target (it will be commented by default)
<Target Name="BeforeBuild">
<HeatDirectory OutputFile="SatelliteAsm_Files.wxs" Directory="$(SolutionDir)PathToYourAssemblies" DirectoryRefId="MODULELOCATION" ComponentGroupName="Modules" SuppressCom="true" SuppressFragments="true" SuppressRegistry="true" SuppressRootDirectory="true" AutoGenerateGuids="false" GenerateGuidsNow="true" ToolPath="$(WixToolPath)" PreprocessorVariable="var.ApplicationModuleDir" SuppressUniqueIds="True" />
2) Build your installer first time. After that you will find SatelliteAsm_Files.wxs file inside your WIX project. It will have structure similar to this:
<Fragment>
<DirectoryRef Id="DIRVARIABLE">
<Directory Id="dir8B97956DEA791D69AB336941C9163652" Name="x64">
<Component Id="cmpE72E1056FC8A2AE97260E772A6386763" Guid="{481FF1F3-7AFF-4C17-9AE0-5347BEEB3726}">
<File Id="filACCD137532BB3AE1F4B3BC207018585B" KeyPath="yes" Source="$(var.ApplicationLibDir)\x64\name.txt" />
</Component>
...
<Fragment>
<ComponentGroup Id="GroupName">
<ComponentRef Id="cmpE72E1056FC8A2AE97260E772A6386763" />
...
3) Add it as link (it's important you don't want source control to set this file to read only, coz WIX will fail on build) to your project.
4)Reference this ComponentGroup in any of your features
<Feature Id="MyFeauture">
<ComponentGroupRef Id="GroupName" />
Finally it's ready! Now files collected from folder you specified in harvest task will be included in your installer. But don't expect this to work out of box because it's royal pain to setup this and you may need to try different combinations of task keys or even XSD transformations to leave only needed files (yes WIX can do XSD transforms, and no there is no easy and agile way to filter files or folder harvested by WIX)
Note: you can use it not only for satellite assemblies - it's ok to harvest entire bin, 3rd party libs or whatever you want, so you don't need to update installer project by hand every time you add new assembly to your solution.
I'm using vs 2010 and have a web application project.
I currently use the right-click "publish" option on my project using the "web deploy" option to publish the entire application to a server that is running msdeploy.axd.
I have to publish this project many times (100+), each time just changing the web.config.
I'm looking into what technology I should use to automate this process. I would prefer to use standard MS technology.
Should I be looking at MSBuild? The VS Command Prompt? (are these the same thing?)
What technology should I learn to best automate this scenario. I'm looking for the most standard way to do this...
In my head the script I'm going to write will:
change the web.config file
do the equivalent of right-click my project, click publish, use the web-deploy option, and deploy it to that server using the windows domain\username + password details i've saved in Visual Studio
Any help is appreciated.
I've done this exact thing just with MSBuild. The steps involved in my case were:
TFS performed a build using a custom MSBuild project, not a solution file. Any CI build system would work, nothing special about TFS in this instance.
In the custom build project, in addition to building all the projects, I copied a bunch of web.config "template" files to a special folder under the $(OutDir) folder. These would end up in the build drop from TFS. In my case, the built in configuration file transformations were nowhere close to being sophisticated enough, but if that can work for you it is simpler by far.
The web.config files actually contained references to other config files, one for each configurable feature. This used a custom configuration provider. This technique would also work if you just had a single web.config file though.
I enabled automated deployment (Publish) from the command line, as a new MSBuild target in the same custom MSBuild project that was driving the build.
It was then easy to automate the Publish step in the main build to a VM or QA machine, as well as make it possible to manually deploy to other servers (eventually the staging servers) from the command line.
The web.config templates had stuff like this:
In the connection string: "Data Source=${SQLINSTANCE};Initial Catalog=${SQLDATABASENAME}..."
It is significant that the delimiters for the replacable tokens is ${ } and not $( ), since MSBuild won't mess with the curly brackets.
In the Publish step, use an MSBuild property function to replace bits in the config files, the following is taken from MSDN's description of MSBuild inline tasks:
<UsingTask
TaskName="ReplaceToken"
TaskFactory="CodeTaskFactory"
AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.v4.0.dll">
<ParameterGroup>
<File ParameterType="System.String" Required="true" />
<Token ParameterType="System.String" Required="true" />
<Replacement ParameterType="System.String" Required="true" />
</ParameterGroup>
<Task>
<Code Type="Fragment" Language="cs">
<![CDATA[
string content = File.ReadAllText(File);
content = content.Replace(Token, Replacement);
File.WriteAllText(File, content);
]]>
</Code>
</Task>
</UsingTask>
Then in your project, you can use this task with,
<ReplaceToken
File="PathTo\Web.config"
Token="${SQLINSTANCE}"
Replacement=".\SQLEXPRESS"
/>
This is really just a rough guide, all of the parameters to ReplaceToken were also configured in MSBuild item metadata, allowing options for which database, server, security, etc., each of which could be specified individually.
So, for each build/deployment, it would perform the build, copy the config templates, do string replacements on them, then automate the Package/Publish, which is the final bit to go over.
Your best bet is to start with this blog: http://vishaljoshi.blogspot.com/2009/02/web-packaging-creating-web-packages.html which explains a bit, and this answer, which contains likes to a bunch of other related StackOverflow posts MsBuild and MsDeploy with multiple environments, then search online with http://www.bing.com/search?q=msbuild+msdeploy+command+line&go=&form=QBLH&qs=n&sk= to dig deeper. I really hate to just dump you off to a search engine for this part of it, but I've found that there are so many different scenarios it is tough to single one out. About half of the top ten responses have insight on some valuable angle. Reply with more info to help narrow down my response at this point. For my implementation, I called MSDeploy.exe directly from MSBuild using an Exec task. Some things to consider:
How to deal with security on the various publish sites. I had to set up a mess of build service accounts, and always required passing the password on the msbuild command line. If you can get by with Windows auth like you suggest it will be a bit easier.
For services, I had to use PSExec to run installutil on the remote server
There were some additional configuration items that I automated using PSExec calling appcmd on the remote server.
It is easy to open up a remote share on the server and use a "net use" command to map and unmap it during the build, you may have some other preference.
Performance is tough. For larger sites it can run quite long doing it file by file. RoboCopy isn't any faster. I found using MSDeploy to package remotely (point to local drop as source, and remote share for the package source) was very fast to Rackspace. You may need to first package to a zip file using one MSDeploy call, then push the package remotely using a second.
Hope this gets you started. Please comment or supply more detail in your question if there is something I really missed or glossed over.
Response to comment:
The "Publish" target is something along these lines,
<Target Name="Publish">
<!-- token replacement in config files, as above -->
<!-- ...lots of custom setup, selection of various properties used below -->
<PropertyGroup>
<_MsDeployExe>$(PROGRAMFILES)\IIS\Microsoft Web Deploy\msdeploy</_MsDeployExe>
<_MsDeploySourceArg>-source:contentpath="$(_BuildDropFolder)"</_MsDeploySourceArg>
<_MsDeployDestArg>-dest:contentpath=\\$(_RemoteComputerName)\DropFolder</_MsDeployDestArg>
</PropertyGroup>
<Message
Text=""$(_MsDeployExe)" -verb:sync $(_MsDeploySourceArg) $(_MsDeployDestArg)"
/>
<Exec
Condition="'$(DryRun)' != 'true'"
Command=""$(_MsDeployExe)" -verb:sync $(_MsDeploySourceArg) $(_MsDeployDestArg)"
ContinueOnError="false"
WorkingDirectory="$(MSBuildThisFileDirectory)"
/>
</Target>
After way too many experiments, I've come to the conclusion that Windows Installer is simply bad technology. But the customers want MSI files.
So, how can I create an MSI file that extracts an EXE file to a temporary directory and runs it with options same or similar as were passed to the EXE file?
Options to an MSI are explained in Msiexec (command-line options) (low level "run" of an MSI is msiexec option package.msi).
EDIT: mjmarsh's WiX solution looks like it works. I just haven't had a chance to try it yet (crunch time). If it works, I'll be accepting it.
EDIT: it does not work. Missing piece: attended/unattended does not seem to be available.
Anyway, the only to make this work at all would be for the custom action to kill its parent process!
EDIT: So somebody posted as a further answer wrapping the whole thing as a post-install custom action. Theoretically possible but since a reboot may be required (thanks MS for .NET 4 requiring a reboot sometimes) we have to do further hackery. So from the matrix of advantages:
Transparency: No. One big custom action.
Customizability: No.
Standardization: No.
Management and reporting: No. Appears to work but will not.
Security: No benefit.
Validation: No. The hackery required to survive reboot makes this sure to not work.
Resiliency: Completely defeated.
Rollback: No. Rollback didn't work when we were using MSI anyway.
Patching & Updates: No. We have a local solution anyway.
Logging: No. Appears to work but will not.
No point.
Well, there is the free way and the $$$ way. I cannot document everything here, but this should get you started.
On a side note, yes, Windows Installer is a maddening technology. There are many times where I think a task will be straightforward, but it actually becomes complicated. You definitely have to immerse yourself to understand it.
In any case, here goes:
Free: WiX (here)
This is a free tool to generate MSI files from a set of XML configuration files. I'll leave you to find tutorials online, but here is the crux:
You can compress your EXE into the installer by using the following tag in the WXS file:
<Binary Id="MYEXE" src="<path to my exe?"/>
Then you can create a custom action which launches your EXE file:
<CustomAction Id="EXECA_CALLMYEXE" Return="check" Execute="deferred" BinaryKey="MYEXE"
ExeCommand="my command line"/>
Then you insert your custom action into the InstallExecuteSequence in the appropriate spot (I almost always run mine somewhere between InstallInitialize and InstallFinalize)
<InstallExecuteSequence>
<Custom Action="EXECA_CALLMYEXE" After="InstallInitialize"><![CDATA[Not REMOVE]]></Custom>
$$$: Get InstallShield (HERE)
First create a "Basic MSI" project and make sure you say you want no setup.exe generated. You set this in the Release settings.
Then you essentially do the same thing as with WiX, but you have a UI for it.
You can specify your helper EXE file by using the Direct Editor and putting your EXE file in the 'Binary' table
You can create a custom action to launch that EXE file from the "Custom Actions" Node in the tree on the left
You can insert the custom action by selecting "Install Sequences" and putting it in the InstallExecuteSequence somewhere between InstallInitialize and InstallFinalize as I said before.
Sorry, I could not be more detailed, but this should be a good start.
I think the easiest way to create a .MSI file is to use WiX.
Lesson 1 from the WiX tutorial is all you need to create a simple install.
Joshua, I understand your frustration very well. MSI is quirky to say the least - a completely new way to think of deployment. Still, applied correctly MSI offers the best possible deployment, especially for corporate customers.
What operations does your installer EXE perform? Is it largely file copy, some COM registration and some registry writes, or does it run complex installation logic, setting up databases etc...? The reason I ask is because it probably would be very quick to create a well functioning WIX MSI for you so you can abandon the EXE approach.
It is indeed possible to run an EXE from inside an MSI, but it requires proper sequencing, and it is guaranteed to cause you more blues than a simple MSI. If the app is small, and not doing anything crazy during installation, I would be happy to provide you with a basic WIX conversion.
A summary of deployment tools and their strengths and weaknesses
Advantages of using MSI files
Adding to weir's answer, change the custom action attribute like below:
<!--Run Action-->
<CustomAction Id="RunWrappedExe"
Return="asyncNoWait"
FileKey="ApplicationFileId"
Execute="deferred"
ExeCommand=""
HideTarget="no"
Impersonate="yes"/>
Setting Return=asyncNoWai does not wait for the exe to return. The installer does it's job and closes normally. Meanwhile, the exe continous its execution.
-Madhuresh
No solution. We went NSIS as corporate MSI install is going to be broken anyway due to MSI nesting problem (just try installing EXE wrapping MSI from inside MSI someday).
If you don't want to manage MSI, but only execute EXE, try Exe to MSI Converter Free. You just put in the path to the EXE and get an MSI.
There is also a free version of the MSI Wrapper. It also supports uninstall and upgrades. Also, it only creates one entry in the Add or Remove programs.
try this:
In MSI package, there is a behaviour call "Launch an application after installation", that means your exe file will be executed after the MSI installation(the MSI is closed).
Try to execute your exe there, so when your exe invoke other MSI packages, it won't conflict with the first one.
Wix can do it. Here is my sample code for wix 3.5:
<?xml version='1.0'?>
<Wix xmlns='http://schemas.microsoft.com/wix/2006/wi'>
<Product Id='*' UpgradeCode="11111111-2222-3333-4444-555555555555"
Name='My Setup' Language='1033' Version='1.0.0.0'
Manufacturer='Your company'>
<Package Description='pak' InstallerVersion='200' Compressed='yes' />
<Media Id='1' Cabinet='setup.cab' EmbedCab='yes' />
<Directory Id='TARGETDIR' Name='SourceDir'>
<Directory Id="TempFolder">
<Directory Id="INSTALLLOCATION" Name="~_tmpdir">
<Component Id='MyComponent' DiskId='1' Guid=''>
<File Id="File0" Name="setup.exe" Source="setup.exe" />
<File Id="File1" Name="file1.txt" Source="file1.txt" />
</Component>
</Directory>
</Directory>
</Directory>
<Feature Id='InstallFeature' Title='Install Feature' Level='1'>
<ComponentRef Id='MyComponent' />
</Feature>
<!-- Run Action -->
<CustomAction Id="RunWrapExe" Return="ignore" Execute="deferred"
FileKey="File0" ExeCommand="setup.exe param here"
HideTarget="no" Impersonate="no" />
<InstallExecuteSequence>
<Custom Action="RunWrapExe"
After="InstallFiles">NOT REMOVE~="ALL"</Custom>
</InstallExecuteSequence>
</Product>
</Wix>
I was having the same problem (wrap EXE, call other MSI from the EXE including .net setup, etc.),
and here is my solution:
I build the setup exe using InstallAware.
It has its own MSI Wrapper that wraps the generated EXE with MSI.
It works OK, the EXE can call other MSIs without any problem (including .net setup, other 3rd party setups), but that is because the launching MSI ends ("returns") rights after it launches the setup EXE file, and that way they avoid the MSI limitation of recursive MSI calls.
BUT - some customers (companies) that uses MSI deployment tools, requires the MSI (msiexec) to return (end) only after the setup process ends, and that is a problem with the above solution.
So - to solve this:
There is another MSI Wrapper (exemsi.com) that generates MSI that returns only after the EXE setup ends, but for using that you must use another unique option of InstallAware:
InstallAware has the option to generate the EXE setup using their own native engine, and not based on Windows Installer engine, to avoid MSI recursive limitation.
Combine those both, and you have the perfect solution.
Hope this will help someone, although many years passed since this question was first posted.
Simple trick:
Project image
using System;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.InteropServices;
namespace Setup
{
internal class Program
{
[DllImport("kernel32.dll")]
private static extern IntPtr GetConsoleWindow();
[DllImport("user32.dll")]
private static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
private static void Main(string[] args)
{
ShowWindow(GetConsoleWindow(), 0);
Stream st = Assembly.GetExecutingAssembly().GetManifestResourceStream("Setup.MSI.Temp.msi");
string path = Path.Combine(System.IO.Path.GetTempPath(), "Temp.msi");
using (var fileStream = new FileStream(path, FileMode.Create, FileAccess.Write))
{
st.CopyTo(fileStream);
}
Process p = new Process();
p.StartInfo.FileName = path;
p.Start();
p.WaitForExit();
File.Delete(path);
}
}
}
Nah man, just use Inno Setup's wizard. It makes an setup EXE but not an MSI. It's like 5 mins and you'll have a windows installer.
Simply download it, install it, point it to your EXE, and follow the on-screen prompts
Using WiX (Windows Installer XML) I have created an MSI installer which installs Word templates into the users Application Data folder, e.g. on Windows XP
C:\Documents and Settings\<user>\Application Data\Microsoft\Templates
I'm retrieving the path to this folder from the registry:
<Property Id="APPDIR" Secure="yes">
<RegistrySearch Id="RegSearch_AppData"
Type="directory"
Key="Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders"
Name="AppData"
Root="HKCU" />
</Property>
<CustomAction Id="ActionWordTemplateFolderAssign"
Property="TEMPLATEFOLDER"
Value="[APPDIR]Microsoft\Templates" />
<InstallExecuteSequence>
<Custom Action="ActionWordTemplateFolderAssign" Sequence="1" />
</InstallExecuteSequence>
However, some users installing the MSI file on Windows Vista receive an error because the APPDIR property is empty.
Is APPDIR not the correct way to retrieve the Application Data folder? Or do I have to consider another property on Vista?
EDIT: This is just a short version of the WiX Code to retrieve Word's template folder. First I'm actually checking whether the user has a custom template folder defined by a policy or under HKCU\Software\Microsoft\Office\12.0\Common\General\UserTemplates. However, if none of these are set the fallback is to use the default location under %APPDATA%\Microsoft\Templates which is retrieved by the above code.
You should use [AppDataFolder] instead. I can't find anything about "appdir" in the windows installer property reference.
Edit after question edit: The shell folders key (great blogpost btw) where you get your appdir value from is a very old and deprecated way to get at the system folders. It is only there for backwards compatibility and you should not rely on it. Especially if you live near Raymond Chen.
Edit 2: Since the real question turns out to be "how do I find the user's word template folder"... The word template folder is not always
[AppDataFolder]\Microsoft\Templates
This is because the template folder can be configured under tools - options - file locations - user templates. Ironically we are back to searching the registry if we want to detect this:
<Property Id="USERTEMPLATES">
<RegistrySearch Id="SearchUserTemplates"
Root="HKCU"
Key="Software\Microsoft\Office\11.0\Common\General"
Name="UserTemplates"
Type="raw" />
</Property>
This registry value is normally not present however, and you cannot specify a default value that contains [AppDataFolder] here (I tried).
Instead, I would try to define two components, one which installs to USERTEMPLATES and one which installs to [AppData]\Microsoft\Templates. You can then make use of Condition elements to test for the existence of USERTEMPLATES, and install only the right one.
Some additional information:
The reference for MSI properties containing special folders:
http://msdn.microsoft.com/en-us/library/aa370905(VS.85).aspx#system_folder_properties
And a link to a related blog post:
What is the WiX equivilent of Environment.SpecialFolder.ApplicationData from .NET?
Divo - In response to your comment on localized Vista installations, the problem probably isn't so much localized Vista (unless I'm reading you wrong) but localized Office.
Microsoft\Templates might become Microsoft\Vorlagen with German office for example. It's a pain in the ass, because I haven't found a reliable source of documentation on what folder names have been localized in Office, and what haven't.
My particular problem was with installing Macros to [AppDataFolder]Microsft\Word\STARTUP - which is localized for some languages only. #$%# in the end we just get customers to manually move the templates, the majority of our markets don't have a problem but we've noticed Italian and Turkish office plus a couple of others seems to exhibit this rather annoying behaviour.
On Vista there is a new standard folder available called TemplateFolder. I think that is what you want. To use it in WiX just do something like:
<DirectoryRef Id="TARGETDIR">
<Directory Id="TemplateFolder" Name="Templates"/>
</DirectoryRef>
Then you can reference the TemplateFolder Directory where ever you may need it.
After following the advice in this question successfully, I added a couple additional lines of code for another custom action. This one is intended to call regsvr32 on the copy of capicom which I've tried to put in the user's system folder. However, I'm getting error 2721, which seems to be a custom action not found error, from what I've seen. Any suggestions? I'm trying to maintain consistency with previous iterations of my installer by calling regsvr, rather than just adding the registry entries during install, which could be a good idea instead. :::shrug:::
<Directory Id="SystemFolder" Name="Sys">
...
<component ...>
...
<File Id="CapiCom.Dll" LongName="CapiCom.Dll" Name="CAPICOM.DLL" Source=... />
</component>
</directory>
...
<CustomAction Id="REGCAPICOM" ExeCommand='regsvr32.exe "[SystemFolder]capicom.dll"' Return = "ignore" Execute="deferred" />
...
<InstallExecuteSequence>
...
<Custom Action="REGCAPICOM" After="InstallFiles" />
</InstallExecuteSequence>
Edit: Yes, using regsvr32 as an installer is ugly. But when I downloaded the Capicom SDK, that is what MS said to do in order to install it. Searching around has found many people saying that this is a stupid way to do it...but it's also mechanism MS provided. I'll listen to suggestions for a better way. I don't consider it a big deal if Capicom being left behind when my application is uninstalled, considering that it's a standard windows component.
Edit: Hmmm. Apparently, one of the things running selfreg on the dll does is to create a random seed to add to the registry. Not sure what mechanism it uses to generate this seed but I suspect it would be considered in poor taste to just generate one myself, especially if I gave all the users the same seed. Not sure.... Apparently if I skip this Capicom does it on its own, so I'm fine.
The Right way:
c:\Program Files\Microsoft Visual Studio .NET 2003\Common7\Tools\Deployment\regcap.exe" /O capicom.reg capicom.dll
Run program from Adam Tengen's post here.
Note that Heat (and Tallow, IIRC) do not, as of this posting, work properly on Capicom.
The Wrong Way:
<CustomAction Id="RegisterCapicom" Directory="SystemFolder" ExeCommand="regsvr32.exe /s "[SystemFolder]Capicom.dll"" Return="check" Execute="deferred" />
...
<InstallExecuteSequence>
<Custom Action="RegisterCapicom" After="InstallFiles" />
</InstallExecuteSequence>
Uhh, are you really trying to install a Windows system file yourself? That's not allowed on a great many levels. Also, regsvr32.exe is SelfReg and SelfReg is well known to be evil in installations. Actually using the Windows Installer to write the registration is far superiour
However, the whole design here is very suspect.
You could use heat on the File to create a output WXS file, that will put the capicom.dll information in the registry without the use of regsvr32, when the msi is executed
Something like so:
heat file [Path\Capicom.dll] -template:product -out capicom.wxs
Then add the capicom.wxs to your installer, in that file create a ComponentGroup Element, that contains the Component(s) element(s):
<ComponentGroup Id="capicom">
<ComponentRef Id="capicom.dll"/>
</ComponentGroup>
After in the main WXS file add the Fragment element that will link the capicom component
The last step is to add the ComponentGroupRef to the feature that it belongs to:
<Feature Id="PRODUCTFEATURE">
<ComponentGroupRef Id="capicom" />
... [Other components or ComponentGroups references]
</Feature>