diff --git a/build/scripts/Build.fs b/build/scripts/Build.fs index c5fe17fad..8d1f6240c 100644 --- a/build/scripts/Build.fs +++ b/build/scripts/Build.fs @@ -124,7 +124,7 @@ module Build = /// When running on Windows and not CI, also runs MSBuild Build on .NET Framework let Build () = // On Windows, pre-build this first to prevent a parallel build race condition: both - // Elastic.Apm.Profiler.Managed (net462 TFM) and AspNetFullFrameworkSampleApp reference + // Elastic.Apm.Profiler.Managed (net462 and net472 TFMs) and AspNetFullFrameworkSampleApp reference // Elastic.Apm.AspNetFullFramework, so a parallel solution build can hit a file lock // on the obj DLL. Pre-building ensures the output exists and incremental build skips it. if isWindows then dotnet "build" aspNetFullFramework @@ -320,7 +320,7 @@ module Build = ) Directory.GetDirectories((Paths.BuildOutput "Elastic.Apm.Profiler.Managed"), "*", SearchOption.TopDirectoryOnly) - |> Array.filter (fun dir -> isWindows || not (dir.EndsWith("net462"))) + |> Array.filter (fun dir -> isWindows || (not (dir.EndsWith("net462")) && not (dir.EndsWith("net472")))) |> Seq.map DirectoryInfo |> Seq.iter (fun sourceDir -> copyDllsAndPdbs (profilerDir.CreateSubdirectory(sourceDir.Name)) sourceDir) diff --git a/src/profiler/Elastic.Apm.Profiler.Managed.Loader/Startup.NetFramework.cs b/src/profiler/Elastic.Apm.Profiler.Managed.Loader/Startup.NetFramework.cs index e5cd72d9e..562052db7 100644 --- a/src/profiler/Elastic.Apm.Profiler.Managed.Loader/Startup.NetFramework.cs +++ b/src/profiler/Elastic.Apm.Profiler.Managed.Loader/Startup.NetFramework.cs @@ -11,6 +11,7 @@ using System; using System.IO; using System.Reflection; +using Microsoft.Win32; namespace Elastic.Apm.Profiler.Managed.Loader { @@ -18,9 +19,46 @@ public partial class Startup { private static string ResolveDirectory() { - var framework = "net462"; var directory = ReadEnvironmentVariable("ELASTIC_APM_PROFILER_HOME") ?? string.Empty; - return Path.Combine(directory, framework); + + // Prefer the net472 build on .NET Framework 4.7.2+ — it supports VerifyServerCert and ServerCert + // via HttpClientHandler.ServerCertificateCustomValidationCallback (added in 4.7.1). + // Fall back to net462 if the net472 directory is absent (e.g. older profiler zip) so loading + // still succeeds rather than silently failing to resolve assemblies. + if (IsNet472OrHigher()) + { + var net472Dir = Path.Combine(directory, "net472"); + if (System.IO.Directory.Exists(net472Dir)) + { + Logger.Log(LogLevel.Debug, "Resolving assemblies from {0}", net472Dir); + return net472Dir; + } + + Logger.Log(LogLevel.Warning, + "Running on .NET Framework 4.7.2+ but net472 directory not found at {0}. " + + "Falling back to net462: VerifyServerCert and ServerCert will have no effect.", + net472Dir); + } + + var net462Dir = Path.Combine(directory, "net462"); + Logger.Log(LogLevel.Debug, "Resolving assemblies from {0}", net462Dir); + return net462Dir; + } + + private static bool IsNet472OrHigher() + { + try + { + using var key = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Full"); + // Release value 461808 corresponds to .NET Framework 4.7.2 on Windows 10 1803+; + // 461814 is the value for all other OS versions. Checking >= 461808 covers both. + var release = Convert.ToInt32(key?.GetValue("Release") ?? 0); + return release >= 461808; + } + catch + { + return false; + } } private static Assembly ResolveDependencies(object sender, ResolveEventArgs args) diff --git a/src/profiler/Elastic.Apm.Profiler.Managed/Elastic.Apm.Profiler.Managed.csproj b/src/profiler/Elastic.Apm.Profiler.Managed/Elastic.Apm.Profiler.Managed.csproj index c476d2bd4..183530d7e 100644 --- a/src/profiler/Elastic.Apm.Profiler.Managed/Elastic.Apm.Profiler.Managed.csproj +++ b/src/profiler/Elastic.Apm.Profiler.Managed/Elastic.Apm.Profiler.Managed.csproj @@ -1,7 +1,7 @@ - net462;netstandard2.0;netstandard2.1;net8.0 + net462;net472;netstandard2.0;netstandard2.1;net8.0 false $(DefineConstants);PROFILER_MANAGED @@ -12,26 +12,24 @@ - - + - - + + - - + + -