Use std::filesystem in hostfxr utilities (#926)

This commit is contained in:
Pavel Krymets 2018-06-19 11:29:42 -07:00 committed by GitHub
parent 17fdbf6514
commit 60711bfca1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 276 additions and 424 deletions

View File

@ -0,0 +1,6 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
#pragma once
#define EVENTLOG(log, name, ...) UTILITY::LogEventF(log, ASPNETCORE_EVENT_ ## name ## _LEVEL, ASPNETCORE_EVENT_ ## name, ASPNETCORE_EVENT_ ## name ## _MSG, __VA_ARGS__)

View File

@ -2,7 +2,6 @@
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
#include "stdafx.h"
#include <experimental/filesystem>
namespace fs = std::experimental::filesystem;
@ -96,7 +95,7 @@ GlobalVersionUtility::RemoveFileNameFromFolderPath(std::wstring fileName)
return path.parent_path();
}
std::wstring
std::wstring
GlobalVersionUtility::GetModuleName(HMODULE hModuleName)
{
DWORD dwSize = MAX_PATH;
@ -126,6 +125,6 @@ GlobalVersionUtility::GetModuleName(HMODULE hModuleName)
fDone = TRUE;
}
}
return retVal;
}

View File

@ -8,15 +8,15 @@ class GlobalVersionUtility
{
public:
static
std::wstring
static
std::wstring
GetGlobalRequestHandlerPath(PCWSTR pwzAspNetCoreFolderPath, PCWSTR pwzHandlerVersion, PCWSTR pwzHandlerName);
static
std::wstring
FindHighestGlobalVersion(PCWSTR pwzAspNetCoreFolderPath);
static
static
std::wstring
RemoveFileNameFromFolderPath(std::wstring fileName);

View File

@ -212,6 +212,12 @@ Language=English
%1
.
Messageid=1031
SymbolicName=ASPNETCORE_EVENT_INVALID_PROCESS_PATH
Language=English
%1
.
;
;#endif // _ASPNETCORE_MODULE_MSG_H_
;

View File

@ -2,14 +2,9 @@
// Licensed under the MIT License. See License.txt in the project root for license information.
#include "stdafx.h"
#include <string>
HOSTFXR_UTILITY::HOSTFXR_UTILITY()
{
}
HOSTFXR_UTILITY::~HOSTFXR_UTILITY()
{
}
namespace fs = std::experimental::filesystem;
//
// Runs a standalone appliction.
@ -25,131 +20,72 @@ HOSTFXR_UTILITY::~HOSTFXR_UTILITY()
//
HRESULT
HOSTFXR_UTILITY::GetStandaloneHostfxrParameters(
PCWSTR pwzExeAbsolutePath, // includes .exe file extension.
PCWSTR pcwzApplicationPhysicalPath,
PCWSTR pcwzArguments,
HANDLE hEventLog,
_Inout_ STRU* pStruHostFxrDllLocation,
_Out_ DWORD* pdwArgCount,
_Out_ BSTR** ppwzArgv
PCWSTR pwzExeAbsolutePath, // includes .exe file extension.
PCWSTR pcwzApplicationPhysicalPath,
PCWSTR pcwzArguments,
HANDLE hEventLog,
_Inout_ STRU* pStruHostFxrDllLocation,
_Out_ DWORD* pdwArgCount,
_Out_ BSTR** ppwzArgv
)
{
HRESULT hr = S_OK;
STRU struDllPath;
STRU struArguments;
STRU struHostFxrPath;
STRU struRuntimeConfigLocation;
DWORD dwPosition;
HRESULT hr = S_OK;
// Obtain the app name from the processPath section.
if (FAILED(hr = struDllPath.Copy(pwzExeAbsolutePath)))
const fs::path exePath(pwzExeAbsolutePath);
if (!exePath.has_extension())
{
goto Finished;
return false;
}
dwPosition = struDllPath.LastIndexOf(L'.', 0);
if (dwPosition == -1)
{
hr = E_FAIL;
goto Finished;
}
const fs::path physicalPath(pcwzApplicationPhysicalPath);
const fs::path hostFxrLocation = physicalPath / "hostfxr.dll";
hr = UTILITY::ConvertPathToFullPath(L".\\hostfxr.dll", pcwzApplicationPhysicalPath, &struHostFxrPath);
if (FAILED(hr))
if (!is_regular_file(hostFxrLocation))
{
goto Finished;
}
fs::path runtimeConfigLocation = exePath;
runtimeConfigLocation.replace_extension(L".runtimeconfig.json");
struDllPath.QueryStr()[dwPosition] = L'\0';
if (FAILED(hr = struDllPath.SyncWithBuffer()))
{
goto Finished;
}
if (!UTILITY::CheckIfFileExists(struHostFxrPath.QueryStr()))
{
// Most likely a full framework app.
// Check that the runtime config file doesn't exist in the folder as another heuristic.
if (FAILED(hr = struRuntimeConfigLocation.Copy(struDllPath)) ||
FAILED(hr = struRuntimeConfigLocation.Append(L".runtimeconfig.json")))
if (!is_regular_file(runtimeConfigLocation))
{
goto Finished;
}
if (!UTILITY::CheckIfFileExists(struRuntimeConfigLocation.QueryStr()))
{
hr = E_APPLICATION_ACTIVATION_EXEC_FAILURE;
UTILITY::LogEventF(hEventLog,
EVENTLOG_ERROR_TYPE,
ASPNETCORE_EVENT_INPROCESS_FULL_FRAMEWORK_APP,
ASPNETCORE_EVENT_INPROCESS_FULL_FRAMEWORK_APP_MSG,
pcwzApplicationPhysicalPath,
hr);
}
else
{
// If a runtime config file does exist, report a file not found on the app.exe
hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
UTILITY::LogEventF(hEventLog,
EVENTLOG_ERROR_TYPE,
ASPNETCORE_EVENT_APPLICATION_EXE_NOT_FOUND,
ASPNETCORE_EVENT_APPLICATION_EXE_NOT_FOUND_MSG,
pcwzApplicationPhysicalPath,
hr);
EVENTLOG(hEventLog, INPROCESS_FULL_FRAMEWORK_APP, pcwzApplicationPhysicalPath, 0);
return E_FAIL;
}
goto Finished;
EVENTLOG(hEventLog, APPLICATION_EXE_NOT_FOUND, pcwzApplicationPhysicalPath, 0);
return E_FAIL;
}
if (FAILED(hr = pStruHostFxrDllLocation->Copy(struHostFxrPath)))
fs::path dllPath = exePath;
dllPath.replace_extension(".dll");
if (!is_regular_file(dllPath))
{
goto Finished;
return E_FAIL;
}
auto arguments = std::wstring(dllPath) + L" " + pcwzArguments;
if (FAILED(hr = struDllPath.Append(L".dll")))
if (FAILED(hr = pStruHostFxrDllLocation->Copy(hostFxrLocation.c_str())))
{
goto Finished;
return hr;
}
if (!UTILITY::CheckIfFileExists(struDllPath.QueryStr()))
{
// Treat access issue as File not found
hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
goto Finished;
}
if (FAILED(hr = struArguments.Copy(struDllPath)) ||
FAILED(hr = struArguments.Append(L" ")) ||
FAILED(hr = struArguments.Append(pcwzArguments)))
{
goto Finished;
}
if (FAILED(hr = ParseHostfxrArguments(
struArguments.QueryStr(),
return ParseHostfxrArguments(
arguments.c_str(),
pwzExeAbsolutePath,
pcwzApplicationPhysicalPath,
hEventLog,
pdwArgCount,
ppwzArgv)))
{
goto Finished;
}
Finished:
return hr;
ppwzArgv);
}
BOOL
HOSTFXR_UTILITY::IsDotnetExecutable(STRU *struExecutablePath)
HOSTFXR_UTILITY::IsDotnetExecutable(const std::experimental::filesystem::path & dotnetPath)
{
if (struExecutablePath == NULL)
{
return FALSE;
}
return struExecutablePath->EndsWith(L"dotnet.exe") || struExecutablePath->EndsWith(L"dotnet");
auto name = dotnetPath.filename();
name.replace_extension("");
return _wcsnicmp(name.c_str(), L"dotnet", 6) == 0;
}
HRESULT
@ -165,70 +101,58 @@ HOSTFXR_UTILITY::GetHostFxrParameters(
)
{
HRESULT hr = S_OK;
STRU struSystemPathVariable;
STRU struAbsolutePathToHostFxr;
STRU struAbsolutePathToDotnet;
STRU struEventMsg;
STACK_STRU(struExpandedProcessPath, MAX_PATH);
STACK_STRU(struExpandedArguments, MAX_PATH);
// Copy and Expand the processPath and Arguments.
if (FAILED(hr = struExpandedProcessPath.CopyAndExpandEnvironmentStrings(pcwzProcessPath))
|| FAILED(hr = struExpandedArguments.CopyAndExpandEnvironmentStrings(pcwzArguments)))
const fs::path applicationPhysicalPath = pcwzApplicationPhysicalPath;
fs::path processPath = ExpandEnvironmentVariables(pcwzProcessPath);
std::wstring arguments = ExpandEnvironmentVariables(pcwzArguments);
if (processPath.is_relative())
{
goto Finished;
}
// Convert the process path an absolute path to our current application directory.
// If the path is already an absolute path, it will be unchanged.
hr = UTILITY::ConvertPathToFullPath(
struExpandedProcessPath.QueryStr(),
pcwzApplicationPhysicalPath,
&struAbsolutePathToDotnet
);
if (FAILED(hr))
{
goto Finished;
processPath = applicationPhysicalPath / processPath;
}
// Check if the absolute path is to dotnet or not.
if (HOSTFXR_UTILITY::IsDotnetExecutable(&struAbsolutePathToDotnet))
if (IsDotnetExecutable(processPath))
{
//
// 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
//
if (FAILED(hr = HOSTFXR_UTILITY::GetAbsolutePathToDotnet(&struAbsolutePathToDotnet))) // Make sure to append the dotnet.exe path correctly here (pass in regular path)?
// Make sure to append the dotnet.exe path correctly here (pass in regular path)?
auto fullProcessPath = GetAbsolutePathToDotnet(processPath);
if (!fullProcessPath.has_value())
{
goto Finished;
return E_FAIL;
}
if (FAILED(hr = GetAbsolutePathToHostFxr(&struAbsolutePathToDotnet, hEventLog, &struAbsolutePathToHostFxr)))
processPath = fullProcessPath.value();
auto hostFxrPath = GetAbsolutePathToHostFxr(processPath, hEventLog);
if (!hostFxrPath.has_value())
{
goto Finished;
return E_FAIL;
}
if (FAILED(hr = HOSTFXR_UTILITY::ParseHostfxrArguments(
struExpandedArguments.QueryStr(),
struAbsolutePathToDotnet.QueryStr(),
arguments.c_str(),
processPath.c_str(),
pcwzApplicationPhysicalPath,
hEventLog,
pdwArgCount,
pbstrArgv)))
{
goto Finished;
return hr;
}
if (FAILED(hr = pStruHostFxrDllLocation->Copy(struAbsolutePathToHostFxr)))
if (FAILED(hr = pStruHostFxrDllLocation->Copy(hostFxrPath->c_str())))
{
goto Finished;
return hr;
}
if (FAILED(hr = pStruExeAbsolutePath->Copy(struAbsolutePathToDotnet)))
if (FAILED(hr = pStruExeAbsolutePath->Copy(processPath.c_str())))
{
goto Finished;
return hr;
}
}
else
@ -238,25 +162,25 @@ HOSTFXR_UTILITY::GetHostFxrParameters(
// like: C:\test\MyApp.Exe or MyApp.Exe
// Check if the file exists, and if it does, get the parameters for a standalone application
//
if (UTILITY::CheckIfFileExists(struAbsolutePathToDotnet.QueryStr()))
if (is_regular_file(processPath))
{
hr = HOSTFXR_UTILITY::GetStandaloneHostfxrParameters(
struAbsolutePathToDotnet.QueryStr(),
if (FAILED(hr = GetStandaloneHostfxrParameters(
processPath.c_str(),
pcwzApplicationPhysicalPath,
struExpandedArguments.QueryStr(),
arguments.c_str(),
hEventLog,
pStruHostFxrDllLocation,
pdwArgCount,
pbstrArgv);
if (FAILED(hr))
pbstrArgv)))
{
goto Finished;
return hr;
}
if (FAILED(hr = pStruExeAbsolutePath->Copy(struAbsolutePathToDotnet)))
if (FAILED(hr = pStruExeAbsolutePath->Copy(processPath.c_str())))
{
goto Finished;
return hr;
}
}
else
{
@ -264,19 +188,13 @@ HOSTFXR_UTILITY::GetHostFxrParameters(
// If the processPath file does not exist and it doesn't include dotnet.exe or dotnet
// then it is an invalid argument.
//
hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);;
UTILITY::LogEventF(hEventLog,
EVENTLOG_ERROR_TYPE,
ASPNETCORE_EVENT_GENERAL_ERROR_MSG,
ASPNETCORE_EVENT_INVALID_PROCESS_PATH_MSG,
struExpandedProcessPath.QueryStr(),
hr);
hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
UTILITY::LogEventF(hEventLog, ASPNETCORE_EVENT_INVALID_PROCESS_PATH_LEVEL, ASPNETCORE_EVENT_INVALID_PROCESS_PATH, ASPNETCORE_EVENT_INVALID_PROCESS_PATH_MSG, processPath.c_str(), hr);
return hr;
}
}
Finished:
return hr;
return S_OK;
}
//
@ -394,163 +312,82 @@ Finished:
return hr;
}
HRESULT
std::optional<fs::path>
HOSTFXR_UTILITY::GetAbsolutePathToDotnet(
_Inout_ STRU* pStruAbsolutePathToDotnet
const fs::path & requestedPath
)
{
HRESULT hr = S_OK;
//
// If we are given an absolute path to dotnet.exe, we are done
//
if (UTILITY::CheckIfFileExists(pStruAbsolutePathToDotnet->QueryStr()))
if (is_regular_file(requestedPath))
{
goto Finished;
return std::make_optional(requestedPath);
}
//
// If the path was C:\Program Files\dotnet\dotnet
// We need to try appending .exe and check if the file exists too.
//
if (FAILED(hr = pStruAbsolutePathToDotnet->Append(L".exe")))
{
goto Finished;
}
auto pathWithExe = requestedPath;
pathWithExe.concat(L".exe");
if (UTILITY::CheckIfFileExists(pStruAbsolutePathToDotnet->QueryStr()))
if (is_regular_file(pathWithExe))
{
goto Finished;
return std::make_optional(pathWithExe);
}
// At this point, we are calling where.exe to find dotnet.
// If we encounter any failures, try getting dotnet.exe from the
// backup location.
if (!InvokeWhereToFindDotnet(pStruAbsolutePathToDotnet))
// Only do it if no path is specified
if (!requestedPath.has_parent_path())
{
hr = GetAbsolutePathToDotnetFromProgramFiles(pStruAbsolutePathToDotnet);
return std::nullopt;
}
Finished:
const auto dotnetViaWhere = InvokeWhereToFindDotnet();
if (dotnetViaWhere.has_value())
{
return dotnetViaWhere;
}
return hr;
return GetAbsolutePathToDotnetFromProgramFiles();
}
HRESULT
std::optional<fs::path>
HOSTFXR_UTILITY::GetAbsolutePathToHostFxr(
STRU* pStruAbsolutePathToDotnet,
HANDLE hEventLog,
STRU* pStruAbsolutePathToHostfxr
const fs::path & dotnetPath,
HANDLE hEventLog
)
{
HRESULT hr = S_OK;
STRU struHostFxrPath;
STRU struHostFxrSearchExpression;
STRU struHighestDotnetVersion;
STRU struEventMsg;
std::vector<std::wstring> vVersionFolders;
DWORD dwPosition = 0;
std::vector<std::wstring> versionFolders;
const auto hostFxrBase = dotnetPath.parent_path() / "host" / "fxr";
if (FAILED(hr = struHostFxrPath.Copy(pStruAbsolutePathToDotnet)))
if (!is_directory(hostFxrBase))
{
goto Finished;
EVENTLOG(hEventLog, HOSTFXR_DIRECTORY_NOT_FOUND, hostFxrBase.c_str(), HRESULT_FROM_WIN32(ERROR_BAD_ENVIRONMENT));
return std::nullopt;
}
dwPosition = struHostFxrPath.LastIndexOf(L'\\', 0);
if (dwPosition == -1)
auto searchPattern = std::wstring(hostFxrBase) + L"\\*";
FindDotNetFolders(searchPattern.c_str(), versionFolders);
if (versionFolders.empty())
{
hr = E_FAIL;
goto Finished;
EVENTLOG(hEventLog, HOSTFXR_DIRECTORY_NOT_FOUND, hostFxrBase.c_str(), HRESULT_FROM_WIN32(ERROR_BAD_ENVIRONMENT));
return std::nullopt;
}
struHostFxrPath.QueryStr()[dwPosition] = L'\0';
const auto highestVersion = FindHighestDotNetVersion(versionFolders);
const auto hostFxrPath = hostFxrBase / highestVersion / "hostfxr.dll";
if (FAILED(hr = struHostFxrPath.SyncWithBuffer()) ||
FAILED(hr = struHostFxrPath.Append(L"\\")))
if (!is_regular_file(hostFxrPath))
{
goto Finished;
EVENTLOG(hEventLog, HOSTFXR_DLL_NOT_FOUND, hostFxrPath.c_str(), HRESULT_FROM_WIN32(ERROR_FILE_INVALID));
return std::nullopt;
}
hr = struHostFxrPath.Append(L"host\\fxr");
if (FAILED(hr))
{
goto Finished;
}
if (!UTILITY::DirectoryExists(&struHostFxrPath))
{
hr = ERROR_BAD_ENVIRONMENT;
UTILITY::LogEventF(hEventLog,
EVENTLOG_ERROR_TYPE,
ASPNETCORE_EVENT_HOSTFXR_DIRECTORY_NOT_FOUND,
struEventMsg.QueryStr(),
ASPNETCORE_EVENT_HOSTFXR_DIRECTORY_NOT_FOUND_MSG,
struHostFxrPath.QueryStr(),
hr);
goto Finished;
}
// Find all folders under host\\fxr\\ for version numbers.
hr = struHostFxrSearchExpression.Copy(struHostFxrPath);
if (FAILED(hr))
{
goto Finished;
}
hr = struHostFxrSearchExpression.Append(L"\\*");
if (FAILED(hr))
{
goto Finished;
}
// As we use the logic from core-setup, we are opting to use std here.
HOSTFXR_UTILITY::FindDotNetFolders(struHostFxrSearchExpression.QueryStr(), &vVersionFolders);
if (vVersionFolders.size() == 0)
{
hr = HRESULT_FROM_WIN32(ERROR_BAD_ENVIRONMENT);
UTILITY::LogEventF(hEventLog,
EVENTLOG_ERROR_TYPE,
ASPNETCORE_EVENT_HOSTFXR_DIRECTORY_NOT_FOUND,
ASPNETCORE_EVENT_HOSTFXR_DIRECTORY_NOT_FOUND_MSG,
struHostFxrPath.QueryStr(),
hr);
goto Finished;
}
hr = FindHighestDotNetVersion(vVersionFolders, &struHighestDotnetVersion);
if (FAILED(hr))
{
goto Finished;
}
if (FAILED(hr = struHostFxrPath.Append(L"\\"))
|| FAILED(hr = struHostFxrPath.Append(struHighestDotnetVersion.QueryStr()))
|| FAILED(hr = struHostFxrPath.Append(L"\\hostfxr.dll")))
{
goto Finished;
}
if (!UTILITY::CheckIfFileExists(struHostFxrPath.QueryStr()))
{
// ASPNETCORE_EVENT_HOSTFXR_DLL_NOT_FOUND_MSG
hr = HRESULT_FROM_WIN32(ERROR_FILE_INVALID);
UTILITY::LogEventF(hEventLog,
EVENTLOG_ERROR_TYPE,
ASPNETCORE_EVENT_HOSTFXR_DLL_NOT_FOUND,
ASPNETCORE_EVENT_HOSTFXR_DLL_NOT_FOUND_MSG,
struHostFxrPath.QueryStr(),
hr);
goto Finished;
}
if (FAILED(hr = pStruAbsolutePathToHostfxr->Copy(struHostFxrPath)))
{
goto Finished;
}
Finished:
return hr;
return std::make_optional(hostFxrPath);
}
//
@ -559,10 +396,8 @@ Finished:
// worker process bitness.
// Returns true if a valid dotnet was found, else false.
//
BOOL
HOSTFXR_UTILITY::InvokeWhereToFindDotnet(
_Inout_ STRU* pStruAbsolutePathToDotnet
)
std::optional<fs::path>
HOSTFXR_UTILITY::InvokeWhereToFindDotnet()
{
HRESULT hr = S_OK;
// Arguments to call where.exe
@ -586,16 +421,13 @@ HOSTFXR_UTILITY::InvokeWhereToFindDotnet(
INT prevIndex = 0;
BOOL fProcessCreationResult = FALSE;
BOOL fResult = FALSE;
std::optional<fs::path> result;
// Set the security attributes for the read/write pipe
securityAttributes.nLength = sizeof(securityAttributes);
securityAttributes.lpSecurityDescriptor = NULL;
securityAttributes.bInheritHandle = TRUE;
// Reset the path to dotnet as we will be using whether the string is
// empty or not as state
pStruAbsolutePathToDotnet->Reset();
// Create a read/write pipe that will be used for reading the result of where.exe
if (!CreatePipe(&hStdOutReadPipe, &hStdOutWritePipe, &securityAttributes, 0))
{
@ -736,10 +568,7 @@ HOSTFXR_UTILITY::InvokeWhereToFindDotnet(
fIsCurrentProcess64Bit == (dwBinaryType == SCS_64BIT_BINARY))
{
// The bitness of dotnet matched with the current worker process bitness.
if (FAILED(hr = pStruAbsolutePathToDotnet->Copy(struDotnetSubstring)))
{
goto Finished;
}
result = std::make_optional(struDotnetSubstring.QueryStr());
fResult = TRUE;
break;
}
@ -768,71 +597,21 @@ Finished:
SysFreeString(pwzDotnetName);
}
return fResult;
return result;
}
HRESULT
HOSTFXR_UTILITY::GetAbsolutePathToDotnetFromProgramFiles(
_Inout_ STRU* pStruAbsolutePathToDotnet
)
std::optional<fs::path>
HOSTFXR_UTILITY::GetAbsolutePathToDotnetFromProgramFiles()
{
HRESULT hr = S_OK;
BOOL fFound = FALSE;
DWORD dwNumBytesRead = 0;
DWORD dwPathSize = MAX_PATH;
STRU struDotnetSubstring;
while (!fFound)
{
if (FAILED(hr = struDotnetSubstring.Resize(dwPathSize)))
{
goto Finished;
}
dwNumBytesRead = GetEnvironmentVariable(L"ProgramFiles", struDotnetSubstring.QueryStr(), dwPathSize);
if (dwNumBytesRead == 0)
{
hr = HRESULT_FROM_WIN32(GetLastError());
goto Finished;
}
else if (dwNumBytesRead >= dwPathSize)
{
//
// The path to ProgramFiles should never be this long, but resize and try again.
dwPathSize *= 2 + 30; // for dotnet substring
}
else
{
if (FAILED(hr = struDotnetSubstring.SyncWithBuffer()) ||
FAILED(hr = struDotnetSubstring.Append(L"\\dotnet\\dotnet.exe")))
{
goto Finished;
}
if (!UTILITY::CheckIfFileExists(struDotnetSubstring.QueryStr()))
{
hr = HRESULT_FROM_WIN32(GetLastError());
goto Finished;
}
if (FAILED(hr = pStruAbsolutePathToDotnet->Copy(struDotnetSubstring)))
{
goto Finished;
}
fFound = TRUE;
}
}
Finished:
return hr;
const auto programFilesDotnet = fs::path(ExpandEnvironmentVariables(L"%ProgramFiles%")) / "dotnet" / "dotnet.exe";
return is_regular_file(programFilesDotnet) ? std::make_optional(programFilesDotnet) : std::nullopt;
}
HRESULT
std::wstring
HOSTFXR_UTILITY::FindHighestDotNetVersion(
_In_ std::vector<std::wstring> vFolders,
_Out_ STRU *pstrResult
_In_ std::vector<std::wstring> & vFolders
)
{
HRESULT hr = S_OK;
fx_ver_t max_ver(-1, -1, -1);
for (const auto& dir : vFolders)
{
@ -844,16 +623,13 @@ HOSTFXR_UTILITY::FindHighestDotNetVersion(
}
}
hr = pstrResult->Copy(max_ver.as_str().c_str());
// we check FAILED(hr) outside of function
return hr;
return max_ver.as_str();
}
VOID
HOSTFXR_UTILITY::FindDotNetFolders(
_In_ PCWSTR pszPath,
_Out_ std::vector<std::wstring> *pvFolders
_Out_ std::vector<std::wstring> & pvFolders
)
{
HANDLE handle = NULL;
@ -868,8 +644,34 @@ HOSTFXR_UTILITY::FindDotNetFolders(
do
{
std::wstring folder(data.cFileName);
pvFolders->push_back(folder);
pvFolders.push_back(folder);
} while (FindNextFileW(handle, &data));
FindClose(handle);
}
std::wstring
HOSTFXR_UTILITY::ExpandEnvironmentVariables(const std::wstring & str)
{
DWORD requestedSize = ExpandEnvironmentStringsW(str.c_str(), nullptr, 0);
if (requestedSize == 0)
{
throw std::system_error(GetLastError(), std::system_category(), "ExpandEnvironmentVariables");
}
std::wstring expandedStr;
do
{
expandedStr.resize(requestedSize);
requestedSize = ExpandEnvironmentStringsW(str.c_str(), &expandedStr[0], requestedSize);
if (requestedSize == 0)
{
throw std::system_error(GetLastError(), std::system_category(), "ExpandEnvironmentVariables");
}
} while (expandedStr.size() != requestedSize);
// trim null character as ExpandEnvironmentStringsW returns size including null character
expandedStr.resize(requestedSize - 1);
return expandedStr;
}

View File

@ -6,6 +6,8 @@
#include "precomp.h"
#include <vector>
#include <experimental/filesystem>
#include <optional>
typedef INT(*hostfxr_get_native_search_directories_fn) (CONST INT argc, CONST PCWSTR* argv, PWSTR buffer, DWORD buffer_size, DWORD* required_buffer_size);
typedef INT(*hostfxr_main_fn) (CONST DWORD argc, CONST PCWSTR argv[]);
@ -42,7 +44,7 @@ public:
static
BOOL
IsDotnetExecutable(
STRU* struExecutablePath
_In_ const std::experimental::filesystem::path & dotnetPath
);
static
@ -58,47 +60,41 @@ public:
_Out_ BSTR **ppwzArgv
);
static
HRESULT
GetAbsolutePathToDotnet(
STRU* pStruAbsolutePathToDotnet
);
static
HRESULT
GetAbsolutePathToHostFxr(
_In_ STRU* pStruAbsolutePathToDotnet,
_In_ HANDLE hEventLog,
_Out_ STRU* pStruAbsolutePathToHostfxr
);
static
BOOL
InvokeWhereToFindDotnet(
_Inout_ STRU* pStruAbsolutePathToDotnet
);
static
HRESULT
GetAbsolutePathToDotnetFromProgramFiles(
_Inout_ STRU* pStruAbsolutePathToDotnet
);
static
HRESULT
FindHighestDotNetVersion(
_In_ std::vector<std::wstring> vFolders,
_Out_ STRU *pstrResult
);
static
VOID
FindDotNetFolders(
_In_ PCWSTR pszPath,
_Out_ std::vector<std::wstring> *pvFolders
_Out_ std::vector<std::wstring> & pvFolders
);
HOSTFXR_UTILITY();
~HOSTFXR_UTILITY();
static
std::wstring
FindHighestDotNetVersion(
_In_ std::vector<std::wstring> & vFolders
);
static
std::optional<std::experimental::filesystem::path>
GetAbsolutePathToHostFxr(
_In_ const std::experimental::filesystem::path & dotnetPath,
_In_ HANDLE hEventLog
);
static
std::optional<std::experimental::filesystem::path>
GetAbsolutePathToDotnetFromProgramFiles();
static
std::optional<std::experimental::filesystem::path>
InvokeWhereToFindDotnet();
static
std::optional<std::experimental::filesystem::path>
GetAbsolutePathToDotnet(
_In_ const std::experimental::filesystem::path & requestedPath
);
static
std::wstring ExpandEnvironmentVariables(const std::wstring & str);
};

View File

@ -39,7 +39,7 @@ HRESULT HOSTFXR_OPTIONS::Create(
goto Finished;
}
}
else if (HOSTFXR_UTILITY::IsDotnetExecutable(&struExeLocation))
else if (HOSTFXR_UTILITY::IsDotnetExecutable(struExeLocation.QueryStr()))
{
if (FAILED(hr = HOSTFXR_UTILITY::ParseHostfxrArguments(
pcwzArguments,

View File

@ -36,12 +36,17 @@
#define ASPNETCORE_EVENT_RECYCLE_CONFIGURATION_MSG L"Application '%s' was recycled due to configuration change"
#define ASPNETCORE_EVENT_RECYCLE_FAILURE_CONFIGURATION_MSG L"Failed to recycle application due to a configuration change at '%s'. Recycling worker process."
#define ASPNETCORE_EVENT_MODULE_DISABLED_MSG L"AspNetCore Module is disabled"
#define ASPNETCORE_EVENT_INPROCESS_FULL_FRAMEWORK_APP_LEVEL EVENTLOG_ERROR_TYPE
#define ASPNETCORE_EVENT_INPROCESS_FULL_FRAMEWORK_APP_MSG L"Application '%s' was compiled for .NET Framework. Please compile for .NET core to run the inprocess application or change the process mode to out of process. ErrorCode = '0x%x'."
#define ASPNETCORE_EVENT_PORTABLE_APP_DOTNET_MISSING_MSG L"Could not find dotnet.exe on the system PATH environment variable for portable application '%s'. Check that a valid path to dotnet is on the PATH and the bitness of dotnet matches the bitness of the IIS worker process. ErrorCode = '0x%x'."
#define ASPNETCORE_EVENT_HOSTFXR_DIRECTORY_NOT_FOUND_LEVEL EVENTLOG_ERROR_TYPE
#define ASPNETCORE_EVENT_HOSTFXR_DIRECTORY_NOT_FOUND_MSG L"Could not find the hostfxr directory '%s' in the dotnet directory. ErrorCode = '0x%x'."
#define ASPNETCORE_EVENT_HOSTFXR_DLL_NOT_FOUND_LEVEL EVENTLOG_ERROR_TYPE
#define ASPNETCORE_EVENT_HOSTFXR_DLL_NOT_FOUND_MSG L"Could not find hostfxr.dll in '%s'. ErrorCode = '0x%x'."
#define ASPNETCORE_EVENT_APPLICATION_EXE_NOT_FOUND_LEVEL EVENTLOG_ERROR_TYPE
#define ASPNETCORE_EVENT_APPLICATION_EXE_NOT_FOUND_MSG L"Could not find application executable in '%s'. ErrorCode = '0x%x'."
#define ASPNETCORE_EVENT_INPROCESS_THREAD_EXCEPTION_MSG L"Application '%s' with physical root '%s' hit unexpected managed exception, ErrorCode = '0x%x. Please check the stderr logs for more information."
#define ASPNETCORE_EVENT_INVALID_PROCESS_PATH_LEVEL EVENTLOG_ERROR_TYPE
#define ASPNETCORE_EVENT_INVALID_PROCESS_PATH_MSG L"Invalid or unknown processPath provided in web.config: processPath = %s, ErrorCode = '0x%x'."
#define ASPNETCORE_EVENT_INPROCESS_RH_MISSING_MSG L"Could not find the assembly '%s' for in-process application. Please confirm the Microsoft.AspNetCore.Server.IIS package is referenced in your application."
#define ASPNETCORE_EVENT_OUT_OF_PROCESS_RH_MISSING_MSG L"Could not find the assembly '%s' for out-of-process application. Please confirm the assembly is installed correctly for IIS or IISExpress."

View File

@ -38,5 +38,6 @@
#include "resources.h"
#include "aspnetcore_msg.h"
#include "hostfxr_utility.h"
#include "EventLog.h"
#include "hostfxroptions.h"

View File

@ -2,6 +2,7 @@
// Licensed under the MIT License. See License.txt in the project root for license information.
#include "stdafx.h"
#include "debugutil.h"
// static
HRESULT
@ -569,9 +570,12 @@ UTILITY::LogEvent(
);
}
if (dwEventInfoType == EVENTLOG_ERROR_TYPE)
STACK_STRA(converted, 256);
if (converted.CopyW(pstrMsg))
{
fwprintf(stderr, L"ERROR: %s\n", pstrMsg);
DebugPrintf(
dwEventInfoType == EVENTLOG_ERROR_TYPE ? ASPNETCORE_DEBUG_FLAG_ERROR : ASPNETCORE_DEBUG_FLAG_INFO,
"Event Log: %s", converted.QueryStr());
}
}

View File

@ -54,7 +54,6 @@
#include "debugutil.h"
// Common lib
#include "hostfxr_utility.h"
#include "requesthandler.h"
#include "utility.h"
#include "application.h"

View File

@ -84,7 +84,6 @@ TEST(ParseHostFxrArguments, ProvideNoArgs_InvalidArgs)
TEST(GetAbsolutePathToDotnetFromProgramFiles, BackupWorks)
{
STRU struAbsolutePathToDotnet;
HRESULT hr = S_OK;
BOOL fDotnetInProgramFiles;
BOOL is64Bit;
BOOL fIsWow64 = FALSE;
@ -109,15 +108,14 @@ TEST(GetAbsolutePathToDotnetFromProgramFiles, BackupWorks)
fDotnetInProgramFiles = UTILITY::CheckIfFileExists(L"C:/Program Files (x86)/dotnet/dotnet.exe");
}
hr = HOSTFXR_UTILITY::GetAbsolutePathToDotnetFromProgramFiles(&struAbsolutePathToDotnet);
auto dotnetPath = HOSTFXR_UTILITY::GetAbsolutePathToDotnetFromProgramFiles();
if (fDotnetInProgramFiles)
{
EXPECT_EQ(hr, S_OK);
EXPECT_TRUE(dotnetPath.has_value());
}
else
{
EXPECT_NE(hr, S_OK);
EXPECT_TRUE(struAbsolutePathToDotnet.IsEmpty());
EXPECT_FALSE(dotnetPath.has_value());
}
}
@ -138,5 +136,5 @@ TEST(GetHostFxrArguments, InvalidParams)
&retVal, // arg count
&bstrArray); // args array.
EXPECT_EQ(E_INVALIDARG, hr);
}
EXPECT_EQ(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), hr);
}

View File

@ -2,10 +2,9 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Linq;
using System.IO;
using System.Net;
using System.Threading.Tasks;
using System.Xml.Linq;
using IISIntegration.FunctionalTests.Utilities;
using Microsoft.AspNetCore.Server.IntegrationTesting;
using Microsoft.AspNetCore.Testing.xunit;
@ -18,30 +17,22 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests
[SkipIfIISExpressSchemaMissingInProcess]
public class StartupTests : IISFunctionalTestBase
{
private readonly string _dotnetLocation = DotNetMuxer.MuxerPathOrDefault();
public StartupTests(ITestOutputHelper output) : base(output)
{
}
[Fact]
[ConditionalFact]
public async Task ExpandEnvironmentVariableInWebConfig()
{
var dotnetLocation = DotNetMuxer.MuxerPathOrDefault();
var deploymentParameters = GetBaseDeploymentParameters();
// Point to dotnet installed in user profile.
deploymentParameters.EnvironmentVariables["DotnetPath"] = dotnetLocation;
var deploymentResult = await DeployAsync(deploymentParameters);
Helpers.ModifyAspNetCoreSectionInWebConfig(deploymentResult, "processPath", "%DotnetPath%");
var response = await deploymentResult.RetryingHttpClient.GetAsync("HelloWorld");
var responseText = await response.Content.ReadAsStringAsync();
Assert.Equal("Hello World", responseText);
await AssertStarts(
deploymentResult => Helpers.ModifyAspNetCoreSectionInWebConfig(deploymentResult, "processPath", "%DotnetPath%"),
deploymentParameters => deploymentParameters.EnvironmentVariables["DotnetPath"] = _dotnetLocation);
}
[Fact]
[ConditionalFact]
public async Task InvalidProcessPath_ExpectServerError()
{
var dotnetLocation = "bogus";
@ -60,6 +51,51 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests
Assert.Equal(HttpStatusCode.InternalServerError, response.StatusCode);
}
[ConditionalFact]
public async Task StartsWithDotnetLocationWithoutExe()
{
var dotnetLocationWithoutExtension = _dotnetLocation.Substring(0, _dotnetLocation.LastIndexOf("."));
await AssertStarts(
deploymentResult => Helpers.ModifyAspNetCoreSectionInWebConfig(deploymentResult, "processPath", dotnetLocationWithoutExtension));
}
[ConditionalFact]
public async Task StartsWithDotnetLocationUppercase()
{
var dotnetLocationWithoutExtension = _dotnetLocation.Substring(0, _dotnetLocation.LastIndexOf(".")).ToUpperInvariant();
await AssertStarts(
deploymentResult => Helpers.ModifyAspNetCoreSectionInWebConfig(deploymentResult, "processPath", dotnetLocationWithoutExtension));
}
[ConditionalTheory]
[InlineData("dotnet")]
[InlineData("dotnet.EXE")]
public async Task StartsWithDotnetOnThePath(string path)
{
await AssertStarts(
deploymentResult => Helpers.ModifyAspNetCoreSectionInWebConfig(deploymentResult, "processPath", path),
deploymentParameters => deploymentParameters.EnvironmentVariables["PATH"] = Path.GetDirectoryName(_dotnetLocation));
}
private async Task AssertStarts(Action<IISDeploymentResult> postDeploy, Action<DeploymentParameters> preDeploy = null)
{
var deploymentParameters = GetBaseDeploymentParameters();
preDeploy?.Invoke(deploymentParameters);
var deploymentResult = await DeployAsync(deploymentParameters);
postDeploy?.Invoke(deploymentResult);
var response = await deploymentResult.RetryingHttpClient.GetAsync("HelloWorld");
var responseText = await response.Content.ReadAsStringAsync();
Assert.Equal("Hello World", responseText);
}
public static TestMatrix TestVariants
=> TestMatrix.ForServers(ServerType.IISExpress)
.WithTfms(Tfm.NetCoreApp22)