Custom Wix Burn bootstrapper doesn't detect MSI install state - wix

I'm creating a custom wizard-style bootstrapper based on Wix/Burn (3.6 release version). I've based in on the Wix 3.6 bootstrapper code.
The problem is that I cannot get the bootstrapper to detect the install state of my setup.msi that is part of the bundle.
As I understand it, all that's required is to call Engine.Detect(), where Engine is an instance of the Wix Engine from the Bootstrapper Application. At that point I should be able to look in Bootstrapper.Command.Action to see what the required launch action is.
My bundle contains two items: .NET 4 (web install) and my setup.msi.
I suspect that I'm missing a step to determine whether I should put my wizard into maintenance mode vs. install mode.

First, to determine if the package is being detected or not you can check the log files in the temp directory of the current user. It will tell you whether or not the package has been detected.
Now to determine whether or not to go into maintenance mode vs. install mode, you can check the package state by subscribing to the DetectPackageComplete event. In the example below, my UI uses two properties, InstallEnabled and UninstallEnabled to determine what "mode" to present to the user.
private void OnDetectPackageComplete(object sender, DetectPackageCompleteEventArgs e)
{
if (e.PackageId == "DummyInstallationPackageId")
{
if (e.State == PackageState.Absent)
InstallEnabled = true;
else if (e.State == PackageState.Present)
UninstallEnabled = true;
}
}
The code sample above is from my blog post on the minimum pieces needed to create a Custom WiX Managed Bootstrapper Application.

An easy way to determine if your Bundle is already installed is to use the WixBundleInstalled variable. That will be set to non-zero after your Bundle is successfully installed.
Additionally, in WiX v3.7+ the OnDetectBegin callback now tells you if the bundle is installed so you don't have to query the variable normally.
These changes were made to make it easier to detect maintenance mode to avoid the completely reasonable solution that #BryanJ suggested.

Related

How do I set up my Wix installer to rollback all installed packages after mid-install restart

I have a WiX 3.11-based installer with a managed bootstrapper application. One of the packages in the installer chain could trigger a restart. In my MBA, I handle the resume-from-restart behavior in the PlanPackageBegin event handler, inspired by the standard bootstrapper app:
private void Bootstrapper_PlanPackageBegin(object sender, PlanPackageBeginEventArgs e)
{
// _bootstrapper.Status.ForcedRestartPackage comes from the WixBundleForcedRestartPackage engine variable
if (_bootstrapper.Status.ForcedRestartPackage != null)
{
// Resuming after forced restart
// Skip packages until after the package that forced the restart
e.State = RequestState.None;
if (e.PackageId == _bootstrapper.Status.ForcedRestartPackage)
{
_bootstrapper.Status.ForcedRestartPackage = null;
}
}
else
{
...
}
}
My problem is, if the installer fails to install a package after the restart, it will only rollback the packages that were installed after the restart. If the install fails and starting rolling back, I want it to rollback all installed packages, even ones before the restart. Is there a way that I can configure my bundle or MBA to do this?
There's no way to do this today. The BA has pretty much no control over rollback behavior.
If you're trying to install and want to completely uninstall after the failure, then the BA can choose to uninstall if the user wants to close the installer instead of trying again.

Upgrade from older Wix project won't uninstall older instance

I've upgraded an old Wix based installer that has both an MSI generating project and a bootstrapper project (in Visual Studio). The bootstrapper sequences the MSI with a check for .Net pre-requisite/download. The new Wix# project just produces an MSI and it is installing and working just fine other than I cannot get it to replace the older installs. I've set the new project UpgradeCode to match what I see in the old MSI project (and which is in the windows registry in the uninstall list). I see the UpgradeCode being generated into the WXF file. But when I couldn't get that to work I tried setting it to match the UpgradeCode from the self-installing bootstrapper (which is what the end user runs). That didn't help.
I've read about similar experiences here and here. I've also set the upgrade strategy a few different ways including:
project.MajorUpgrade = MajorUpgrade.Default;
And:
project.MajorUpgrade = new MajorUpgrade
{
Schedule = UpgradeSchedule.afterInstallInitialize,
AllowDowngrades = true,
IgnoreRemoveFailure = true,
AllowSameVersionUpgrades = false
};
And:
project.MajorUpgradeStrategy = new MajorUpgradeStrategy
{
RemoveExistingProductAfter = Step.InstallInitialize,
UpgradeVersions = new VersionRange
{
Maximum = project.Version.ToString(),
IncludeMaximum = false,
MigrateFeatures = false
},
PreventDowngradingVersions = new VersionRange
{
Minimum = project.Version.ToString(),
IncludeMinimum = false
},
NewerProductInstalledErrorMessage = "A newer version of [ProductName] is already installed. Setup will now exit."
};
And various variations on those themes. Nothing I've done has impacted behavior, which is to say after installing, both the old and the new version of the application is listed in the add/remove Windows list. Is there some missing secret sauce to this migration path?
UPDATE:
I've realized my last snippet attempt there is actually working*. It's triggering the removal of everything except the entry in the Windows add/remove programs! Can this be due to a name change for the application in the new installer? Or maybe it's remaining around due to the old bootstrapper somehow, since I'm using the UpgradeCode from the old MSI and not doing anything specific with the bootstrapper?
UPDATE2:
The "old" installer has both an MSI and EXE (bootstrapper) version of the setup. If I just install using the old MSI, then run the new MSI, all works perfectly. So it seems that it is something related to running the bootstrapper EXE that is not getting removed.

Force WIX to install 3rdparty msipackage no matter the currently installed version

I am developing a wix installer. This wix installer installs a 3rdparty msipackage.
I want my wix bootstrapper project to install this msipackage no matter what version that should already exist on the users pc. This means that if the same version(or a newer version) exists it should overwrite that installation.
I install my msipackage like this:
<MsiPackage Id="InstacalFull" Name="Measurement Computing InstaCal" Vital="yes" Compressed="yes" SourceFile="../Suite.SetupBootstrapper/3rdparty/Instacal/InstaCal.msi">
Does anyone have any ideas on how to achieve this?
Use InstallCondition="1"
This will install it every time
http://wixtoolset.org/documentation/manual/v3/xsd/wix/msipackage.html
InstallCondition
String
A condition to evaluate before installing the package. The package will only be installed if the condition evaluates to true. If the condition evaluates to false and the bundle is being installed, repaired, or modified, the package will be uninstalled.
I know this one is old, but since I came across this issue, maybe it would help someone, too.
In my case Repair was enough, so although technically it wasn't reinstall, practically Repair = Reinstall.
I needed to reinstall URLrewrite, because it could get broken when IIS Windows feature was disabled.
What you need it to add custom handler for PlanPackageBegin in you custom BootstrapperApplication class, for example:
CustomBootstrapperApplication.Model.Bootstrapper.PlanPackageBegin += this.PlanPackageBegin;
...........
private void PlanPackageBegin(object sender, PlanPackageBeginEventArgs e)
{
if (e.PackageId.ToLower().Contains("urlrewrite"))
{
if (CustomBootstrapperApplication.Model.Command.Action != LaunchAction.Uninstall && e.State == RequestState.Present)
{
CustomBootstrapperApplication.Model.Engine.Log(LogLevel.Verbose, string.Format("{0} is installed, forcing Repair", e.PackageId));
e.State = RequestState.Repair;
}
}
_packageList.Add(e.PackageId, e.State);
}
And in the the Bundle:
<!-- Note: this Id is used in PlanPackageBegin -->
<MsiPackage Id='urlrewrite2X64' Vital='no'
Permanent='yes'
SourceFile="rewrite_amd64.msi"
DownloadUrl="http://example.com/rewrite_amd64.msi"
DisplayInternalUI="no"
Visible="yes"
InstallCondition="VersionNT64"/>
You can force uninstallation of previous MSI during upgrade by something like this inside PlanPackageBegin:
if (LaunchAction.Uninstall == CustomBootstrapperApplication.Model.Command.Action && (CustomBootstrapperApplication.Model.Command.Relation == RelationType.Upgrade))
{
e.State = RequestState.None;
}

Using Attach API Outside Of JDK

I have a small application that uses the Attach API to modify some third party classes during runtime. Alas, I have run into a large problem: the Attach API only comes with the JDK. The necessary files I can copy from the JDK and add into my project, but the library responsible for this(attach.(dll|so)) I can't. This is because I would have to copy attach.lib from a resource inside jar, and put it in the JRE/lib directory.
An action that would not work if the user isn't root on a Linux machine, therefore losing compatibility to alot of users (as this app is supposed to run on a server, and most servers are Linux, and I can't be sure all are root)
I looked into all the classes responsible for the attach API (VirtualMachine, AttachProvider etc) but found no place where it is loading the library.
Is it possible to do this? I mean, can I use the Attach API outside of a JDK installation? If so, how?
You can do so by modifying java.library.path:
static void addToLibPath(String path) throws NoSuchFieldException,
SecurityException,
IllegalArgumentException,
IllegalAccessException
{
if (System.getProperty("java.library.path") != null) {
// If java.library.path is not empty, we will prepend our path
// Note that path.separator is ; on Windows and : on Unix-like,
// so we can't hard code it.
System.setProperty("java.library.path",
path + System.getProperty("path.separator")
+ System.getProperty("java.library.path"));
} else {
System.setProperty("java.library.path", path);
}
// Important: java.library.path is cached
// We will be using reflection to clear the cache
Field fieldSysPath = ClassLoader.class.getDeclaredField("sys_paths");
fieldSysPath.setAccessible(true);
fieldSysPath.set(null, null);
}
Call addToLibPath("path") will add "path" to java.library.path.
Please note that java.library.path is cached, and reflection is required to clear the cache.
As far as I know, you need to run the application looking to do the "attach" from within a JDK (not a JRE). By doing this, you don't need to worry about providing the Attach API or its dependencies - as they are all provided for and managed by the JDK. That said, you shouldn't have any "root" concerns with doing this - as you can extract and run/use a JDK as any user (it doesn't have to be installed / executed as "root"). That said, you'll just need to ensure that your program doing the attaching and the program being attached to are running as the same OS user as to not run into security restrictions.
Our experience is that there is no reliable way to use the attach API without a full JDK. This was particularly acute on Windows. You might get it to work, but you might want to look into plain old JMX instead.

WCF service not working after program update

I have recently added a WCF service reference to my program. When I perform a clean install of this program, everything seems to work as expected. But, when I install the program on a client which already has a previous version (without the new service reference) installed, I get a exception telling me the default endpoint for this particular service could not be found.
It seems that the appname.exe.config is not being updated with the new endpoint settings. Is there any reason for this and how can I force the installer to overwrite the config file? I'm using the default Visual Studio 2008 installer project with RemovePreviousVersions set to True.
Update:
My program encrypts the settings section after the first run with the following code
Configuration config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
ConfigurationSection section = config.GetSection(sectionKey);
if (section != null)
{
if (!section.SectionInformation.IsProtected)
{
if (!section.ElementInformation.IsLocked)
{
section.SectionInformation.ProtectSection("DataProtectionConfigurationProvider");
section.SectionInformation.ForceSave = true;
config.Save(ConfigurationSaveMode.Full);
}
}
}
When I do not run the program before installing the new version the app.config gets updated.
You are right that it is the config file that is not updated.
There are several possibilities:
The installer has the old version of the config file
The installer does not have a config file and the program is using the old one on the machine
Try uninstalling the project first, then install and check that the config file has been copied in.