Wix burn doesn't allow to remove file - wix

I'm creating a bootsrapper and I want to remove links that are created during installation. So I write following step:
<Chain>
...
<ExePackage Id="removelnk" Cache="no" SourceFile="run.bat" InstallCommand="del "C:\Users\Public\Desktop\Parity UI.lnk"" />
</Chain>
Where run.bat is simply %* which allows to run arbitrary code as described here.
However, it doesn't work:
[19EC:0E2C][2018-06-16T18:32:27]i301: Applying execute package: removelnk, action: Install, path: C:\ProgramData\Package Cache\1608BB75347CD8C40187E5F3C0A969ED73A98D51\run.bat, arguments: '"C:\ProgramData\Package Cache\1608BB75347CD8C40187E5F3C0A969ED73A98D51\run.bat" del "C:\Users\Public\Desktop\Parity UI.lnk"'
[19EC:0E2C][2018-06-16T18:32:27]e000: Error 0x80070001: Process returned error: 0x1
[19EC:0E2C][2018-06-16T18:32:27]e000: Error 0x80070001: Failed to execute EXE package.
[0AE4:2B94][2018-06-16T18:32:27]e000: Error 0x80070001: Failed to configure per-machine EXE package.
[0AE4:2B94][2018-06-16T18:32:27]i319: Applied execute package: removelnk, result: 0x80070001, restart: None
[0AE4:2B94][2018-06-16T18:32:27]e000: Error 0x80070001: Failed to execute EXE package.
If I execute this command from log in my cmd then it works as expected. It even works without admin privileges.
What's wrong here?

Creating a Transform: You can use a transform to modify any MSI file - a very common use for a transform is to remove such shortcuts. You should be able to apply that transform on the command line specified in your bootstrapper - though I have never tried this with WiX bootstrappers.
Transforms are "little database fragments" that are applied to the original MSI. It changes the MSI file in memory and you can pretty much change whatever you want. You can create transforms with Orca or an equivalent free tool. Commercial tools - such as Advanced Installer - can also be used of course. In fact they have a nice little video showing the process (towards the bottom).
There is a long explanation of transforms (among other things) here: How to make better use of MSI files.
Applying a Transform: You apply transforms via the Transforms property during installation.
Quick Sample Command Line:
msiexec.exe /I "My.msi" /QN /L*V "C:\My.log" TRANSFORMS="C:\1031.mst;C:\My.mst"
Quick Parameter Explanation:
/I = run installation sequence
/QN = run completely silently
/L*V "C:\My.log"= verbose logging
TRANSFORMS="C:\1031.mst;C:\My.mst" = Apply transforms 1031.mst and My.mst.
Burn Bundle Details: I have not tried applying a transform in a Burn bundle (so I should have the sense not to answer), but the MsiPackage element is what you need I believe. I found this rather complicated sample of a Burn bundle source file - perhaps it is worth a look? It seems the magic is in the MsiProperty child element for the MsiPackage element.
UPDATE:
Burn Hello-World Style Example: Finally got to run a quick test on a Windows computer (was on a Linux one). Here is how you can apply a transform via Burn (minimal sample, just intended to show the basics, not pretending to be good markup).
Just inlining the warning: I hear some rumors that the application of
the transform in this way might not work in all cases - such as
repair. Please test thoroughly. It worked for my test. Also test upgrade scenarios (major upgrade for example).
This will apply the transform ShortcutDesktop.mst to the original MSI ShortcutDesktop.msi:
<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi"
xmlns:bal="http://schemas.microsoft.com/wix/BalExtension">
<!-- Maybe generate yourself an Upgrade-GUID here: https://www.guidgenerator.com/ -->
<Bundle Name="MyCoolTestApp" Version="1.0.0.0" Manufacturer="Someone"
UpgradeCode="PUT-GUID-HERE">
<BootstrapperApplicationRef Id="WixStandardBootstrapperApplication.RtfLicense" />
<Chain>
<MsiPackage SourceFile="ShortcutDesktop.msi">
<MsiProperty Name="TRANSFORMS" Value="ShortcutDesktop.mst" />
</MsiPackage>
</Chain>
</Bundle>
</Wix>
To build the Burn bundle BurnTest.wxs above:
set SetupName=BurnTest
candle.exe %SetupName%.wxs -ext WixBalExtension >> %SetupName%.log
light.exe %SetupName%.wixobj -ext WixBalExtension >> %SetupName%.log
And a link to a better Burn example on github:
https://github.com/frederiksen/Classic-WiX-Burn-Theme

Burn doesn't support batch files. Everything that modifies the machine should be done in a package.

Related

How do I limit Wix bootstrapper bundle installs based on whether it is installed in silent mode?

I would like to check whether the UI is in quiet/silent mode or gui mode and then either error or exit installation if the ui is displayed, preferably with a message indicating that this "cannot be run in gui mode"
in WiX 3.11 there is a variable WixBundleUILevel:
http://wixtoolset.org/documentation/manual/v3/bundle/bundle_built_in_variables.html
but how do I give an error if this value is set to show the UI?
Thanks
add to: Bundle wxs
xmlns:bal="http://schemas.microsoft.com/wix/BalExtension">
<Bundle .... \>
<bal:Condition Message="reason">WixBundleUILevel=2</bal:Condition>
<BootstrapperApplicationRef Id="WixStandardBootstrapperApplication.RtfLicense"/>
<Chain>
when calling candle and light, include
-ext (wixfolder)bin\WixBalExtension.dll"

Wix - automate 32bit and 64bit build

Going from this answer https://stackoverflow.com/a/15985524/552448
<Wix>
<Bundle...>
<BootstrapperApplicationRef Id='WixStandardBootstrapperApplication.HyperlinkLicense' />
<Chain>
<MsiPackage SourceFile="prereq1.msi" />
....
<MsiPackage InstallCondition='NOT VersionNT64' SourceFile='path\to\x86.msi' />
<MsiPackage InstallCondition='VersionNT64' SourceFile='path\to\x64.msi' />
</Chain>
</Bundle>
</Wix>
All the projects in my solution target "Any CPU", with the exception of the bootstrapper and the MSI package, thus I'm using a shared cabinet to avoid doubling the final size of the bootstrapper.
I know I have to build the MSI with x86, then with x64 and finally build the bootstrapper. Did this manually and it works.
However, I'd like to automate these steps.
I've been trying to add a Prebuild event to the Bundle wixproj file, something along the lines of
$(MSBuildBinPath)\msbuild "$(SolutionDir)MyMSI\MyMSI.wixproj" /p:Platform=x86;Configuration=Release
$(MSBuildBinPath)\msbuild "$(SolutionDir)MyMSI\MyMSI.wixproj" /p:Platform=x64;Configuration=Release
However, this fails with this error:
The OutputPath property is not set for project 'MyLibrary.csproj'. Please check to make sure that you have specified a valid combination of Configuration and Platform for this project. Configuration='Release' Platform='x86'. You may be seeing this message because you are trying to build a project without a solution file, and have specified a non-default Configuration or Platform that doesn't exist for this project.
I'd like to make it work without having to go through all the projects in order to add the combination of platform/configuration. After all, this works if I right click MyMSI and choose build.
Surely I must be missing something.
It was pretty obvious - create two separate MSI projects - one with x86, the other with x64, both using the same files.

How to build a bootstrapper for multiple MSI files?

I've got following problem: With WiX 3.7 I have built an installer which creates several localized MSI files, for example:
..\bin\x86\Release\en-us\myProject.msi
..\bin\x86\Release\fr-fr\myProject.msi
..\bin\x86\Release\de-de\myProject.msi
Furthermore, I created a Burn bootstrapper project which should ensure that .NET 4.5 is installed:
...
<Chain>
<PackageGroupRef Id="NetFx45Web"/>
<MsiPackage SourceFile="$(var.myProject.TargetPath)"></MsiPackage>
</Chain>
...
Now I have expected that the Burn bootstrapper project creates:
..\bin\x86\Release\en-us\myProject.exe
..\bin\x86\Release\fr-fr\myProject.exe
..\bin\x86\Release\de-de\myProject.exe
but MsiPackage expects a single file.
Is it generally possible to make the Burn project work as I expect?
If this is not the case, is it possible to get to know how the exact name of the created MSI file? I can not hard code the MSI file name, because the output name can vary.
I don't think that this is possible... If you are using MSBuild or something similar you can build it as you're building your MSI packages.
Or you can use a simple script which will call candle.exe with parameter -dYOUR_VARIABLE=myProject.msi.
See Working with MSBuild for more information.

Wix 3.6 Burn: unmanaged custom UI

I want to package multiple MSIs into a single install package, hence I am using Burn from Wix3.6.
I want to have a simple user interface allowing to select which package(s) should be installed.
I understand the standard BA (wixstdba.dll) does not provide this functionnality and that I need to write my own BA.
I have been looking at project 'wixstdba' from the 'wix36-sources' package as an example of a C++ BA. To get started I have tried simply rebuilding the project and adding the resulting DLL to my Bundle as follows:
<Bundle
Name="$(var.ProductName)"
Version="$(var.ProductVersion)"
Manufacturer="$(var.VendorName)"
UpgradeCode="$(var.UpgradeCode)" >
<BootstrapperApplication SourceFile="wixstdba.dll" />
<Chain>
...
I succesfully built the Bundle:
light -ext WixBalExtension.dll -ext WixUIExtension -ext WixUtilExtension installer-v$(VERSION).wixobj -o installer-v$(VERSION).exe
candle -o installer-v$(VERSION).wixobj bundle.wxs -d"Platform=x64"
However, when I run the resulting .exe, nothing happens. No UI appears, no software is installed and no error message.
Any idea what I might be doing wrong?
When you run the .exe, it should create a log file in your system's %TEMP% folder. This should tell you if any errors are being encountered. The file name will be the product name (with spaces replaced with underscores). The easiest way to find it is to open a Windows Explorer window, type "%TEMP%" for the folder name, and sort by Date Modified desc. The top file is likely the right one.
It is likely that the bootstrapper is running, but when it attempts to load your code it is unable to load some dependency, or otherwise has some error. Hopefully, the log will provide enough hints for you to find the issue.
If you end up needing to add additional libraries/files to be used by your BA, add them to the bundle payload files, like this:
<BootstrapperApplicationRef SourceFile="wixstdba.dll" >
<Payload SourceFile="$(var.ReferencedProject.TargetDir)\file.needed.at.runtime" />
</BootstrapperApplicationRef>
This will place the file in the same folder as your unpacked BA at runtime.

Can a .msi file install itself (presumably via a Custom Action)?

I wand to construct an MSI which, in its installation process, will deploy itself along with its contained Files/Components, to the TargetDir.
So MyApp.msi contains MyApp.exe and MyAppBootstrapperEmpty.exe (with no resources) in its File Table.
The user launches a MyAppBootstrapperPackaged.exe (containing MyApp.msi as a resource, obtained from the internet somewhere, or email or otherwise). MyAppBootStrapperPackaged.exe extracts MyApp.msi to a temp folder and executes it via msiexec.exe.
After the msiexec.exe process completes, I want MyApp.msi, MyBootstrapperEmpty.exe (AND MyApp.exe in %ProgramFiles%\MyApp folder so MyApp.exe can be assured access to MyApp.msi when it runs (for creating the below-mentioned packaged content).
MyAppBootstrapper*.exe could try and copy MyApp.msi to %ProgramFiles%\MyApp folder, but would need elevation to do so, and would not allow for its removal via Windows Installer uninstall process (from Add/Remove Programs or otherwise), which should be preserved.
Obviously (I think it's obvious - am I wrong?) I can't include the MSI as a file in my Media/CAB (chicken and egg scenario), so I believe it would have to be done via a Custom Action before the install process, adding the original MSI to the MSI DB's Media/CAB and the appropriate entry in the File table on the fly. Can this be done and if so how?
Think of a content distribution model where content files are only ever to be distributed together with the App. Content is produced by the end user via the App at run time, and packaged into a distributable EXE which includes both the App and the content.
MyApp's installer must remain an MSI, but may be executed by a Bootstrapper EXE. The installed MyApp.exe must have access to both MyApp.msi and EXE is to be "assembled" at runtime by the App from a base (empty) MyAppBootstrapper.exe, which is also installed by the MSI, and the content created by the end-user. The EXE's resource MSI must be the same as that used to install the App which is doing the runtime packaging.
WIX is not to be installed with MyApp.
There can be no network dependencies at run-/packaging- time (i.e. can't do the packaging via a Webservice - must be done locally).
I am familiar with (and using) Custom Actions (managed and unmanaged, via DTF and otherwise).
Add an uncompressed medium to your wxs like this:
<Media Id='2'/>
And then create a component with a File element like this:
<File Source='/path/to/myinstaller.msi' Compressed='no' DiskId='2' />
This will make the installer look for a file called "myinstaller.msi" on the installation medium, in the same folder as the msi that is being installed. The source path above should point to a dummy file, it is only there to appease wix.
Edit: The following sample test.wxs demonstrates that it works. It produces a test.msi file which installs itself to c:\program files\test. Note that you need to put a dummy test.msi file in the same folder as text.wxs to appease wix.
<?xml version='1.0' encoding='utf-8'?>
<Wix xmlns='http://schemas.microsoft.com/wix/2006/wi'>
<Product
Name='ProductName'
Id='*'
Language='1033'
Version='0.0.1'
Manufacturer='ManufacturerName' >
<Package
Keywords='Installer'
Description='Installer which installs itself'
Manufacturer='ManufactererName'
InstallerVersion='100'
Languages='1033'
Compressed='yes'
SummaryCodepage='1252'/>
<Media Id='1' Cabinet='test.cab' EmbedCab='yes'/>
<Media Id='2' />
<Directory Id='TARGETDIR' Name="SourceDir">
<Directory Id='ProgramFilesFolder'>
<Directory Id='TestFolder' Name='Test' >
<Component Id="InstallMyself">
<File Source="./test.msi" Compressed="no" DiskId="2" />
</Component>
</Directory>
</Directory>
</Directory>
<Feature
Id='Complete'
Display='expand'
Level='1'
Title='Copy msi file to program files folder'
Description='Test'>
<ComponentRef Id="InstallMyself" />
</Feature>
</Product>
</Wix>
Having one .MSI package launch another .MSI package from "within" itself is called a nested install, and it's bad juju (see Rule 20). Windows Installer has some global data that it uses to manage the current install, and it doesn't handle well multiple installs at the same time. For the same reason, if you start one install and then try to start another while the first is still in progress, you'll usually see a pop-up to the effect of "another install in progress, please wait until it's done".
You can have a program, usually called a bootstrapper (I think that's what you're referring to) which is itself not an install package, but which contains an install package (such as an .MSI or an .EXE) as a resource, possibly compressed. The action of the bootstrapper program is to extract/expand the resource to a file, commonly in a %TEMP% directory, then either launch the extracted .EXE or run MSIEXEC on the extracted .MSI. The bootstrapper can contain multiple resources and extract+install them one by one, if you need to install prerequisites before the main package. Or you can ship multiple packages as separate files, and have the bootstrapper execute/install them directly from the distribution media one by one, or copy them down to the target machine and run the series of install from there, or...
WiX itself does not get installed, no. It's a tool with which .MSI packages can be built. The WiX project has on its wishlist a generic bootstrapper program, but it hasn't been implemented yet. There are other bootstrappers available, e.g. this one.
You won't need a custom action -- in fact, since the bootstrapper isn't itself a Windows Installer installation package, "custom action" has no meaning to it. And, if you're familiar enough with CAs to know about managed/unmanaged/DTF, then you know enough to avoid custom actions whenever you can. (grin)
I think it's much easier for your bootstrapper to extract MSI file to some predefined location rather than to the temp folder. For example, to C:\Documents and Settings\All Users\Application Data\My Company\My Product Install Cache. After installation finishes bootstrapper would leave MSI file sitting there. If at some stage user decides to reinstall your product Windows Installer will be able to locate source MSI file.
Also, add path to this file to RemoveFile table so that it gets deleted on uninstall. You can use RemoveFile element in WiX for that.
So if I understand, then I think I would have the app create a transform (MST) that has the content files and apply that to the base MSI. I'm still not convinced that I understand though. :)
I'd configure the MSI cache path to a known location.
Then at runtime if you need to "edit" the MSI use VBScript or similar.
But still, I ask WHY!?!
I am also working on a way to deploy multiple MSI files. I have a bootstrapper.exe program that bundles the MSI files and runs them one at a time. This solves my problem for most cases.
The case it does not solve is GPO (Global Policy Object) distribution of the install. GPO requires a dot-msi file to run an install.
To do this here's what I did which almost solved the problem (but not quite). I put the dot-msi files in the file table of an installer and put my bootstrapper in the binary table and run it from a custom action inserted after InstallFinalize in the InstallExecuteSequence. Of course the bootstrapper won't be able to run other MSI's because the top level MSI holds the _MSIExecute mutex.
It was pretty easy to get a little further. I made the bootstrapper return control to the top level installer and continute. And then I added a WaitForSingleObject call to wait for the top level install to finish, and the bootstrapper can then continue to finish the install.
My problem is that the GPO installation happens at boot time and the top level install completes before the sub installers are done and GPO reboots the machine.
The toplevel install also returns a success status when the install may actually fail later on.
I'm still looking for a way to block the top level install from completing until after the bootstrapper completes.