ISWIX, LLC View Christopher Painter's profile on LinkedIn profile for Christopher Painter at Stack Overflow, Q&A for professional and enthusiast programmers

Wednesday, January 16, 2008

Even developers are concerned about the environment...

Recently in the wix-users email list the following question was asked about hybrid installations:
Is it possible to create one MSI for both 32bit and 64 bit OS

Oh wait... You mean it's not that type of hybrid? Well then, I think I can still help you out.

5 years ago, there were very few people would have even thought about supporting the x64 environment. However, the current processor lineups from the major vendors support both instruction sets natively. The usage of 64-bit operating systems is increasing. Making it more prevalent is the fact that the x64 versions of Windows contain a 32-bit subsystem to support 32-bit applications. This means in most cases, you don't have to abandon your beloved 32-bit applications.

No one wants to maintain two installation codebases, it would be most convenient if both versions of the binaries could be supported in one installation package. Here is where the problem creeps in. Windows Installer only supports one processor architecture in the Summary Information stream. Of course, I wouldn't even bother telling you this if there wasn't a convenient work-around.

Since x64 versions of Windows support 32-bit applications, we can mark the Template Summary property as Intel;1033. While this does instruct Windows Installer to treat this package as a 32-bit package, Windows Installer allows for this package to contain components marked as 64-bit. Since the question comes from the wix-users list, we'll focus on WiX for this entry, but the same principles can be applied to any Windows Installer project no matter what your tool of choice is.

Consider the following snippet from a WiX product file:
<Directory Id="TARGETDIR" Name="SOURCEDIR">
<Directory Id="ProgramFilesFolder"
ShortSourceName="Progra~1" SourceName="Program Files">
<Directory Id="INSTALLDIR"
Name="Test">
<Component
Id="Common.txt" Guid="{FC288E49-D459-4EE8-AC1F-375497A6E09B}">
<File Id=" Common.txt" Name="Common.txt" KeyPath="yes" DiskId="1" Source="SourceDir\File\Common.txt"/>
</Component>
<Component
Id="_32bit.txt" Guid="{E4D22ADB-9A91-4388-8F5E-391DB1CCB211}">
<Condition>(VersionNT) AND (NOT VersionNT64)</Condition>
<File Id="_32bit.txt" Name="32bit.txt" KeyPath="yes" DiskId="1" Source="SourceDir\File\_32bit.txt"/>
</Component>
</Directory>
</Directory>
<Directory Id="ProgramFiles64Folder"
ShortSourceName="PROGRA~1" SourceName="Program Files">
<Directory Id="INSTALLDIR1" Name="Test">
<Component
Id="_64bit.txt" Guid="{8EE8C9CE-2F2A-43B8-B6B6-AD8E01A4109F}" Win64="yes">
<Condition>(VersionNT64)</Condition>
<File Id="_64bit.txt" Name="64bit.txt" KeyPath="yes" DiskId="1" Source="SourceDir\File\_64bit.txt"/>
</Component>
</Directory>
</Directory>
</Directory>
<Feature Id="32bit" AllowAdvertise="system"
Display="expand" Level="3" Title="32bit">
<ComponentRef Id="_32bit.txt"/>
<Condition Level="0">VersionNT64</Condition>
</Feature>
<Feature Id="64bit" AllowAdvertise="system"
Display="expand" Level="3" Title="64bit">
<ComponentRef Id="_64bit.txt"/>
<Condition Level="0">VersionNT
AND NOT VersionNT64</Condition>
</Feature>
<Feature Id="Common" AllowAdvertise="system"
Display="expand" Level="3" Title="Common">
<ComponentRef Id="Common.txt"/>
</Feature>

The application is divided in to three features, 32-bit, 64-bit, and a common feature. The common feature contains the bits that will be common to both architectures and of course the other features contain the architecture specific bits. The components are then conditionalized to only install on their supported architectures. The good news is, this is not theory, I have tested this and it works.

There is of course a reason why you would not want to do this. Look at the size of the .NET Framework 3.5 redistributable. It's HUGE! ...and why do you think that is? Correct, it contains the binaries for all of the different processor architectures. Aaron Stebner has discussed this in a pretty recent blog post of his.

So no, it's not that kind of hybrid, but it should at least help you in your development efforts.

6 comments:

  1. Thanks for the tip! This would have helped a lot in a recent installation that I created. In that case I resorted to using custom actions to determine if it was a 32 bit or 64 bit OS.

    ReplyDelete
  2. I'm suprised MSI doesn't error on this since they say you can't do that.

    Cool stuff :-)

    ReplyDelete
  3. I have some concerns about recommending this method. You propose that the Summary Information Stream not supporting multiple processors is a design limitation based on the fact that technology changed. I disagree with that assessment. I believe the limitation is intentional and that there are valid reasons for the limitation.

    The install you created is very basic. In particular, have you tested this with a 64-bit component that contains registry entries? I haven't tried this so I'm not certain, but one of my concerns is that registry entries associated with the 64-bit component would be placed in the 32-bit registry location instead of the 64-bit registry location. Another concern (also not tested) would be if both 32-bit components and 64-bit components are added to the same "Program Files (x86)" folder on 64-bit systems. There are a couple of reasons why you don't want that to happen: 1) If you have a 32-bit executable with the same name as a 64-bit executable. 2) Future OS upgrades - the reason MS has you split the two locations is so that when 32-bit support is dropped, the 64-bit parts of the application will still work (if developed according to MS best practices, in theory anyway). Lastly, what if you need to do a 64-bit registry lookup? Will a 32-bit package with 64-bit components allow/properly evaluate using the msidbLocatorType64bit in the Type field of the RegLocator table? These are just a few of my concerns that apply to more complex installation packages. Users should evaluate their needs based on their own situations and realize that, yes, there really is a reason why the Summary Information Stream doesn't support specifying multiple processor architectures.

    ReplyDelete
  4. Colby,
    Your concerns are valid. I should have emphasized in the post that this should be used for a truly hybrid application. A pure 64-bit application should be in a 64-bit Windows Installer package. An application that contains a mix of architectures works fine with this approach. I have used this several times.

    i.e. You have an application where certain binaries are changed for 64-bit, but the application retains some 32-bit binaries. The example will work with the scenario described.

    Perhaps I made the mistake of assuming that hybrid applied to both the installation and the application.

    ReplyDelete
  5. Aaron,
    I got that. I was referring to hybrid installations as well (packages containing both 32-bit components and 64-bit components).

    ReplyDelete
  6. Colby,
    Right, your application will use the 32-bit portion of the registry, except for your 64-bit components which will use reflection to also write to the 32-bit portion of the registry.

    You can also have hybrid registry edits as well. Create two components with the same registry keys. Mark one as a 64-bit component. It will write to the 64-bit portion of the registry. You would want to do this because not all of the registry is reflected.

    This is not the end-all, be-all, guide. Every application will behave differently for a given circumstance. Just a guideline because someone asked.

    ReplyDelete