42

In 64 bit versions of windows, 32 bit software is installed in "c:\program files (x86)". This means you cannot use $(programfiles) to get the path to (32 bit) software. So I need a $(ProgramFiles32) to overcome this in my MSBuild project. I don't want to change the project depending on the os it is running on.

I have a solution that I will post, but maybe there is a easier/better way.

Ruben Bartelink
  • 59,778
  • 26
  • 187
  • 249
wimh
  • 15,072
  • 6
  • 47
  • 98
  • A lot of the answers are getting a little bit nitpicky on whether a given answer will work on .NET 2.0 and 4.0 -- but I'm not seeing it in the question -- Perhaps you can tell us what version(s) of .NET you plan to target? – BrainSlugs83 Feb 01 '16 at 22:55
  • @BrainSlugs83 This question was asked in 2008 when .net 4.0 did not exist yet. But generally, I would always prefer a solution which works in any version, to avoid having to make changes when switching version. The accepted answer explains what to use in newer msbuild versions, but also provides a solution for older versions which also works in newer version. So I don't see the point of limiting my question to a specific version. – wimh Feb 01 '16 at 23:15

7 Answers7

49

In MSBuild 4.0+, there's a $(MSBuildProgramFiles32) property for it, which you can confidently employ directly (especially if you're prepared to put a ToolsVersion="4.0" at the top of the file to guarantee it's going to be available and Fail Fast if it's not).

If you're not and need something that can Do The Right Thing even when executed in an MSBuild 2.0 or later environment (i.e., back to VS 2005 environments), the complete solution is:

<PropertyGroup>
    <!--MSBuild 4.0 property-->
    <ProgramFiles32>$(MSBuildProgramFiles32)</ProgramFiles32> 
    <!--Use OS env var as a fallback:- 32 bit MSBuild 2.0/3.5 on x64 will use this-->
    <ProgramFiles32 Condition=" '' == '$(ProgramFiles32)'">$(ProgramFiles%28x86%29)</ProgramFiles32>

    <!-- Handle MSBuild 2.0/3.5 running in 64 bit mode - neither of the above env vars are available. http://stackoverflow.com/questions/336633
       NB this trick (Adding a literal " (x86)" to the 64 bit Program Files path) may or may not work on all versions/locales of Windows -->
    <ProgramFiles32 Condition ="'$(ProgramFiles32)'=='' AND 'AMD64' == '$(PROCESSOR_ARCHITECTURE)'">$(ProgramFiles) (x86)</ProgramFiles32>

    <!--Catch-all - handles .NET 2.0/3.5 non-AMD64 and .NET 2.0 on x86 -->
    <ProgramFiles32 Condition=" '' == '$(ProgramFiles32)' ">$(ProgramFiles)</ProgramFiles32>
</PropertyGroup>

Unfortunately Progressive enhancement / polyfill overriding of the MSBuild reserved property name MSBuildProgramFiles32 via either a <PropertyGroup> or <CreateProperty> is rejected by MSBuild 4.0+ so it can't be made tidier and still support .NET 2.0.

Ruben Bartelink
  • 59,778
  • 26
  • 187
  • 249
  • 1
    The progressive enhancement gives me this error on MSBuild 4: `error MSB4004: The "MSBuildProgramFiles32" property is reserved, and cannot be modified.` – Wernight Oct 27 '11 at 15:49
  • @Wernight Thanks for the catch - removed the progressive enhancement stuff. If your comment disappears, I'll eventually get around to deleting this! – Ruben Bartelink Apr 11 '12 at 09:56
16

My solution is to look whether "c:\program files (x86)" exists, if it exists, asume this is a 64 bit os. Otherwise use the normal program files directory:

<PropertyGroup>
  <ProgramFiles32 Condition="Exists('$(PROGRAMFILES) (x86)')">$(PROGRAMFILES) (x86)</ProgramFiles32>
  <ProgramFiles32 Condition="$(ProgramFiles32) == ''">$(PROGRAMFILES)</ProgramFiles32>
</PropertyGroup>

I can use it like this

<Exec WorkingDirectory="src\app1" Command='"$(ProgramFiles32)\doxygen\bin\doxygen" Doxyfile' />
wimh
  • 15,072
  • 6
  • 47
  • 98
  • 6
    This will break horribly on non-english versions of Windows, as program files is not always called program files. – jalf Dec 06 '08 at 16:19
  • 1
    I think the environment variable %programfiles% points to the program files directory in any language. And at least in German the 32 bits version adds just " (x86)": http://www.tipps-fuer-windows-vista.de/img/Navigation/Navi1.gif Don't know about japanese though. – wimh Dec 06 '08 at 19:04
  • 3
    You need to check for an environment variable called %ProgramFiles(x86)%. If that exists, then you're on a 64-bit OS, and that's the path you want. If it doesn't exist, then use the path specified by %ProgramFiles%. – RobH Jun 02 '09 at 21:35
  • 3
    Using Condition="Exists('$(PROGRAMFILES) (x86)')" searches for C:\Program Files (x86) (x86) in the msbuild from .net 4.0.21006 – Jedidja Mar 25 '10 at 20:59
  • 1
    @Jedidja You mean when one runs the __32 bit__ edition of MSBuild. It works when you use the `Framework64` edition. Confirmed for 4.0.30319 including .NET 4.5 update Beta. Also note that your point, while valid does not prevent the answer from actually 'working', i.e., it does compute `program files (x86)` on MSBuild 2 and 4 x86 and x64 (had been looking to turn my +1 to a -1 based on your info) – Ruben Bartelink Apr 12 '12 at 07:56
12

In MSBuild 4.0, $(MSBuildProgramFiles32) will give you the 32-bit Program Files directory.

Rory MacLeod
  • 11,012
  • 7
  • 41
  • 43
  • 1
    +1 Had written [my answer](http://stackoverflow.com/questions/346175/use-32bit-program-files-directory-in-msbuild/5650767#5650767) before I saw this so hope no offence is taken at the duplication (the example serves to explain my comment on @JaredPar's answer) – Ruben Bartelink Apr 13 '11 at 14:23
  • @Ruben-Bartelink, I like the fallback you mention in your answer. So I chose that as accepted answer. – wimh Apr 14 '11 at 07:24
10

Try "$(MSBuildExtensionsPath32)\.."

ulrichb
  • 19,610
  • 8
  • 73
  • 87
  • There was meant to be a slash before the double dot. Maybe I mistyped or the web page ate it. –  Jun 27 '09 at 06:38
  • With the slash it works fine. And it looks cleaner than my solution. This is how I used it: $(MSBuildExtensionsPath32)\.. – wimh Jul 02 '09 at 21:13
  • -1 This approach doesnt add anything. If MSBuildExtensionsPath32 is available, MSBuildProgramFiles32 will also be available. (Tested - this is definitely not supported on FW 2 MSBuild (inc when specifying a ToolsVersion of 3.5) despite what [this post](http://www.sedodream.com/PermaLink,guid,7e11f92e-e778-4e69-9441-9a6be3512e71.aspx) suggests) – Ruben Bartelink Apr 12 '12 at 07:50
  • This resolves to `C:\Program Files\dotnet\sdk\...` if you are using dotnet.exe – alastairtree Jul 28 '17 at 12:53
4

I think a slighly more reliable way is to grab the Environment variable "ProgramFiles(x86)". In a 64 bit process on Windows this will point to the 32 bit program files directory. It will be empty on a 32 bit version of windows and I believe on a wow64 process

I ran into virtually same problem recently with some PowerShell scripts. I wrote a blog entry on how a worked around the program files directory issue. Different language obviously but it may help you out.

http://blogs.msdn.com/jaredpar/archive/2008/10/21/program-files-i-just-want-the-32-bit-version.aspx

JaredPar
  • 733,204
  • 149
  • 1,241
  • 1,454
  • tried it, but it does not work. "$(PROGRAMFILES(x86))" evaluates to ")". So it looks like it in not possible in msbuild to use an environment variable which includes a ')' character. I did not see anything else in the environment which can be used instead. – wimh Dec 06 '08 at 18:51
  • Did you try escaping the ('s? I'm not a heavy msbuild user so i don't know if this is possible or not – JaredPar Dec 06 '08 at 19:47
  • escaping with a backslash or single quotes did not work. But I'm also not a heavy msbuild user. Probably should ask a new question the escaping... – wimh Dec 06 '08 at 21:16
  • Yeah, I couldn't ever get it to evaluate either. I used Wimmel's solution. It works. – Brendan Enrick Jan 13 '09 at 15:30
  • 6
    +1; syntax for escaping is $(ProgramFiles%28x86%29) See http://msdn.microsoft.com/en-us/library/ms228186%28VS.80%29.aspx – Ruben Bartelink Dec 08 '09 at 15:45
  • 1
    Productized comment in [this answer](http://stackoverflow.com/questions/346175/use-32bit-program-files-directory-in-msbuild/5650767#5650767) (Actually landed here having forgotten the answer!) but if you want to upvote something, please upvote [this one which was there first](http://stackoverflow.com/questions/346175/use-32bit-program-files-directory-in-msbuild/4380939#4380939) – Ruben Bartelink Apr 13 '11 at 14:21
  • @JaredPar: While you're right that a WOW64 process has a `Program Files(x86)` env var and can be used to produce a useful result for 32 bit boxes, unfortunately the `Program Files(x86)` env var is not available in a 64 MSBuild process (2.0 or 4.0-based). (`msbuild /diag` shows the env vars that are present). `PROCESSOR_ARCHITECTURE` (`=AMD64`) is the most useful value that is present. – Ruben Bartelink Apr 11 '12 at 10:46
1

If you run the 32-bit version of the Visual Studio tools (especially in VS2012, there are like 3 different command prompts you can choose from), $(ProgramFiles) points to "Program Files (x86)"

mejdev
  • 3,509
  • 4
  • 24
  • 38
1

I stumbled across this question trying to find a generic way in MSbuild to see if it was a 32- or 64-bit os. In case someone else also find this, I used the following:

<PropertyGroup>
  <OSBits Condition="$(ProgramW6432) != ''">x64</OSBits>
  <OSBits Condition="$(OSBits) == ''">x32</OSBits>
</PropertyGroup>

Apparently %ProgramW6432% is only set on 64-bit systems.

Jedidja
  • 16,610
  • 17
  • 73
  • 112
  • 1
    Consider asking the question [as a top-level question] and answering it yourself - it's generally accepted as a Good Thing – Ruben Bartelink May 20 '11 at 07:24
  • See also the `PROCESSOR_ARCHITECTURE` (which can be e.g., `AMD64` ) variable for tighter control – Ruben Bartelink Apr 11 '12 at 10:49
  • Yeah OP's question can be solved just with $(ProgramW6432). I'm guessing this variable didn't exist when OP posted the question. – smead Jan 22 '16 at 01:15