diff --git a/scripts/common.lib.ps1 b/scripts/common.lib.ps1
index bd5adaba25..1e44242bd0 100644
--- a/scripts/common.lib.ps1
+++ b/scripts/common.lib.ps1
@@ -60,7 +60,7 @@ function Write-Log {
         [string]
         $Level = "Success"
     )
-    
+
     if ($message)
     {
         $color = if ("Success" -eq $Level) { "Green" } else { "Red" }
@@ -116,7 +116,7 @@ function Install-DotNetCli
     & $dotnetInstallScript -InstallDir "${dotnetInstallPath}_x86" -Runtime 'dotnet' -Channel '5.0' -Architecture x86 -NoPath -Version '5.0.16'
     & $dotnetInstallScript -InstallDir "${dotnetInstallPath}_x86" -Runtime 'dotnet' -Channel '6.0' -Architecture x86 -NoPath -Version '6.0.4'
     & $dotnetInstallScript -InstallDir "${dotnetInstallPath}_x86" -Channel '7.0' -Architecture x86 -NoPath -Version $env:DOTNET_CLI_VERSION
-    
+
     $env:DOTNET_ROOT= $dotnetInstallPath
     ${env:DOTNET_ROOT(x86)} = "${dotnetInstallPath}_x86"
 
@@ -260,10 +260,10 @@ public class ProcessOutputter
             AppendLine(e.Data);
 
             if (suppressOutput || e.Data == null)
-            { 
+            {
                 return;
             }
-    
+
             // These handlers can run at the same time,
             // without lock they sometimes grab the color the other
             // one set.
@@ -278,7 +278,7 @@ public class ProcessOutputter
                         // one extra space before the word, to avoid highlighting
                         // warnaserror and similar parameters that are not actual errors
                         //
-                        // The comparison is not done using the insensitive overload because that 
+                        // The comparison is not done using the insensitive overload because that
                         // is too new for PowerShell 5 compiler
                         var lineToLower = line.ToLowerInvariant();
                         Console.ForegroundColor = lineToLower.Contains(" warning")
@@ -387,4 +387,4 @@ function Start-InlineProcess {
         $process.remove_ErrorDataReceived($errorDataReceived)
         $process.Dispose()
     }
-}
\ No newline at end of file
+}
diff --git a/src/Microsoft.TestPlatform.TestHostProvider/Hosting/DotnetTestHostManager.cs b/src/Microsoft.TestPlatform.TestHostProvider/Hosting/DotnetTestHostManager.cs
index 34c4515d10..5b5ea4b682 100644
--- a/src/Microsoft.TestPlatform.TestHostProvider/Hosting/DotnetTestHostManager.cs
+++ b/src/Microsoft.TestPlatform.TestHostProvider/Hosting/DotnetTestHostManager.cs
@@ -52,6 +52,8 @@ public class DotnetTestHostManager : ITestRuntimeProvider2
     private const string TestAdapterRegexPattern = @"TestAdapter.dll";
     private const string PROCESSOR_ARCHITECTURE = nameof(PROCESSOR_ARCHITECTURE);
 
+    private static readonly Version Version6_0 = new(6, 0);
+
     private readonly IDotnetHostHelper _dotnetHostHelper;
     private readonly IEnvironment _platformEnvironment;
     private readonly IProcessHelper _processHelper;
@@ -469,24 +471,7 @@ public virtual TestProcessStartInfo GetTestHostProcessStartInfo(
         // i.e. I've got only private install and no global installation, in this case apphost needs to use env var to locate runtime.
         if (testHostExeFound)
         {
-            string prefix = "VSTEST_WINAPPHOST_";
-            string dotnetRootEnvName = $"{prefix}DOTNET_ROOT(x86)";
-            var dotnetRoot = _environmentVariableHelper.GetEnvironmentVariable(dotnetRootEnvName);
-            if (dotnetRoot is null)
-            {
-                dotnetRootEnvName = $"{prefix}DOTNET_ROOT";
-                dotnetRoot = _environmentVariableHelper.GetEnvironmentVariable(dotnetRootEnvName);
-            }
-
-            if (dotnetRoot != null)
-            {
-                EqtTrace.Verbose($"DotnetTestHostmanager.LaunchTestHostAsync: Found '{dotnetRootEnvName}' in env variables, value '{dotnetRoot}', forwarding to '{dotnetRootEnvName.Replace(prefix, string.Empty)}'");
-                startInfo.EnvironmentVariables.Add(dotnetRootEnvName.Replace(prefix, string.Empty), dotnetRoot);
-            }
-            else
-            {
-                EqtTrace.Verbose($"DotnetTestHostmanager.LaunchTestHostAsync: Prefix '{prefix}*' not found in env variables");
-            }
+            ForwardDotnetRootEnvironmentVariable(startInfo);
         }
 
         startInfo.WorkingDirectory = sourceDirectory;
@@ -565,6 +550,53 @@ bool SilentlyForceToX64()
         }
     }
 
+    internal /* for testing purposes */ void ForwardDotnetRootEnvironmentVariable(TestProcessStartInfo startInfo)
+    {
+        const string prefix = "VSTEST_WINAPPHOST_";
+        const string dotnetRoot = "DOTNET_ROOT";
+        string vstestDotnetRootEnvName = $"{prefix}{dotnetRoot}(x86)";
+
+        // Check if VSTEST_WINAPPHOST_DOTNET_ROOT(x86) is set, if not then looks for VSTEST_WINAPPHOST_DOTNET_ROOT.
+        // If none of these variables is set we exit as we have nothing to forward.
+        var vstestDotnetRootEnvValue = _environmentVariableHelper.GetEnvironmentVariable(vstestDotnetRootEnvName);
+        if (vstestDotnetRootEnvValue is null)
+        {
+            vstestDotnetRootEnvName = $"{prefix}{dotnetRoot}";
+            vstestDotnetRootEnvValue = _environmentVariableHelper.GetEnvironmentVariable(vstestDotnetRootEnvName);
+
+            // None of the forwarding environment variables are set so exit.
+            if (vstestDotnetRootEnvValue is null)
+            {
+                EqtTrace.Verbose($"DotnetTestHostmanager.LaunchTestHostAsync: Prefix '{prefix}*' not found in env variables");
+                return;
+            }
+        }
+
+        // For .NET 6.0 onward, the DOTNET_ROOT* environment variable to set was changed.
+        // This implementation is based on the logic defined in SDK:
+        // https://github.com/dotnet/sdk/blob/c3f8d746f4d5cd87f462d711a3caa7a4f6621826/src/Cli/dotnet/commands/dotnet-run/RunCommand.cs#L264-L279
+        string dotnetRootEnvName;
+        if (Version.Parse(_targetFramework.Version) >= Version6_0)
+        {
+            dotnetRootEnvName = $"{dotnetRoot}_{_processHelper.GetCurrentProcessArchitecture().ToString().ToUpperInvariant()}";
+
+            // SDK side of TP is not checking for the .NET6.0+ environment variables so we want to make sure we
+            // are not overriding user definition.
+            if (_environmentVariableHelper.GetEnvironmentVariable(dotnetRootEnvName) is string dotnetRootEnvValue)
+            {
+                EqtTrace.Verbose($"DotnetTestHostmanager.LaunchTestHostAsync: Found '{vstestDotnetRootEnvName}' in env variables but also found '{dotnetRootEnvName}' with value '{dotnetRootEnvValue}'. Skipping forwarding.");
+                return;
+            }
+        }
+        else
+        {
+            dotnetRootEnvName = vstestDotnetRootEnvName.Replace(prefix, string.Empty);
+        }
+
+        EqtTrace.Verbose($"DotnetTestHostmanager.LaunchTestHostAsync: Found '{vstestDotnetRootEnvName}' in env variables, value '{vstestDotnetRootEnvValue}', forwarding to '{dotnetRootEnvName}' (target framework is {_targetFramework.Name}, Version={_targetFramework.Version}).");
+        startInfo.EnvironmentVariables.Add(dotnetRootEnvName, vstestDotnetRootEnvValue);
+    }
+
     /// <inheritdoc/>
     public IEnumerable<string> GetTestPlatformExtensions(IEnumerable<string> sources, IEnumerable<string> extensions)
     {
diff --git a/test/Microsoft.TestPlatform.TestHostProvider.UnitTests/Hosting/DotnetTestHostManagerTests.cs b/test/Microsoft.TestPlatform.TestHostProvider.UnitTests/Hosting/DotnetTestHostManagerTests.cs
index ac1811ac62..cca14ba21e 100644
--- a/test/Microsoft.TestPlatform.TestHostProvider.UnitTests/Hosting/DotnetTestHostManagerTests.cs
+++ b/test/Microsoft.TestPlatform.TestHostProvider.UnitTests/Hosting/DotnetTestHostManagerTests.cs
@@ -987,6 +987,143 @@ public async Task CleanTestHostAsyncShouldNotThrowIfTestHostIsNotStarted()
         Assert.IsTrue(isVerified);
     }
 
+    [TestMethod]
+    [DataRow("VSTEST_WINAPPHOST_DOTNET_ROOT(x86)", "DOTNET_ROOT(x86)")]
+    [DataRow("VSTEST_WINAPPHOST_DOTNET_ROOT", "DOTNET_ROOT")]
+    public void ForwardDotnetRootEnvironmentVariableWhenTargetFrameworkIsLessThanNet6SetsCorrectEnvVariable(string envVarName, string expectedForwaredName)
+    {
+        // Arrange
+        const string envVarValue = "c:\\SomePath";
+        _mockEnvironmentVariable.Reset();
+        _mockEnvironmentVariable.Setup(x => x.GetEnvironmentVariable(envVarName)).Returns(envVarValue);
+        string runSettingsXml = """
+            <RunSettings>
+                <RunConfiguration>
+                    <TargetFrameworkVersion>net5.0</TargetFrameworkVersion>
+                </RunConfiguration>
+            </RunSettings>
+            """;
+        _dotnetHostManager.Initialize(_mockMessageLogger.Object, runSettingsXml);
+
+        var startInfo = new TestProcessStartInfo { EnvironmentVariables = new Dictionary<string, string>() };
+        // Sanity check
+        Assert.AreEqual(0, startInfo.EnvironmentVariables.Count);
+
+        // Act
+        _dotnetHostManager.ForwardDotnetRootEnvironmentVariable(startInfo);
+
+        // Assert
+        Assert.AreEqual(1, startInfo.EnvironmentVariables.Count);
+        Assert.IsTrue(startInfo.EnvironmentVariables.TryGetValue(expectedForwaredName, out var actualEnvVarValue));
+        Assert.AreEqual(envVarValue, actualEnvVarValue);
+    }
+
+    [TestMethod]
+    [DataRow("DOTNET_ROOT(x86)", "net5.0")]
+    [DataRow("DOTNET_ROOT(x86)", "net6.0")]
+    [DataRow("DOTNET_ROOT", "net5.0")]
+    [DataRow("DOTNET_ROOT", "net6.0")]
+    [DataRow("DOTNET_ROOT_X86", "net5.0")]
+    [DataRow("DOTNET_ROOT_X86", "net6.0")]
+    [DataRow("DOTNET_ROOT_X64", "net5.0")]
+    [DataRow("DOTNET_ROOT_X64", "net6.0")]
+    [DataRow("DOTNET_ROOT_ARM64", "net5.0")]
+    [DataRow("DOTNET_ROOT_ARM64", "net6.0")]
+    public void ForwardDotnetRootEnvironmentVariableWhenIncorrectEnvVarDoesNothing(string envVarName, string framework)
+    {
+        // Arrange
+        const string envVarValue = "c:\\SomePath";
+        _mockEnvironmentVariable.Reset();
+        _mockEnvironmentVariable.Setup(x => x.GetEnvironmentVariable(envVarName)).Returns(envVarValue);
+        string runSettingsXml = $"""
+            <RunSettings>
+                <RunConfiguration>
+                    <TargetFrameworkVersion>{framework}</TargetFrameworkVersion>
+                </RunConfiguration>
+            </RunSettings>
+            """;
+        _dotnetHostManager.Initialize(_mockMessageLogger.Object, runSettingsXml);
+
+        var startInfo = new TestProcessStartInfo { EnvironmentVariables = new Dictionary<string, string>() };
+
+        // Act
+        _dotnetHostManager.ForwardDotnetRootEnvironmentVariable(startInfo);
+
+        // Assert
+        Assert.AreEqual(0, startInfo.EnvironmentVariables.Count);
+    }
+
+    [TestMethod]
+    [DataRow("VSTEST_WINAPPHOST_DOTNET_ROOT(x86)", PlatformArchitecture.X86)]
+    [DataRow("VSTEST_WINAPPHOST_DOTNET_ROOT(x86)", PlatformArchitecture.X64)]
+    [DataRow("VSTEST_WINAPPHOST_DOTNET_ROOT", PlatformArchitecture.X86)]
+    [DataRow("VSTEST_WINAPPHOST_DOTNET_ROOT", PlatformArchitecture.X64)]
+    public void ForwardDotnetRootEnvironmentVariableWhenTargetFrameworkIsGreaterOrEqualsToNet6SetsCorrectEnvVariable(string envVarName, PlatformArchitecture platformArchitecture)
+    {
+        // Arrange
+        const string envVarValue = "c:\\SomePath";
+        _mockEnvironmentVariable.Reset();
+        _mockEnvironmentVariable.Setup(x => x.GetEnvironmentVariable(envVarName)).Returns(envVarValue);
+        _mockProcessHelper.Setup(x => x.GetCurrentProcessArchitecture()).Returns(platformArchitecture);
+        string runSettingsXml = """
+            <RunSettings>
+                <RunConfiguration>
+                    <TargetFrameworkVersion>net6.0</TargetFrameworkVersion>
+                </RunConfiguration>
+            </RunSettings>
+            """;
+        _dotnetHostManager.Initialize(_mockMessageLogger.Object, runSettingsXml);
+
+        var startInfo = new TestProcessStartInfo { EnvironmentVariables = new Dictionary<string, string>() };
+        // Sanity check
+        Assert.AreEqual(0, startInfo.EnvironmentVariables.Count);
+
+        // Act
+        _dotnetHostManager.ForwardDotnetRootEnvironmentVariable(startInfo);
+
+        // Assert
+        Assert.AreEqual(1, startInfo.EnvironmentVariables.Count);
+        Assert.IsTrue(startInfo.EnvironmentVariables.TryGetValue($"DOTNET_ROOT_{platformArchitecture.ToString().ToUpperInvariant()}", out var actualEnvVarValue));
+        Assert.AreEqual(envVarValue, actualEnvVarValue);
+    }
+
+    [TestMethod]
+    [DataRow("VSTEST_WINAPPHOST_DOTNET_ROOT(x86)", PlatformArchitecture.X86)]
+    [DataRow("VSTEST_WINAPPHOST_DOTNET_ROOT(x86)", PlatformArchitecture.X64)]
+    [DataRow("VSTEST_WINAPPHOST_DOTNET_ROOT(x86)", PlatformArchitecture.ARM64)]
+    [DataRow("VSTEST_WINAPPHOST_DOTNET_ROOT", PlatformArchitecture.X86)]
+    [DataRow("VSTEST_WINAPPHOST_DOTNET_ROOT", PlatformArchitecture.X64)]
+    [DataRow("VSTEST_WINAPPHOST_DOTNET_ROOT", PlatformArchitecture.ARM64)]
+    public void ForwardDotnetRootEnvironmentVariableWhenTargetFrameworkIsGreaterOrEqualsToNet6DoesNotOverrideEnvVar(string envVarName, PlatformArchitecture platformArchitecture)
+    {
+        // Arrange
+        const string expectedEnvVarValue = "c:\\SomePath";
+        const string nonForwardedEnvVarValue = "C:\\SomeOtherPath";
+        var expectedForwardedEnvVarName = $"DOTNET_ROOT_{platformArchitecture.ToString().ToUpperInvariant()}";
+        _mockEnvironmentVariable.Reset();
+        _mockEnvironmentVariable.Setup(x => x.GetEnvironmentVariable(envVarName)).Returns(expectedEnvVarValue);
+        _mockEnvironmentVariable.Setup(x => x.GetEnvironmentVariable(expectedForwardedEnvVarName)).Returns(nonForwardedEnvVarValue);
+        _mockProcessHelper.Setup(x => x.GetCurrentProcessArchitecture()).Returns(platformArchitecture);
+        string runSettingsXml = """
+            <RunSettings>
+                <RunConfiguration>
+                    <TargetFrameworkVersion>net6.0</TargetFrameworkVersion>
+                </RunConfiguration>
+            </RunSettings>
+            """;
+        _dotnetHostManager.Initialize(_mockMessageLogger.Object, runSettingsXml);
+
+        var startInfo = new TestProcessStartInfo { EnvironmentVariables = new Dictionary<string, string>() };
+        // Sanity check
+        Assert.AreEqual(0, startInfo.EnvironmentVariables.Count);
+
+        // Act
+        _dotnetHostManager.ForwardDotnetRootEnvironmentVariable(startInfo);
+
+        // Assert
+        Assert.AreEqual(0, startInfo.EnvironmentVariables.Count);
+    }
+
     private void DotnetHostManagerExitCodeTesterHostExited(object? sender, HostProviderEventArgs e)
     {
         _errorMessage = e.Data.TrimEnd(Environment.NewLine.ToCharArray());
diff --git a/test/Microsoft.TestPlatform.TestUtilities/IntegrationTestBase.cs b/test/Microsoft.TestPlatform.TestUtilities/IntegrationTestBase.cs
index 0c29109a95..bfde968231 100644
--- a/test/Microsoft.TestPlatform.TestUtilities/IntegrationTestBase.cs
+++ b/test/Microsoft.TestPlatform.TestUtilities/IntegrationTestBase.cs
@@ -192,7 +192,7 @@ public void InvokeVsTest(string arguments, Dictionary<string, string>? environme
     /// Invokes our local copy of dotnet that is patched with artifacts from the build with specified arguments.
     /// </summary>
     /// <param name="arguments">Arguments provided to <c>vstest.console</c>.exe</param>
-    public void InvokeDotnetTest(string arguments, Dictionary<string, string>? environmentVariables = null)
+    public void InvokeDotnetTest(string arguments, Dictionary<string, string>? environmentVariables = null, bool useDotnetFromTools = false, string? workingDirectory = null)
     {
         var debugEnvironmentVariables = AddDebugEnvironmentVariables(environmentVariables);
 
@@ -207,7 +207,7 @@ public void InvokeDotnetTest(string arguments, Dictionary<string, string>? envir
         // https://github.com/dotnet/sdk/blob/main/src/Cli/dotnet/commands/dotnet-test/VSTestForwardingApp.cs#L30-L39
         debugEnvironmentVariables["VSTEST_CONSOLE_PATH"] = vstestConsolePath;
 
-        ExecutePatchedDotnet("test", arguments, out _standardTestOutput, out _standardTestError, out _runnerExitCode, debugEnvironmentVariables);
+        ExecutePatchedDotnet("test", arguments, out _standardTestOutput, out _standardTestError, out _runnerExitCode, debugEnvironmentVariables, useDotnetFromTools, workingDirectory);
         FormatStandardOutCome();
     }
 
@@ -761,7 +761,7 @@ protected void ExecuteVsTestConsole(string args, out string stdOut, out string s
     /// <param name="stdError"></param>
     /// <param name="exitCode"></param>
     private void ExecutePatchedDotnet(string command, string args, out string stdOut, out string stdError, out int exitCode,
-        Dictionary<string, string>? environmentVariables = null)
+        Dictionary<string, string>? environmentVariables = null, bool useDotnetFromTools = false, string? workingDirectory = null)
     {
         if (environmentVariables is null)
         {
@@ -771,8 +771,8 @@ private void ExecutePatchedDotnet(string command, string args, out string stdOut
         environmentVariables["DOTNET_MULTILEVEL_LOOKUP"] = "0";
 
         var executablePath = IsWindows ? @"dotnet\dotnet.exe" : @"dotnet-linux/dotnet";
-        var patchedDotnetPath = Path.Combine(_testEnvironment.TestArtifactsDirectory, executablePath);
-        ExecuteApplication(patchedDotnetPath, string.Join(" ", command, args), out stdOut, out stdError, out exitCode, environmentVariables);
+        var patchedDotnetPath = Path.Combine(useDotnetFromTools ? _testEnvironment.ToolsDirectory : _testEnvironment.TestArtifactsDirectory, executablePath);
+        ExecuteApplication(patchedDotnetPath, string.Join(" ", command, args), out stdOut, out stdError, out exitCode, environmentVariables, workingDirectory);
     }
 
     protected static void ExecuteApplication(string path, string args, out string stdOut, out string stdError, out int exitCode,
diff --git a/test/TestAssets/ProjectLaunch32BitsProcess/ProjectLaunch32BitsProcess.csproj b/test/TestAssets/ProjectLaunch32BitsProcess/ProjectLaunch32BitsProcess.csproj
new file mode 100644
index 0000000000..0e3ae170ab
--- /dev/null
+++ b/test/TestAssets/ProjectLaunch32BitsProcess/ProjectLaunch32BitsProcess.csproj
@@ -0,0 +1,22 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+  <!-- Imports Common TestAssets props. -->
+  <Import Project="..\..\..\scripts\build\TestAssets.props" />
+
+  <PropertyGroup>
+    <TargetFrameworks>net5.0;net6.0</TargetFrameworks>
+    <Nullable>enable</Nullable>
+    <LangVersion>Preview</LangVersion>
+  </PropertyGroup>
+
+  <ItemGroup>
+    <PackageReference Include="MSTest.TestFramework" Version="$(MSTestFrameworkVersion)" />
+    <PackageReference Include="MSTest.TestAdapter" Version="$(MSTestAdapterVersion)" />
+    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="$(NETTestSdkVersion)" />
+  </ItemGroup>
+
+  <ItemGroup>
+    <ProjectReference Include="..\TestProcess32\TestProcess32.csproj" />
+  </ItemGroup>
+
+</Project>
diff --git a/test/TestAssets/ProjectLaunch32BitsProcess/Test32Bit.cs b/test/TestAssets/ProjectLaunch32BitsProcess/Test32Bit.cs
new file mode 100644
index 0000000000..e6bcc1bffe
--- /dev/null
+++ b/test/TestAssets/ProjectLaunch32BitsProcess/Test32Bit.cs
@@ -0,0 +1,68 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using System;
+using System.Diagnostics;
+
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+namespace UnitTest;
+
+[TestClass]
+public class Test32Bit
+{
+    [TestMethod]
+    public void TheTest()
+    {
+        // Test is based on reproducer from following SDK issue: https://github.com/dotnet/sdk/issues/22647
+        // We cannot run the test in an automatic/coded way because we need to have .NET 5 and 6 SDKs installed
+        // which leads to full disk capacity on CI.
+        // As this change of behavior is quite important we want to make sure we can rerun such manual test
+        // later on so we are keeping it here.
+        // Repro steps:
+        // 1. Build project
+        // 2. Set $env:DOTNET_ROOT_ENV_VAR_NAME = "DOTNET_ROOT(x86)"
+        // 3. Set $env:DOTNET_ROOT_ENV_VAR_VALUE to a 32bits dotnet installation directory for the matching TFM
+        //    (e.g. C:\src\vstest\tools\dotnet_x86)
+        // 4. Add a global.json pinning the .NET version to 5 or 6
+        // 5. Run 'dotnet test ./bin/<Config>/<Pinned_TFM>/ProjectLaunch32BitsProcess.dll'
+        // 6. Test should be succesful
+        // 7. Repeat steps 2 to 5 with $env:DOTNET_ROOT_ENV_VAR_NAME = "DOTNET_ROOT". Although we are running a
+        //    32 bits process, the dotnet detection logic is set to fallback to this variable.
+        // 8. Repeat steps 2 to 5 with $env:DOTNET_ROOT_ENV_VAR_NAME = "DOTNET_ROOT_X86".
+        //    This should work for .NET 6 but fail for .NET 5 (if you don't have a global .NET 5 SDK installed),
+        //    as this new variable is not understood by the .NET 5 detection algorithm.
+        // Debugging tips:
+        // Use the following environment variables to understand how is .NET being resolved.
+        // COREHOST_TRACE = 1
+        // COREHOST_TRACEFILE = "C:\fxr.tx"
+        var process = new Process();
+        process.StartInfo = new ProcessStartInfo
+        {
+            FileName = "TestProcess32.exe",
+            RedirectStandardError = true,
+            RedirectStandardOutput = true,
+        };
+
+        var envVarName = Environment.GetEnvironmentVariable("DOTNET_ROOT_ENV_VAR_NAME");
+        Assert.IsNotNull(envVarName, "Calling process didn't set DOTNET_ROOT_ENV_VAR_NAME.");
+        var envVarValue = Environment.GetEnvironmentVariable("DOTNET_ROOT_ENV_VAR_VALUE");
+        Assert.IsNotNull(envVarValue, "Calling process didn't set DOTNET_ROOT_ENV_VAR_VALUE.");
+        // Set the DOTNET_ROOT* env variable so that the 32bits process can locate dotnet
+        // even if there is no global installation.
+        process.StartInfo.EnvironmentVariables[envVarName] = envVarValue;
+        process.StartInfo.EnvironmentVariables["DOTNET_ROOT_ENV_VAR_NAME"] = envVarName;
+        // Ensure multi-level lookup is disabled so that we don't fallback to machine-wide installation
+        process.StartInfo.EnvironmentVariables["DOTNET_MULTILEVEL_LOOKUP"] = "0";
+
+        process.Start();
+        var stderr = process.StandardError.ReadToEnd();
+        var stdout = process.StandardOutput.ReadToEnd();
+
+        Console.WriteLine($"32bit stdout: {stdout}");
+        Console.WriteLine($"32bit err: {stderr}");
+
+        Assert.IsTrue(string.IsNullOrEmpty(stderr),
+            $"There was some error in process run: {stderr}");
+    }
+}
diff --git a/test/TestAssets/TestAssets.sln b/test/TestAssets/TestAssets.sln
index 8fae3a203e..32b4fcd42a 100644
--- a/test/TestAssets/TestAssets.sln
+++ b/test/TestAssets/TestAssets.sln
@@ -124,6 +124,12 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tools", "Tools\Tools.csproj
 EndProject
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Perfy.TestAdapter", "performance\Perfy.TestAdapter\Perfy.TestAdapter.csproj", "{71BF7EC9-7BEE-4038-8F4E-87032FA4E995}"
 EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "DOTNET_ROOT", "DOTNET_ROOT", "{C06EFF20-F1EA-42B7-B404-D8AB98AA78C0}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestProcess32", "TestProcess32\TestProcess32.csproj", "{4FA80E2C-B3D4-4A6B-99D2-12F95F2C0506}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ProjectLaunch32BitsProcess", "ProjectLaunch32BitsProcess\ProjectLaunch32BitsProcess.csproj", "{A0F37C16-FB73-4538-8DAF-EBF6EB3251FA}"
+EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 		Debug|Any CPU = Debug|Any CPU
@@ -806,6 +812,30 @@ Global
 		{71BF7EC9-7BEE-4038-8F4E-87032FA4E995}.Release|x64.Build.0 = Release|Any CPU
 		{71BF7EC9-7BEE-4038-8F4E-87032FA4E995}.Release|x86.ActiveCfg = Release|Any CPU
 		{71BF7EC9-7BEE-4038-8F4E-87032FA4E995}.Release|x86.Build.0 = Release|Any CPU
+		{4FA80E2C-B3D4-4A6B-99D2-12F95F2C0506}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{4FA80E2C-B3D4-4A6B-99D2-12F95F2C0506}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{4FA80E2C-B3D4-4A6B-99D2-12F95F2C0506}.Debug|x64.ActiveCfg = Debug|Any CPU
+		{4FA80E2C-B3D4-4A6B-99D2-12F95F2C0506}.Debug|x64.Build.0 = Debug|Any CPU
+		{4FA80E2C-B3D4-4A6B-99D2-12F95F2C0506}.Debug|x86.ActiveCfg = Debug|Any CPU
+		{4FA80E2C-B3D4-4A6B-99D2-12F95F2C0506}.Debug|x86.Build.0 = Debug|Any CPU
+		{4FA80E2C-B3D4-4A6B-99D2-12F95F2C0506}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{4FA80E2C-B3D4-4A6B-99D2-12F95F2C0506}.Release|Any CPU.Build.0 = Release|Any CPU
+		{4FA80E2C-B3D4-4A6B-99D2-12F95F2C0506}.Release|x64.ActiveCfg = Release|Any CPU
+		{4FA80E2C-B3D4-4A6B-99D2-12F95F2C0506}.Release|x64.Build.0 = Release|Any CPU
+		{4FA80E2C-B3D4-4A6B-99D2-12F95F2C0506}.Release|x86.ActiveCfg = Release|Any CPU
+		{4FA80E2C-B3D4-4A6B-99D2-12F95F2C0506}.Release|x86.Build.0 = Release|Any CPU
+		{A0F37C16-FB73-4538-8DAF-EBF6EB3251FA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{A0F37C16-FB73-4538-8DAF-EBF6EB3251FA}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{A0F37C16-FB73-4538-8DAF-EBF6EB3251FA}.Debug|x64.ActiveCfg = Debug|Any CPU
+		{A0F37C16-FB73-4538-8DAF-EBF6EB3251FA}.Debug|x64.Build.0 = Debug|Any CPU
+		{A0F37C16-FB73-4538-8DAF-EBF6EB3251FA}.Debug|x86.ActiveCfg = Debug|Any CPU
+		{A0F37C16-FB73-4538-8DAF-EBF6EB3251FA}.Debug|x86.Build.0 = Debug|Any CPU
+		{A0F37C16-FB73-4538-8DAF-EBF6EB3251FA}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{A0F37C16-FB73-4538-8DAF-EBF6EB3251FA}.Release|Any CPU.Build.0 = Release|Any CPU
+		{A0F37C16-FB73-4538-8DAF-EBF6EB3251FA}.Release|x64.ActiveCfg = Release|Any CPU
+		{A0F37C16-FB73-4538-8DAF-EBF6EB3251FA}.Release|x64.Build.0 = Release|Any CPU
+		{A0F37C16-FB73-4538-8DAF-EBF6EB3251FA}.Release|x86.ActiveCfg = Release|Any CPU
+		{A0F37C16-FB73-4538-8DAF-EBF6EB3251FA}.Release|x86.Build.0 = Release|Any CPU
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE
@@ -826,6 +856,8 @@ Global
 		{10AA955C-B412-41A8-899F-8609AAE19F61} = {2633D125-64A7-456C-AD37-F8A6B56C2403}
 		{E166D337-4033-4209-863F-8F77675EAEE8} = {2633D125-64A7-456C-AD37-F8A6B56C2403}
 		{71BF7EC9-7BEE-4038-8F4E-87032FA4E995} = {0C9CA869-32FD-4A9E-8885-E2E19786C746}
+		{4FA80E2C-B3D4-4A6B-99D2-12F95F2C0506} = {C06EFF20-F1EA-42B7-B404-D8AB98AA78C0}
+		{A0F37C16-FB73-4538-8DAF-EBF6EB3251FA} = {C06EFF20-F1EA-42B7-B404-D8AB98AA78C0}
 	EndGlobalSection
 	GlobalSection(ExtensibilityGlobals) = postSolution
 		SolutionGuid = {D2334DAA-F7B2-450E-ABA4-FBC185152500}
diff --git a/test/TestAssets/TestProcess32/Program.cs b/test/TestAssets/TestProcess32/Program.cs
new file mode 100644
index 0000000000..107b3f8908
--- /dev/null
+++ b/test/TestAssets/TestProcess32/Program.cs
@@ -0,0 +1,32 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+using System;
+
+if (Environment.Is64BitProcess)
+{
+    throw new InvalidOperationException("Process is supposed to be 32bits.");
+}
+
+var envVarName = Environment.GetEnvironmentVariable("DOTNET_ROOT_ENV_VAR_NAME");
+if (string.IsNullOrEmpty(envVarName))
+{
+    throw new InvalidOperationException("Could not find 'DOTNET_ROOT_ENV_VAR_NAME' which is supposed to tell us which DOTNET_ROOTXXX to look up.");
+}
+
+var dotnetRootPath = Environment.GetEnvironmentVariable(envVarName);
+if (string.IsNullOrEmpty(dotnetRootPath))
+{
+    throw new InvalidOperationException($"{dotnetRootPath} was not set.");
+}
+
+Console.WriteLine("DOTNET_ROOT(x86)={0}", Environment.GetEnvironmentVariable("DOTNET_ROOT(x86)"));
+Console.WriteLine("DOTNET_ROOT={0}", Environment.GetEnvironmentVariable("DOTNET_ROOT"));
+Console.WriteLine("DOTNET_ROOT_X86={0}", Environment.GetEnvironmentVariable("DOTNET_ROOT_X86"));
+Console.WriteLine("DOTNET_ROOT_X64={0}", Environment.GetEnvironmentVariable("DOTNET_ROOT_X64"));
+
+if (!typeof(object).Assembly.Location.StartsWith(dotnetRootPath, StringComparison.OrdinalIgnoreCase))
+{
+    throw new InvalidOperationException($"{typeof(object).Assembly.Location} was not found in {dotnetRootPath}. .NET was not resolved from the correct path.");
+}
+
+Console.WriteLine("Process and DOTNET_ROOT* were correctly set.");
diff --git a/test/TestAssets/TestProcess32/TestProcess32.csproj b/test/TestAssets/TestProcess32/TestProcess32.csproj
new file mode 100644
index 0000000000..0a5f4d7e1a
--- /dev/null
+++ b/test/TestAssets/TestProcess32/TestProcess32.csproj
@@ -0,0 +1,10 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+  <PropertyGroup>
+    <PlatformTarget>x86</PlatformTarget>
+    <OutputType>Exe</OutputType>
+    <TargetFrameworks>net5.0;net6.0</TargetFrameworks>
+    <Nullable>enable</Nullable>
+  </PropertyGroup>
+
+</Project>