Click here to Skip to main content
15,614,766 members
Please Sign up or sign in to vote.
4.00/5 (1 vote)
Hi experts,

that solution I'm working on includes, besides others, a setup project and a class library that holds code for the setup's custom actions.

One of the tasks of said library is to "pre-install" a customized FTDI USB driver[^]. It edits the registry value HKLM\Software\Microsoft\Windows\CurrentVersion\DevicePath to let the actual driver installation know where to find the driver files.
Microsoft.Win32.RegistryKey key = Microsoft.Win32.Registry.LocalMachine;
key = key.CreateSubKey("Software");
key = key.CreateSubKey("Microsoft");
key = key.CreateSubKey("Windows");
key = key.CreateSubKey("CurrentVersion");

string name = "DevicePath";

string value = (string)key.GetValue(name, null, Microsoft.Win32.RegistryValueOptions.DoNotExpandEnvironmentNames);

key.SetValue(name, value + "; MyExtension", Microsoft.Win32.RegistryValueKind.ExpandString);
This used to work fine.

Now I'm testing on a Windows 7 64 bit machine and the correct registry entry doesn't get edited but HKLM\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\DevicePath instead (note the "Wow6432Node" just below "Software").

The actual USB driver installation starts when the corresponding device is plugged in the PC for the first time, just as normal. With the info at this point in the registry, it doesn't succeed, though.

My solution
The setup's custom action determines what platform it runs on and accordingly calls a 32- or 64bit version of Microsoft.Win32.Registry.LocalMachine[^].
using System;
using System.Collections.Generic;
using System.Text;

namespace PlatformDependent32
    public class RegistryKeys
        public static Microsoft.Win32.RegistryKey HKLM
            get { return (Microsoft.Win32.Registry.LocalMachine); }
Above code is the only code in a class library project with compilation set to x86. Nearly a duplicate exists in another class library project set to compile for x64.

That doesn't work.

The setup project lists both projects' resulting dll files in its Found Dependencies section, but refuses to include the 64bit version because the setup itself is set to x86. And there's no Any CPU setting available.

Does anyone have an idea how to get those two libraries into the setup so the custom action can write to the correct registry key?
Updated 24-Oct-13 20:31pm

As far as I was able to figure out, more or less recently, in this year, the Windows Installer itself is not capable of targeting Any CPU model. It's looks like it is not designed to cover this feature specific to .NET. So, in practice, you need to provide two or three MSIs for two or three target instruction-set architectures. The user need to use proper 64-bit architecture or choose to use only the i86 (32-bit) architecture as compatible with any of the Intel-based systems.

This is of course funny, because, if your product itself uses pure "Any CPU" target (if so, good for you :-)), there won't be any difference in functionality. Even if you install the product from x86 MSI, on 64-bit systems it will be executed as a 64-bit process (in contrast to application compiled to x86: they will be executed via WoW64 as 32-bit processes). There is only one difference: even if you implement custom directory for the installation (which I would highly recommend), the user won't be able to use x86 MSI to put anything in the "Program Files" directory: on 64-bit systems, it will be forced to "Program Files (x86)". To me, it looks like a total lame, but this is one of those Windows lame things. Hope it could be somehow improved in future.

I don't know what toolset do you use. If you want to see how to take into account 1) target project instruction set ("Platform", in Microsoft jargon), 2) the instruction set default for target OS, (note that these things are very different), I can only suggest my past answers on WiX toolset (which I highly recommend though):
Custom setup project[^],
How to conditionally install components based on processor architecture type (32-bit or 64-bit)?[^],
How to conditionally install components based on processor architecture type (32-bit or 64-bit)?[^],
installshield and Visual Studio 2012[^].

Share this answer
Dave Kreskowiak 25-Oct-13 16:04pm    
Right on the money. THere's no such thing as a purely "AnyCPU" equivalent in an MSI. You have to supply a 32-bit only and a 64-bit only MSI. This is usually wrapped by a Setup.exe that executes the appropriate installer depending on platform.
Sergey Alexandrovich Kryukov 25-Oct-13 18:14pm    
Thank you, Dave.
Good point; I forgot to mention that we wrap different MSIs in a single setup, which asks the current OS about its natively-supported instruction-set architecture and just launch the most suitable MSI (or deny to install). One little problem that the ways to ask OS about it are platform-dependent (Windows-specific) and not implemented in .NET. What's funny is that even a batch file can do it, by asking appropriate environment variable (PROCESSOR_ARCHITECTURE). By the way, most popular one, x86-64, in Microsoft jargon is called AMD64, by well-known historical reasons...
The problem is a known issue in Visual Studio:

The setup project, even if you set it to be x64 only(!), always inserts the 32 bit version of InstallUtilLib.dll into the msi (we had the problem even when building the setup project on x64 machines!)

InstallUtilLib.dll is used to run the C# installer class(es) in your executable.
Since the 32-bit version is used, it always runs your exe as 32-bit when it does the custom action, which means any registry keys affected by redirection will get mapped to Wow6432Node.

A solution is to fix the MSI using Orca to replace the 32-bit InstallUtilLib.dll with the 64-bit version.

A previous colleague of mine even included a project in some of our solutions to do this, and made it automatic by including it to run after the build. (I doubt I would be allowed to provide the code for this).

A second option, and one that I personally have favoured, is to turn my executables into "self-installing" exes. I.e. get it to run the installation classes (using System.Configuration.Install.AssemblyInstaller) when a /install switch is included on the command line. (An alternative may be to get it to run the installation when it detects that it is the first time it has been run on a machine, such as finding a required registry key is not present).
If you go down this route, don't forget to include /uninstall as well!

This second option makes the installation routine truly "any cpu" because when it is run it will run as 32-bit or 64-bit depending on the CLR in use and will therefore load the correct modules.

I have for the time-being given up with msi completely for new stuff I develop. I simply copy the files into the correct location and run the exe with a /install switch. Even using simple batch scripts to copy everything into the correct location and then run it.
However, I realise this is not a useful solution for wider deployment to joe public (not least because unless you also add all the appropriate install registry keys yourself, the application won't appear in control panel). Nevertheless it was the easier option for what we needed.

In your case, fixing the MSI using Orca is probably the way to go.

Let me know if you need more information about how to do it, and I'll see if I can provide it.

In the meantime, look at your MSI using Orca, and you should see what I mean by InstallUtilLib.dll being 32 bit.

Info on Orca.exe ([^]

Finally, as an afterthought, I have just found this as a possible alternative; it looks as if you might be able to switch off "registry reflection" for your keys - see the 4th paragraph on this page. As I say, I've just found this, so I'll leave you to decide if you want to experiment:[^]

Share this answer

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900