diff --git a/src/Servers/IIS/AspNetCoreModuleV2/AspNetCore/dllmain.cpp b/src/Servers/IIS/AspNetCoreModuleV2/AspNetCore/dllmain.cpp
index 3fce85afdd..a9c328c3fd 100644
--- a/src/Servers/IIS/AspNetCoreModuleV2/AspNetCore/dllmain.cpp
+++ b/src/Servers/IIS/AspNetCoreModuleV2/AspNetCore/dllmain.cpp
@@ -10,6 +10,7 @@
#include "resources.h"
#include "exceptions.h"
#include "EventLog.h"
+#include "RegistryKey.h"
DECLARE_DEBUG_PRINT_OBJECT("aspnetcorev2.dll");
@@ -88,9 +89,6 @@ HRESULT
--*/
{
- HKEY hKey {};
- BOOL fDisableANCM = FALSE;
-
UNREFERENCED_PARAMETER(dwServerVersion);
if (pHttpServer->IsCommandLineLaunch())
@@ -102,38 +100,11 @@ HRESULT
g_hEventLog = RegisterEventSource(nullptr, ASPNETCORE_EVENT_PROVIDER);
}
- // check whether the feature is disabled due to security reason
- if (RegOpenKeyEx(HKEY_LOCAL_MACHINE,
- L"SOFTWARE\\Microsoft\\IIS Extensions\\IIS AspNetCore Module V2\\Parameters",
- 0,
- KEY_READ,
- &hKey) == NO_ERROR)
+ auto fDisableModule = RegistryKey::TryGetDWORD(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\IIS Extensions\\IIS AspNetCore Module V2\\Parameters", L"DisableANCM");
+
+ if (fDisableModule.has_value() && fDisableModule.value() != 0)
{
- DWORD dwType = 0;
- DWORD dwData = 0;
- DWORD cbData;
-
- cbData = sizeof(dwData);
- if ((RegQueryValueEx(hKey,
- L"DisableANCM",
- nullptr,
- &dwType,
- (LPBYTE)&dwData,
- &cbData) == NO_ERROR) &&
- (dwType == REG_DWORD))
- {
- fDisableANCM = (dwData != 0);
- }
-
- RegCloseKey(hKey);
- }
-
- if (fDisableANCM)
- {
- // Logging
- EventLog::Warn(
- ASPNETCORE_EVENT_MODULE_DISABLED,
- ASPNETCORE_EVENT_MODULE_DISABLED_MSG);
+ EventLog::Warn(ASPNETCORE_EVENT_MODULE_DISABLED, ASPNETCORE_EVENT_MODULE_DISABLED_MSG);
// this will return 500 error to client
// as we did not register the module
return S_OK;
diff --git a/src/Servers/IIS/AspNetCoreModuleV2/CommonLib/CommonLib.vcxproj b/src/Servers/IIS/AspNetCoreModuleV2/CommonLib/CommonLib.vcxproj
index 066b433676..e6bb6ab200 100644
--- a/src/Servers/IIS/AspNetCoreModuleV2/CommonLib/CommonLib.vcxproj
+++ b/src/Servers/IIS/AspNetCoreModuleV2/CommonLib/CommonLib.vcxproj
@@ -225,6 +225,7 @@
+
@@ -253,6 +254,7 @@
+
diff --git a/src/Servers/IIS/AspNetCoreModuleV2/CommonLib/Environment.cpp b/src/Servers/IIS/AspNetCoreModuleV2/CommonLib/Environment.cpp
index 7e5ff9ca47..be6484e16b 100644
--- a/src/Servers/IIS/AspNetCoreModuleV2/CommonLib/Environment.cpp
+++ b/src/Servers/IIS/AspNetCoreModuleV2/CommonLib/Environment.cpp
@@ -4,6 +4,7 @@
#include "Environment.h"
#include
+#include "exceptions.h"
std::wstring
Environment::ExpandEnvironmentVariables(const std::wstring & str)
@@ -120,3 +121,22 @@ std::wstring Environment::GetDllDirectoryValue()
return expandedStr;
}
+
+bool Environment::IsRunning64BitProcess()
+{
+ // Check the bitness of the currently running process
+ // matches the dotnet.exe found.
+ BOOL fIsWow64Process = false;
+ THROW_LAST_ERROR_IF(!IsWow64Process(GetCurrentProcess(), &fIsWow64Process));
+
+ if (fIsWow64Process)
+ {
+ // 32 bit mode
+ return false;
+ }
+
+ // Check the SystemInfo to see if we are currently 32 or 64 bit.
+ SYSTEM_INFO systemInfo;
+ GetNativeSystemInfo(&systemInfo);
+ return systemInfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64;
+}
diff --git a/src/Servers/IIS/AspNetCoreModuleV2/CommonLib/Environment.h b/src/Servers/IIS/AspNetCoreModuleV2/CommonLib/Environment.h
index 8886ad428e..4f2611ef71 100644
--- a/src/Servers/IIS/AspNetCoreModuleV2/CommonLib/Environment.h
+++ b/src/Servers/IIS/AspNetCoreModuleV2/CommonLib/Environment.h
@@ -20,5 +20,7 @@ public:
std::wstring GetCurrentDirectoryValue();
static
std::wstring GetDllDirectoryValue();
+ static
+ bool IsRunning64BitProcess();
};
diff --git a/src/Servers/IIS/AspNetCoreModuleV2/CommonLib/RegistryKey.cpp b/src/Servers/IIS/AspNetCoreModuleV2/CommonLib/RegistryKey.cpp
new file mode 100644
index 0000000000..9f8bdeb409
--- /dev/null
+++ b/src/Servers/IIS/AspNetCoreModuleV2/CommonLib/RegistryKey.cpp
@@ -0,0 +1,55 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the MIT License. See License.txt in the project root for license information.
+
+#include "RegistryKey.h"
+#include "exceptions.h"
+
+std::optional RegistryKey::TryGetDWORD(HKEY section, const std::wstring& subSectionName, const std::wstring& valueName, DWORD flags)
+{
+ DWORD dwData = 0;
+ DWORD cbData = sizeof(dwData);
+ if (!CheckReturnValue(RegGetValue(section, subSectionName.c_str(), valueName.c_str(), RRF_RT_REG_DWORD | flags, nullptr, reinterpret_cast(&dwData), &cbData)))
+ {
+ return std::nullopt;
+ }
+
+ return dwData;
+}
+
+std::optional RegistryKey::TryGetString(HKEY section, const std::wstring& subSectionName, const std::wstring& valueName, DWORD flags)
+{
+ DWORD cbData;
+
+ if (!CheckReturnValue(RegGetValue(section, subSectionName.c_str(), valueName.c_str(), RRF_RT_REG_SZ | flags, nullptr, nullptr, &cbData) != NO_ERROR))
+ {
+ return std::nullopt;
+ }
+
+ std::wstring data;
+ data.resize(cbData / sizeof(wchar_t));
+
+ if (!CheckReturnValue(RegGetValue(section, subSectionName.c_str(), valueName.c_str(), RRF_RT_REG_SZ | flags, nullptr, data.data(), &cbData) != NO_ERROR))
+ {
+ return std::nullopt;
+ }
+
+ data.resize(cbData / sizeof(wchar_t) - 1);
+
+ return data;
+}
+
+bool RegistryKey::CheckReturnValue(int errorCode)
+{
+ if (errorCode == NO_ERROR)
+ {
+ return true;
+ }
+ // NotFound result is expected, don't spam logs with failures
+ if (errorCode != ERROR_FILE_NOT_FOUND)
+ {
+ LOG_IF_FAILED(HRESULT_FROM_WIN32(errorCode));
+ }
+
+ return false;
+}
+
diff --git a/src/Servers/IIS/AspNetCoreModuleV2/CommonLib/RegistryKey.h b/src/Servers/IIS/AspNetCoreModuleV2/CommonLib/RegistryKey.h
new file mode 100644
index 0000000000..817e254f6f
--- /dev/null
+++ b/src/Servers/IIS/AspNetCoreModuleV2/CommonLib/RegistryKey.h
@@ -0,0 +1,22 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the MIT License. See License.txt in the project root for license information.
+
+#pragma once
+
+#include
+#include "HandleWrapper.h"
+
+class RegistryKey
+{
+public:
+ static
+ std::optional TryGetDWORD(HKEY section, const std::wstring& subSectionName, const std::wstring& valueName, DWORD flags = 0);
+
+ static
+ std::optional TryGetString(HKEY section, const std::wstring& subSectionName, const std::wstring& valueName, DWORD flags = 0);
+
+private:
+ static
+ bool
+ CheckReturnValue(int errorCode);
+};
diff --git a/src/Servers/IIS/AspNetCoreModuleV2/CommonLib/hostfxr_utility.cpp b/src/Servers/IIS/AspNetCoreModuleV2/CommonLib/hostfxr_utility.cpp
index bc27526a41..f0be6e855b 100644
--- a/src/Servers/IIS/AspNetCoreModuleV2/CommonLib/hostfxr_utility.cpp
+++ b/src/Servers/IIS/AspNetCoreModuleV2/CommonLib/hostfxr_utility.cpp
@@ -10,6 +10,7 @@
#include "HandleWrapper.h"
#include "Environment.h"
#include "StringHelpers.h"
+#include "RegistryKey.h"
namespace fs = std::filesystem;
@@ -257,6 +258,28 @@ HOSTFXR_UTILITY::GetAbsolutePathToDotnet(
return dotnetViaWhere.value();
}
+ auto isWow64Process = Environment::IsRunning64BitProcess();
+ const auto platform = isWow64Process? L"x64" : L"x86";
+
+ const auto installationLocation = RegistryKey::TryGetString(
+ HKEY_LOCAL_MACHINE,
+ std::wstring(L"SOFTWARE\\dotnet\\Setup\\InstalledVersions\\") + platform + L"\\sdk",
+ L"InstallLocation",
+ RRF_SUBKEY_WOW6432KEY);
+
+ if (installationLocation.has_value())
+ {
+ LOG_INFOF(L"InstallLocation registry key is set to '%ls'", installationLocation.value().c_str());
+
+ auto const installationLocationDotnet = fs::path(installationLocation.value()) / "dotnet.exe";
+
+ if (is_regular_file(installationLocationDotnet))
+ {
+ LOG_INFOF(L"Found dotnet.exe in InstallLocation at '%ls'", installationLocationDotnet.c_str());
+ return installationLocationDotnet;
+ }
+ }
+
const auto programFilesLocation = GetAbsolutePathToDotnetFromProgramFiles();
if (programFilesLocation.has_value())
{
@@ -328,7 +351,6 @@ HOSTFXR_UTILITY::InvokeWhereToFindDotnet()
HandleWrapper hThread;
CComBSTR pwzDotnetName = NULL;
DWORD dwFilePointer;
- BOOL fIsWow64Process;
BOOL fIsCurrentProcess64Bit;
DWORD dwExitCode;
STRU struDotnetSubstring;
@@ -421,22 +443,7 @@ HOSTFXR_UTILITY::InvokeWhereToFindDotnet()
LOG_INFOF(L"where.exe invocation returned: '%ls'", struDotnetLocationsString.QueryStr());
- // Check the bitness of the currently running process
- // matches the dotnet.exe found.
- FINISHED_LAST_ERROR_IF (!IsWow64Process(GetCurrentProcess(), &fIsWow64Process));
-
- if (fIsWow64Process)
- {
- // 32 bit mode
- fIsCurrentProcess64Bit = FALSE;
- }
- else
- {
- // Check the SystemInfo to see if we are currently 32 or 64 bit.
- SYSTEM_INFO systemInfo;
- GetNativeSystemInfo(&systemInfo);
- fIsCurrentProcess64Bit = systemInfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64;
- }
+ fIsCurrentProcess64Bit = Environment::IsRunning64BitProcess();
LOG_INFOF(L"Current process bitness type detected as isX64=%d", fIsCurrentProcess64Bit);
diff --git a/src/Servers/IIS/IIS/test/Common.FunctionalTests/Inprocess/StartupTests.cs b/src/Servers/IIS/IIS/test/Common.FunctionalTests/Inprocess/StartupTests.cs
index db1570b960..76c5482fba 100644
--- a/src/Servers/IIS/IIS/test/Common.FunctionalTests/Inprocess/StartupTests.cs
+++ b/src/Servers/IIS/IIS/test/Common.FunctionalTests/Inprocess/StartupTests.cs
@@ -13,7 +13,7 @@ using Microsoft.AspNetCore.Server.IIS.FunctionalTests.Utilities;
using Microsoft.AspNetCore.Server.IntegrationTesting;
using Microsoft.AspNetCore.Server.IntegrationTesting.IIS;
using Microsoft.AspNetCore.Testing.xunit;
-using Newtonsoft.Json;
+using Microsoft.Win32;
using Xunit;
namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests
@@ -107,6 +107,67 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests
Assert.Equal(1, TestSink.Writes.Count(w => w.Message.Contains("Invoking where.exe to find dotnet.exe")));
}
+ [ConditionalTheory]
+ [InlineData(RuntimeArchitecture.x64)]
+ [InlineData(RuntimeArchitecture.x86)]
+ [RequiresNewShim]
+ [RequiresIIS(IISCapability.PoolEnvironmentVariables)]
+ public async Task StartsWithDotnetInstallLocation(RuntimeArchitecture runtimeArchitecture)
+ {
+ var deploymentParameters = _fixture.GetBaseDeploymentParameters(publish: true);
+ deploymentParameters.RuntimeArchitecture = runtimeArchitecture;
+
+ // IIS doesn't allow empty PATH
+ deploymentParameters.EnvironmentVariables["PATH"] = ".";
+ deploymentParameters.WebConfigActionList.Add(WebConfigHelpers.AddOrModifyAspNetCoreSection("processPath", "dotnet"));
+
+ // Key is always in 32bit view
+ using (var localMachine = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry32))
+ {
+ var installDir = DotNetCommands.GetDotNetInstallDir(runtimeArchitecture);
+ using (new TestRegistryKey(
+ localMachine,
+ "SOFTWARE\\dotnet\\Setup\\InstalledVersions\\" + runtimeArchitecture + "\\sdk",
+ "InstallLocation",
+ installDir))
+ {
+ var deploymentResult = await DeployAsync(deploymentParameters);
+ await deploymentResult.AssertStarts();
+ StopServer();
+ // Verify that in this scenario dotnet.exe was found using InstallLocation lookup
+ // I would've liked to make a copy of dotnet directory in this test and use it for verification
+ // but dotnet roots are usually very large on dev machines so this test would take disproportionally long time and disk space
+ Assert.Equal(1, TestSink.Writes.Count(w => w.Message.Contains($"Found dotnet.exe in InstallLocation at '{installDir}\\dotnet.exe'")));
+ }
+ }
+ }
+
+ [ConditionalFact]
+ [RequiresIIS(IISCapability.PoolEnvironmentVariables)]
+ public async Task DoesNotStartIfDisabled()
+ {
+ var deploymentParameters = _fixture.GetBaseDeploymentParameters(publish: true);
+
+ using (new TestRegistryKey(
+ Registry.LocalMachine,
+ "SOFTWARE\\Microsoft\\IIS Extensions\\IIS AspNetCore Module V2\\Parameters",
+ "DisableANCM",
+ 1))
+ {
+ var deploymentResult = await DeployAsync(deploymentParameters);
+ // Disabling ANCM produces no log files
+ deploymentResult.AllowNoLogs();
+
+ var response = await deploymentResult.HttpClient.GetAsync("/HelloWorld");
+
+ Assert.False(response.IsSuccessStatusCode);
+
+ StopServer();
+
+ EventLogHelpers.VerifyEventLogEvent(deploymentResult, "AspNetCore Module is disabled");
+ }
+ }
+
public static TestMatrix TestVariants
=> TestMatrix.ForServers(DeployerSelector.ServerType)
.WithTfms(Tfm.NetCoreApp30)
diff --git a/src/Servers/IIS/IIS/test/Common.FunctionalTests/Utilities/TestRegistryKey.cs b/src/Servers/IIS/IIS/test/Common.FunctionalTests/Utilities/TestRegistryKey.cs
new file mode 100644
index 0000000000..4ed7bf9c54
--- /dev/null
+++ b/src/Servers/IIS/IIS/test/Common.FunctionalTests/Utilities/TestRegistryKey.cs
@@ -0,0 +1,28 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using Microsoft.Win32;
+
+namespace Microsoft.AspNetCore.Server.IntegrationTesting
+{
+ public class TestRegistryKey : IDisposable
+ {
+ private readonly RegistryKey _baseHive;
+ private readonly RegistryKey _subKey;
+ private readonly string _keyName;
+
+ public TestRegistryKey(RegistryKey baseHive, string keyName, string valueName, object value)
+ {
+ _baseHive = baseHive;
+ _keyName = keyName;
+ _subKey = baseHive.CreateSubKey(keyName);
+ _subKey.SetValue(valueName, value);
+ }
+
+ public void Dispose()
+ {
+ _baseHive.DeleteSubKeyTree(_keyName, throwOnMissingSubKey: true);
+ }
+ }
+}
diff --git a/src/Servers/IIS/IntegrationTesting.IIS/src/IISDeployer.cs b/src/Servers/IIS/IntegrationTesting.IIS/src/IISDeployer.cs
index 4f2e525e72..f5b5f14e10 100644
--- a/src/Servers/IIS/IntegrationTesting.IIS/src/IISDeployer.cs
+++ b/src/Servers/IIS/IntegrationTesting.IIS/src/IISDeployer.cs
@@ -325,6 +325,11 @@ namespace Microsoft.AspNetCore.Server.IntegrationTesting.IIS
}
+ if (DeploymentParameters.RuntimeArchitecture == RuntimeArchitecture.x86)
+ {
+ pool.SetAttributeValue("enable32BitAppOnWin64", "true");;
+ }
+
RunServerConfigActions(config, contentRoot);
}
diff --git a/src/Servers/IIS/tools/SetupTestEnvironment.ps1 b/src/Servers/IIS/tools/SetupTestEnvironment.ps1
index 3adaf79045..36b98a8bf7 100644
--- a/src/Servers/IIS/tools/SetupTestEnvironment.ps1
+++ b/src/Servers/IIS/tools/SetupTestEnvironment.ps1
@@ -1,5 +1,11 @@
param($Mode)
+# TEMP TEMP TEMP
+# While doing https://github.com/aspnet/AspNetCore/pull/5705 I accidentally disabled ANCM on CI machines using
+# the registy key. Remove it to allow tests to pass
+
+Remove-Item "HKLM:\SOFTWARE\Microsoft\IIS Extensions\IIS AspNetCore Module V2\Parameters" -ErrorAction Ignore;
+
$DumpFolder = "$env:ASPNETCORE_TEST_LOG_DIR\dumps"
if (!($DumpFolder))
{
@@ -70,7 +76,7 @@ function Setup-Dumps()
New-Item -Path $werHive -Name LocalDumps
}
- Move-Item $env:windir\System32\vsjitdebugger.exe $env:windir\System32\_vsjitdebugger.exe;
+ Move-Item $env:windir\System32\vsjitdebugger.exe $env:windir\System32\_vsjitdebugger.exe -ErrorAction Ignore;
New-ItemProperty $werHive -Name "DontShowUI" -Value 1 -PropertyType "DWORD" -Force;