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

View File

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

View File

@ -212,6 +212,12 @@ Language=English
%1 %1
. .
Messageid=1031
SymbolicName=ASPNETCORE_EVENT_INVALID_PROCESS_PATH
Language=English
%1
.
; ;
;#endif // _ASPNETCORE_MODULE_MSG_H_ ;#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. // Licensed under the MIT License. See License.txt in the project root for license information.
#include "stdafx.h" #include "stdafx.h"
#include <string>
HOSTFXR_UTILITY::HOSTFXR_UTILITY() namespace fs = std::experimental::filesystem;
{
}
HOSTFXR_UTILITY::~HOSTFXR_UTILITY()
{
}
// //
// Runs a standalone appliction. // Runs a standalone appliction.
@ -25,131 +20,72 @@ HOSTFXR_UTILITY::~HOSTFXR_UTILITY()
// //
HRESULT HRESULT
HOSTFXR_UTILITY::GetStandaloneHostfxrParameters( HOSTFXR_UTILITY::GetStandaloneHostfxrParameters(
PCWSTR pwzExeAbsolutePath, // includes .exe file extension. PCWSTR pwzExeAbsolutePath, // includes .exe file extension.
PCWSTR pcwzApplicationPhysicalPath, PCWSTR pcwzApplicationPhysicalPath,
PCWSTR pcwzArguments, PCWSTR pcwzArguments,
HANDLE hEventLog, HANDLE hEventLog,
_Inout_ STRU* pStruHostFxrDllLocation, _Inout_ STRU* pStruHostFxrDllLocation,
_Out_ DWORD* pdwArgCount, _Out_ DWORD* pdwArgCount,
_Out_ BSTR** ppwzArgv _Out_ BSTR** ppwzArgv
) )
{ {
HRESULT hr = S_OK; HRESULT hr = S_OK;
STRU struDllPath;
STRU struArguments;
STRU struHostFxrPath;
STRU struRuntimeConfigLocation;
DWORD dwPosition;
// Obtain the app name from the processPath section. const fs::path exePath(pwzExeAbsolutePath);
if (FAILED(hr = struDllPath.Copy(pwzExeAbsolutePath)))
if (!exePath.has_extension())
{ {
goto Finished; return false;
} }
dwPosition = struDllPath.LastIndexOf(L'.', 0); const fs::path physicalPath(pcwzApplicationPhysicalPath);
if (dwPosition == -1) const fs::path hostFxrLocation = physicalPath / "hostfxr.dll";
{
hr = E_FAIL;
goto Finished;
}
hr = UTILITY::ConvertPathToFullPath(L".\\hostfxr.dll", pcwzApplicationPhysicalPath, &struHostFxrPath); if (!is_regular_file(hostFxrLocation))
if (FAILED(hr))
{ {
goto Finished; fs::path runtimeConfigLocation = exePath;
} runtimeConfigLocation.replace_extension(L".runtimeconfig.json");
struDllPath.QueryStr()[dwPosition] = L'\0'; if (!is_regular_file(runtimeConfigLocation))
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")))
{ {
goto Finished; EVENTLOG(hEventLog, INPROCESS_FULL_FRAMEWORK_APP, pcwzApplicationPhysicalPath, 0);
} return E_FAIL;
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);
} }
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())) return ParseHostfxrArguments(
{ arguments.c_str(),
// 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(),
pwzExeAbsolutePath, pwzExeAbsolutePath,
pcwzApplicationPhysicalPath, pcwzApplicationPhysicalPath,
hEventLog, hEventLog,
pdwArgCount, pdwArgCount,
ppwzArgv))) ppwzArgv);
{
goto Finished;
}
Finished:
return hr;
} }
BOOL BOOL
HOSTFXR_UTILITY::IsDotnetExecutable(STRU *struExecutablePath) HOSTFXR_UTILITY::IsDotnetExecutable(const std::experimental::filesystem::path & dotnetPath)
{ {
if (struExecutablePath == NULL) auto name = dotnetPath.filename();
{ name.replace_extension("");
return FALSE; return _wcsnicmp(name.c_str(), L"dotnet", 6) == 0;
}
return struExecutablePath->EndsWith(L"dotnet.exe") || struExecutablePath->EndsWith(L"dotnet");
} }
HRESULT HRESULT
@ -165,70 +101,58 @@ HOSTFXR_UTILITY::GetHostFxrParameters(
) )
{ {
HRESULT hr = S_OK; 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. const fs::path applicationPhysicalPath = pcwzApplicationPhysicalPath;
if (FAILED(hr = struExpandedProcessPath.CopyAndExpandEnvironmentStrings(pcwzProcessPath)) fs::path processPath = ExpandEnvironmentVariables(pcwzProcessPath);
|| FAILED(hr = struExpandedArguments.CopyAndExpandEnvironmentStrings(pcwzArguments))) std::wstring arguments = ExpandEnvironmentVariables(pcwzArguments);
if (processPath.is_relative())
{ {
goto Finished; processPath = applicationPhysicalPath / processPath;
}
// 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;
} }
// Check if the absolute path is to dotnet or not. // 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 // The processPath ends with dotnet.exe or dotnet
// like: C:\Program Files\dotnet\dotnet.exe, C:\Program Files\dotnet\dotnet, 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 // 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( if (FAILED(hr = HOSTFXR_UTILITY::ParseHostfxrArguments(
struExpandedArguments.QueryStr(), arguments.c_str(),
struAbsolutePathToDotnet.QueryStr(), processPath.c_str(),
pcwzApplicationPhysicalPath, pcwzApplicationPhysicalPath,
hEventLog, hEventLog,
pdwArgCount, pdwArgCount,
pbstrArgv))) 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 else
@ -238,25 +162,25 @@ HOSTFXR_UTILITY::GetHostFxrParameters(
// like: C:\test\MyApp.Exe or MyApp.Exe // like: C:\test\MyApp.Exe or MyApp.Exe
// Check if the file exists, and if it does, get the parameters for a standalone application // 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( if (FAILED(hr = GetStandaloneHostfxrParameters(
struAbsolutePathToDotnet.QueryStr(), processPath.c_str(),
pcwzApplicationPhysicalPath, pcwzApplicationPhysicalPath,
struExpandedArguments.QueryStr(), arguments.c_str(),
hEventLog, hEventLog,
pStruHostFxrDllLocation, pStruHostFxrDllLocation,
pdwArgCount, pdwArgCount,
pbstrArgv); pbstrArgv)))
if (FAILED(hr))
{ {
goto Finished; return hr;
} }
if (FAILED(hr = pStruExeAbsolutePath->Copy(struAbsolutePathToDotnet))) if (FAILED(hr = pStruExeAbsolutePath->Copy(processPath.c_str())))
{ {
goto Finished; return hr;
} }
} }
else else
{ {
@ -264,19 +188,13 @@ HOSTFXR_UTILITY::GetHostFxrParameters(
// If the processPath file does not exist and it doesn't include dotnet.exe or dotnet // If the processPath file does not exist and it doesn't include dotnet.exe or dotnet
// then it is an invalid argument. // then it is an invalid argument.
// //
hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);; hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
UTILITY::LogEventF(hEventLog, UTILITY::LogEventF(hEventLog, ASPNETCORE_EVENT_INVALID_PROCESS_PATH_LEVEL, ASPNETCORE_EVENT_INVALID_PROCESS_PATH, ASPNETCORE_EVENT_INVALID_PROCESS_PATH_MSG, processPath.c_str(), hr);
EVENTLOG_ERROR_TYPE, return hr;
ASPNETCORE_EVENT_GENERAL_ERROR_MSG,
ASPNETCORE_EVENT_INVALID_PROCESS_PATH_MSG,
struExpandedProcessPath.QueryStr(),
hr);
} }
} }
Finished: return S_OK;
return hr;
} }
// //
@ -394,163 +312,82 @@ Finished:
return hr; return hr;
} }
HRESULT std::optional<fs::path>
HOSTFXR_UTILITY::GetAbsolutePathToDotnet( 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 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);
} }
// auto pathWithExe = requestedPath;
// If the path was C:\Program Files\dotnet\dotnet pathWithExe.concat(L".exe");
// We need to try appending .exe and check if the file exists too.
//
if (FAILED(hr = pStruAbsolutePathToDotnet->Append(L".exe")))
{
goto Finished;
}
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. // At this point, we are calling where.exe to find dotnet.
// If we encounter any failures, try getting dotnet.exe from the // If we encounter any failures, try getting dotnet.exe from the
// backup location. // 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( HOSTFXR_UTILITY::GetAbsolutePathToHostFxr(
STRU* pStruAbsolutePathToDotnet, const fs::path & dotnetPath,
HANDLE hEventLog, HANDLE hEventLog
STRU* pStruAbsolutePathToHostfxr
) )
{ {
HRESULT hr = S_OK; std::vector<std::wstring> versionFolders;
STRU struHostFxrPath; const auto hostFxrBase = dotnetPath.parent_path() / "host" / "fxr";
STRU struHostFxrSearchExpression;
STRU struHighestDotnetVersion;
STRU struEventMsg;
std::vector<std::wstring> vVersionFolders;
DWORD dwPosition = 0;
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); auto searchPattern = std::wstring(hostFxrBase) + L"\\*";
if (dwPosition == -1) FindDotNetFolders(searchPattern.c_str(), versionFolders);
if (versionFolders.empty())
{ {
hr = E_FAIL; EVENTLOG(hEventLog, HOSTFXR_DIRECTORY_NOT_FOUND, hostFxrBase.c_str(), HRESULT_FROM_WIN32(ERROR_BAD_ENVIRONMENT));
goto Finished;
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()) || if (!is_regular_file(hostFxrPath))
FAILED(hr = struHostFxrPath.Append(L"\\")))
{ {
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"); return std::make_optional(hostFxrPath);
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;
} }
// //
@ -559,10 +396,8 @@ Finished:
// worker process bitness. // worker process bitness.
// Returns true if a valid dotnet was found, else false. // Returns true if a valid dotnet was found, else false.
// //
BOOL std::optional<fs::path>
HOSTFXR_UTILITY::InvokeWhereToFindDotnet( HOSTFXR_UTILITY::InvokeWhereToFindDotnet()
_Inout_ STRU* pStruAbsolutePathToDotnet
)
{ {
HRESULT hr = S_OK; HRESULT hr = S_OK;
// Arguments to call where.exe // Arguments to call where.exe
@ -586,16 +421,13 @@ HOSTFXR_UTILITY::InvokeWhereToFindDotnet(
INT prevIndex = 0; INT prevIndex = 0;
BOOL fProcessCreationResult = FALSE; BOOL fProcessCreationResult = FALSE;
BOOL fResult = FALSE; BOOL fResult = FALSE;
std::optional<fs::path> result;
// Set the security attributes for the read/write pipe // Set the security attributes for the read/write pipe
securityAttributes.nLength = sizeof(securityAttributes); securityAttributes.nLength = sizeof(securityAttributes);
securityAttributes.lpSecurityDescriptor = NULL; securityAttributes.lpSecurityDescriptor = NULL;
securityAttributes.bInheritHandle = TRUE; 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 // Create a read/write pipe that will be used for reading the result of where.exe
if (!CreatePipe(&hStdOutReadPipe, &hStdOutWritePipe, &securityAttributes, 0)) if (!CreatePipe(&hStdOutReadPipe, &hStdOutWritePipe, &securityAttributes, 0))
{ {
@ -736,10 +568,7 @@ HOSTFXR_UTILITY::InvokeWhereToFindDotnet(
fIsCurrentProcess64Bit == (dwBinaryType == SCS_64BIT_BINARY)) fIsCurrentProcess64Bit == (dwBinaryType == SCS_64BIT_BINARY))
{ {
// The bitness of dotnet matched with the current worker process bitness. // The bitness of dotnet matched with the current worker process bitness.
if (FAILED(hr = pStruAbsolutePathToDotnet->Copy(struDotnetSubstring))) result = std::make_optional(struDotnetSubstring.QueryStr());
{
goto Finished;
}
fResult = TRUE; fResult = TRUE;
break; break;
} }
@ -768,71 +597,21 @@ Finished:
SysFreeString(pwzDotnetName); SysFreeString(pwzDotnetName);
} }
return fResult; return result;
} }
std::optional<fs::path>
HRESULT HOSTFXR_UTILITY::GetAbsolutePathToDotnetFromProgramFiles()
HOSTFXR_UTILITY::GetAbsolutePathToDotnetFromProgramFiles(
_Inout_ STRU* pStruAbsolutePathToDotnet
)
{ {
HRESULT hr = S_OK; const auto programFilesDotnet = fs::path(ExpandEnvironmentVariables(L"%ProgramFiles%")) / "dotnet" / "dotnet.exe";
BOOL fFound = FALSE; return is_regular_file(programFilesDotnet) ? std::make_optional(programFilesDotnet) : std::nullopt;
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;
} }
HRESULT std::wstring
HOSTFXR_UTILITY::FindHighestDotNetVersion( HOSTFXR_UTILITY::FindHighestDotNetVersion(
_In_ std::vector<std::wstring> vFolders, _In_ std::vector<std::wstring> & vFolders
_Out_ STRU *pstrResult
) )
{ {
HRESULT hr = S_OK;
fx_ver_t max_ver(-1, -1, -1); fx_ver_t max_ver(-1, -1, -1);
for (const auto& dir : vFolders) for (const auto& dir : vFolders)
{ {
@ -844,16 +623,13 @@ HOSTFXR_UTILITY::FindHighestDotNetVersion(
} }
} }
hr = pstrResult->Copy(max_ver.as_str().c_str()); return max_ver.as_str();
// we check FAILED(hr) outside of function
return hr;
} }
VOID VOID
HOSTFXR_UTILITY::FindDotNetFolders( HOSTFXR_UTILITY::FindDotNetFolders(
_In_ PCWSTR pszPath, _In_ PCWSTR pszPath,
_Out_ std::vector<std::wstring> *pvFolders _Out_ std::vector<std::wstring> & pvFolders
) )
{ {
HANDLE handle = NULL; HANDLE handle = NULL;
@ -868,8 +644,34 @@ HOSTFXR_UTILITY::FindDotNetFolders(
do do
{ {
std::wstring folder(data.cFileName); std::wstring folder(data.cFileName);
pvFolders->push_back(folder); pvFolders.push_back(folder);
} while (FindNextFileW(handle, &data)); } while (FindNextFileW(handle, &data));
FindClose(handle); 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 "precomp.h"
#include <vector> #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_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[]); typedef INT(*hostfxr_main_fn) (CONST DWORD argc, CONST PCWSTR argv[]);
@ -42,7 +44,7 @@ public:
static static
BOOL BOOL
IsDotnetExecutable( IsDotnetExecutable(
STRU* struExecutablePath _In_ const std::experimental::filesystem::path & dotnetPath
); );
static static
@ -58,47 +60,41 @@ public:
_Out_ BSTR **ppwzArgv _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 static
VOID VOID
FindDotNetFolders( FindDotNetFolders(
_In_ PCWSTR pszPath, _In_ PCWSTR pszPath,
_Out_ std::vector<std::wstring> *pvFolders _Out_ std::vector<std::wstring> & pvFolders
); );
HOSTFXR_UTILITY(); static
~HOSTFXR_UTILITY(); 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; goto Finished;
} }
} }
else if (HOSTFXR_UTILITY::IsDotnetExecutable(&struExeLocation)) else if (HOSTFXR_UTILITY::IsDotnetExecutable(struExeLocation.QueryStr()))
{ {
if (FAILED(hr = HOSTFXR_UTILITY::ParseHostfxrArguments( if (FAILED(hr = HOSTFXR_UTILITY::ParseHostfxrArguments(
pcwzArguments, 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_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_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_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_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_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_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_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_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_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_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_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." #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 "resources.h"
#include "aspnetcore_msg.h" #include "aspnetcore_msg.h"
#include "hostfxr_utility.h" #include "hostfxr_utility.h"
#include "EventLog.h"
#include "hostfxroptions.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. // Licensed under the MIT License. See License.txt in the project root for license information.
#include "stdafx.h" #include "stdafx.h"
#include "debugutil.h"
// static // static
HRESULT 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" #include "debugutil.h"
// Common lib // Common lib
#include "hostfxr_utility.h"
#include "requesthandler.h" #include "requesthandler.h"
#include "utility.h" #include "utility.h"
#include "application.h" #include "application.h"

View File

@ -84,7 +84,6 @@ TEST(ParseHostFxrArguments, ProvideNoArgs_InvalidArgs)
TEST(GetAbsolutePathToDotnetFromProgramFiles, BackupWorks) TEST(GetAbsolutePathToDotnetFromProgramFiles, BackupWorks)
{ {
STRU struAbsolutePathToDotnet; STRU struAbsolutePathToDotnet;
HRESULT hr = S_OK;
BOOL fDotnetInProgramFiles; BOOL fDotnetInProgramFiles;
BOOL is64Bit; BOOL is64Bit;
BOOL fIsWow64 = FALSE; BOOL fIsWow64 = FALSE;
@ -109,15 +108,14 @@ TEST(GetAbsolutePathToDotnetFromProgramFiles, BackupWorks)
fDotnetInProgramFiles = UTILITY::CheckIfFileExists(L"C:/Program Files (x86)/dotnet/dotnet.exe"); fDotnetInProgramFiles = UTILITY::CheckIfFileExists(L"C:/Program Files (x86)/dotnet/dotnet.exe");
} }
hr = HOSTFXR_UTILITY::GetAbsolutePathToDotnetFromProgramFiles(&struAbsolutePathToDotnet); auto dotnetPath = HOSTFXR_UTILITY::GetAbsolutePathToDotnetFromProgramFiles();
if (fDotnetInProgramFiles) if (fDotnetInProgramFiles)
{ {
EXPECT_EQ(hr, S_OK); EXPECT_TRUE(dotnetPath.has_value());
} }
else else
{ {
EXPECT_NE(hr, S_OK); EXPECT_FALSE(dotnetPath.has_value());
EXPECT_TRUE(struAbsolutePathToDotnet.IsEmpty());
} }
} }
@ -138,5 +136,5 @@ TEST(GetHostFxrArguments, InvalidParams)
&retVal, // arg count &retVal, // arg count
&bstrArray); // args array. &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. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System; using System;
using System.Linq; using System.IO;
using System.Net; using System.Net;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Xml.Linq;
using IISIntegration.FunctionalTests.Utilities; using IISIntegration.FunctionalTests.Utilities;
using Microsoft.AspNetCore.Server.IntegrationTesting; using Microsoft.AspNetCore.Server.IntegrationTesting;
using Microsoft.AspNetCore.Testing.xunit; using Microsoft.AspNetCore.Testing.xunit;
@ -18,30 +17,22 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests
[SkipIfIISExpressSchemaMissingInProcess] [SkipIfIISExpressSchemaMissingInProcess]
public class StartupTests : IISFunctionalTestBase public class StartupTests : IISFunctionalTestBase
{ {
private readonly string _dotnetLocation = DotNetMuxer.MuxerPathOrDefault();
public StartupTests(ITestOutputHelper output) : base(output) public StartupTests(ITestOutputHelper output) : base(output)
{ {
} }
[Fact] [ConditionalFact]
public async Task ExpandEnvironmentVariableInWebConfig() public async Task ExpandEnvironmentVariableInWebConfig()
{ {
var dotnetLocation = DotNetMuxer.MuxerPathOrDefault();
var deploymentParameters = GetBaseDeploymentParameters();
// Point to dotnet installed in user profile. // Point to dotnet installed in user profile.
deploymentParameters.EnvironmentVariables["DotnetPath"] = dotnetLocation; await AssertStarts(
deploymentResult => Helpers.ModifyAspNetCoreSectionInWebConfig(deploymentResult, "processPath", "%DotnetPath%"),
var deploymentResult = await DeployAsync(deploymentParameters); deploymentParameters => deploymentParameters.EnvironmentVariables["DotnetPath"] = _dotnetLocation);
Helpers.ModifyAspNetCoreSectionInWebConfig(deploymentResult, "processPath", "%DotnetPath%");
var response = await deploymentResult.RetryingHttpClient.GetAsync("HelloWorld");
var responseText = await response.Content.ReadAsStringAsync();
Assert.Equal("Hello World", responseText);
} }
[Fact] [ConditionalFact]
public async Task InvalidProcessPath_ExpectServerError() public async Task InvalidProcessPath_ExpectServerError()
{ {
var dotnetLocation = "bogus"; var dotnetLocation = "bogus";
@ -60,6 +51,51 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests
Assert.Equal(HttpStatusCode.InternalServerError, response.StatusCode); 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 public static TestMatrix TestVariants
=> TestMatrix.ForServers(ServerType.IISExpress) => TestMatrix.ForServers(ServerType.IISExpress)
.WithTfms(Tfm.NetCoreApp22) .WithTfms(Tfm.NetCoreApp22)