3

Just a little setup. We have the .Net framework 3.5, 4.0, 4.5, and 4.6.1 installed.

If we build a .Net application or assembly using .Net framework 3.5 and then we set the app.config to run using a single supported runtime of 4.6.1:

<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.1"/></startup>

Which version of the framework is actually being utilized?

This question came from from reading this post: How do I force an application compiled to target .NET Framework 4 to run under .NET framework 4.6.1?. @Hans Passant states that the TargetFrameworkAttribute will dictate how the framework will behave and he talks about there being switches that cause specific code to run, etc.. However, I haven't found anything that explains which version of the core .Net framework will be running in this scenario.

Will any calls to the .Net framework use the .Net 3.5 code base, the .Net 4.0 code base (because of the 4.0 clr version), or will the latest and greatest .Net 4.6.1 code base be run because that is the latest version installed using the clr 4.0?

Edit: To show the manifest that does not specifically state the runtime version.

// Metadata version: v2.0.50727
.assembly extern mscorlib
{
  .publickeytoken = (B7 7A 5C 56 19 34 E0 89 )                         // .z\V.4..
  .ver 2:0:0:0
}
.assembly ConsoleApplicationVersionTest
{
  .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilationRelaxationsAttribute::.ctor(int32) = ( 01 00 08 00 00 00 00 00 ) 
  .custom instance void [mscorlib]System.Runtime.CompilerServices.RuntimeCompatibilityAttribute::.ctor() = ( 01 00 01 00 54 02 16 57 72 61 70 4E 6F 6E 45 78   // ....T..WrapNonEx
                                                                                                             63 65 70 74 69 6F 6E 54 68 72 6F 77 73 01 )       // ceptionThrows.

  // --- The following custom attribute is added automatically, do not uncomment -------
  //  .custom instance void [mscorlib]System.Diagnostics.DebuggableAttribute::.ctor(valuetype [mscorlib]System.Diagnostics.DebuggableAttribute/DebuggingModes) = ( 01 00 07 01 00 00 00 00 ) 

  .custom instance void [mscorlib]System.Reflection.AssemblyTitleAttribute::.ctor(string) = ( 01 00 1D 43 6F 6E 73 6F 6C 65 41 70 70 6C 69 63   // ...ConsoleApplic
                                                                                              61 74 69 6F 6E 56 65 72 73 69 6F 6E 54 65 73 74   // ationVersionTest
                                                                                              00 00 ) 
  .custom instance void [mscorlib]System.Reflection.AssemblyDescriptionAttribute::.ctor(string) = ( 01 00 00 00 00 ) 
  .custom instance void [mscorlib]System.Reflection.AssemblyConfigurationAttribute::.ctor(string) = ( 01 00 00 00 00 ) 
  .custom instance void [mscorlib]System.Reflection.AssemblyCompanyAttribute::.ctor(string) = ( 01 00 00 00 00 ) 
  .custom instance void [mscorlib]System.Reflection.AssemblyProductAttribute::.ctor(string) = ( 01 00 1D 43 6F 6E 73 6F 6C 65 41 70 70 6C 69 63   // ...ConsoleApplic
                                                                                                61 74 69 6F 6E 56 65 72 73 69 6F 6E 54 65 73 74   // ationVersionTest
                                                                                                00 00 ) 
  .custom instance void [mscorlib]System.Reflection.AssemblyCopyrightAttribute::.ctor(string) = ( 01 00 12 43 6F 70 79 72 69 67 68 74 20 C2 A9 20   // ...Copyright .. 
                                                                                                  20 32 30 31 36 00 00 )                            //  2016..
  .custom instance void [mscorlib]System.Reflection.AssemblyTrademarkAttribute::.ctor(string) = ( 01 00 00 00 00 ) 
  .custom instance void [mscorlib]System.Runtime.InteropServices.ComVisibleAttribute::.ctor(bool) = ( 01 00 00 00 00 ) 
  .custom instance void [mscorlib]System.Runtime.InteropServices.GuidAttribute::.ctor(string) = ( 01 00 24 34 36 36 31 33 34 32 33 2D 38 39 34 30   // ..$46613423-8940
                                                                                                  2D 34 39 36 65 2D 61 31 37 32 2D 37 36 36 31 31   // -496e-a172-76611
                                                                                                  64 30 66 31 32 32 38 00 00 )                      // d0f1228..
  .custom instance void [mscorlib]System.Reflection.AssemblyFileVersionAttribute::.ctor(string) = ( 01 00 07 31 2E 30 2E 30 2E 30 00 00 )             // ...1.0.0.0..
  .hash algorithm 0x00008004
  .ver 1:0:0:0
}
.module ConsoleApplicationVersionTest.exe
// MVID: {11493526-C9AC-45F0-9784-D7712809998C}
.imagebase 0x00400000
.file alignment 0x00000200
.stackreserve 0x00100000
.subsystem 0x0003       // WINDOWS_CUI
.corflags 0x00000001    //  ILONLY
// Image base: 0x00000000010B0000
Community
  • 1
  • 1
Mikanikal
  • 1,082
  • 8
  • 12
  • Did you check the TargetFrameworkAttribute attribute as Hans stated with ildasm on your exe? What value has it? – rene Aug 03 '16 at 20:19
  • @rene I have looked through the manifest from ildasm but I do not see where it states that attribute. Is there a specific line that gives that information? – Mikanikal Aug 03 '16 at 20:39
  • You're looking for `.custom instance void [mscorlib]System.Runtime.Versioning.TargetFrameworkAttribute::.ctor(string) ` – rene Aug 03 '16 at 20:44
  • @rene That does not exist in my manifest. I will update the question in just a second to include the manifest showing what is included. – Mikanikal Aug 03 '16 at 20:45
  • That attribute is probably introduced after 2.0. Your exe is targetting the 2.0 runtime – rene Aug 03 '16 at 21:02
  • It is not clear why you think you need to know which version of .NET your process is using. But the marked duplicate explains how to get the version of the CLR being used for the current process. Note that this is subtly different from the framework version; see related duplicate https://stackoverflow.com/questions/4556423/how-do-i-detect-what-net-version-the-application-is-running-on for details on that. – Peter Duniho Aug 03 '16 at 22:01

2 Answers2

2

We have the .Net framework 3.5, 4.0, 4.5, and 4.6.1 installed

That is not accurate, such a configuration is not possible. The rule is that you can have only one version of .NET 2.0 through 3.5 installed, it targets the v2.0.50727 runtime. And one version of .NET 4.0 through 4.6.2 installed, it targets the v4.0.30319 runtime.

So your machine might have had 4.0 installed. When you installed 4.5 it overwrote the 4.0 install. No trace of it is remaining. 4.5 is quite capable of running programs that target 4.0, Microsoft spent an enormous amount of effort to ensure that 4.5 is backwards compatible with 4.0. And likewise, when you installed 4.6.1 it overwrote 4.5

// Metadata version: v2.0.50727

The ildasm.exe dump you posted clearly shows that the assembly targets the 2.0.50727 runtime. So easy peasy, it is going to run on the 3.5 version. It will run just as well on a 2.0 install, it doesn't use any assemblies that were added in 3.0 and 3.5. Maybe that's your cue, Microsoft increments the framework version when it adds new assemblies. Only when it creates a new runtime version can you have another side-by-side install. That's not very likely to happen anytime soon, not counting .NETCore which took a completely different approach.

Hans Passant
  • 922,412
  • 146
  • 1,693
  • 2,536
  • It's not going to run on the 3.5 version if that version isn't installed. With the `supportedRuntime` setting permitting the use of 4.0, if the older runtime version isn't installed, the program will still run on a newer installation with only 4.0 or higher. And in that case, it will be running using the newer runtime, since that's the only one available. If someone really needs to know what version of the runtime is being used in a running process, they need to check the `Environment.Version` variable (or inspect the registry and derive that information). – Peter Duniho Aug 03 '16 at 21:59
  • Hmm, not that relevant given the first line in the post. Adding a .config file is not that superior to just adding the framework version. Which is automagic, Windows just offers to install it for you when you start the program. And no, Environment.Version does *not* tell you anything about the framework version you are using, only the runtime version. – Hans Passant Aug 03 '16 at 22:05
  • And if they one day remove .NET 3.5? Or run on a different system? Or maybe someone else with the same basic question comes here looking for an answer, but they have a slightly different configuration? Questions and answers are supposed to be useful to future readers, and new Q&A shouldn't duplicate existing Q&A; there's nothing here that adds to the information already available on Stack Overflow. – Peter Duniho Aug 03 '16 at 22:08
  • Then it will offer to put it back again when you start the program of course. Even experienced .NET programmers have trouble distinguishing the framework version from the runtime version. Consider reading the post more carefully, you'll learn something new. – Hans Passant Aug 03 '16 at 22:12
  • _"Then it will offer to put it back again when you start the program of course"_ -- I haven't done a ton of config testing, but that's not been my experience. The reason I've ever run into the `supportedRuntime` setting is because I've had situations where I've got a .NET program targeting pre-4.0 which I want to run on a 4.0-or-later system. I just get an error, not an offer to re-install the old .NET. But even if I did get an offer to re-install the old .NET, if I uninstalled it, it was for a reason. Why would I reinstall it when I can run my program fine just by changing a setting? – Peter Duniho Aug 03 '16 at 22:24
  • Click the Ask Question button please. Describe your problem in detail, the Windows version is important. And do keep in mind that Win7 is the new XP, never uninstall pre-installed system components. – Hans Passant Aug 03 '16 at 22:35
  • @HansPassant Thank you for that information. Some concepts are more clear now but the root of my question wasn't entirely answered. Please see the **Edit 2** section at the bottom of my question, I added a scenario that probably should have been part of the original question for clarity on what we are trying to verify. – Mikanikal Aug 04 '16 at 16:16
  • "Boom, our application dies" requires another question that properly documents the mishap along with a Fuslogvw.exe trace. You cannot invalidate my answer by just adding new questions to your original. – Hans Passant Aug 04 '16 at 16:19
  • @HansPassant I was not invalidating your answer. "Boom, our application dies" is because you cannot load a 4.6.1 assembly into a running 3.5 application *without* the .config supportRuntime="v4.0" entry. It will throw a `BadImageFormatException`, which i understand and did not want to detail because I did not want to get away from my original question. What code paths will each application or loaded assembly follow? My edit was to further explain my specific question as I felt I may have not been as clear as I hoped when first writing it. The question has always stayed the same. Thank you. – Mikanikal Aug 04 '16 at 19:38
  • I understand your answer and I greatly appreciate you responding as you have. The answer you gave clarifies a confusion with the installed frameworks and also describes the path that the application built to 3.5 will take, but it does not describe the path that the loaded assembly built to a later framework will take. That was the part that I felt was not clear enough in my initial posting. That is the more important part of the question also. – Mikanikal Aug 04 '16 at 19:48
0

You can use ILDasm.exe and parse the output. The below is powershell function I uses all the time, and can be very easily migrated to .net VB/C#. Modify the ILDasm.exe path as per your environment.

function Get-NetFrameworkVersion
{
  param([string]$path)

if($path)
 {

    $ildasmpath = 'C:\Program Files (x86)\Microsoft SDKs\Windows\v8.1A\bin\NETFX 4.5.1 Tools\ildasm.exe'
    $ildasmswitches = '/header /noil /text'

    $netframeworkverspattern="//\s+'v(?<version>[0-9.]+)'\s+Version String"

    $pinfo = New-Object System.Diagnostics.ProcessStartInfo
    $pinfo.FileName = $ildasmpath
    $pinfo.RedirectStandardOutput = $true
    $pinfo.UseShellExecute = $false
    $pinfo.CreateNoWindow = $true

    $pinfo.WindowStyle = [System.Diagnostics.ProcessWindowStyle]::Hidden
    $pinfo.Arguments = '"' + $path + '" ' + $ildasmswitches
    $p = New-Object System.Diagnostics.Process
    $p.StartInfo = $pinfo
    $p.Start() | Out-Null

    $dumpcontent = $p.StandardOutput.ReadToEnd()

    $p.WaitForExit()

    if([string]$dumpcontent -match $netframeworkverspattern)
    {
       $Matches.version
    }

 }
}
Cogent
  • 404
  • 7
  • 16