在MSBuild中使用32位的“Program Files”目录

42
在64位版本的Windows中,32位软件被安装在"c:\program files (x86)"。这意味着您不能使用$(programfiles)来获取到(32位)软件的路径。所以我需要一个$(ProgramFiles32)在我的MSBuild项目中克服这个问题。我不想根据正在运行的操作系统改变项目。
我有一个解决方案,我会发布出来,但也许有更简单/更好的方法。

很多答案都在纠结于一个给定的答案是否适用于.NET 2.0和4.0,但我没有在问题中看到这一点。也许你可以告诉我们你计划针对哪个版本的.NET? - BrainSlugs83
@BrainSlugs83 这个问题是在 .net 4.0 不存在的时候提出的。但通常情况下,我总是更喜欢一个适用于任何版本的解决方案,以避免在切换版本时需要进行更改。被接受的答案解释了在较新的 msbuild 版本中要使用什么,但也提供了一个适用于旧版本并且在新版本中也有效的解决方案。因此,我不明白将我的问题限制在特定版本上的意义所在。 - wimh
7个回答

49
在MSBuild 4.0+中,有一个$(MSBuildProgramFiles32)属性可以直接使用(特别是如果您准备在文件顶部放置ToolsVersion="4.0"以确保它可用,并在不可用时Fail Fast)。
如果您需要在MSBuild 2.0或更高版本的环境中执行正确操作(即VS 2005环境),完整解决方案如下:
<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. https://dev59.com/z3RC5IYBdhLWcg3wUvQS
       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>

很遗憾,通过<PropertyGroup><CreateProperty>进行渐进增强/polyfill覆盖MSBuild保留属性名称MSBuildProgramFiles32的操作被MSBuild 4.0+拒绝,因此无法使其更加整洁并仍然支持.NET 2.0。


1
渐进增强在 MSBuild 4 上给我报错了:错误 MSB4004: "MSBuildProgramFiles32" 属性已被保留,无法修改。 - Wernight
@Wernight 感谢您的指出 - 已删除渐进增强的内容。如果您的评论消失了,我最终会删除这个! - Ruben Bartelink

16

我的解决方案是查看是否存在"c:\program files (x86)",如果存在,则认为这是64位操作系统。否则使用普通的程序文件目录:

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

我可以像这样使用它

<Exec WorkingDirectory="src\app1" Command='"$(ProgramFiles32)\doxygen\bin\doxygen" Doxyfile' />

6
在非英语版的Windows上,这将会出现严重问题,因为“program files” 不总是被称作“program files”。 - jalf
1
我认为环境变量%programfiles%指向任何语言中的程序文件目录。至少在德语中,32位版本只添加了“(x86)”:http://www.tipps-fuer-windows-vista.de/img/Navigation/Navi1.gif 不过,对于日语我不确定。 - wimh
3
请检查名为%ProgramFiles(x86)%的环境变量是否存在。如果存在,则表示您正在使用64位操作系统,这就是您想要的路径。如果不存在,则使用由%ProgramFiles%指定的路径。 - RobH
3
使用 Condition="Exists('$(PROGRAMFILES) (x86)')" 在 .net 4.0.21006 的 msbuild 中搜索 C:\Program Files (x86) (x86)。 - Jedidja
1
@Jedidja 你的意思是当使用__32位__版本的MSBuild时。如果使用Framework64版本,则可以正常工作。已确认适用于4.0.30319,包括.NET 4.5更新版。还要注意的是,尽管你的观点是正确的,但并不妨碍答案实际上是有效的,即在MSBuild 2和4 x86和x64上计算program files (x86)(根据你提供的信息,我本来想把我的+1变成-1)。 - Ruben Bartelink

12
在MSBuild 4.0中,$(MSBuildProgramFiles32)将会给出32位的Program Files目录。

1
之前我已经写了我的答案,希望不会因为重复而引起不快(这个例子是为了解释我对@JaredPar答案的评论)。 - Ruben Bartelink
@Ruben-Bartelink,我喜欢你在回答中提到的备选方案。所以我选择了它作为采纳的答案。 - wimh

10

尝试使用"$(MSBuildExtensionsPath32)\.."


双点之前应该有一个斜杠。也许我打错了,或者网页吃掉了它。 - dan
带斜杠的方式可以正常工作。而且看起来比我的解决方案更简洁。这是我使用它的方法: <PropertyGroup> <ProgramFiles32>$(MSBuildExtensionsPath32)..</ProgramFiles32> </PropertyGroup> - wimh
-1 这种方法没有任何添加的价值。如果可用MSBuildExtensionsPath32,MSBuildProgramFiles32也将可用。(经过测试 - 尽管此帖子表明在指定ToolsVersion为3.5时,在FW 2 MSBuild上绝对不支持) - Ruben Bartelink
如果您正在使用dotnet.exe,则此解析为C:\ Program Files \ dotnet \ sdk \ ... - alastairtree

4
我认为一个稍微可靠一点的方法是获取环境变量 "ProgramFiles(x86)"。在Windows的64位进程中,这将指向32位程序文件目录。在32位Windows版本上它将为空,我相信在wow64进程上也是如此。
最近我在一些PowerShell脚本中遇到了几乎同样的问题。我写了一篇博客文章,介绍了如何解决程序文件目录问题。虽然语言不同,但可能会对您有所帮助。 http://blogs.msdn.com/jaredpar/archive/2008/10/21/program-files-i-just-want-the-32-bit-version.aspx

你尝试过转义括号吗?我不是一个经常使用msbuild的用户,所以我不知道这是否可能。 - JaredPar
使用反斜杠或单引号转义并没有起作用。但我也不是一个熟练的MSBuild用户。可能应该提出一个新问题来解决这个转义问题... - wimh
6
+1;转义语法为$(ProgramFiles%28x86%29),详情请参阅http://msdn.microsoft.com/en-us/library/ms228186%28VS.80%29.aspx。 - Ruben Bartelink
1
此答案中有产品化的评论(实际上是因为忘记答案才来到这里的!)但如果你想点赞, 请点赞 这个先存在的答案 - Ruben Bartelink
@JaredPar:虽然你是对的,WOW64进程拥有“Program Files(x86)”环境变量并且可以用于为32位系统生成有用的结果,但不幸的是,“Program Files(x86)”环境变量在64位MSBuild进程(基于2.0或4.0)中不可用。 (msbuild/diag显示存在的环境变量)。PROCESSOR_ARCHITECTURE(=AMD64)是存在的最有用的值。 - Ruben Bartelink
显示剩余2条评论

1
我在寻找一种通用的MSbuild方式来查看操作系统是32位还是64位时,偶然发现了这个问题。如果其他人也遇到同样的问题,可以使用以下方法:
<PropertyGroup>
  <OSBits Condition="$(ProgramW6432) != ''">x64</OSBits>
  <OSBits Condition="$(OSBits) == ''">x32</OSBits>
</PropertyGroup>

显然,%ProgramW6432% 只在64位系统上设置。


1
考虑将问题作为顶级问题提出并自己回答——这通常被认为是一件好事。 - Ruben Bartelink
请参考 PROCESSOR_ARCHITECTURE 变量(例如,AMD64)以获得更严格的控制。 - Ruben Bartelink
是的,OP的问题可以通过$(ProgramW6432)轻松解决。我猜测当OP发布问题时,这个变量可能还不存在。 - smead

1
如果你使用32位的Visual Studio工具(尤其是在VS2012中,有三个不同的命令提示符可供选择),$(ProgramFiles)指向“Program Files (x86)”

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接