Installer for VSTO Word AddIn

Jun 29, 2016 at 5:54 PM
Hi,
I am developping a Microsoft Word AddIn and use WixSharp to create the Installer. Now I figured out that it is required to set the Registry Key 64BIt or WOW32 Node depending on the bitness of Word as installed on the target machine. I know how to detect the bitness, but I have no idea how to change the Registry Key during installation. Current solution is to have two different installers one for x64 Word on x64 machine and one for x86 Word on either x86 odr x64 platform.
I did play around with conditions, but without success. In my custom action scheduled after Costfinalize I hav folloing code:
            if (wordBitness == "x64")
            {
                session[Constants.ISWORDX64] = "1";
            }
            else
            {
                session[Constants.ISWORDX64] = "0";
            }
However the condition was never met:
new RegValue(new Id(RegLoadBehavior), RegistryHive.LocalMachine, WordRegistryKey32, LoadBehavior, 3) { Condition = new Condition($"{Constants.ISWORDX64}=\"0\"") },
new RegValue(new Id(RegLoadBehavior64), RegistryHive.LocalMachine, WordRegistryKey64, LoadBehavior, 3) { Condition = new Condition($"{Constants.ISWORDX64}=\"1\"") },
Is this the right way or am I wrong?
Josef
Jun 30, 2016 at 12:25 AM
Without diving into much details I say that the whole approach seems OK. Though I believe there is a better way. But first thing first.

Yest you cannot have a single msi that transparently installs the product into ProgFiles or ProgFiles(x86) depending on the runtime platform. This is a limitation of MSI as a technology. Though I would expect that if you run x86 msi on x64 OS and write to key like this Registry.LocalMachine.CreateSubKey("MyCompany").SetValue("Name", "Company_Name") it would automatically remap it into the WOW32 Node. Doesn't it? If it does then may be it is what is messing up with your writing there.

Anyway, if for whatever reason I need to write into one or another node depending on some runtime conditions I wouldn't bother with MSI. It's just too clumsy. You Already have a good place for this - your C# routine:
project.Load += msi_Load;
...
static void msi_Load(SetupEventArgs e)
{
    if (e.IsInstalling)
    {
       if (wordBitness == "x64")
           Registry.LocalMachine.CreateSubKey('A')
       else
           Registry.LocalMachine.CreateSubKey('B')
    }
    else
    {
       if (wordBitness == "x64")
           Registry.LocalMachine.DeleteSubKey('A')
       else
           Registry.LocalMachine.DeleteSubKey('B')
    }
}
Jun 30, 2016 at 7:45 AM
Ah, thats a cool approach. If I understand correctly, you propose to hook into the load event of MSI and handle the bitness there.
Yes, MSI messes up the WOW32, especially because the registry subkey depends on a x64 platform on the bitness of installed word and not on th platform itself.
Anyway I'll give it a try.
One more question: It should be possible to package the x86 and x64 installers in on bootstrap package an run them depending on the target platform. Correct?
Somehow in this way:
<Bundle ...>
  <BootstrapperApplicationRef Id='WixStandardBootstrapperApplication.RtfLicense' />

  <Chain>
    <MsiPackage InstallCondition='NOT VersionNT64' SourceFile='path\to\x86.msi' />
    <MsiPackage InstallCondition='VersionNT64' SourceFile='path\to\x64.msi' />
  </Chain>
</Bundle>
How is this done with wixsharp?
Jun 30, 2016 at 8:09 AM
> ...you propose to hook into the load event of MSI and handle the bitness there.
Bingo!

> How is this done with wixsharp?
For bootstrapping API (Burn) Wix# doesn't do anything special as opposite to MSI. Thus it's reasonably straight forward.
var bootstrapper =
        new Bundle("My Product",
            new MsiPackage("path\to\x86.msi"){ InstallCondition = "NOT VersionNT64" },
            new MsiPackage("path\to\x64.msi"){ InstallCondition = "VersionNT64" },
...
Have a look at Bootstrapper samples (e.g. WixBootstrapper.cs).
Jun 30, 2016 at 8:28 AM
Yeah, thats the easy part. You may have noticed I am not an expert in installers ;-), so I am wondering whether its possible to package all that into one single exe file. I had a close look to the samples, but could not find it. In WIX I guess its a Payload group and Exepackage in it.
Reason why I want to have one single file:The application should have a sort of auto-update functionality and a single file would be easer to handle.
Thank you for your support.
Jul 1, 2016 at 2:03 AM
Edited Jul 1, 2016 at 2:03 AM
There is nothing wrong with a single setup.exe containing platform specific packages.
That little code snippet I posted earlier produces a single setup.exe with both x86.msi and x64.msi embedded. There is really nothing special about it. And you already know how to setup the conditions. You are almost there. :)

ExePackage is practically the same as MsiPackage except you need to specify two separate sets of the command line arguments of it for the Install and Uninstall cases.

The WixBootstrapper sample has code that show host to define MsiPackage, ExePackage and even packages that can be downloaded at install time.

Good luck.
Jul 1, 2016 at 10:16 AM
Oh yeah, overlooked msipackage. Thx for pointing me in the right direction
Nov 7, 2016 at 9:37 AM
Hi oleg_s,
I am now in the process of implementing a bootstrapper to have both x86 and x64 packages in one single setup. The bitness of the msi package ist NOT only determined by the bitness of target Windows, but also of the installed version of WORD. In order to handle this I think I need to set a custom variable in the bootstrapper bundle and set it from the bootstrapper application. However I could not find a way to add a custom variable like in this code fragment on Stackoverflow: http://stackoverflow.com/questions/27290653/change-property-msi-in-managed-bootstrapper-wpf
Any suggestion?
Thank you for your support.
Nov 7, 2016 at 2:21 PM
So after some research I belive I found a solution: Seems that DetectPackageComplete Event is the right place to select the desired package for install.
Nov 7, 2016 at 4:08 PM
Edited Nov 7, 2016 at 4:08 PM
Unfortunately no success.
     var bootstrapper =
                new Bundle("My Product",
                    new PackageGroupRef("NetFx40Web"),
                    new MsiPackage(productMsi) { Id = "MyProductPackageId", InstallCondition= "DOINSTALL=True" });

        bootstrapper.Version = new Version("1.0.0.0");
        bootstrapper.UpgradeCode = new Guid("6f330b47-2577-43ad-9095-1861bb25889a");
        bootstrapper.Application = new ManagedBootstrapperApplication("%this%"); // you can also use System.Reflection.Assembly.GetExecutingAssembly().Location
        bootstrapper.StringVariablesDefinition += "DOINSTALL";
        bootstrapper.PreserveTempFiles = true;
Variable DOINSTALL will be set to "False" is not set anywhere, so it evaluates to "Condition 'DOINSTALL=True' evaluates to false" (from log).
results in an entry that cannot be removed.
Whats wrong here?
Thx for your support.
Nov 8, 2016 at 10:31 AM
The bundle variables are not visible to MsiaPackages. You will need to add a package property mapped to the variable and after that you will be able to make this property into a condition:
new ManagedBootstrapperApplication("ManagedBA.dll")
{
    StringVariablesDefinition = "FullInstall=Yes; Silent=No"
}
...
new MsiPackage(msiFile) { MsiProperties = "FULL=[FullInstall]" },
Though I haven't tested setting the variable from the BA event and not sure that bal:Overridable="yes" is critical but at least hardcoded value indeed can be accessed if mapping is done.
Nov 8, 2016 at 10:34 AM
BTW have a look at WixBootstrapper sample it shows how to set up the UtilFileSearch that pushes the search result into the variable.
Nov 9, 2016 at 11:11 AM
Hi,
I think ist time to start all over again and explain.
I have two msi packages, one for x86 and one for x64. Depending on the bitness of another application, in my case MS WORD, it is required to install either x86 or x64 package. I do have working Code to detect the bitness of MS Word, but I have no mechanism to select the related msi package for installation.
The idea I had is to have a BA detecting the bitness and set a Variable, which in turn triggers the Installation of the right package.
May be I am wrong with this approach. Hope you have a hint.
Nov 10, 2016 at 3:32 AM
OK, I see,

I am not entirely sure what is the typical Burn solution for this. I guess that you can
  1. Embed both MSIs into a bundle.
  2. Then set both MSIs to be installed based on mutually exclusive condition InstallCondition="USE_X64=true"
  3. Then ensure that your bundle string variable (USE_X64) is initialized. One way to do this is to use UtilFileSearch as in sample. Remember that you cannot set a bundle string variable from msi. You have to do it from bundle.
If UtilFileSearch is not fit for your task you can set variables from the custom BA. Then you will have the whole power of C# to analyse the target system for 'bitness' at runtime.

Now, my personal feel about all this is that it is all very convoluted. I do not blame Burn. It is supposed to be one truly generic solution and sometimes generic solutions are ugly if they applied for a very specific problem.
I personally would probably go for a very simple completely custom bootstrapper, which is the same as external UI. Samples are in the distro. Have both MSIs embedded as resources, analyse target system, extract one or another one (based on bitness) and just install it. But of course it would make sense only for a simple scenario when only a single (or two) msis is to be installed.

Looks like this guy faced the same challenge: http://stackoverflow.com/questions/36797522/how-to-write-the-msipackage-installcondition-based-on-a-string-comparison-in-a
Nov 16, 2016 at 2:42 PM
Hi,

Finally I dropped the idea to have a bundle. I did not manage to create a stable bundle with X86 and X64 msi packages and select based on a Variable, or at least I did not trust what I have developped, as I found a some of orphanded packages from my experiments. It may also be related to the fact, that I wanted to overcome the need to implementy my own UI for the installer, which handles all the Userinput. All this is not an issue of WixSharp it is related to the way the Windows Installer works, which is indeed somtimes a pain.
At the end I came up with a custom Action for the X64 installer which checks the bitness of word and returns an error if Word bitness is X86. Implementing this with WixSharp is very easy.

Pros: Super simple and reliable with WixSharp
Cons: Two msi packages and enduser Needs to select which one to install.
Nov 17, 2016 at 11:06 AM
:)
After dealing a lot with MSI I came to the same conclusion: not the most elegant solution may be the most practical one.
Glad you sorted it all out.
Nov 17, 2016 at 4:46 PM
Edited Nov 17, 2016 at 4:48 PM
What's with a simple .NET application written for Framework 3.5 which is by default installed since Windows 7 doing it?
Very small footprint not bigger than 20-30 kb. Uses WebClient to asynchronously download and install the right package, you may put a progrssbar on the main form.
Before that it detects Office bitness.

Pro: simple, works 100%, you can use this app for other installations too, saves network traffic
Con: not that nerdy :-)

Regards
Nov 17, 2016 at 11:12 PM
Second it.
Wanted to suggest the same but... forgot :)
Nov 18, 2016 at 9:12 AM
Basically this exists already as part of the half-automated update process. I "just" need to extract it and make it the "distribution" Package. And I can tell the update process works for 100%. It is accompained by a signed XML holding the Version Number of the most recent packages. The installed software downloads the XML, compares to the installed Version and notifies the user....
I vote for WIxSharp!
Thx a lot for the support.