Can't get a simple .Net 4 bootstrap to work.

Sep 15, 2015 at 12:42 PM
Hi,

I've managed to get a test Wix# setup working great (thanks btw) and now want to ensure that the pre-requisite - Framework 4.0 is install if required. I've added my bootstrap code to the end of my working installer code and it generates a setup.exe. Problem is when I double click it, nothing happens. Can you tell me what I'm doing wrong?

Here's my code:
            // Create a list of source files here:
            File mainExe = new File(@"..\WixTest\bin\Debug\WixTest.exe", 
                new FileShortcut("Test App", @"%Desktop%") { IconFile = @"..\WixTest\bin\Debug\WixTest.exe" }, // Desktop shortcut
                new FileShortcut("Test App", @"%ProgramMenu%\Miner Computers\Test App")); // Menu shortcut
            File configFile = new File(@"..\WixTest\bin\Debug\WixTest.exe.config") { Condition = Condition.NOT_Installed }; // Only if not installed.
            DirFiles testFiles = new DirFiles(@"..\WixTest\bin\Debug\TestFolder1\*.*");
            Dir testDir = new Dir("Test", testFiles);

            // Add them to the source directory
            Dir sourceDir = new Dir(@"%ProgramFiles%\Miner Computers\Test App", mainExe, configFile, testDir);

            // Add any custom C# code you want to run
            ManagedAction managedAction = new ManagedAction("MyAction", Return.check, When.After, Step.InstallInitialize, Condition.NOT_Installed);

            // Elevated Action - in this case save install date to registry
            ElevatedManagedAction elevatedManagedAction = new ElevatedManagedAction("SaveInstallDate", Return.check, When.After, Step.InstallFiles, Condition.NOT_Installed);
            
            // Reg entry to save install location            
            RegValueProperty regProperty = new RegValueProperty("INSTALLDIR", RegistryHive.LocalMachine, @"Software\My Company\My Product", "InstallationDirectory");
            RegValue regValue = new RegValue(RegistryHive.LocalMachine, @"Software\My Company\My Product", "InstallationDirectory", "[INSTALLDIR]") { AttributesDefinition = "Component:Permanent=yes" };


            // Pass them into new setup project
            var project = new Project("Test App", sourceDir, managedAction, regProperty, regValue, elevatedManagedAction);
            project.UI = WUI.WixUI_InstallDir; // This is the standard wizard: License - Location - Install.
            project.GUID = new Guid("84ED5984-B146-4900-A25C-55F376E71ABE"); // MUST GENERATE A NEW GUID FROM TOOLS | CREATE GUID
            project.LicenceFile = @"..\WixTest\License.rtf";

            // Version & Upgrade
            project.MajorUpgradeStrategy = MajorUpgradeStrategy.Default;
            project.Version = new Version("1.0.0.0");

            // Control panel info
            project.ControlPanelInfo.Comments = "Simple test msi";
            project.ControlPanelInfo.Readme = "https://wixsharp.codeplex.com/manual";
            project.ControlPanelInfo.HelpLink = "https://wixsharp.codeplex.com/support";
            project.ControlPanelInfo.HelpTelephone = "111-222-333-444";
            project.ControlPanelInfo.UrlInfoAbout = "https://wixsharp.codeplex.com/About";
            project.ControlPanelInfo.UrlUpdateInfo = "https://wixsharp.codeplex.com/update";
            project.ControlPanelInfo.ProductIcon = @"..\WixTest\Boo.ico";
            project.ControlPanelInfo.Contact = "Product owner";
            project.ControlPanelInfo.Manufacturer = "My Company";
            project.ControlPanelInfo.InstallLocation = "[INSTALLDIR]";
            project.ControlPanelInfo.NoModify = true;

            string productMsi = Compiler.BuildMsi(project);



            // BOOTSTRAP CODE - Install Framework 4.0 if not installed
            var bootstrapper = new Bundle("Test App",
                                    new PackageGroupRef("NetFx40Web"),
                                    new MsiPackage(productMsi) { DisplayInternalUI = true });

            bootstrapper.Version = new Version("1.0.0.0");
            bootstrapper.UpgradeCode = new Guid("2C5A75C0-99C9-4D8A-88FE-A49A0BA15405");
            bootstrapper.Application = new SilentBootstrapperApplication();
            //bootstrapper.PreserveTempFiles = true;
            bootstrapper.Build();
Coordinator
Sep 16, 2015 at 6:06 AM
Before we continue can you please test the latest release (v1.0.26.0), which contains a major change with respect to hosting .NET v4.0 CLR. There is a good chance that it will work just fine in your case.
Sep 16, 2015 at 8:49 AM
Hi Oleg,

Updated to .26 via Nuget and now the build fails (code 255). Just to let you know I've also tried completely removing the // BOOTSTRAP CODE temporarily and it still won't build anymore. Both test application and setup project are targeting 4.0.
Coordinator
Sep 16, 2015 at 10:34 AM
Not a problem. I will start working on it..
Sep 16, 2015 at 10:39 AM
Edited Sep 16, 2015 at 11:35 AM
Found the .26 issue!

I've moved the whole thing into a new clean .26 project and tested it line by line. It's the following line that causes the problem and even creates a "Visual Studio has stopped working" message:
project.LicenceFile = @"..\WixTest\License.rtf";
Remove that line and it builds fine, add it back in and it fails. This definitely worked in .24.

Hope that helps.
Coordinator
Sep 16, 2015 at 12:48 PM
Great. Thank you.
I have created the Issue on your behalf: https://wixsharp.codeplex.com/workitem/67

Can you tell me if the rtf file is actually present in the expected location. And can you please test if you can execute the produced 'Test App.msi'.

Thank you.
Sep 16, 2015 at 1:25 PM
Edited Sep 16, 2015 at 2:06 PM
Hi Oleg,

Yes, I can confirm the file is present. I've been working on this test app for about a week and it has been building consistently with the license displaying so I'm 99% sure this occurred only in .26. Test App.msi has been working well and continues to do so providing I remove that line.


If i can just get back to my original issue (which unfortunately hasn't been resolved with .26), I've found the following happens. If I set it to SilentBootstrapperApplication nothing happens - neither the bootstrap wizard or the MSI's wizard with "DisplayInternalUI = true" shows on screen. However, if I leave the application property blank it loads the default bootstrap wizard, installs framework 4.0 (this is good!) and then displays the MSI's installer. This isn't ideal for a number of reasons:

1) The bootstrapper install has an option to select a install path which isn't carried over to the MSI installers path so it's not user friendly.
2) My Setup MSI is designed to remember the install path on re-installation but the bootstrapper doesn't see this and if I hide my UI don't I lose that functionality?
3) I'm running an elevated managed action which will not run unless my MSI's DisplayInternalUI is set to true.

At the moment these 3 things are forcing me to have both the bootstrapper and MSI's UIs visible which isn't very user friendly (in part because of issues 1 & 2).

Have I bitten off more than I can chew? All I essentially want is for my setup program to visibly run and be installed in control panel, and have framework prerequisites be checked and installed if required. I assumed it was recommended to use bootstrapper to do that. Unfortunately I can't get the user experience to something I'm happy with. Apologies if this is moving more towards a WixToolset issue that a WixSharp issue.
Coordinator
Sep 17, 2015 at 4:20 AM
> ...Yes, I can confirm the file is present.
This problem is solved now thank's. It turns out that the exception "System.Xml.XmlException: The prefix '' cannot be redefined from..." is triggered by the same XML serialization flaw, which fails a simple XElement.ToString() if the element has namespace reassigned. I didn't believe my eyes at first but a simple code below will always throw the exception and return false:
static bool IsSettingNamespaceSafe()
{
    var doc = XDocument.Parse(@"<Root xmlns=""http://www.test.com/xml/2015"">
                                    <Element xmlns=""""/>
                                </Root>");
    var xml = doc.ToString();

    XNamespace ns = doc.Root.Name.NamespaceName;

    var e = doc.Root.Elements().First();
    e.Name = ns + e.Name.LocalName;

    try
    {
        xml = doc.ToString();
        return true;
    }
    catch
    {
        return false;
    }
}
Thus the XDocument based namespace normalization algorithm is replaced now with a simple XML string manipulation.

> Have I bitten off more than I can chew?
I don't know. May be. :o)

I am not really sure what's going on there but I do know that a simple project created from the VS Wix# Bootstrapper template implements exactly your scenario. Web-based .NET4.0 installer and native MSI UI. I just tested it and it behaves as expected.

Unfortunately I am not an expert in this specific field and I cannot give you a 'killer' advise. But I would start from the reviewing and comparing the XMLs generated by the default bootstrapper project I mentioned and the one produced by your code.

Good luck.
Sep 17, 2015 at 11:32 AM
Glad I could help in any way.

I've gone back and tested the VS template and you're right, the example code works fine on my Win 10 development machine which already has Framework 4 installed.

However I've noticed what appears to be a serious issue which you may not have come across:

I've tested the same bootstrapper template exe on a clean Windows 7 virtual PC that doesn't have Framework 4 installed and nothing happen when you run the exe. No windows are visible and there is no tasks running. It simply does not work.

If I manually install framework 4 and try the installer again, then it works fine and the native MSI UI loads.

Alternatively, if I remove the "SilentBoostrapperApplication" line from the example code then it does download framework 4 and then runs the native MSI UI.

In short, if you don't have framework 4 already installed then the SilentBootstrapperApplication option stops the setup.exe from working which defeats the point of having PackageGroupRef("NetFX40Web")

Sorry to be a pain and I hope that makes sense. I
Coordinator
Sep 18, 2015 at 5:51 AM
The bootstrapper definition of the 'WixBootstrapper_NoU' sample code it extremely simple. You can always generate it with bootstrapper.BuildCmd();:
<?xml version="1.0" encoding="Windows-1252"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
  <Bundle Name="My Product" UpgradeCode="6f330b47-2577-43ad-9095-1861bb25889b" Version="1.0.0.0" >
    <BootstrapperApplicationRef Id="ManagedBootstrapperApplicationHost">
      <Payload SourceFile="..\..\..\VSProjects\bin\Debug\WixSharp.UI.dll" />
      <Payload SourceFile="BootstrapperCore.config" />
      <Payload SourceFile="..\..\..\VSProjects\bin\Debug\Microsoft.Deployment.WindowsInstaller.dll" />
    </BootstrapperApplicationRef>

    <Variable Name="_WixSharp.Bootstrapper.SilentManagedBA.PrimaryPackageId" Value="My_Product.msi" Persisted="yes" Type="string" />

    <Chain>
      <PackageGroupRef Id="NetFx40Web" />

      <MsiPackage Name="My Product.msi" Id="My_Product.msi" DisplayInternalUI="yes" SourceFile="My Product.msi" />
    </Chain>
  </Bundle>
</Wix>
As I can tell there is not much here can go wrong. The fact that the bundle installs NetFx40Web indicates that the custom UI successfully started and the only problem is that after the first step (NetFx40Web) it doesn't carry on to the next one (My Product.msi). Ony would suggest the bootstrapper items dependencies and conditions are wrong (e.g. My Product.msi can only start when .NET4 is present but the presence is checked only before the first step). But unlikely.

If you run out of options you can try to base your solution on WixBootstrapper_UI sample. It is the same bootstrapped with custom UI as the silent one, except in this case it has a visible WPF Window. With this approach you can intercept and analyse your OnDetectPackageComplete and OnPlanComplete events. So you know if My Product.msi was even triggered.

You can even completely abandon your msi UI and do the all job in the bootstrapper UI. For example collect user installdir selection, push it to msi.
Sep 18, 2015 at 9:29 AM
Hi Oleg,

Thanks for your time on this. I think I'll probably leave it for the moment as I need to get something out the door, but just to clarify (as I think there is a slight misunderstanding):
"The fact that the bundle installs NetFx40Web indicates that the custom UI successfully started and the only problem is that after the first step (NetFx40Web)"
It does not run the first part of the chain when set to silent mode. If no .net 4 is on the machine you get nothing happening at all. (The bundle only installs 4.0 IF you remove the Silent Application line from the template.)

My feeling is that this is almost by design in the sense that if you signify you don't want the Bootstrapper UI visible, then how would the end user know that .net4 is being installed for the next few minutes? I'll maybe see what the Wix guys have to say as it's their issue and not yours, but I'd urge you to try the basic template on a PC with no 4.0 installed. I've tried it on a few Windows 7 machines with no 4.0 on and it doesn't install.

Thanks again.
Coordinator
Sep 18, 2015 at 11:26 AM
Not a problem.

Keep in mind that in this case NO UI doesn't mean that there is no custom bootstraper application. WixSharp.SilentManagedBA is a mabaged bootstrapper application built the same way as the one with the full UI. Except that it has no displayed window. But it is still responsible for facilitating steps planing. That is why I mentioned OnPlanComplete. This application is a managed application as well but it is built against .NET3.5 so it should survive absence of .NET4.0. However if no .NET present at all then it is a problem. Of course it's possible to set a net35 precondition for the bootstrapper itself and get anice informative message but... now I ma not sure that it is the best approach for the no-UI scenario.

I will try to see if there is a better, native (probably burn based) option for no-UI case.
Coordinator
Sep 18, 2015 at 12:07 PM
OK. It seems that the windowless managed BA is the way of achieving silent Bootstrapper. Apparently Burn will bootstrap .NET first if custom BA is defined.

There is an interesting discussion on the matter: http://windows-installer-xml-wix-toolset.687559.n2.nabble.com/Burn-without-UI-td7239834.html
Sep 24, 2015 at 9:23 AM
Sorry, only just seen this.

So to clarify, are we saying that setting the bootstrapper to silent mode won't work if it needs to do something like download and install .net? Presumably because it needs it's UI to show a progress bar etc?

I'm assuming then that the template example works in silent mode if you have .net 4 already installed because the bootstrapper doesn't actually need to do anything other pass onto the next part of the chain - the MSI?

Those threads you linked to are quite interesting, but they all seem to suggest that you can't not display the burn UI. If so, what does "bootstrapper.Application = new SilentBootstrapperApplication();" do?
Coordinator
Sep 25, 2015 at 7:30 AM
Edited Sep 25, 2015 at 7:33 AM
OK there quite a few questions here:

> What does "bootstrapper.Application = new SilentBootstrapperApplication();" do?
It set Burn custom UI assembly to WixSharp.UI.dll. This assembly contains WixSharp.Bootstrapper.SilentManagedBA class marked with the BootstrepperApplication attribute. SilentManagedBA is a typical Burn custom UI application, which just happens to have no visible windows during the installation.

> hose threads you linked to are quite interesting, but they all seem to suggest that you can't not display the burn UI.
Not exactly.
In this discussion they touched practically all aspects of custom BA. These are the most important points:
  1. Burn does not have no-UI mode naively.
  2. If one is to implement no-UI mode then it needs to be done via custom BA application, which displays no UI at runtime (e.g. WixSharp.SilentManagedBA).
  3. Burn itself has no dependency on .NET.
  4. Burn custom BA introduces .NET dependency. However Burn handles this dependency in automatically by downloading and installing .NET first. (Bruce Cran-2: "That's right - Burn will bootstrap .NET first.")
> I'm assuming then that the template example works in silent mode if you have .net 4 already installed because the bootstrapper doesn't actually need to do anything other pass onto the next part of the chain - the MSI?
It seems this way though it's not how Burn is expected to behave. It should download and install .NET before starting BA application.

> So to clarify, are we saying that setting the bootstrapper to silent mode won't work if it needs to do something like download and install .net?
I know in your case it doesn't but it supposed to.

Thus the problem you are trying to solve is supposed to be handled by Burn automatically. But it doesn't. There may be a few reasons why it isn't.
  • Burn doesn't download and installs .NET 4.0. You can easily check it by verifying .NET presence after the failed install. Possibly you already did it.
  • Burn downloads and installs .NET 4.0 but it fails to initialize SilentManagedBA, which is compiled for .NET 3.5. You can easily check it by building a v3.5 bundle.
  • Burn downloads and installs .NET 4.0, starts SilentManagedBA but BA misbehaves. You can easily check it by analyzing the burn log and possibly creating your own creating NonSilentManagedBA that has extended logging. BA is simple you can even reuse the one from WixBootstrapper_UI sample.
Good luck.
Coordinator
Oct 6, 2015 at 12:06 AM
Edited Oct 6, 2015 at 12:32 PM
After finding the time to play around the Burn installs on virgin PC many aspects of its behavior became more clear.

Thanks, your posts the deficiency/flaw in Burn EmbeddedUI has been discovered and the corresponding issue has been logged.

I have captured all the points (including yours) regarding the Burn integration in this post: https://wixsharp.codeplex.com/discussions/645838
Oct 6, 2015 at 8:41 AM
I'm glad I could help and apologies for being so persistent!

For the moment I'm toying with the idea of leaving burn out completely and modifying the MSI project to do .net detecting and installation. That seems in some regards to actually be the simpler and more self-contained solution.

Regards