From d43b8459f35f1e16ee8c9d160f6f84234078b5c4 Mon Sep 17 00:00:00 2001 From: Elinor Fung Date: Fri, 19 Apr 2024 15:42:21 -0700 Subject: [PATCH] Add tests --- .../Projects/HostApiInvokerApp/HostFXR.cs | 124 +++++- .../HostActivation.Tests/NativeHostApis.cs | 356 +++++++++++++++++- src/installer/tests/TestUtils/Constants.cs | 2 + 3 files changed, 474 insertions(+), 8 deletions(-) diff --git a/src/installer/tests/Assets/Projects/HostApiInvokerApp/HostFXR.cs b/src/installer/tests/Assets/Projects/HostApiInvokerApp/HostFXR.cs index 3dc91d5ea69f7b..1a67e91e1d8c72 100644 --- a/src/installer/tests/Assets/Projects/HostApiInvokerApp/HostFXR.cs +++ b/src/installer/tests/Assets/Projects/HostApiInvokerApp/HostFXR.cs @@ -8,7 +8,7 @@ namespace HostApiInvokerApp { - public static class HostFXR + public static unsafe class HostFXR { internal static class hostfxr { @@ -59,6 +59,34 @@ internal struct hostfxr_dotnet_environment_info internal IntPtr frameworks; } + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] + internal struct hostfxr_framework_result + { + public nuint size; + public string name; + public string requested_version; + public string resolved_version; + public string resolved_path; + }; + + [StructLayout(LayoutKind.Sequential)] + internal struct hostfxr_resolve_frameworks_result + { + public nuint size; + public nuint resolved_count; + public IntPtr resolved_frameworks; + public nuint unresolved_count; + public IntPtr unresolved_frameworks; + }; + + [StructLayout(LayoutKind.Sequential)] + internal struct hostfxr_initialize_parameters + { + public nuint size; + public IntPtr host_path; + public IntPtr dotnet_root; + } + [UnmanagedFunctionPointer(CallingConvention.Cdecl, CharSet = CharSet.Auto)] internal delegate void hostfxr_resolve_sdk2_result_fn( hostfxr_resolve_sdk2_result_key_t key, @@ -101,6 +129,18 @@ internal static extern int hostfxr_get_dotnet_environment_info( IntPtr reserved, hostfxr_get_dotnet_environment_info_result_fn result, IntPtr result_context); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + internal delegate void hostfxr_resolve_frameworks_result_fn( + IntPtr result, + IntPtr result_context); + + [DllImport(nameof(hostfxr), CharSet = CharSet.Auto, ExactSpelling = true, CallingConvention = CallingConvention.Cdecl)] + internal static extern int hostfxr_resolve_frameworks_for_runtime_config( + string runtime_config_path, + hostfxr_initialize_parameters* parameters, + hostfxr_resolve_frameworks_result_fn callback, + IntPtr result_context); } /// @@ -250,6 +290,85 @@ static void Test_hostfxr_get_dotnet_environment_info(string[] args) Console.WriteLine($"{api} framework paths:[{string.Join(";", frameworks.Select(f => f.path).ToList())}]"); } + /// + /// Test that invokes hostfxr_resolve_frameworks_for_runtime_config. + /// + /// Path to runtime config file + /// (Optional) Path to the directory with dotnet.exe + static unsafe void Test_hostfxr_resolve_frameworks_for_runtime_config(string[] args) + { + if (args.Length < 1) + throw new ArgumentException($"Invalid arguments. Expected: {nameof(hostfxr.hostfxr_resolve_frameworks_for_runtime_config)} []"); + + string runtimeConfigPath = args[0]; + string dotnetRoot = null; + if (args.Length >= 2) + dotnetRoot = args[1]; + + List resolved = new(); + List unresolved = new(); + + IntPtr resultContext = new IntPtr(123); + + hostfxr.hostfxr_resolve_frameworks_result_fn callback = (IntPtr resultPtr, IntPtr contextPtr) => + { + hostfxr.hostfxr_resolve_frameworks_result result = Marshal.PtrToStructure(resultPtr); + + if (result.size != (nuint)sizeof(hostfxr.hostfxr_resolve_frameworks_result)) + throw new Exception($"Unexpected {nameof(hostfxr.hostfxr_resolve_frameworks_result)}.size: {result.size}. Expected: {sizeof(hostfxr.hostfxr_resolve_frameworks_result)}."); + + if (contextPtr != resultContext) + throw new Exception($"Unexpected result_context value: {contextPtr}. Expected: {resultContext}."); + + for (int i = 0; i < (int)result.resolved_count; i++) + { + nint ptr = result.resolved_frameworks + i * Marshal.SizeOf(); + resolved.Add(Marshal.PtrToStructure(ptr)); + } + + for (int i = 0; i < (int)result.unresolved_count; i++) + { + nint ptr = result.unresolved_frameworks + i * Marshal.SizeOf(); + unresolved.Add(Marshal.PtrToStructure(ptr)); + } + }; + + int rc; + hostfxr.hostfxr_initialize_parameters parameters = new() + { + size = (nuint)sizeof(hostfxr.hostfxr_initialize_parameters), + host_path = IntPtr.Zero, + dotnet_root = dotnetRoot != null ? Marshal.StringToCoTaskMemAuto(dotnetRoot) : IntPtr.Zero + }; + try + { + rc = hostfxr.hostfxr_resolve_frameworks_for_runtime_config( + runtime_config_path: runtimeConfigPath, + parameters: ¶meters, + callback: callback, + result_context: resultContext); + } + finally + { + Marshal.FreeCoTaskMem(parameters.dotnet_root); + } + + string api = nameof(hostfxr.hostfxr_resolve_frameworks_for_runtime_config); + LogResult(api, rc); + + Console.WriteLine($"{api} resolved_count: {resolved.Count}"); + foreach (var framework in resolved) + { + Console.WriteLine($"{api} resolved_framework: name={framework.name}, version={framework.resolved_version}, path=[{framework.resolved_path}]"); + } + + Console.WriteLine($"{api} unresolved_count: {unresolved.Count}"); + foreach (var framework in unresolved) + { + Console.WriteLine($"{api} unresolved_framework: name={framework.name}, requested_version={framework.requested_version}, path=[{framework.resolved_path}]"); + } + } + private static void LogResult(string apiName, int rc) => Console.WriteLine(rc == 0 ? $"{apiName}:Success" : $"{apiName}:Fail[0x{rc:x}]"); @@ -269,6 +388,9 @@ public static bool RunTest(string apiToTest, string[] args) case nameof(hostfxr.hostfxr_get_dotnet_environment_info): Test_hostfxr_get_dotnet_environment_info(args); break; + case nameof(hostfxr.hostfxr_resolve_frameworks_for_runtime_config): + Test_hostfxr_resolve_frameworks_for_runtime_config(args); + break; default: return false; } diff --git a/src/installer/tests/HostActivation.Tests/NativeHostApis.cs b/src/installer/tests/HostActivation.Tests/NativeHostApis.cs index c865567ec23c82..f829ac046a61f2 100644 --- a/src/installer/tests/HostActivation.Tests/NativeHostApis.cs +++ b/src/installer/tests/HostActivation.Tests/NativeHostApis.cs @@ -11,6 +11,14 @@ namespace Microsoft.DotNet.CoreSetup.Test.HostActivation { + internal class ApiNames + { + public const string hostfxr_get_available_sdks = nameof(hostfxr_get_available_sdks); + public const string hostfxr_resolve_sdk2 = nameof(hostfxr_resolve_sdk2); + public const string hostfxr_get_dotnet_environment_info = nameof(hostfxr_get_dotnet_environment_info); + public const string hostfxr_resolve_frameworks_for_runtime_config = nameof(hostfxr_resolve_frameworks_for_runtime_config); + } + public class NativeHostApis : IClassFixture { private SharedTestState sharedTestState; @@ -20,13 +28,6 @@ public NativeHostApis(SharedTestState fixture) sharedTestState = fixture; } - private class ApiNames - { - public const string hostfxr_get_available_sdks = nameof(hostfxr_get_available_sdks); - public const string hostfxr_resolve_sdk2 = nameof(hostfxr_resolve_sdk2); - public const string hostfxr_get_dotnet_environment_info = nameof(hostfxr_get_dotnet_environment_info); - } - internal sealed class SdkAndFrameworkFixture : IDisposable { private readonly TestArtifact _artifact; @@ -410,6 +411,332 @@ public void Hostfxr_get_dotnet_environment_info_reserved_is_not_nullptr_fails() .And.HaveStdErrContaining($"{api} received an invalid argument: reserved should be null."); } + [Theory] + [InlineData(false)] + [InlineData(true)] + public void Hostfxr_resolve_frameworks_for_runtime_config(bool isMissing) + { + string api = ApiNames.hostfxr_resolve_frameworks_for_runtime_config; + using (TestArtifact artifact = TestArtifact.Create(api)) + { + (string Name, string Version) requested = ("Framework", "1.2.3"); + string configPath = Path.Combine(artifact.Location, "test.runtimeconfig.json"); + RuntimeConfig.FromFile(configPath) + .WithFramework(requested.Name, requested.Version) + .Save(); + + var builder = new DotNetBuilder(artifact.Location, TestContext.BuiltDotNet.BinPath, "dotnet"); + if (!isMissing) + builder.AddFramework(requested.Name, requested.Version, c => { }); + + DotNetCli dotnet = builder.Build(); + var result = TestContext.BuiltDotNet.Exec(sharedTestState.HostApiInvokerApp.AppDll, api, configPath, dotnet.BinPath) + .CaptureStdOut() + .CaptureStdErr() + .Execute(); + result.Should().Pass() + .And.NotHaveStdErr(); + if (isMissing) + { + result.Should() + .ReturnStatusCode(api, Constants.ErrorCode.FrameworkMissingFailure) + .And.ReturnUnresolvedFramework(requested.Name, requested.Version); + } + else + { + result.Should() + .ReturnStatusCode(api, Constants.ErrorCode.Success) + .And.ReturnResolvedFramework(requested.Name, requested.Version, GetFrameworkPath(requested.Name, requested.Version, dotnet.BinPath)); + + } + } + } + + [Fact] + public void Hostfxr_resolve_frameworks_for_runtime_config_SelfContained() + { + string api = ApiNames.hostfxr_resolve_frameworks_for_runtime_config; + using (TestArtifact artifact = TestArtifact.Create(api)) + { + (string Name, string Version)[] includedFrameworks = new[] { ("Framework", "1.0.0"), ("OtherFramework", "2.3.4"), ("Another", "5.6.7") }; + string configPath = Path.Combine(artifact.Location, "test.runtimeconfig.json"); + RuntimeConfig config = RuntimeConfig.FromFile(configPath); + foreach (var framework in includedFrameworks) + { + config.WithIncludedFramework(framework.Name, framework.Version); + } + + config.Save(); + + var result = TestContext.BuiltDotNet.Exec(sharedTestState.HostApiInvokerApp.AppDll, api, configPath) + .CaptureStdOut() + .CaptureStdErr() + .Execute(); + result.Should().Pass() + .And.NotHaveStdErr() + .And.ReturnStatusCode(api, Constants.ErrorCode.Success); + foreach (var framework in includedFrameworks) + { + // All frameworks included in a self-contained config are resolved to be next to the config + result.Should().ReturnResolvedFramework(framework.Name, framework.Version, artifact.Location); + } + } + } + + // This test only does basic validation the host API with roll-forward settings. Logic is shared for this API + // and framework resolution for running an app. More complex scenarios are covered in FrameworkResolution tests. + [Theory] + [InlineData(false, Constants.RollForwardSetting.Disable)] + [InlineData(false, Constants.RollForwardSetting.LatestPatch)] + [InlineData(false, Constants.RollForwardSetting.Minor)] + [InlineData(false, Constants.RollForwardSetting.LatestMinor)] + [InlineData(false, Constants.RollForwardSetting.Major)] + [InlineData(false, Constants.RollForwardSetting.LatestMajor)] + [InlineData(false, null)] // Default: Minor + [InlineData(true, Constants.RollForwardSetting.Disable)] + [InlineData(true, Constants.RollForwardSetting.LatestPatch)] + [InlineData(true, Constants.RollForwardSetting.Minor)] + [InlineData(true, Constants.RollForwardSetting.LatestMinor)] + [InlineData(true, Constants.RollForwardSetting.Major)] + [InlineData(true, Constants.RollForwardSetting.LatestMajor)] + [InlineData(true, null)] // Default: Minor + public void Hostfxr_resolve_frameworks_for_runtime_config_RollForward(bool isMissing, string rollForward) + { + string api = ApiNames.hostfxr_resolve_frameworks_for_runtime_config; + using (TestArtifact artifact = TestArtifact.Create(api)) + { + (string Name, string Version) requested = ("Framework", "1.2.3"); + string configPath = Path.Combine(artifact.Location, "test.runtimeconfig.json"); + RuntimeConfig.FromFile(configPath) + .WithFramework(requested.Name, requested.Version) + .WithRollForward(rollForward) + .Save(); + + // Use version that matches or doesn't based on roll-forward settings and expected result + Version requestedVersion = Version.Parse(requested.Version); + Version version = rollForward switch + { + Constants.RollForwardSetting.Disable + => isMissing + ? new (requestedVersion.Major, requestedVersion.Minor, requestedVersion.Build + 1) + : requestedVersion, + Constants.RollForwardSetting.LatestPatch + => isMissing + ? new (requestedVersion.Major, requestedVersion.Minor + 1, requestedVersion.Build) + : new (requestedVersion.Major, requestedVersion.Minor, requestedVersion.Build + 1), + Constants.RollForwardSetting.Minor or Constants.RollForwardSetting.LatestMinor or null + => isMissing + ? new (requestedVersion.Major + 1, requestedVersion.Minor, requestedVersion.Build) + : new (requestedVersion.Major, requestedVersion.Minor + 1, requestedVersion.Build), + Constants.RollForwardSetting.Major or Constants.RollForwardSetting.LatestMajor + => isMissing + ? new (requestedVersion.Major - 1, requestedVersion.Minor, requestedVersion.Build) + : new (requestedVersion.Major + 1, requestedVersion.Minor, requestedVersion.Build), + _ => throw new ArgumentException($"Invalid roll forward setting: {rollForward}") + }; + + string actualVersion = version.ToString(3); + DotNetCli dotnet = new DotNetBuilder(artifact.Location, TestContext.BuiltDotNet.BinPath, "dotnet") + .AddFramework(requested.Name, actualVersion, c => { }) + .Build(); + + var result = TestContext.BuiltDotNet.Exec(sharedTestState.HostApiInvokerApp.AppDll, api, configPath, dotnet.BinPath) + .CaptureStdOut() + .CaptureStdErr() + .Execute(); + result.Should().Pass() + .And.NotHaveStdErr(); + if (isMissing) + { + result.Should() + .ReturnStatusCode(api, Constants.ErrorCode.FrameworkMissingFailure) + .And.ReturnUnresolvedFramework(requested.Name, requested.Version); + } + else + { + result.Should() + .ReturnStatusCode(api, Constants.ErrorCode.Success) + .And.ReturnResolvedFramework(requested.Name, actualVersion, GetFrameworkPath(requested.Name, actualVersion, dotnet.BinPath)); + } + } + } + + [Theory] + [InlineData(false)] + [InlineData(true)] + public void Hostfxr_resolve_frameworks_for_runtime_config_NoDotnetRoot(bool isMissing) + { + string api = ApiNames.hostfxr_resolve_frameworks_for_runtime_config; + using (TestArtifact artifact = TestArtifact.Create(api)) + { + string requestedVersion; + if (isMissing) + { + // Request a higher major framework version than the one available relative to the running hostfxr + Version existingVersion = Version.Parse(TestContext.MicrosoftNETCoreAppVersion.Contains('-') + ? TestContext.MicrosoftNETCoreAppVersion[..TestContext.MicrosoftNETCoreAppVersion.IndexOf('-')] + : TestContext.MicrosoftNETCoreAppVersion); + Version newerVersion = new Version(existingVersion.Major + 1, existingVersion.Minor, existingVersion.Build); + requestedVersion = newerVersion.ToString(3); + } + else + { + // Request the framework version that is available relative to the running hostfxr + requestedVersion = TestContext.MicrosoftNETCoreAppVersion; + } + + (string Name, string Version) requested = (Constants.MicrosoftNETCoreApp, requestedVersion); + + + string configPath = Path.Combine(artifact.Location, "test.runtimeconfig.json"); + RuntimeConfig.FromFile(configPath) + .WithFramework(requested.Name, requested.Version) + .Save(); + + // API should use the running hostfxr when dotnet root is not specified + var result = TestContext.BuiltDotNet.Exec(sharedTestState.HostApiInvokerApp.AppDll, api, configPath) + .CaptureStdOut() + .CaptureStdErr() + .Execute(); + result.Should().Pass() + .And.NotHaveStdErr(); + if (isMissing) + { + result.Should() + .ReturnStatusCode(api, Constants.ErrorCode.FrameworkMissingFailure) + .And.ReturnUnresolvedFramework(requested.Name, requested.Version); + } + else + { + result.Should() + .ReturnStatusCode(api, Constants.ErrorCode.Success) + .And.ReturnResolvedFramework(requested.Name, requested.Version, GetFrameworkPath(requested.Name, requested.Version, TestContext.BuiltDotNet.BinPath)); + } + } + } + + [Theory] + [InlineData(false)] + [InlineData(true)] + public void Hostfxr_resolve_frameworks_for_runtime_config_MultipleFrameworks(bool isMissing) + { + string api = ApiNames.hostfxr_resolve_frameworks_for_runtime_config; + using (TestArtifact artifact = TestArtifact.Create(api)) + { + (string Name, string Version)[] requestedFrameworks = new[] { ("Framework", "1.0.0"), ("OtherFramework", "2.3.4"), ("Another", "5.6.7") }; + (string Name, string Version)[] expectedFrameworks = isMissing ? requestedFrameworks[..^1] : requestedFrameworks; + + string configPath = Path.Combine(artifact.Location, "test.runtimeconfig.json"); + RuntimeConfig config = RuntimeConfig.FromFile(configPath); + foreach (var framework in requestedFrameworks) + { + config.WithFramework(framework.Name, framework.Version); + } + + config.Save(); + + var builder = new DotNetBuilder(artifact.Location, TestContext.BuiltDotNet.BinPath, "dotnet"); + foreach (var framework in expectedFrameworks) + { + builder.AddFramework(framework.Name, framework.Version, c => { }); + } + + DotNetCli dotnet = builder.Build(); + + var result = TestContext.BuiltDotNet.Exec(sharedTestState.HostApiInvokerApp.AppDll, api, configPath, dotnet.BinPath) + .CaptureStdOut() + .CaptureStdErr() + .Execute(); + result.Should().Pass() + .And.NotHaveStdErr(); + foreach (var framework in expectedFrameworks) + { + result.Should().ReturnResolvedFramework(framework.Name, framework.Version, GetFrameworkPath(framework.Name, framework.Version, dotnet.BinPath)); + } + + if (isMissing) + { + var missingFramework = requestedFrameworks[^1]; + result.Should() + .ReturnStatusCode(api, Constants.ErrorCode.FrameworkMissingFailure) + .And.ReturnUnresolvedFramework(missingFramework.Name, missingFramework.Version); + } + else + { + result.Should() + .ReturnStatusCode(api, Constants.ErrorCode.Success); + } + } + } + + [Fact] + public void Hostfxr_resolve_frameworks_for_runtime_config_IncompatibleFrameworks() + { + string api = ApiNames.hostfxr_resolve_frameworks_for_runtime_config; + using (TestArtifact artifact = TestArtifact.Create(api)) + { + (string Name, string Version) incompatibleLower = ("OtherFramework", "1.2.3"); + (string Name, string Version) incompatibleHigher = (incompatibleLower.Name, "2.0.0"); + (string Name, string Version)[] requested = new[] { ("Framework", "3.0.0"), incompatibleLower }; + + string configPath = Path.Combine(artifact.Location, "test.runtimeconfig.json"); + RuntimeConfig config = RuntimeConfig.FromFile(configPath); + foreach (var framework in requested) + { + config.WithFramework(framework.Name, framework.Version); + } + + config.Save(); + + var expectedFramework = requested[0]; + DotNetCli dotnet = new DotNetBuilder(artifact.Location, TestContext.BuiltDotNet.BinPath, "dotnet") + .AddFramework(expectedFramework.Name, expectedFramework.Version, + c => c.WithFramework(incompatibleHigher.Name, incompatibleHigher.Version)) + .Build(); + + TestContext.BuiltDotNet.Exec(sharedTestState.HostApiInvokerApp.AppDll, api, configPath, dotnet.BinPath) + .CaptureStdOut() + .CaptureStdErr() + .Execute() + .Should().Pass() + .And.NotHaveStdErr() + .And.ReturnStatusCode(api, Constants.ErrorCode.FrameworkCompatFailure) + .And.ReturnResolvedFramework(expectedFramework.Name, expectedFramework.Version, GetFrameworkPath(expectedFramework.Name, expectedFramework.Version, dotnet.BinPath)) + .And.ReturnUnresolvedFramework(incompatibleLower.Name, incompatibleLower.Version) + .And.ReturnUnresolvedFramework(incompatibleHigher.Name, incompatibleHigher.Version); + } + } + + [Fact] + public void Hostfxr_resolve_frameworks_for_runtime_config_InvalidConfig() + { + string api = ApiNames.hostfxr_resolve_frameworks_for_runtime_config; + using (TestArtifact artifact = TestArtifact.Create(api)) + { + (string Name, string Version) requested = ("Framework", "1.2.3"); + string configPath = Path.Combine(artifact.Location, "test.runtimeconfig.json"); + RuntimeConfig.FromFile(configPath) + .WithFramework(requested.Name, requested.Version) + .Save(); + + DotNetCli dotnet = new DotNetBuilder(artifact.Location, TestContext.BuiltDotNet.BinPath, "dotnet") + .AddFramework(requested.Name, requested.Version, c => { }) + .Build(); + + string frameworkPath = Path.Combine(dotnet.BinPath, "shared", requested.Name, requested.Version); + File.WriteAllText(Path.Combine(frameworkPath, $"{requested.Name}.runtimeconfig.json"), "{}"); + + TestContext.BuiltDotNet.Exec(sharedTestState.HostApiInvokerApp.AppDll, api, configPath, dotnet.BinPath) + .CaptureStdOut() + .CaptureStdErr() + .Execute() + .Should().Pass() + .And.NotHaveStdErr() + .And.ReturnStatusCode(api, Constants.ErrorCode.InvalidConfigFile) + .And.ReturnUnresolvedFramework(requested.Name, requested.Version, frameworkPath); + } + } + [Fact] public void Hostpolicy_corehost_set_error_writer_test() { @@ -443,6 +770,9 @@ public void HostRuntimeContract_bundle_probe() .And.HaveStdOutContaining("host_runtime_contract.bundle_probe is not set"); } + private static string GetFrameworkPath(string name, string version, string dotnetRoot) + => Path.Combine(dotnetRoot, "shared", name, version); + public class SharedTestState : IDisposable { public TestApp HostApiInvokerApp { get; } @@ -490,5 +820,17 @@ public static AndConstraint ReturnStatusCode(this Comma ? assertion.HaveStdOutContaining($"{apiName}:Success") : assertion.HaveStdOutContaining($"{apiName}:Fail[0x{statusCode:x}]"); } + + public static AndConstraint ReturnResolvedFramework(this CommandResultAssertions assertion, string name, string version, string path) + { + string api = ApiNames.hostfxr_resolve_frameworks_for_runtime_config; + return assertion.HaveStdOutContaining($"{api} resolved_framework: name={name}, version={version}, path=[{path}]"); + } + public static AndConstraint ReturnUnresolvedFramework(this CommandResultAssertions assertion, string name, string version, string path = "") + { + string api = ApiNames.hostfxr_resolve_frameworks_for_runtime_config; + return assertion.HaveStdOutContaining($"{api} unresolved_framework: name={name}, requested_version={version}, path=[{path}]") + .And.NotHaveStdOutContaining($"{api} resolved_framework: name={name}"); + } } } diff --git a/src/installer/tests/TestUtils/Constants.cs b/src/installer/tests/TestUtils/Constants.cs index 2a5ce2f53f5593..f79889dc17aee7 100644 --- a/src/installer/tests/TestUtils/Constants.cs +++ b/src/installer/tests/TestUtils/Constants.cs @@ -119,8 +119,10 @@ public static class ErrorCode public const int ResolverInitFailure = unchecked((int)0x8000808b); public const int ResolverResolveFailure = unchecked((int)0x8000808c); public const int LibHostInvalidArgs = unchecked((int)0x80008092); + public const int InvalidConfigFile = unchecked((int)0x80008093); public const int AppArgNotRunnable = unchecked((int)0x80008094); public const int FrameworkMissingFailure = unchecked((int)0x80008096); + public const int FrameworkCompatFailure = unchecked((int)0x8000809c); public const int BundleExtractionFailure = unchecked((int)0x8000809f); public const int COMPlusException = unchecked((int)0xe0434352);