diff --git a/.vscode/settings.json b/.vscode/settings.json index 89a3c7cca0c7..14126dac8b89 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -4,6 +4,69 @@ "*.*proj": "xml", "*.props": "xml", "*.targets": "xml", - "*.tasks": "xml" + "*.tasks": "xml", + "filesystem": "cpp", + "array": "cpp", + "initializer_list": "cpp", + "list": "cpp", + "random": "cpp", + "type_traits": "cpp", + "vector": "cpp", + "xhash": "cpp", + "xstring": "cpp", + "xtree": "cpp", + "xutility": "cpp", + "algorithm": "cpp", + "atomic": "cpp", + "cctype": "cpp", + "chrono": "cpp", + "cmath": "cpp", + "cstddef": "cpp", + "cstdint": "cpp", + "cstdio": "cpp", + "cstdlib": "cpp", + "cstring": "cpp", + "ctime": "cpp", + "cwchar": "cpp", + "exception": "cpp", + "fstream": "cpp", + "functional": "cpp", + "iomanip": "cpp", + "ios": "cpp", + "iosfwd": "cpp", + "istream": "cpp", + "iterator": "cpp", + "limits": "cpp", + "locale": "cpp", + "map": "cpp", + "memory": "cpp", + "new": "cpp", + "optional": "cpp", + "ostream": "cpp", + "ratio": "cpp", + "stdexcept": "cpp", + "streambuf": "cpp", + "string": "cpp", + "system_error": "cpp", + "thread": "cpp", + "tuple": "cpp", + "typeinfo": "cpp", + "unordered_map": "cpp", + "utility": "cpp", + "xfacet": "cpp", + "xiosbase": "cpp", + "xlocale": "cpp", + "xlocbuf": "cpp", + "xlocinfo": "cpp", + "xlocmes": "cpp", + "xlocmon": "cpp", + "xlocnum": "cpp", + "xloctime": "cpp", + "xmemory": "cpp", + "xstddef": "cpp", + "xtr1common": "cpp", + "iostream": "cpp", + "set": "cpp", + "sstream": "cpp" } } diff --git a/eng/Dependencies.props b/eng/Dependencies.props index 754b192df387..3e35ae7b3666 100644 --- a/eng/Dependencies.props +++ b/eng/Dependencies.props @@ -184,6 +184,9 @@ and are generated based on the last package release. + + + diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 69452ee23103..6be083dfdf2d 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -405,6 +405,10 @@ https://github.com/dotnet/core-setup 2bb2dcaeffb1dfeda077354449868ddac254bc3d + + https://github.com/dotnet/core-setup + 3f9156d5d9b5ae4b100baeaa75011aaee88a3f4b + diff --git a/eng/Versions.props b/eng/Versions.props index df0077a0a312..61440c281ccb 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -57,7 +57,9 @@ 3.0.0-preview8-27914-06 3.0.0-preview8-27914-06 3.0.0-preview8-27914-06 + 3.0.0-preview8-27914-06 2.1.0-preview8-27914-06 + 1.0.0-preview8.19364.1 4.6.0-preview8.19364.1 diff --git a/eng/targets/Cpp.Common.targets b/eng/targets/Cpp.Common.targets index f32f3f18213b..2b8733ae9f81 100644 --- a/eng/targets/Cpp.Common.targets +++ b/eng/targets/Cpp.Common.targets @@ -12,6 +12,7 @@ + diff --git a/src/Installers/Windows/AspNetCoreModule-Setup/ANCMIISExpressV2/ancm_iis_expressv2.wxs b/src/Installers/Windows/AspNetCoreModule-Setup/ANCMIISExpressV2/ancm_iis_expressv2.wxs index fa6326578ae2..f63ef673f4d9 100644 --- a/src/Installers/Windows/AspNetCoreModule-Setup/ANCMIISExpressV2/ancm_iis_expressv2.wxs +++ b/src/Installers/Windows/AspNetCoreModule-Setup/ANCMIISExpressV2/ancm_iis_expressv2.wxs @@ -35,10 +35,12 @@ + + @@ -177,6 +179,14 @@ + + + + + + + + @@ -251,10 +269,12 @@ + + diff --git a/src/Installers/Windows/AspNetCoreModule-Setup/ANCMV2/aspnetcoremodulev2.wxs b/src/Installers/Windows/AspNetCoreModule-Setup/ANCMV2/aspnetcoremodulev2.wxs index b04b1e21f5bd..fe294ee69302 100644 --- a/src/Installers/Windows/AspNetCoreModule-Setup/ANCMV2/aspnetcoremodulev2.wxs +++ b/src/Installers/Windows/AspNetCoreModule-Setup/ANCMV2/aspnetcoremodulev2.wxs @@ -27,9 +27,11 @@ + + @@ -144,7 +146,7 @@ - + + + + + + + @@ -200,6 +212,16 @@ + + + + + + + + diff --git a/src/Installers/Windows/AspNetCoreModule-Setup/Directory.Build.props b/src/Installers/Windows/AspNetCoreModule-Setup/Directory.Build.props index d08b147e4ae5..cab788f32c8e 100644 --- a/src/Installers/Windows/AspNetCoreModule-Setup/Directory.Build.props +++ b/src/Installers/Windows/AspNetCoreModule-Setup/Directory.Build.props @@ -36,6 +36,8 @@ AspNetCoreSchemaPath=$(_ServerIISBasePath)AspNetCoreModuleV2\AspNetCore\aspnetcore_schema_v2.xml; AspNetCoreMofPath=$(_ServerIISBasePath)AspNetCoreModuleV2\AspNetCore\ancm.mof; + NetHost64bit=$(Pkgruntime_win-x64_Microsoft_NETCore_DotNetAppHost)\runtimes\win-x64\native\nethost.dll; + NetHost32bit=$(Pkgruntime_win-x86_Microsoft_NETCore_DotNetAppHost)\runtimes\win-x86\native\nethost.dll; $(DefineConstants) @@ -70,5 +72,7 @@ false Platform=Win32 + + diff --git a/src/Servers/IIS/AspNetCoreModuleV2/AspNetCore/HandlerResolver.cpp b/src/Servers/IIS/AspNetCoreModuleV2/AspNetCore/HandlerResolver.cpp index bb4202398864..bc82981e9c65 100644 --- a/src/Servers/IIS/AspNetCoreModuleV2/AspNetCore/HandlerResolver.cpp +++ b/src/Servers/IIS/AspNetCoreModuleV2/AspNetCore/HandlerResolver.cpp @@ -65,6 +65,7 @@ HandlerResolver::LoadRequestHandlerAssembly(const IHttpApplication &pApplication pApplication.GetApplicationPhysicalPath(), pConfiguration.QueryArguments(), errorContext, + m_hModule, options)); location = options->GetDotnetExeLocation(); diff --git a/src/Servers/IIS/AspNetCoreModuleV2/CommonLib/HostFxrResolutionResult.cpp b/src/Servers/IIS/AspNetCoreModuleV2/CommonLib/HostFxrResolutionResult.cpp index 9a80749f2c70..d2356f54c105 100644 --- a/src/Servers/IIS/AspNetCoreModuleV2/CommonLib/HostFxrResolutionResult.cpp +++ b/src/Servers/IIS/AspNetCoreModuleV2/CommonLib/HostFxrResolutionResult.cpp @@ -24,6 +24,7 @@ HRESULT HostFxrResolutionResult::Create( _In_ const std::wstring& pcwzApplicationPhysicalPath, _In_ const std::wstring& pcwzArguments, _In_ ErrorContext& errorContext, + _In_ HMODULE aspNetCoreModule, _Out_ std::unique_ptr& ppWrapper) { std::filesystem::path knownDotnetLocation; @@ -44,7 +45,8 @@ HRESULT HostFxrResolutionResult::Create( hostFxrDllPath, knownDotnetLocation, arguments, - errorContext); + errorContext, + aspNetCoreModule); LOG_INFOF(L"Parsed hostfxr options: dotnet location: '%ls' hostfxr path: '%ls' arguments:", knownDotnetLocation.c_str(), hostFxrDllPath.c_str()); for (size_t i = 0; i < arguments.size(); i++) diff --git a/src/Servers/IIS/AspNetCoreModuleV2/CommonLib/HostFxrResolutionResult.h b/src/Servers/IIS/AspNetCoreModuleV2/CommonLib/HostFxrResolutionResult.h index a0bbf1700356..a4e90451fb98 100644 --- a/src/Servers/IIS/AspNetCoreModuleV2/CommonLib/HostFxrResolutionResult.h +++ b/src/Servers/IIS/AspNetCoreModuleV2/CommonLib/HostFxrResolutionResult.h @@ -47,6 +47,7 @@ class HostFxrResolutionResult _In_ const std::wstring& pcwzApplicationPhysicalPath, _In_ const std::wstring& pcwzArguments, _In_ ErrorContext& errorContext, + _In_ HMODULE aspNetCoreModule, _Out_ std::unique_ptr& ppWrapper); private: diff --git a/src/Servers/IIS/AspNetCoreModuleV2/CommonLib/HostFxrResolver.cpp b/src/Servers/IIS/AspNetCoreModuleV2/CommonLib/HostFxrResolver.cpp index 45e3699fadc3..e972f726188c 100644 --- a/src/Servers/IIS/AspNetCoreModuleV2/CommonLib/HostFxrResolver.cpp +++ b/src/Servers/IIS/AspNetCoreModuleV2/CommonLib/HostFxrResolver.cpp @@ -11,9 +11,13 @@ #include "Environment.h" #include "StringHelpers.h" #include "RegistryKey.h" +#include "ModuleHelpers.h" +#include "GlobalVersionUtility.h" namespace fs = std::filesystem; +typedef INT(*get_hostfxr_path) (PWSTR buffer, size_t* bufferSize, PCWSTR assemblyPath); + void HostFxrResolver::GetHostFxrParameters( const fs::path &processPath, @@ -22,7 +26,8 @@ HostFxrResolver::GetHostFxrParameters( fs::path &hostFxrDllPath, fs::path &dotnetExePath, std::vector &arguments, - ErrorContext& errorContext + ErrorContext& errorContext, + HMODULE aspNetCoreModule ) { LOG_INFOF(L"Resolving hostfxr parameters for application: '%ls' arguments: '%ls' path: '%ls'", @@ -46,7 +51,17 @@ HostFxrResolver::GetHostFxrParameters( throw InvalidOperationException(format(L"Process path '%s' doesn't have '.exe' extension.", expandedProcessPath.c_str())); } - // Check if the absolute path is to dotnet or not. + // TODO make sure we figure out a way to load this dll sxs + std::wstring modulePath = GlobalVersionUtility::GetModuleName(aspNetCoreModule); + + // If we are in the shim, load hostfxr. + // Otherwise, throw. + // I think we need to assume that hostfxr is loaded by the shim. + // but we may need a backup, which really sucks. + modulePath = GlobalVersionUtility::RemoveFileNameFromFolderPath(modulePath); + + auto moduleHandle = LoadLibrary(modulePath.append(L"\\nethost.dll").c_str()); + if (IsDotnetExecutable(expandedProcessPath)) { LOG_INFOF(L"Process path '%ls' is dotnet, treating application as portable", expandedProcessPath.c_str()); @@ -58,17 +73,45 @@ HostFxrResolver::GetHostFxrParameters( if (dotnetExePath.empty()) { - dotnetExePath = GetAbsolutePathToDotnet(applicationPhysicalPath, expandedProcessPath); - } + if (moduleHandle != nullptr) + { + auto get_host_fxr_path = ModuleHelpers::GetKnownProcAddress(moduleHandle, "get_hostfxr_path"); + + std::wstring hostfxrPath; + size_t size = MAX_PATH * 2; + hostfxrPath.resize(size); + + AppendArguments( + expandedApplicationArguments, + applicationPhysicalPath, + arguments, + true); + + get_host_fxr_path(hostfxrPath.data(), &size, arguments[0].c_str()); - hostFxrDllPath = GetAbsolutePathToHostFxr(dotnetExePath); + // If this fails, path probe - arguments.push_back(dotnetExePath); - AppendArguments( - expandedApplicationArguments, - applicationPhysicalPath, - arguments, - true); + hostfxrPath.resize(size); + hostFxrDllPath = hostfxrPath; + dotnetExePath = GetAbsolutePathToDotnetFromHostfxr(hostFxrDllPath); + + arguments.insert(arguments.begin(), dotnetExePath); + } + else + { + // TODO need to review 2.2 code, this may need to still be allowed for handler if we shipped 2.2 without this assumption + throw InvalidOperationException(L"Must have hostfxr loaded in shim or have nethost.dll next to app."); + } + } + else + { + arguments.push_back(dotnetExePath); + AppendArguments( + expandedApplicationArguments, + applicationPhysicalPath, + arguments, + true); + } } else { @@ -113,17 +156,31 @@ HostFxrResolver::GetHostFxrParameters( } else { - LOG_INFOF(L"hostfxr.dll found app local at '%ls', treating application as portable with launcher", hostFxrDllPath.c_str()); - - // passing "dotnet" here because we don't know where dotnet.exe should come from - // so trying all fallbacks is appropriate if (dotnetExePath.empty()) { - dotnetExePath = GetAbsolutePathToDotnet(applicationPhysicalPath, L"dotnet"); + if (moduleHandle != nullptr) + { + auto get_host_fxr_path = ModuleHelpers::GetKnownProcAddress(moduleHandle, "get_hostfxr_path"); + + LOG_INFOF(L"hostfxr.dll found app local at '%ls', treating application as portable with launcher", hostFxrDllPath.c_str()); + + std::wstring hostfxrPath; + size_t size = MAX_PATH * 2; + hostfxrPath.resize(size); + + get_host_fxr_path(hostfxrPath.data(), &size, applicationDllPath.c_str()); + hostfxrPath.resize(size); + hostFxrDllPath = hostfxrPath; + + dotnetExePath = GetAbsolutePathToDotnetFromHostfxr(hostFxrDllPath); + } + else + { + // TODO need to review 2.2 code, this may need to still be allowed for handler if we shipped 2.2 without this assumption + throw InvalidOperationException(L"Must have hostfxr loaded in shim or have nethost.dll next to app."); + } } - hostFxrDllPath = GetAbsolutePathToHostFxr(dotnetExePath); - // For portable with launcher apps we need dotnet.exe to be argv[0] and .dll be argv[1] arguments.push_back(dotnetExePath); arguments.push_back(applicationDllPath); } @@ -144,12 +201,6 @@ HostFxrResolver::GetHostFxrParameters( } } -BOOL -HostFxrResolver::IsDotnetExecutable(const std::filesystem::path & dotnetPath) -{ - return ends_with(dotnetPath, L"dotnet.exe", true); -} - void HostFxrResolver::AppendArguments( const std::wstring &applicationArguments, @@ -219,324 +270,14 @@ HostFxrResolver::AppendArguments( } } -// The processPath ends with dotnet.exe or dotnet -// like: C:\Program Files\dotnet\dotnet.exe, C:\Program Files\dotnet\dotnet, dotnet.exe, or dotnet. -// Get the absolute path to dotnet. If the path is already an absolute path, it will return that path fs::path -HostFxrResolver::GetAbsolutePathToDotnet( - const fs::path & applicationPath, - const fs::path & requestedPath -) +HostFxrResolver::GetAbsolutePathToDotnetFromHostfxr(const fs::path& hostfxrPath) { - LOG_INFOF(L"Resolving absolute path to dotnet.exe from '%ls'", requestedPath.c_str()); - - auto processPath = requestedPath; - if (processPath.is_relative()) - { - processPath = applicationPath / processPath; - } - - // - // If we are given an absolute path to dotnet.exe, we are done - // - if (is_regular_file(processPath)) - { - LOG_INFOF(L"Found dotnet.exe at '%ls'", processPath.c_str()); - - return processPath; - } - - // At this point, we are calling where.exe to find dotnet. - // If we encounter any failures, try getting dotnet.exe from the - // backup location. - // Only do it if no path is specified - if (requestedPath.has_parent_path()) - { - LOG_INFOF(L"Absolute path to dotnet.exe was not found at '%ls'", requestedPath.c_str()); - - throw InvalidOperationException(format(L"Could not find dotnet.exe at '%s'", processPath.c_str())); - } - - const auto dotnetViaWhere = InvokeWhereToFindDotnet(); - if (dotnetViaWhere.has_value()) - { - LOG_INFOF(L"Found dotnet.exe via where.exe invocation at '%ls'", dotnetViaWhere.value().c_str()); - - return dotnetViaWhere.value(); - } - - auto isWow64Process = Environment::IsRunning64BitProcess(); - - std::wstring regKeySubSection; - - if (isWow64Process) - { - regKeySubSection = L"SOFTWARE\\WOW6432Node\\dotnet\\Setup\\InstalledVersions\\x64"; - } - else - { - regKeySubSection = L"SOFTWARE\\dotnet\\Setup\\InstalledVersions\\x86"; - } - - const auto installationLocation = RegistryKey::TryGetString( - HKEY_LOCAL_MACHINE, - regKeySubSection, - L"InstallLocation"); - - 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()) - { - LOG_INFOF(L"Found dotnet.exe in Program Files at '%ls'", programFilesLocation.value().c_str()); - - return programFilesLocation.value(); - } - - LOG_INFOF(L"dotnet.exe not found"); - throw InvalidOperationException(format( - L"Could not find dotnet.exe at '%s' or using the system PATH environment variable." - " Check that a valid path to dotnet is on the PATH and the bitness of dotnet matches the bitness of the IIS worker process.", - processPath.c_str())); + return hostfxrPath.parent_path().parent_path().parent_path().parent_path() / "dotnet.exe"; } -fs::path -HostFxrResolver::GetAbsolutePathToHostFxr( - const fs::path & dotnetPath -) -{ - std::vector versionFolders; - const auto hostFxrBase = dotnetPath.parent_path() / "host" / "fxr"; - - LOG_INFOF(L"Resolving absolute path to hostfxr.dll from '%ls'", dotnetPath.c_str()); - - if (!is_directory(hostFxrBase)) - { - throw InvalidOperationException(format(L"Unable to find hostfxr directory at %s", hostFxrBase.c_str())); - } - - FindDotNetFolders(hostFxrBase, versionFolders); - - if (versionFolders.empty()) - { - throw InvalidOperationException(format(L"Hostfxr directory '%s' doesn't contain any version subdirectories", hostFxrBase.c_str())); - } - - const auto highestVersion = FindHighestDotNetVersion(versionFolders); - const auto hostFxrPath = hostFxrBase / highestVersion / "hostfxr.dll"; - - if (!is_regular_file(hostFxrPath)) - { - throw InvalidOperationException(format(L"hostfxr.dll not found at '%s'", hostFxrPath.c_str())); - } - - LOG_INFOF(L"hostfxr.dll located at '%ls'", hostFxrPath.c_str()); - return hostFxrPath; -} - -// -// Tries to call where.exe to find the location of dotnet.exe. -// Will check that the bitness of dotnet matches the current -// worker process bitness. -// Returns true if a valid dotnet was found, else false.R -// -std::optional -HostFxrResolver::InvokeWhereToFindDotnet() -{ - HRESULT hr = S_OK; - // Arguments to call where.exe - STARTUPINFOW startupInfo = { 0 }; - PROCESS_INFORMATION processInformation = { 0 }; - SECURITY_ATTRIBUTES securityAttributes; - - CHAR pzFileContents[READ_BUFFER_SIZE]; - HandleWrapper hStdOutReadPipe; - HandleWrapper hStdOutWritePipe; - HandleWrapper hProcess; - HandleWrapper hThread; - CComBSTR pwzDotnetName = NULL; - DWORD dwFilePointer; - BOOL fIsCurrentProcess64Bit; - DWORD dwExitCode; - STRU struDotnetSubstring; - STRU struDotnetLocationsString; - DWORD dwNumBytesRead; - DWORD dwBinaryType; - INT index = 0; - INT prevIndex = 0; - std::optional result; - - // Set the security attributes for the read/write pipe - securityAttributes.nLength = sizeof(securityAttributes); - securityAttributes.lpSecurityDescriptor = NULL; - securityAttributes.bInheritHandle = TRUE; - - LOG_INFO(L"Invoking where.exe to find dotnet.exe"); - - // Create a read/write pipe that will be used for reading the result of where.exe - FINISHED_LAST_ERROR_IF(!CreatePipe(&hStdOutReadPipe, &hStdOutWritePipe, &securityAttributes, 0)); - FINISHED_LAST_ERROR_IF(!SetHandleInformation(hStdOutReadPipe, HANDLE_FLAG_INHERIT, 0)); - - // Set the stdout and err pipe to the write pipes. - startupInfo.cb = sizeof(startupInfo); - startupInfo.dwFlags |= STARTF_USESTDHANDLES; - startupInfo.hStdOutput = hStdOutWritePipe; - startupInfo.hStdError = hStdOutWritePipe; - - // CreateProcess requires a mutable string to be passed to commandline - // See https://blogs.msdn.microsoft.com/oldnewthing/20090601-00/?p=18083/ - pwzDotnetName = L"\"where.exe\" dotnet.exe"; - - // Create a process to invoke where.exe - FINISHED_LAST_ERROR_IF(!CreateProcessW(NULL, - pwzDotnetName, - NULL, - NULL, - TRUE, - CREATE_NO_WINDOW, - NULL, - NULL, - &startupInfo, - &processInformation - )); - - // Store handles into wrapper so they get closed automatically - hProcess = processInformation.hProcess; - hThread = processInformation.hThread; - - // Wait for where.exe to return - WaitForSingleObject(processInformation.hProcess, INFINITE); - - // - // where.exe will return 0 on success, 1 if the file is not found - // and 2 if there was an error. Check if the exit code is 1 and set - // a new hr result saying it couldn't find dotnet.exe - // - FINISHED_LAST_ERROR_IF (!GetExitCodeProcess(processInformation.hProcess, &dwExitCode)); - - // - // In this block, if anything fails, we will goto our fallback of - // looking in C:/Program Files/ - // - if (dwExitCode != 0) - { - FINISHED_IF_FAILED(E_FAIL); - } - - // Where succeeded. - // Reset file pointer to the beginning of the file. - dwFilePointer = SetFilePointer(hStdOutReadPipe, 0, NULL, FILE_BEGIN); - if (dwFilePointer == INVALID_SET_FILE_POINTER) - { - FINISHED_IF_FAILED(E_FAIL); - } - - // - // As the call to where.exe succeeded (dotnet.exe was found), ReadFile should not hang. - // TODO consider putting ReadFile in a separate thread with a timeout to guarantee it doesn't block. - // - FINISHED_LAST_ERROR_IF (!ReadFile(hStdOutReadPipe, pzFileContents, READ_BUFFER_SIZE, &dwNumBytesRead, NULL)); - - if (dwNumBytesRead >= READ_BUFFER_SIZE) - { - // This shouldn't ever be this large. We could continue to call ReadFile in a loop, - // however if someone had this many dotnet.exes on their machine. - FINISHED_IF_FAILED(E_FAIL); - } - - FINISHED_IF_FAILED(struDotnetLocationsString.CopyA(pzFileContents, dwNumBytesRead)); - - LOG_INFOF(L"where.exe invocation returned: '%ls'", struDotnetLocationsString.QueryStr()); - - fIsCurrentProcess64Bit = Environment::IsRunning64BitProcess(); - - LOG_INFOF(L"Current process bitness type detected as isX64=%d", fIsCurrentProcess64Bit); - - while (TRUE) - { - index = struDotnetLocationsString.IndexOf(L"\r\n", prevIndex); - if (index == -1) - { - break; - } - - FINISHED_IF_FAILED(struDotnetSubstring.Copy(&struDotnetLocationsString.QueryStr()[prevIndex], index - prevIndex)); - // \r\n is two wchars, so add 2 here. - prevIndex = index + 2; - - LOG_INFOF(L"Processing entry '%ls'", struDotnetSubstring.QueryStr()); - - if (LOG_LAST_ERROR_IF(!GetBinaryTypeW(struDotnetSubstring.QueryStr(), &dwBinaryType))) - { - continue; - } - - LOG_INFOF(L"Binary type %d", dwBinaryType); - - if (fIsCurrentProcess64Bit == (dwBinaryType == SCS_64BIT_BINARY)) - { - // The bitness of dotnet matched with the current worker process bitness. - return std::make_optional(struDotnetSubstring.QueryStr()); - } - } - - Finished: - return result; -} - -std::optional -HostFxrResolver::GetAbsolutePathToDotnetFromProgramFiles() -{ - const auto programFilesDotnet = fs::path(Environment::ExpandEnvironmentVariables(L"%ProgramFiles%")) / "dotnet" / "dotnet.exe"; - return is_regular_file(programFilesDotnet) ? std::make_optional(programFilesDotnet) : std::nullopt; -} - -std::wstring -HostFxrResolver::FindHighestDotNetVersion( - _In_ std::vector & vFolders -) -{ - fx_ver_t max_ver(-1, -1, -1); - for (const auto& dir : vFolders) - { - fx_ver_t fx_ver(-1, -1, -1); - if (fx_ver_t::parse(dir, &fx_ver, false)) - { - max_ver = max(max_ver, fx_ver); - } - } - - return max_ver.as_str(); -} - -VOID -HostFxrResolver::FindDotNetFolders( - const std::filesystem::path &path, - _Out_ std::vector &pvFolders -) +BOOL +HostFxrResolver::IsDotnetExecutable(const std::filesystem::path& dotnetPath) { - WIN32_FIND_DATAW data = {}; - const auto searchPattern = std::wstring(path) + L"\\*"; - HandleWrapper handle = FindFirstFileExW(searchPattern.c_str(), FindExInfoStandard, &data, FindExSearchNameMatch, nullptr, 0); - if (handle == INVALID_HANDLE_VALUE) - { - LOG_LAST_ERROR(); - return; - } - - do - { - pvFolders.emplace_back(data.cFileName); - } while (FindNextFileW(handle, &data)); + return ends_with(dotnetPath, L"dotnet.exe", true); } diff --git a/src/Servers/IIS/AspNetCoreModuleV2/CommonLib/HostFxrResolver.h b/src/Servers/IIS/AspNetCoreModuleV2/CommonLib/HostFxrResolver.h index 70f63fca6f90..ea67d692f17a 100644 --- a/src/Servers/IIS/AspNetCoreModuleV2/CommonLib/HostFxrResolver.h +++ b/src/Servers/IIS/AspNetCoreModuleV2/CommonLib/HostFxrResolver.h @@ -26,7 +26,8 @@ class HostFxrResolver std::filesystem::path &hostFxrDllPath, std::filesystem::path &dotnetExePath, std::vector &arguments, - ErrorContext& errorContext + ErrorContext& errorContext, + HMODULE aspNetCoreModule ); static @@ -38,9 +39,6 @@ class HostFxrResolver bool expandDllPaths = false ); - static - std::optional - GetAbsolutePathToDotnetFromProgramFiles(); private: static @@ -49,35 +47,9 @@ class HostFxrResolver const std::filesystem::path & dotnetPath ); - static - VOID - FindDotNetFolders( - const std::filesystem::path& path, - std::vector & pvFolders - ); - - static - std::wstring - FindHighestDotNetVersion( - std::vector & vFolders - ); - static std::filesystem::path - GetAbsolutePathToHostFxr( - const std::filesystem::path & dotnetPath - ); - - static - std::optional - InvokeWhereToFindDotnet(); - - static - std::filesystem::path - GetAbsolutePathToDotnet( - const std::filesystem::path & applicationPath, - const std::filesystem::path & requestedPath - ); + GetAbsolutePathToDotnetFromHostfxr(const std::filesystem::path& hostfxrPath); struct LocalFreeDeleter { diff --git a/src/Servers/IIS/AspNetCoreModuleV2/CommonLib/hostfxroptions.cpp b/src/Servers/IIS/AspNetCoreModuleV2/CommonLib/hostfxroptions.cpp deleted file mode 100644 index 060224c60d9b..000000000000 --- a/src/Servers/IIS/AspNetCoreModuleV2/CommonLib/hostfxroptions.cpp +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the MIT License. See License.txt in the project root for license information. - -#include "hostfxroptions.h" - -#include "hostfxr_utility.h" -#include "debugutil.h" -#include "exceptions.h" -#include "EventLog.h" - -HRESULT HOSTFXR_OPTIONS::Create( - _In_ const std::wstring& pcwzDotnetExePath, - _In_ const std::wstring& pcwzProcessPath, - _In_ const std::wstring& pcwzApplicationPhysicalPath, - _In_ const std::wstring& pcwzArguments, - _Out_ std::unique_ptr& ppWrapper) -{ - std::filesystem::path knownDotnetLocation; - - if (!pcwzDotnetExePath.empty()) - { - knownDotnetLocation = pcwzDotnetExePath; - } - - try - { - std::filesystem::path hostFxrDllPath; - std::vector arguments; - HOSTFXR_UTILITY::GetHostFxrParameters( - pcwzProcessPath, - pcwzApplicationPhysicalPath, - pcwzArguments, - hostFxrDllPath, - knownDotnetLocation, - arguments); - - LOG_INFOF(L"Parsed hostfxr options: dotnet location: '%ls' hostfxr path: '%ls' arguments:", knownDotnetLocation.c_str(), hostFxrDllPath.c_str()); - for (size_t i = 0; i < arguments.size(); i++) - { - LOG_INFOF(L"Argument[%d] = '%ls'", i, arguments[i].c_str()); - } - ppWrapper = std::make_unique(knownDotnetLocation, hostFxrDllPath, arguments); - } - catch (InvalidOperationException &ex) - { - EventLog::Error( - ASPNETCORE_EVENT_INPROCESS_START_ERROR, - ASPNETCORE_EVENT_INPROCESS_START_ERROR_MSG, - pcwzApplicationPhysicalPath.c_str(), - ex.as_wstring().c_str()); - - RETURN_CAUGHT_EXCEPTION(); - } - catch (std::runtime_error &ex) - { - EventLog::Error( - ASPNETCORE_EVENT_INPROCESS_START_ERROR, - ASPNETCORE_EVENT_INPROCESS_START_ERROR_MSG, - pcwzApplicationPhysicalPath.c_str(), - GetUnexpectedExceptionMessage(ex).c_str()); - - RETURN_CAUGHT_EXCEPTION(); - } - CATCH_RETURN(); - - return S_OK; -} diff --git a/src/Servers/IIS/AspNetCoreModuleV2/CommonLib/hostfxroptions.h b/src/Servers/IIS/AspNetCoreModuleV2/CommonLib/hostfxroptions.h deleted file mode 100644 index 243cc53ae7b8..000000000000 --- a/src/Servers/IIS/AspNetCoreModuleV2/CommonLib/hostfxroptions.h +++ /dev/null @@ -1,61 +0,0 @@ -// 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 -#include -#include -#include -#include - -class HOSTFXR_OPTIONS -{ -public: - HOSTFXR_OPTIONS( - std::filesystem::path dotnetExeLocation, - std::filesystem::path hostFxrLocation, - std::vector arguments - ) noexcept - : m_dotnetExeLocation(std::move(dotnetExeLocation)), - m_hostFxrLocation(std::move(hostFxrLocation)), - m_arguments(std::move(arguments)) - {} - - void - GetArguments(DWORD &hostfxrArgc, std::unique_ptr &hostfxrArgv) const - { - hostfxrArgc = static_cast(m_arguments.size()); - hostfxrArgv = std::make_unique(hostfxrArgc); - for (DWORD i = 0; i < hostfxrArgc; ++i) - { - hostfxrArgv[i] = m_arguments[i].c_str(); - } - } - - const std::filesystem::path& - GetHostFxrLocation() const noexcept - { - return m_hostFxrLocation; - } - - const std::filesystem::path& - GetDotnetExeLocation() const noexcept - { - return m_dotnetExeLocation; - } - - static - HRESULT Create( - _In_ const std::wstring& pcwzExeLocation, - _In_ const std::wstring& pcwzProcessPath, - _In_ const std::wstring& pcwzApplicationPhysicalPath, - _In_ const std::wstring& pcwzArguments, - _Out_ std::unique_ptr& ppWrapper); - -private: - const std::filesystem::path m_dotnetExeLocation; - const std::filesystem::path m_hostFxrLocation; - const std::vector m_arguments; -}; diff --git a/src/Servers/IIS/AspNetCoreModuleV2/CommonLibTests/hostfxr_utility_tests.cpp b/src/Servers/IIS/AspNetCoreModuleV2/CommonLibTests/hostfxr_utility_tests.cpp new file mode 100644 index 000000000000..f3530e3254e8 --- /dev/null +++ b/src/Servers/IIS/AspNetCoreModuleV2/CommonLibTests/hostfxr_utility_tests.cpp @@ -0,0 +1,52 @@ +// 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. + +#include "stdafx.h" +#include +#include +#include +#include "HostFxrResolver.h" +#include "Environment.h" + +TEST(ParseHostFxrArguments, BasicHostFxrArguments) +{ + std::vector bstrArray; + + HostFxrResolver::AppendArguments( + L"exec \"test.dll\"", // args + L"invalid", // physical path to application + bstrArray); // args array. + + EXPECT_EQ(2, bstrArray.size()); + ASSERT_STREQ(L"exec", bstrArray[0].c_str()); + ASSERT_STREQ(L"test.dll", bstrArray[1].c_str()); +} + +TEST(ParseHostFxrArguments, NoExecProvided) +{ + std::vector bstrArray; + + HostFxrResolver::AppendArguments( + L"test.dll", // args + L"ignored", // physical path to application + bstrArray); // args array. + + EXPECT_EQ(1, bstrArray.size()); + ASSERT_STREQ(L"test.dll", bstrArray[0].c_str()); +} + +TEST(ParseHostFxrArguments, ConvertDllToAbsolutePath) +{ + std::vector bstrArray; + // we need to use existing dll so let's use ntdll that we know exists everywhere + auto system32 = Environment::ExpandEnvironmentVariables(L"%WINDIR%\\System32"); + HostFxrResolver::AppendArguments( + L"exec \"ntdll.dll\"", // args + system32, // physical path to application + bstrArray, // args array. + true); // expandDllPaths + + EXPECT_EQ(2, bstrArray.size()); + ASSERT_STREQ(L"exec", bstrArray[0].c_str()); + ASSERT_STREQ((system32 + L"\\ntdll.dll").c_str(), bstrArray[1].c_str()); +} diff --git a/src/Servers/IIS/AspNetCoreModuleV2/InProcessRequestHandler/inprocessapplication.cpp b/src/Servers/IIS/AspNetCoreModuleV2/InProcessRequestHandler/inprocessapplication.cpp index 5707e4cd7728..dfe17f645b1f 100644 --- a/src/Servers/IIS/AspNetCoreModuleV2/InProcessRequestHandler/inprocessapplication.cpp +++ b/src/Servers/IIS/AspNetCoreModuleV2/InProcessRequestHandler/inprocessapplication.cpp @@ -195,7 +195,7 @@ IN_PROCESS_APPLICATION::ExecuteApplication() auto context = std::make_shared(); - ErrorContext errorContext; // unused + ErrorContext errorContext; // unused if (s_fMainCallback == nullptr) { @@ -205,6 +205,7 @@ IN_PROCESS_APPLICATION::ExecuteApplication() QueryApplicationPhysicalPath(), m_pConfig->QueryArguments(), errorContext, + GetModuleHandle(TEXT("aspnetcorev2.dll")), hostFxrResolutionResult )); 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 11467d4424bc..cac6bcc70d77 100644 --- a/src/Servers/IIS/IIS/test/Common.FunctionalTests/Inprocess/StartupTests.cs +++ b/src/Servers/IIS/IIS/test/Common.FunctionalTests/Inprocess/StartupTests.cs @@ -15,7 +15,6 @@ using Microsoft.AspNetCore.Server.IIS.FunctionalTests.Utilities; using Microsoft.AspNetCore.Server.IntegrationTesting; using Microsoft.AspNetCore.Server.IntegrationTesting.IIS; -using Microsoft.AspNetCore.Testing; using Microsoft.AspNetCore.Testing.xunit; using Microsoft.Win32; using Xunit; @@ -44,8 +43,6 @@ public async Task ExpandEnvironmentVariableInWebConfig() [ConditionalTheory] [InlineData("bogus", "", @"Executable was not found at '.*?\\bogus.exe")] - [InlineData("c:\\random files\\dotnet.exe", "something.dll", @"Could not find dotnet.exe at '.*?\\dotnet.exe'")] - [InlineData(".\\dotnet.exe", "something.dll", @"Could not find dotnet.exe at '.*?\\.\\dotnet.exe'")] [InlineData("dotnet.exe", "", @"Application arguments are empty.")] [InlineData("dotnet.zip", "", @"Process path 'dotnet.zip' doesn't have '.exe' extension.")] public async Task InvalidProcessPath_ExpectServerError(string path, string arguments, string subError) @@ -104,8 +101,6 @@ public async Task StartsWithDotnetOnThePath(string path) await deploymentResult.AssertStarts(); StopServer(); - // Verify that in this scenario where.exe was invoked only once by shim and request handler uses cached value - Assert.Equal(1, TestSink.Writes.Count(w => w.Message.Contains("Invoking where.exe to find dotnet.exe"))); } [ConditionalTheory] @@ -140,7 +135,6 @@ public async Task StartsWithDotnetInstallLocation(RuntimeArchitecture runtimeArc // 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'"))); } } } diff --git a/src/Servers/IIS/IntegrationTesting.IIS/src/Microsoft.AspNetCore.Server.IntegrationTesting.IIS.csproj b/src/Servers/IIS/IntegrationTesting.IIS/src/Microsoft.AspNetCore.Server.IntegrationTesting.IIS.csproj index 424be8c5f5d6..1bc403b14b5f 100644 --- a/src/Servers/IIS/IntegrationTesting.IIS/src/Microsoft.AspNetCore.Server.IntegrationTesting.IIS.csproj +++ b/src/Servers/IIS/IntegrationTesting.IIS/src/Microsoft.AspNetCore.Server.IntegrationTesting.IIS.csproj @@ -45,6 +45,8 @@ + + @@ -61,6 +63,8 @@ + + diff --git a/src/Servers/IIS/build.cmd b/src/Servers/IIS/build.cmd index 4369bfd550d3..ceccd91c6eaa 100644 --- a/src/Servers/IIS/build.cmd +++ b/src/Servers/IIS/build.cmd @@ -1,3 +1,3 @@ @ECHO OFF SET RepoRoot=%~dp0..\..\.. -%RepoRoot%\build.cmd -BuildNative -projects %~dp0**\*.csproj %* +%RepoRoot%\build.cmd -BuildNative -projects %~dp0**\*.csproj %*