如何强制运行编译为 .NET Framework 4 的应用程序以在 .NET Framework 4.6.1 下运行?

9

我进行了相当多的研究,但没有找到任何合适的答案。以下是情况描述。

我有一个应用程序,编译目标为.NET Framework 4。在运行时,我希望该应用程序实际上在.NET Framework 4.6.1中执行。到目前为止,我发现了两个选项。

  1. 重新编译应用程序以针对.NET Framework 4.6.1
  2. 在app.config中添加configuration/startup/supportedRuntime元素,其版本为"v4.0",sku=".NETFramework,Version=v4.6.1"

选项1不可取,因为这将需要重新发布软件。

选项2并未达到我的预期。它似乎会检查是否安装了CLR 4.0(而非Framework 4.0),如果没有,则提示下载适当的SKU以安装它。安装后,应用程序仍在.NET Framework 4.0下执行。

作为测试,也是发布此问题的原因,我创建了一个简单的控制台应用程序,其代码如下:

Console.WriteLine(System.Net.ServicePointManager.SecurityProtocol);

如果编译针对 .NET Framework 4,那么输出结果是

Ssl3,Tls

如果编译针对 .NET Framework 4.6.1,那么输出结果是

Tls,Tls11,Tls12


2
https://dev59.com/vV4c5IYBdhLWcg3wHXNS#28502562 - Hans Passant
1
谢谢Hans。这确实解决了我的示例中的问题,我们将使用它。我也对更大的问题的答案感兴趣。你是否碰巧知道是否可能,如果可能的话,如何实现? - Greg Bogumil
1
哎呀,.NET 4.0版本的SecurityProtocolType没有你需要的枚举值。你的应用程序如何可能正确解释属性值是一个非常难猜测的问题。但如果它能工作,那就好了。 - Hans Passant
1
只有安装了4.5框架才能正常运行,即使应用程序是针对4.0编译的。 - Greg Bogumil
为什么不将您的目标更改为4.5.2+?您正在尝试的是一种可能会以无法测试的方式破坏的黑客行为。从4.0到4.5.2的变化*并不是那么大。 - Panagiotis Kanavos
2个回答

11
我正在询问强制使用框架4.6.1运行编译在框架4.0下的应用程序的一般情况。好的,您已经通过app.config文件条目实现了这个功能。这使得this feature成为可能,用户在安装4.6.1之前无法运行程序。他所要做的就是点击“是”按钮。虽然这种情况不经常发生,但只要用户负责通过Windows Update保持机器更新,4.6.1应该始终存在于机器上。如果他有意不这样做,那么“强制”很可能不会被接受。
但这并不是你问题的实质。你想让你的程序表现得像它安装在4.6.1上一样。这是一个非常不同的问题。请注意,2)没有起作用,你不能那么轻易地愚弄运行时。编译器在可执行文件中嵌入了TargetFrameworkAttribute属性,这是运行时用来确定它应该如何行事的属性。使用ildasm.exe查看,双击清单即可查看。你的app.config条目不能覆盖它。
最大的问题是,.NET 4.5在运行时和框架程序集中都有重大的变化。这些变化足以使版本号提升到5.0。但这总是会给客户带来很多痛苦和困难,因此微软动用了所有的技巧,使4.5(及以上版本)能够像4.0一样运行以前定位于4.0的程序。

不是单一的技巧。一个核心方法是参考程序集存储在c:\ Program Files(x86)\ Reference Assemblies目录中。它们存储了目标包文件。您在最初构建程序时使用存储在C:\ Program Files(x86)\ Reference Assemblies \ Microsoft \ Framework.NETFramework \ v4.0中的那些。如果您重新定向项目,则将使用存储在v4.6.1中的那些。它们非常不同。值得注意的是,您所谈论的 SecurityProtocolType枚举不同,它获得了两个新值。这是一个破坏性的变化,.NET 4.0程序可能会在看到SecurityProtocolType.Tls12时遭受心脏病发作,因为它根本不知道这意味着什么。使用错误的目标包文件构建程序可能会导致深奥的异常

并且其他技巧。在.NET 4.0后的Bug修复版本中进行的修复是有选择性地开启的,具体取决于[TargetFrameworkAttribute],向后兼容性对于确保程序不观察到更改的运行时行为非常重要。而CLR则充满了appcompat开关。我可以指向CoreCLR中的源代码文件,但那实在是太可怕了:)
因此,不,无法使编译为.NET 4.0目标的程序表现得像在更高版本上运行一样。你学到的注册表键和appcontext开关是非常特定的,只适用于ServicePointManager.SecurityProtocol属性。它们存在的原因很简单,因为你不是唯一一个想这样做的客户,TLS版本非常重要。只需确保新的枚举值不会让你的程序出错即可。

标记为答案,因为似乎无法做到我所要求的。您的解释包含了很好的细节和参考。谢谢Hans。 - Greg Bogumil
@GregBogumil,您所尝试的是强制对最终用户进行黑客攻击——您不想升级编译目标(为什么?),但想强制使用新的运行时来使用在4.0中不存在的TLS值。真正的解决方法是更改您的目标。 - Panagiotis Kanavos

4
具体的ServicePointManager.SecurityProtocol决定与4 vs 4.6.1框架的一般问题无关,恐怕没有明确的答案,因为在一般情况下(如果您愿意),这与主题无关。关于具体的答案,请参见此处:缓解:TLS协议 引用如下:
从.NET Framework 4.6开始,System.Net.ServicePointManager和 System.Net.Security.SslStream 类允许使用以下三种协议之一:Tls1.0、Tls1.1 或 Tls 1.2。不支持SSL3.0协议和RC4密码。
如果已安装4.6+,则可以按照文章中的详细说明更改程序的行为而无需重新编译它,只需将以下行添加到.config文件即可:
<configuration>
  ...
  <runtime>
    ...
    <AppContextSwitchOverrides value="Switch.System.Net.DontEnableSchUseStrongCrypto=false" />
  </runtime>
  ...
</configuration>

1
谢谢你的答案。它对我提出的示例问题非常有效,突显了我想要确定的内容。在这个问题中,我询问的是强制针对框架 4.0 编译的应用程序使用框架 4.6.1 运行的一般情况。相比许多人,我更熟悉 CLR 和框架之间的区别,并且明白强制使用不同 CLR 运行是可以实现的。似乎根据这个问题的答案,很可能不能在重新编译应用程序以针对框架 4.6.1 为目标之前完成此操作。 - Greg Bogumil
1
特别感谢您的额外运行时配置更改。基于大量研究,到目前为止我得到的最佳答案是进行注册表更改。对于客户端机器,有时需要管理人员的帮助。 - Greg Bogumil

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