Add InstallLocation registry key support (#5705)
This commit is contained in:
parent
89ef215146
commit
422b3222d2
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -225,6 +225,7 @@
|
|||
<ClInclude Include="NonCopyable.h" />
|
||||
<ClInclude Include="NullOutputManager.h" />
|
||||
<ClInclude Include="PipeOutputManager.h" />
|
||||
<ClInclude Include="RegistryKey.h" />
|
||||
<ClInclude Include="requesthandler.h" />
|
||||
<ClInclude Include="resources.h" />
|
||||
<ClInclude Include="ServerErrorApplication.h" />
|
||||
|
|
@ -253,6 +254,7 @@
|
|||
<ClCompile Include="hostfxroptions.cpp" />
|
||||
<ClCompile Include="LoggingHelpers.cpp" />
|
||||
<ClCompile Include="PipeOutputManager.cpp" />
|
||||
<ClCompile Include="RegistryKey.cpp" />
|
||||
<ClCompile Include="StdWrapper.cpp" />
|
||||
<ClCompile Include="SRWExclusiveLock.cpp" />
|
||||
<ClCompile Include="SRWSharedLock.cpp" />
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
#include "Environment.h"
|
||||
|
||||
#include <Windows.h>
|
||||
#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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,5 +20,7 @@ public:
|
|||
std::wstring GetCurrentDirectoryValue();
|
||||
static
|
||||
std::wstring GetDllDirectoryValue();
|
||||
static
|
||||
bool IsRunning64BitProcess();
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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<DWORD> 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<LPBYTE>(&dwData), &cbData)))
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
return dwData;
|
||||
}
|
||||
|
||||
std::optional<std::wstring> 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;
|
||||
}
|
||||
|
||||
|
|
@ -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 <optional>
|
||||
#include "HandleWrapper.h"
|
||||
|
||||
class RegistryKey
|
||||
{
|
||||
public:
|
||||
static
|
||||
std::optional<DWORD> TryGetDWORD(HKEY section, const std::wstring& subSectionName, const std::wstring& valueName, DWORD flags = 0);
|
||||
|
||||
static
|
||||
std::optional<std::wstring> TryGetString(HKEY section, const std::wstring& subSectionName, const std::wstring& valueName, DWORD flags = 0);
|
||||
|
||||
private:
|
||||
static
|
||||
bool
|
||||
CheckReturnValue(int errorCode);
|
||||
};
|
||||
|
|
@ -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<InvalidHandleTraits> 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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -325,6 +325,11 @@ namespace Microsoft.AspNetCore.Server.IntegrationTesting.IIS
|
|||
|
||||
}
|
||||
|
||||
if (DeploymentParameters.RuntimeArchitecture == RuntimeArchitecture.x86)
|
||||
{
|
||||
pool.SetAttributeValue("enable32BitAppOnWin64", "true");;
|
||||
}
|
||||
|
||||
RunServerConfigActions(config, contentRoot);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue