About Windows Installer, the .NET Framework, and Visual Studio.
A common question is how to create a Windows Installer package that installs 32-bit binaries on 32-bit platforms, and both 32- and 64-bit binaries on 64-bit platforms. If you're actually trying to install 64-bit binaries to appropriate directories and write to the 64-bit view of the registry, the short answer is that you can't build a single package.
Before delving into the details, let me clarify that both 32- and 64-bit binaries can be installed to either ProgramFilesFolder or ProgramFiles64Folder, but may not load correctly into a 64- or 32-bit process, respectively. The Windows Installer SDK, for example, installs all of x86, IA64, and x64 binaries under ProgramFilesFolder for link time support.
But when you need to install files correctly, multiple packages are required because not all 64-bit features are lit up if the package isn't marked as a 64-bit package. That is, the Template summary property must distinctly list Intel for x86, Intel64 for IA64, and x64 for x64 platforms (AMD64 is supported for backward compatibility). The Template summary property documentation clearly states that only a single platform can be specified.
A Windows Installer package cannot be marked as supporting both Intel64 and x64; for example, the Template Summary property value of Intel64,x64 is invalid. A Windows Installer package cannot be marked as supporting both 32-bit and 64-bit platforms; for example, Template Summary property values such as Intel,x64 or Intel,Intel64 are invalid.
A Windows Installer package cannot be marked as supporting both Intel64 and x64; for example, the Template Summary property value of Intel64,x64 is invalid.
A Windows Installer package cannot be marked as supporting both 32-bit and 64-bit platforms; for example, Template Summary property values such as Intel,x64 or Intel,Intel64 are invalid.
Attempting to install a 64-bit package on a 32-bit platform results in Windows error 1633. Assuming no other errors, installing a 32-bit package on a 64-bit platform appears to work but components may not actually be installed correctly. Files in components to be installed under ProgramFiles64Folder, for example, wind up in ProgramFilesFolder.
If we examine a log from a 32-bit package with 64-bit components defined that is installed on an x64 platform, we can see that directories like ProgramFiles64Folder are initially defined correctly but are redirected back to the 32-bit equivalent directory.
MSI (s) (B4:20) [16:59:17:976]: WIN64DUALFOLDERS: 'C:\Program Files (x86)\' will substitute 17 characters in 'C:\Program Files\' folder path. (mask argument = 0, the folder pair's iSwapAttrib member = 0).MSI (s) (B4:20) [16:59:17:976]: PROPERTY CHANGE: Modifying ProgramFiles64Folder property. Its current value is 'C:\Program Files\'. Its new value: 'C:\Program Files (x86)\'.
But when writing registry values, you can see by the BinaryType parameter that Windows Installer knows the difference and Windows Installer will write to the correct 32- or 64-bit view of the registry. You can confirm this using a tool like regedit.exe.
MSI (s) (B4:20) [16:59:18:445]: Executing op: RegOpenKey(Root=-2147483646,Key=Software\Heath Stewart\Example,,BinaryType=0)MSI (s) (B4:20) [16:59:18:445]: Executing op: RegAddValue(,Value=32-bit Example,)MSI (s) (B4:20) [16:59:18:445]: Executing op: RegAddValue(Name=Example,Value=32-bit Registry Example,)MSI (s) (B4:20) [16:59:18:445]: Executing op: RegOpenKey(Root=-2147483646,Key=Software\Heath Stewart\Example,,BinaryType=1)MSI (s) (B4:20) [16:59:18:445]: Executing op: RegAddValue(,Value=64-bit Example,)MSI (s) (B4:20) [16:59:18:445]: Executing op: RegAddValue(Name=Example,Value=64-bit Registry Example,)
You can't simply condition components using standard properties like VersionNT64. Even when installing a package marked as Intel on a 64-bit machine, properties like VersionNT64, Msix64, and Intel64 are still defined.
Essentially, most features are lit-up depending on the machine type except for where files are installed. Even 64-bit custom actions will run in a 64-bit custom action server despite how the package is attributed as shown in the log snippet below. For binary custom actions, the bitness of the binary will dictate whether a 32- or 64-bit custom action server is created. For script custom actions, the custom action must be attributed with msidbCustomActionType64BitScript (0x1000); of course, you shouldn't use script custom actions anyway.
MSI (s) (74:14) [11:04:43:203]: Executing op: CustomActionSchedule(Action=CA_OutputString32,ActionType=1025,Source=BinaryData,Target=OutputString,)MSI (s) (74:14) [11:04:43:203]: Creating MSIHANDLE (1) of type 790536 for thread 3604MSI (s) (74:44) [11:04:43:203]: Invoking remote custom action. DLL: C:\WINDOWS\Installer\MSI243.tmp, Entrypoint: OutputStringMSI (s) (74:2C) [11:04:43:203]: Generating random cookie.MSI (s) (74:2C) [11:04:43:219]: Created Custom Action Server with PID 2260 (0x8D4).MSI (s) (74:64) [11:04:43:250]: Running as a service.MSI (s) (74:64) [11:04:43:250]: Hello, I'm your 32bit Impersonated custom action server.MSI (s) (74:14) [11:04:43:250]: Executing op: CustomActionSchedule(Action=CA_OutputString64,ActionType=1025,Source=BinaryData,Target=OutputString,)MSI (s) (74:14) [11:04:43:250]: Creating MSIHANDLE (3) of type 790536 for thread 3604MSI (s) (74:8C) [11:04:43:266]: Invoking remote custom action. DLL: C:\WINDOWS\Installer\MSI244.tmp, Entrypoint: OutputStringMSI (s) (74:2C) [11:04:43:266]: Generating random cookie.MSI (s) (74:2C) [11:04:43:266]: Created Custom Action Server with PID 3480 (0xD98).MSI (s) (74:08) [11:04:43:282]: Running as a service.MSI (s) (74:64) [11:04:43:282]: Hello, I'm your 64bit Impersonated custom action server.
This is predicated on whether or not you're invoking embedded binaries or installed binaries. Since your installed binaries in a 32-bit package will install to the 32-bit location even on a 64-bit machine, whichever file was installed last - taking into account default file versioning - would dictate the bitness of the custom action server.
If the goal is to decrease the payload, you can still create separate packages but define your media entries so that 32- and 64-bit payloads (each of IA64 and x64 if you'll be installing either) are in separate cabinets. Read more about file sequences and media entries. You can then put all the .msi packages in the same directory and put all of the cabinets in that directory as well. This is actually how the .NET Framework 2.0 SP1 and 3.0 SP1 packages are laid out. Cabinets can be shared between packages for common files, rather than having distinct sets for each supported platform.
I have a feeling that you are referring to my blog post, but they are not always required depending on how your application is structured. For a purely 64-bit it doesn't make any sense to stuff the bits into a hybrid 32-bit package. Hence, I referred to a "common" feature in my post. When you are dealing with an application that has both 32-bit and 64-bit binaries, using this structure makes perfect sense.
It's not fair to categorically say that it absolutely requires a 64-bit package. Very few "64-bit" applications are purely 64-bit anyway.
AJ, actually I get this question internally quite often. But if you install to 64-bit locations like [ProgramFiles64Folder] the package type must be 64-bit. If you're not installing to redirected folders like [ProgramFiles64Folder] then this isn't required, but then why are you installing to such a location anyway? For per-user installs perhaps, but per-machine installs should be installed to Program Files.
Intel64 should really be IA-64. EM64T was renamed Intel64 with Intel's Core 2.
Yuhong, "Intel64" was created before Intel released EM64T. It was kept consistent with simply "Intel" though "AMD64" was deprecated in favor of "x64" - all fairness given to Intel. The same is not true of "Intel", which was the predominant manufacturer of x86. It all has to do with backward compatibility. When "AMD64" was switched to "x64", not a lot of packages were using "AMD64" so the impact was far less.
I have a 100% .NET application that I now want to be able to install on both 32-bit and 64-bit platforms. All my binaries use the "Any CPU" architecture, so my application is 32/64-bit agnostic. On both 32-bit and 64-bit platforms I want to install to c:\Program Files; in fact, I need to put some files there to integrate with SQL Reporting Services. And since everything is .NET "Any CPU", I want a single package so that I do not have to duplicate my entire feature and component trees.
Is this possible?
MC, to install into Program Files you will need separate packages. But you can at least use the same WiX authoring to conditionally build separate packages. But does SQL Reporting Services require 64-bit binaries on 64-bit machines?
This is a lame limitation. If a .NET binary works on every platform, then the Setup should do the same. The only reason for putting AMD64 in the Platforms (sic) at all is to prevent the hack that MSI redirects system files and registry entries into the wow64 equivelent directories. This sucks.
So basically then, given the long description of how 32 on a 64 machine is "partially" implemented, the best analysis would be that someone designing the installer simply "messed up" and the rest of us have to work around their mistakes :-( Would that be a fair description of the problem?