Support portable.exe apps and better error reporting (#1287)

This commit is contained in:
Pavel Krymets 2018-08-22 12:04:04 -07:00 committed by GitHub
parent 36e5aceb3c
commit eebbb6a602
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 415 additions and 595 deletions

View File

@ -48,7 +48,7 @@
</Target>
<!-- Deps file injection-->
<ItemGroup>
<ItemGroup Condition="('$(InProcessTestSite)' == 'true') AND ('$(ReferenceTestTasks)' != 'false')">
<ProjectReference Include="$(MSBuildThisFileDirectory)..\test\TestTasks\TestTasks.csproj">
<ReferenceOutputAssembly>False</ReferenceOutputAssembly>
</ProjectReference>

View File

@ -60,7 +60,7 @@ HandlerResolver::LoadRequestHandlerAssembly(IHttpApplication &pApplication, ASPN
pConfiguration.QueryArguments().c_str(),
options));
location = options->GetExeLocation();
location = options->GetDotnetExeLocation();
RETURN_IF_FAILED(LoggingHelpers::CreateLoggingProvider(
pConfiguration.QueryStdoutLogEnabled(),
@ -225,7 +225,7 @@ HandlerResolver::FindNativeAssemblyFromHostfxr(
DWORD dwBufferSize = s_initialGetNativeSearchDirectoriesBufferSize;
DWORD dwRequiredBufferSize = 0;
RETURN_LAST_ERROR_IF_NULL(m_hHostFxrDll = LoadLibraryW(hostfxrOptions.GetHostFxrLocation()));
RETURN_LAST_ERROR_IF_NULL(m_hHostFxrDll = LoadLibraryW(hostfxrOptions.GetHostFxrLocation().c_str()));
auto pFnHostFxrSearchDirectories = reinterpret_cast<hostfxr_get_native_search_directories_fn>(GetProcAddress(m_hHostFxrDll, "hostfxr_get_native_search_directories"));
if (pFnHostFxrSearchDirectories == nullptr)
@ -233,7 +233,7 @@ HandlerResolver::FindNativeAssemblyFromHostfxr(
EventLog::Error(
ASPNETCORE_EVENT_GENERAL_ERROR,
ASPNETCORE_EVENT_HOSTFXR_DLL_INVALID_VERSION_MSG,
hostfxrOptions.GetHostFxrLocation()
hostfxrOptions.GetHostFxrLocation().c_str()
);
RETURN_IF_FAILED(E_FAIL);
}
@ -243,9 +243,13 @@ HandlerResolver::FindNativeAssemblyFromHostfxr(
while (TRUE)
{
DWORD hostfxrArgc;
std::unique_ptr<PCWSTR[]> hostfxrArgv;
hostfxrOptions.GetArguments(hostfxrArgc, hostfxrArgv);
const auto intHostFxrExitCode = pFnHostFxrSearchDirectories(
hostfxrOptions.GetArgc(),
hostfxrOptions.GetArgv(),
hostfxrArgc,
hostfxrArgv.get(),
struNativeSearchPaths.data(),
dwBufferSize,
&dwRequiredBufferSize

View File

@ -219,6 +219,7 @@
<ClInclude Include="SRWExclusiveLock.h" />
<ClInclude Include="SRWSharedLock.h" />
<ClInclude Include="stdafx.h" />
<ClInclude Include="StringHelpers.h" />
<ClInclude Include="sttimer.h" />
</ItemGroup>
<ItemGroup>
@ -243,6 +244,7 @@
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader>
</ClCompile>
<ClCompile Include="StringHelpers.cpp" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\IISLib\IISLib.vcxproj">

View File

@ -5,3 +5,4 @@
// Workaround for VC++ bug https://developercommunity.visualstudio.com/content/problem/33928/constexpr-failing-on-nullptr-v141-compiler-regress.html
const HANDLE InvalidHandleTraits::DefaultHandle = INVALID_HANDLE_VALUE;
const HANDLE FindFileHandleTraits::DefaultHandle = INVALID_HANDLE_VALUE;

View File

@ -4,7 +4,7 @@
#pragma once
#include <Windows.h>
#include <ntassert.h>
#include "ntassert.h"
struct InvalidHandleTraits
{
@ -20,6 +20,13 @@ struct NullHandleTraits
static void Close(HANDLE handle) { CloseHandle(handle); }
};
struct FindFileHandleTraits
{
using HandleType = HANDLE;
static const HANDLE DefaultHandle;
static void Close(HANDLE handle) { FindClose(handle); }
};
struct ModuleHandleTraits
{
using HandleType = HMODULE;

View File

@ -0,0 +1,15 @@
// 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 "StringHelpers.h"
bool ends_with(const std::wstring &source, const std::wstring &suffix, bool ignoreCase)
{
if (source.length() < suffix.length())
{
return false;
}
const auto offset = source.length() - suffix.length();
return CSTR_EQUAL == CompareStringOrdinal(source.c_str() + offset, static_cast<int>(suffix.length()), suffix.c_str(), static_cast<int>(suffix.length()), ignoreCase);
}

View File

@ -0,0 +1,20 @@
// 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.
#pragma once
#include <string>
[[nodiscard]]
bool ends_with(const std::wstring &source, const std::wstring &suffix, bool ignoreCase = false);
template<typename ... Args>
[[nodiscard]]
std::wstring format(const std::wstring& format, Args ... args)
{
const size_t size = swprintf(nullptr, 0, format.c_str(), args ...) + 1; // Extra char for '\0'
std::unique_ptr<wchar_t[]> formattedBuffer(new wchar_t[size]);
swprintf(formattedBuffer.get(), size, format.c_str(), args ... );
return std::wstring(formattedBuffer.get(), formattedBuffer.get() + size - 1);
}

View File

@ -38,12 +38,6 @@ Language=English
%1
.
Messageid=1002
SymbolicName=ASPNETCORE_EVENT_PROCESS_CRASH
Language=English
%1
.
Messageid=1003
SymbolicName=ASPNETCORE_EVENT_RAPID_FAIL_COUNT_EXCEEDED
Language=English
@ -110,42 +104,12 @@ Language=English
%1
.
Messageid=1014
SymbolicName=ASPNETCORE_EVENT_INPROCESS_FULL_FRAMEWORK_APP
Language=English
%1
.
Messageid=1015
SymbolicName=ASPNETCORE_EVENT_PORTABLE_APP_DOTNET_MISSING
Language=English
%1
.
Messageid=1016
SymbolicName=ASPNETCORE_EVENT_HOSTFXR_DIRECTORY_NOT_FOUND
Language=English
%1
.
Messageid=1017
SymbolicName=ASPNETCORE_EVENT_HOSTFXR_DLL_NOT_FOUND
Language=English
%1
.
Messageid=1018
SymbolicName=ASPNETCORE_EVENT_INPROCESS_THREAD_EXCEPTION
Language=English
%1
.
Messageid=1019
SymbolicName=ASPNETCORE_EVENT_APPLICATION_EXE_NOT_FOUND
Language=English
%1
.
Messageid=1020
SymbolicName=ASPNETCORE_EVENT_PROCESS_START_FAILURE
Language=English
@ -164,12 +128,6 @@ Language=English
%1
.
Messageid=1023
SymbolicName=ASPNETCORE_EVENT_APP_IN_SHUTDOWN
Language=English
%1
.
Messageid=1024
SymbolicName=ASPNETCORE_EVENT_MONITOR_APPOFFLINE_ERROR
Language=English
@ -213,7 +171,7 @@ Language=English
.
Messageid=1031
SymbolicName=ASPNETCORE_EVENT_INVALID_PROCESS_PATH
SymbolicName=ASPNETCORE_EVENT_INPROCESS_START_ERROR
Language=English
%1
.

View File

@ -43,6 +43,7 @@
#define CATCH_RETURN() catch (...) { RETURN_CAUGHT_EXCEPTION(); }
#define LOG_IF_FAILED(hr) LogHResultFailed(LOCATION_INFO, hr)
#define LOG_LAST_ERROR() LogLastErrorIf(LOCATION_INFO, true)
#define LOG_LAST_ERROR_IF(condition) LogLastErrorIf(LOCATION_INFO, condition)
#define SUCCEEDED_LOG(hr) SUCCEEDED(LOG_IF_FAILED(hr))
#define FAILED_LOG(hr) FAILED(LOG_IF_FAILED(hr))
@ -74,7 +75,7 @@
__declspec(noinline) inline VOID ReportException(LOCATION_ARGUMENTS std::exception& exception)
{
DebugPrintf(ASPNETCORE_DEBUG_FLAG_ERROR, LOCATION_FORMAT "Unhandled exception: %s", LOCATION_CALL exception.what());
DebugPrintf(ASPNETCORE_DEBUG_FLAG_ERROR, "Exception : %s caught at" LOCATION_FORMAT, exception.what(), LOCATION_CALL_ONLY);
}
__declspec(noinline) inline HRESULT LogHResultFailed(LOCATION_ARGUMENTS HRESULT hr)

View File

@ -3,185 +3,78 @@
#include "hostfxr_utility.h"
#include <string>
#include <atlcomcli.h>
#include "EventLog.h"
#include "ntassert.h"
#include "fx_ver.h"
#include "debugutil.h"
#include "exceptions.h"
#include "HandleWrapper.h"
#include "Environment.h"
#include "file_utility.h"
#include "StringHelpers.h"
namespace fs = std::filesystem;
//
// Runs a standalone appliction.
// The folder structure looks like this:
// Application/
// hostfxr.dll
// Application.exe
// Application.dll
// etc.
// We get the full path to hostfxr.dll and Application.dll and run hostfxr_main,
// passing in Application.dll.
// Assuming we don't need Application.exe as the dll is the actual application.
//
HRESULT
HOSTFXR_UTILITY::GetStandaloneHostfxrParameters(
PCWSTR pwzExeAbsolutePath, // includes .exe file extension.
PCWSTR pcwzApplicationPhysicalPath,
PCWSTR pcwzArguments,
_Inout_ STRU* pStruHostFxrDllLocation,
_Out_ DWORD* pdwArgCount,
_Out_ BSTR** ppwzArgv
)
{
LOG_INFOF("Resolving standalone hostfxr parameters for application: %S arguments: %S path: %S",
pwzExeAbsolutePath,
pcwzArguments,
pwzExeAbsolutePath);
const fs::path exePath(pwzExeAbsolutePath);
if (!exePath.has_extension())
{
LOG_INFOF("Exe path has not extension, returning");
return false;
}
const fs::path physicalPath(pcwzApplicationPhysicalPath);
const fs::path hostFxrLocation = physicalPath / "hostfxr.dll";
LOG_INFOF("Checking hostfxr.dll at %S", hostFxrLocation.c_str());
if (!is_regular_file(hostFxrLocation))
{
fs::path runtimeConfigLocation = exePath;
runtimeConfigLocation.replace_extension(L".runtimeconfig.json");
LOG_INFOF("Checking runtimeconfig.json at %S", runtimeConfigLocation.c_str());
if (!is_regular_file(runtimeConfigLocation))
{
EventLog::Error(
ASPNETCORE_EVENT_INPROCESS_FULL_FRAMEWORK_APP,
ASPNETCORE_EVENT_INPROCESS_FULL_FRAMEWORK_APP_MSG,
pcwzApplicationPhysicalPath,
0);
return E_FAIL;
}
EventLog::Error(
ASPNETCORE_EVENT_APPLICATION_EXE_NOT_FOUND,
ASPNETCORE_EVENT_APPLICATION_EXE_NOT_FOUND_MSG,
pcwzApplicationPhysicalPath,
0);
return E_FAIL;
}
fs::path dllPath = exePath;
dllPath.replace_extension(".dll");
if (!is_regular_file(dllPath))
{
LOG_INFOF("Application dll at %S was not found", dllPath.c_str());
return E_FAIL;
}
auto arguments = std::wstring(dllPath) + L" " + pcwzArguments;
RETURN_IF_FAILED(pStruHostFxrDllLocation->Copy(hostFxrLocation.c_str()));
RETURN_IF_FAILED(ParseHostfxrArguments(
arguments.c_str(),
pwzExeAbsolutePath,
pcwzApplicationPhysicalPath,
pdwArgCount,
ppwzArgv));
return S_OK;
}
BOOL
HOSTFXR_UTILITY::IsDotnetExecutable(const std::filesystem::path & dotnetPath)
{
auto name = dotnetPath.filename();
name.replace_extension("");
return _wcsnicmp(name.c_str(), L"dotnet", 6) == 0;
}
HRESULT
void
HOSTFXR_UTILITY::GetHostFxrParameters(
_In_ PCWSTR pcwzProcessPath,
_In_ PCWSTR pcwzApplicationPhysicalPath,
_In_ PCWSTR pcwzArguments,
_Inout_ STRU *pStruHostFxrDllLocation,
_Inout_ STRU *pStruExeAbsolutePath,
_Out_ DWORD *pdwArgCount,
_Out_ BSTR **pbstrArgv
const fs::path &processPath,
const fs::path &applicationPhysicalPath,
const std::wstring &applicationArguments,
fs::path &hostFxrDllPath,
fs::path &dotnetExePath,
std::vector<std::wstring> &arguments
)
{
HRESULT hr = S_OK;
LOG_INFOF("Resolving hostfxr parameters for application: '%S' arguments: '%S' path: '%S'",
processPath.c_str(),
applicationArguments.c_str(),
applicationPhysicalPath.c_str());
LOG_INFOF("Resolving hostfxr parameters for application: %S arguments: %S path: %S",
pcwzProcessPath,
pcwzArguments,
pcwzApplicationPhysicalPath);
fs::path expandedProcessPath = Environment::ExpandEnvironmentVariables(processPath);
const auto expandedApplicationArguments = Environment::ExpandEnvironmentVariables(applicationArguments);
const fs::path applicationPhysicalPath = pcwzApplicationPhysicalPath;
fs::path processPath = Environment::ExpandEnvironmentVariables(pcwzProcessPath);
std::wstring arguments = Environment::ExpandEnvironmentVariables(pcwzArguments);
LOG_INFOF("Expanded hostfxr parameters for application: '%S' arguments: '%S'",
expandedProcessPath.c_str(),
expandedApplicationArguments.c_str());
LOG_INFOF("Known dotnet.exe location: '%S'", dotnetExePath.c_str());
if (!expandedProcessPath.has_extension())
{
// The only executable extension inprocess supports
expandedProcessPath.replace_extension(".exe");
}
else if (!ends_with(expandedProcessPath, L".exe", true))
{
throw StartupParametersResolutionException(format(L"Process path '%s' doesn't have '.exe' extension.", expandedProcessPath.c_str()));
}
// Check if the absolute path is to dotnet or not.
if (IsDotnetExecutable(processPath))
if (IsDotnetExecutable(expandedProcessPath))
{
LOG_INFOF("Process path %S is dotnet, treating application as portable", processPath.c_str());
//
// 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
//
// Make sure to append the dotnet.exe path correctly here (pass in regular path)?
auto fullProcessPath = GetAbsolutePathToDotnet(applicationPhysicalPath, processPath);
if (!fullProcessPath.has_value())
LOG_INFOF("Process path '%S' is dotnet, treating application as portable", expandedProcessPath.c_str());
if (dotnetExePath.empty())
{
hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
EventLog::Error(
ASPNETCORE_EVENT_INVALID_PROCESS_PATH,
ASPNETCORE_EVENT_INVALID_PROCESS_PATH_MSG,
processPath.c_str(),
hr);
return hr;
dotnetExePath = GetAbsolutePathToDotnet(applicationPhysicalPath, expandedProcessPath);
}
processPath = fullProcessPath.value();
hostFxrDllPath = GetAbsolutePathToHostFxr(dotnetExePath);
auto hostFxrPath = GetAbsolutePathToHostFxr(processPath);
if (!hostFxrPath.has_value())
{
hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
return hr;
}
RETURN_IF_FAILED(HOSTFXR_UTILITY::ParseHostfxrArguments(
arguments.c_str(),
processPath.c_str(),
pcwzApplicationPhysicalPath,
pdwArgCount,
pbstrArgv));
RETURN_IF_FAILED(pStruHostFxrDllLocation->Copy(hostFxrPath->c_str()));
RETURN_IF_FAILED(pStruExeAbsolutePath->Copy(processPath.c_str()));
ParseHostfxrArguments(
expandedApplicationArguments,
dotnetExePath,
applicationPhysicalPath,
arguments,
true);
}
else
{
LOG_INFOF("Process path %S is not dotnet, treating application as standalone", processPath.c_str());
LOG_INFOF("Process path '%S' is not dotnet, treating application as standalone or portable with bootstrapper", expandedProcessPath.c_str());
if (processPath.is_relative())
auto executablePath = expandedProcessPath;
if (executablePath.is_relative())
{
processPath = applicationPhysicalPath / processPath;
executablePath = applicationPhysicalPath / expandedProcessPath;
}
//
@ -189,17 +82,42 @@ 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 (is_regular_file(processPath))
if (is_regular_file(executablePath))
{
RETURN_IF_FAILED(GetStandaloneHostfxrParameters(
processPath.c_str(),
pcwzApplicationPhysicalPath,
arguments.c_str(),
pStruHostFxrDllLocation,
pdwArgCount,
pbstrArgv));
auto applicationDllPath = executablePath;
applicationDllPath.replace_extension(".dll");
RETURN_IF_FAILED(pStruExeAbsolutePath->Copy(processPath.c_str()));
LOG_INFOF("Checking application.dll at %S", applicationDllPath.c_str());
if (!is_regular_file(applicationDllPath))
{
throw StartupParametersResolutionException(format(L"Application .dll was not found at %s", applicationDllPath.c_str()));
}
hostFxrDllPath = executablePath.parent_path() / "hostfxr.dll";
LOG_INFOF("Checking hostfxr.dll at %S", hostFxrDllPath.c_str());
if (is_regular_file(hostFxrDllPath))
{
LOG_INFOF("hostfxr.dll found app local at '%S', treating application as standalone", hostFxrDllPath.c_str());
}
else
{
LOG_INFOF("hostfxr.dll found app local at '%S', 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");
}
executablePath = dotnetExePath;
hostFxrDllPath = GetAbsolutePathToHostFxr(dotnetExePath);
}
ParseHostfxrArguments(
applicationDllPath.generic_wstring() + L" " + expandedApplicationArguments,
executablePath,
applicationPhysicalPath,
arguments);
}
else
{
@ -207,140 +125,75 @@ 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);
EventLog::Error(
ASPNETCORE_EVENT_INVALID_PROCESS_PATH,
ASPNETCORE_EVENT_INVALID_PROCESS_PATH_MSG,
processPath.c_str(),
hr);
return hr;
throw StartupParametersResolutionException(format(L"Executable was not found at '%s'", executablePath.c_str()));
}
}
return S_OK;
}
//
// Forms the argument list in HOSTFXR_PARAMETERS.
// Sets the ArgCount and Arguments.
// Arg structure:
// argv[0] = Path to exe activating hostfxr.
// argv[1] = L"exec"
// argv[2] = absolute path to dll.
//
HRESULT
BOOL
HOSTFXR_UTILITY::IsDotnetExecutable(const std::filesystem::path & dotnetPath)
{
return ends_with(dotnetPath, L"dotnet.exe", true);
}
void
HOSTFXR_UTILITY::ParseHostfxrArguments(
PCWSTR pwzArgumentsFromConfig,
PCWSTR pwzExePath,
PCWSTR pcwzApplicationPhysicalPath,
_Out_ DWORD* pdwArgCount,
_Out_ BSTR** pbstrArgv
const std::wstring &applicationArguments,
const fs::path &applicationExePath,
const fs::path &applicationPhysicalPath,
std::vector<std::wstring> &arguments,
bool expandDllPaths
)
{
DBG_ASSERT(pdwArgCount != NULL);
DBG_ASSERT(pbstrArgv != NULL);
DBG_ASSERT(pwzExePath != NULL);
LOG_INFOF("Resolving hostfxr_main arguments application: '%S' arguments: '%S' path: %S", applicationExePath.c_str(), applicationArguments.c_str(), applicationPhysicalPath.c_str());
HRESULT hr = S_OK;
INT argc = 0;
BSTR* argv = NULL;
LPWSTR* pwzArgs = NULL;
STRU struTempPath;
INT intArgsProcessed = 0;
arguments = std::vector<std::wstring>();
arguments.push_back(applicationExePath);
LOG_INFOF("Resolving hostfxr_main arguments application: %S arguments: %S path: %S",
pwzExePath,
pwzArgumentsFromConfig,
pcwzApplicationPhysicalPath);
// If we call CommandLineToArgvW with an empty string, argc is 5 for some interesting reason.
// Protectively guard against this by check if the string is null or empty.
if (pwzArgumentsFromConfig == NULL || wcscmp(pwzArgumentsFromConfig, L"") == 0)
if (applicationArguments.empty())
{
hr = E_INVALIDARG;
goto Finished;
throw StartupParametersResolutionException(L"Application arguments are empty.");
}
pwzArgs = CommandLineToArgvW(pwzArgumentsFromConfig, &argc);
if (pwzArgs == NULL)
int argc = 0;
auto pwzArgs = std::unique_ptr<LPWSTR[], LocalFreeDeleter>(CommandLineToArgvW(applicationArguments.c_str(), &argc));
if (!pwzArgs)
{
hr = LOG_IF_FAILED(HRESULT_FROM_WIN32(GetLastError()));
goto Failure;
throw StartupParametersResolutionException(format(L"Unable parse command line argumens '%s' or '%s'", applicationArguments.c_str()));
}
argv = new BSTR[argc + 1];
for (int intArgsProcessed = 0; intArgsProcessed < argc; intArgsProcessed++)
{
std::wstring argument = pwzArgs[intArgsProcessed];
argv[0] = SysAllocString(pwzExePath);
if (argv[0] == NULL)
{
hr = E_OUTOFMEMORY;
goto Failure;
}
// Try to convert the application dll from a relative to an absolute path
// Don't record this failure as pwzArgs[0] may already be an absolute path to the dll.
for (intArgsProcessed = 0; intArgsProcessed < argc; intArgsProcessed++)
{
DBG_ASSERT(pwzArgs[intArgsProcessed] != NULL);
struTempPath.Copy(pwzArgs[intArgsProcessed]);
if (struTempPath.EndsWith(L".dll"))
// Try expanding arguments ending in .dll to a full paths
if (expandDllPaths && ends_with(argument, L".dll", true))
{
if (SUCCEEDED(FILE_UTILITY::ConvertPathToFullPath(pwzArgs[intArgsProcessed], pcwzApplicationPhysicalPath, &struTempPath)))
fs::path argumentAsPath = argument;
if (argumentAsPath.is_relative())
{
argv[intArgsProcessed + 1] = SysAllocString(struTempPath.QueryStr());
}
else
{
argv[intArgsProcessed + 1] = SysAllocString(pwzArgs[intArgsProcessed]);
}
if (argv[intArgsProcessed + 1] == NULL)
{
hr = E_OUTOFMEMORY;
goto Failure;
}
}
else
{
argv[intArgsProcessed + 1] = SysAllocString(pwzArgs[intArgsProcessed]);
if (argv[intArgsProcessed + 1] == NULL)
{
hr = E_OUTOFMEMORY;
goto Failure;
argumentAsPath = applicationPhysicalPath / argumentAsPath;
if (exists(argumentAsPath))
{
LOG_INFOF("Converted argument '%S' to %S", argument.c_str(), argumentAsPath.c_str());
argument = argumentAsPath;
}
}
}
LOG_INFOF("Argument[%d] = %S",
intArgsProcessed + 1,
argv[intArgsProcessed + 1]);
arguments.push_back(argument);
}
*pbstrArgv = argv;
*pdwArgCount = argc + 1;
goto Finished;
Failure:
if (argv != NULL)
for (size_t i = 0; i < arguments.size(); i++)
{
// intArgsProcess - 1 here as if we fail to allocated the ith string
// we don't want to free it.
for (INT i = 0; i < intArgsProcessed - 1; i++)
{
SysFreeString(argv[i]);
}
LOG_INFOF("Argument[%d] = %S", i, arguments[i].c_str());
}
delete[] argv;
Finished:
if (pwzArgs != NULL)
{
LocalFree(pwzArgs);
}
return hr;
}
std::optional<fs::path>
// 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
HOSTFXR_UTILITY::GetAbsolutePathToDotnet(
const fs::path & applicationPath,
const fs::path & requestedPath
@ -357,21 +210,11 @@ HOSTFXR_UTILITY::GetAbsolutePathToDotnet(
//
// If we are given an absolute path to dotnet.exe, we are done
//
if (is_regular_file(requestedPath))
if (is_regular_file(processPath))
{
LOG_INFOF("Found dotnet.exe at %S", requestedPath.c_str());
LOG_INFOF("Found dotnet.exe at %S", processPath.c_str());
return std::make_optional(requestedPath);
}
auto pathWithExe = requestedPath;
pathWithExe.concat(L".exe");
if (is_regular_file(pathWithExe))
{
LOG_INFOF("Found dotnet.exe at %S", pathWithExe.c_str());
return std::make_optional(pathWithExe);
return processPath;
}
// At this point, we are calling where.exe to find dotnet.
@ -382,7 +225,7 @@ HOSTFXR_UTILITY::GetAbsolutePathToDotnet(
{
LOG_INFOF("Absolute path to dotnet.exe was not found at %S", requestedPath.c_str());
return std::nullopt;
throw StartupParametersResolutionException(format(L"Could not find dotnet.exe at '%s'", processPath.c_str()));
}
const auto dotnetViaWhere = InvokeWhereToFindDotnet();
@ -390,7 +233,7 @@ HOSTFXR_UTILITY::GetAbsolutePathToDotnet(
{
LOG_INFOF("Found dotnet.exe via where.exe invocation at %S", dotnetViaWhere.value().c_str());
return dotnetViaWhere;
return dotnetViaWhere.value();
}
const auto programFilesLocation = GetAbsolutePathToDotnetFromProgramFiles();
@ -398,14 +241,17 @@ HOSTFXR_UTILITY::GetAbsolutePathToDotnet(
{
LOG_INFOF("Found dotnet.exe in Program Files at %S", programFilesLocation.value().c_str());
return programFilesLocation;
return programFilesLocation.value();
}
LOG_INFOF("dotnet.exe not found");
return std::nullopt;
throw StartupParametersResolutionException(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()));
}
std::optional<fs::path>
fs::path
HOSTFXR_UTILITY::GetAbsolutePathToHostFxr(
const fs::path & dotnetPath
)
@ -417,23 +263,14 @@ HOSTFXR_UTILITY::GetAbsolutePathToHostFxr(
if (!is_directory(hostFxrBase))
{
EventLog::Error(ASPNETCORE_EVENT_HOSTFXR_DIRECTORY_NOT_FOUND, ASPNETCORE_EVENT_HOSTFXR_DIRECTORY_NOT_FOUND_MSG, hostFxrBase.c_str(), HRESULT_FROM_WIN32(ERROR_BAD_ENVIRONMENT));
return std::nullopt;
throw StartupParametersResolutionException(format(L"Unable to find hostfxr directory at %s", hostFxrBase.c_str()));
}
auto searchPattern = std::wstring(hostFxrBase) + L"\\*";
FindDotNetFolders(searchPattern.c_str(), versionFolders);
FindDotNetFolders(hostFxrBase, versionFolders);
if (versionFolders.empty())
{
EventLog::Error(
ASPNETCORE_EVENT_HOSTFXR_DIRECTORY_NOT_FOUND,
ASPNETCORE_EVENT_HOSTFXR_DIRECTORY_NOT_FOUND_MSG,
hostFxrBase.c_str(),
HRESULT_FROM_WIN32(ERROR_BAD_ENVIRONMENT));
return std::nullopt;
throw StartupParametersResolutionException(format(L"Hostfxr directory '%s' doesn't contain any version subdirectories", hostFxrBase.c_str()));
}
const auto highestVersion = FindHighestDotNetVersion(versionFolders);
@ -441,24 +278,18 @@ HOSTFXR_UTILITY::GetAbsolutePathToHostFxr(
if (!is_regular_file(hostFxrPath))
{
EventLog::Error(
ASPNETCORE_EVENT_HOSTFXR_DLL_NOT_FOUND,
ASPNETCORE_EVENT_HOSTFXR_DLL_NOT_FOUND_MSG,
hostFxrPath.c_str(),
HRESULT_FROM_WIN32(ERROR_FILE_INVALID));
return std::nullopt;
throw StartupParametersResolutionException(format(L"hostfxr.dll not found at '%s'", hostFxrPath.c_str()));
}
LOG_INFOF("hostfxr.dll located at %S", hostFxrPath.c_str());
return std::make_optional(hostFxrPath);
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.
// Returns true if a valid dotnet was found, else false.R
//
std::optional<fs::path>
HOSTFXR_UTILITY::InvokeWhereToFindDotnet()
@ -638,7 +469,6 @@ HOSTFXR_UTILITY::FindHighestDotNetVersion(
fx_ver_t fx_ver(-1, -1, -1);
if (fx_ver_t::parse(dir, &fx_ver, false))
{
// TODO using max instead of std::max works
max_ver = max(max_ver, fx_ver);
}
}
@ -648,16 +478,16 @@ HOSTFXR_UTILITY::FindHighestDotNetVersion(
VOID
HOSTFXR_UTILITY::FindDotNetFolders(
_In_ PCWSTR pszPath,
_Out_ std::vector<std::wstring> & pvFolders
const std::filesystem::path &path,
_Out_ std::vector<std::wstring> &pvFolders
)
{
HANDLE handle = NULL;
WIN32_FIND_DATAW data = { 0 };
handle = FindFirstFileExW(pszPath, FindExInfoStandard, &data, FindExSearchNameMatch, NULL, 0);
WIN32_FIND_DATAW data = {};
const auto searchPattern = std::wstring(path) + L"\\*";
HandleWrapper<FindFileHandleTraits> handle = FindFirstFileExW(searchPattern.c_str(), FindExInfoStandard, &data, FindExSearchNameMatch, nullptr, 0);
if (handle == INVALID_HANDLE_VALUE)
{
LOG_LAST_ERROR();
return;
}
@ -665,6 +495,4 @@ HOSTFXR_UTILITY::FindDotNetFolders(
{
pvFolders.emplace_back(data.cFileName);
} while (FindNextFileW(handle, &data));
FindClose(handle);
}

View File

@ -7,7 +7,7 @@
#include <vector>
#include <filesystem>
#include <optional>
#include "stringu.h"
#include <string>
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[]);
@ -19,76 +19,88 @@ class HOSTFXR_UTILITY
public:
static
HRESULT
GetStandaloneHostfxrParameters(
PCWSTR pwzExeAbsolutePath, // includes .exe file extension.
PCWSTR pcwzApplicationPhysicalPath,
PCWSTR pcwzArguments,
_Inout_ STRU* pStruHostFxrDllLocation,
_Out_ DWORD* pdwArgCount,
_Out_ BSTR** ppwzArgv
);
static
HRESULT
ParseHostfxrArguments(
PCWSTR pwzArgumentsFromConfig,
PCWSTR pwzExePath,
PCWSTR pcwzApplicationPhysicalPath,
_Out_ DWORD* pdwArgCount,
_Out_ BSTR** pbstrArgv
);
static
BOOL
IsDotnetExecutable(
_In_ const std::filesystem::path & dotnetPath
);
static
HRESULT
void
GetHostFxrParameters(
_In_ PCWSTR pcwzProcessPath,
_In_ PCWSTR pcwzApplicationPhysicalPath,
_In_ PCWSTR pcwzArguments,
_Inout_ STRU *pStruHostFxrDllLocation,
_Inout_ STRU *struExeAbsolutePath,
_Out_ DWORD *pdwArgCount,
_Out_ BSTR **ppwzArgv
const std::filesystem::path &processPath,
const std::filesystem::path &applicationPhysicalPath,
const std::wstring &applicationArguments,
std::filesystem::path &hostFxrDllPath,
std::filesystem::path &dotnetExePath,
std::vector<std::wstring> &arguments
);
static
VOID
FindDotNetFolders(
_In_ PCWSTR pszPath,
_Out_ std::vector<std::wstring> & pvFolders
);
class StartupParametersResolutionException: public std::runtime_error
{
public:
StartupParametersResolutionException(std::wstring msg)
: runtime_error("Startup parameter resulution error occured"), message(std::move(msg))
{
}
std::wstring get_message() const { return message; }
private:
std::wstring message;
};
static
std::wstring
FindHighestDotNetVersion(
_In_ std::vector<std::wstring> & vFolders
);
static
std::optional<std::filesystem::path>
GetAbsolutePathToHostFxr(
_In_ const std::filesystem::path & dotnetPath
void
ParseHostfxrArguments(
const std::wstring &arugments,
const std::filesystem::path &exePath,
const std::filesystem::path &applicationPhysicalPath,
std::vector<std::wstring> &arguments,
bool expandDllPaths = false
);
static
std::optional<std::filesystem::path>
GetAbsolutePathToDotnetFromProgramFiles();
private:
static
BOOL
IsDotnetExecutable(
const std::filesystem::path & dotnetPath
);
static
VOID
FindDotNetFolders(
const std::filesystem::path& path,
std::vector<std::wstring> & pvFolders
);
static
std::wstring
FindHighestDotNetVersion(
std::vector<std::wstring> & vFolders
);
static
std::filesystem::path
GetAbsolutePathToHostFxr(
const std::filesystem::path & dotnetPath
);
static
std::optional<std::filesystem::path>
InvokeWhereToFindDotnet();
static
std::optional<std::filesystem::path>
std::filesystem::path
GetAbsolutePathToDotnet(
_In_ const std::filesystem::path & applicationPath,
_In_ const std::filesystem::path & requestedPath
const std::filesystem::path & applicationPath,
const std::filesystem::path & requestedPath
);
struct LocalFreeDeleter
{
void operator ()(LPWSTR* ptr) const
{
LocalFree(ptr);
}
};
};

View File

@ -6,82 +6,48 @@
#include "hostfxr_utility.h"
#include "debugutil.h"
#include "exceptions.h"
#include "EventLog.h"
HRESULT HOSTFXR_OPTIONS::Create(
_In_ PCWSTR pcwzExeLocation,
_In_ PCWSTR pcwzDotnetExePath,
_In_ PCWSTR pcwzProcessPath,
_In_ PCWSTR pcwzApplicationPhysicalPath,
_In_ PCWSTR pcwzArguments,
_Out_ std::unique_ptr<HOSTFXR_OPTIONS>& ppWrapper)
{
STRU struHostFxrDllLocation;
STRU struExeAbsolutePath;
STRU struExeLocation;
BSTR* pwzArgv;
DWORD dwArgCount;
std::filesystem::path knownDotnetLocation;
if (pcwzExeLocation != NULL)
if (pcwzDotnetExePath != nullptr)
{
RETURN_IF_FAILED(struExeLocation.Copy(pcwzExeLocation));
knownDotnetLocation = pcwzDotnetExePath;
}
try
{
std::filesystem::path hostFxrDllPath;
std::vector<std::wstring> arguments;
HOSTFXR_UTILITY::GetHostFxrParameters(
pcwzProcessPath,
pcwzApplicationPhysicalPath,
pcwzArguments,
hostFxrDllPath,
knownDotnetLocation,
arguments);
// If the exe was not provided by the shim, reobtain the hostfxr parameters (which finds dotnet).
if (struExeLocation.IsEmpty())
{
RETURN_IF_FAILED(HOSTFXR_UTILITY::GetHostFxrParameters(
pcwzProcessPath,
pcwzApplicationPhysicalPath,
pcwzArguments,
&struHostFxrDllLocation,
&struExeAbsolutePath,
&dwArgCount,
&pwzArgv));
ppWrapper = std::make_unique<HOSTFXR_OPTIONS>(knownDotnetLocation, hostFxrDllPath, arguments);
}
else if (HOSTFXR_UTILITY::IsDotnetExecutable(struExeLocation.QueryStr()))
catch (HOSTFXR_UTILITY::StartupParametersResolutionException &resolutionException)
{
RETURN_IF_FAILED(HOSTFXR_UTILITY::ParseHostfxrArguments(
pcwzArguments,
pcwzExeLocation,
pcwzApplicationPhysicalPath,
&dwArgCount,
&pwzArgv));
}
else
{
RETURN_IF_FAILED(HOSTFXR_UTILITY::GetStandaloneHostfxrParameters(
pcwzExeLocation,
pcwzApplicationPhysicalPath,
pcwzArguments,
&struHostFxrDllLocation,
&dwArgCount,
&pwzArgv));
}
OBSERVE_CAUGHT_EXCEPTION();
ppWrapper = std::make_unique<HOSTFXR_OPTIONS>();
RETURN_IF_FAILED(ppWrapper->Populate(struHostFxrDllLocation.QueryStr(), struExeAbsolutePath.QueryStr(), dwArgCount, pwzArgv));
EventLog::Error(
ASPNETCORE_EVENT_INPROCESS_START_ERROR,
ASPNETCORE_EVENT_INPROCESS_START_ERROR_MSG,
pcwzApplicationPhysicalPath,
resolutionException.get_message().c_str());
return E_FAIL;
}
CATCH_RETURN();
return S_OK;
}
HRESULT HOSTFXR_OPTIONS::Populate(PCWSTR hostFxrLocation, PCWSTR struExeLocation, DWORD argc, BSTR argv[])
{
HRESULT hr;
m_argc = argc;
m_argv = argv;
if (FAILED(hr = m_hostFxrLocation.Copy(hostFxrLocation)))
{
goto Finished;
}
if (FAILED(hr = m_exeLocation.Copy(struExeLocation)))
{
goto Finished;
}
Finished:
return hr;
}

View File

@ -5,41 +5,45 @@
#include <Windows.h>
#include <memory>
#include "stringu.h"
#include <filesystem>
#include <utility>
#include <vector>
#include <string>
class HOSTFXR_OPTIONS
{
public:
HOSTFXR_OPTIONS() {}
HOSTFXR_OPTIONS(
std::filesystem::path dotnetExeLocation,
std::filesystem::path hostFxrLocation,
std::vector<std::wstring> arguments
)
: m_dotnetExeLocation(std::move(dotnetExeLocation)),
m_hostFxrLocation(std::move(hostFxrLocation)),
m_arguments(std::move(arguments))
{}
~HOSTFXR_OPTIONS()
void
GetArguments(DWORD &hostfxrArgc, std::unique_ptr<PCWSTR[]> &hostfxrArgv) const
{
delete[] m_argv;
hostfxrArgc = static_cast<DWORD>(m_arguments.size());
hostfxrArgv = std::unique_ptr<PCWSTR[]>(new PCWSTR[hostfxrArgc]);
for (DWORD i = 0; i < hostfxrArgc; ++i)
{
hostfxrArgv[i] = m_arguments[i].c_str();
}
}
DWORD
GetArgc() const
{
return m_argc;
}
BSTR*
GetArgv() const
{
return m_argv;
}
PCWSTR
const std::filesystem::path&
GetHostFxrLocation() const
{
return m_hostFxrLocation.QueryStr();
return m_hostFxrLocation;
}
PCWSTR
GetExeLocation() const
const std::filesystem::path&
GetDotnetExeLocation() const
{
return m_exeLocation.QueryStr();
return m_dotnetExeLocation;
}
static
@ -51,12 +55,7 @@ public:
_Out_ std::unique_ptr<HOSTFXR_OPTIONS>& ppWrapper);
private:
HRESULT Populate(PCWSTR hostFxrLocation, PCWSTR struExeLocation, DWORD argc, BSTR argv[]);
STRU m_exeLocation;
STRU m_hostFxrLocation;
DWORD m_argc;
BSTR* m_argv;
const std::filesystem::path m_dotnetExeLocation;
const std::filesystem::path m_hostFxrLocation;
const std::vector<std::wstring> m_arguments;
};

View File

@ -11,7 +11,6 @@
#define ASPNETCORE_EVENT_PROVIDER L"IIS AspNetCore Module V2"
#define ASPNETCORE_IISEXPRESS_EVENT_PROVIDER L"IIS Express AspNetCore Module V2"
#define ASPNETCORE_EVENT_MSG_BUFFER_SIZE 256
#define ASPNETCORE_EVENT_PROCESS_START_SUCCESS_MSG L"Application '%s' started process '%d' successfully and process '%d' is listening on port '%d'."
#define ASPNETCORE_EVENT_RAPID_FAIL_COUNT_EXCEEDED_MSG L"Maximum rapid fail count per minute of '%d' exceeded."
#define ASPNETCORE_EVENT_PROCESS_START_ERROR_MSG L"Application '%s' with physical root '%s' failed to start process with commandline '%s' at stage '%s', ErrorCode = '0x%x', assigned port %d, retryCounter '%d'."
@ -39,15 +38,9 @@
#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_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_MSG L"Could not find the hostfxr directory '%s' in the dotnet directory. 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_HOSTFXR_DLL_INVALID_VERSION_MSG L"Hostfxr version used does not support 'hostfxr_get_native_search_directories', update the version of hostfxr to a higher version. Path to hostfxr: '%s'."
#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_MSG L"Invalid or unknown processPath provided in web.config: processPath = '%s', ErrorCode = '0x%x'."
#define ASPNETCORE_EVENT_INPROCESS_RH_ERROR_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. Captured output: %s"
#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_INPROCESS_START_SUCCESS_MSG L"Application '%s' started the coreclr in-process successfully."
#define ASPNETCORE_EVENT_INPROCESS_START_ERROR_MSG L"Application '%s' wasn't able to start. %s"

View File

@ -392,11 +392,10 @@ IN_PROCESS_APPLICATION::ExecuteApplication(
{
HRESULT hr;
HMODULE hModule = nullptr;
DWORD hostfxrArgc = 0;
BSTR *hostfxrArgv = NULL;
hostfxr_main_fn pProc;
std::unique_ptr<HOSTFXR_OPTIONS> hostFxrOptions = NULL;
DWORD hostfxrArgc = 0;
std::unique_ptr<PCWSTR[]> hostfxrArgv;
DBG_ASSERT(m_status == MANAGED_APPLICATION_STATUS::STARTING);
pProc = s_fMainCallback;
@ -428,10 +427,7 @@ IN_PROCESS_APPLICATION::ExecuteApplication(
m_pConfig->QueryArguments()->QueryStr(),
hostFxrOptions
));
hostfxrArgc = hostFxrOptions->GetArgc();
hostfxrArgv = hostFxrOptions->GetArgv();
hostFxrOptions->GetArguments(hostfxrArgc, hostfxrArgv);
FINISHED_IF_FAILED(SetEnvironementVariablesOnWorkerProcess());
}
@ -457,7 +453,7 @@ IN_PROCESS_APPLICATION::ExecuteApplication(
// set the callbacks
s_Application = this;
hr = RunDotnetApplication(hostfxrArgc, hostfxrArgv, pProc);
hr = RunDotnetApplication(hostfxrArgc, hostfxrArgv.get(), pProc);
Finished:

View File

@ -1,7 +1,6 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using Microsoft.AspNetCore.Server.IntegrationTesting.IIS;
@ -12,6 +11,7 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests
{
public class EventLogHelpers
{
private static readonly Regex EventLogRegex = new Regex("Event Log: (?<EventLogMessage>.+?)End Event Log Message.", RegexOptions.Singleline | RegexOptions.Compiled);
public static void VerifyEventLogEvent(IISDeploymentResult deploymentResult, ITestSink testSink, string expectedRegexMatchString)
{
Assert.True(deploymentResult.HostProcess.HasExited);
@ -23,9 +23,19 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests
builder.Append(context.Message);
}
var eventLogRegex = new Regex($"Event Log: (.*?){expectedRegexMatchString}(.*?)End Event Log Message.", RegexOptions.Singleline);
var count = 0;
var expectedRegex = new Regex(expectedRegexMatchString, RegexOptions.Singleline);
foreach (Match match in EventLogRegex.Matches(builder.ToString()))
{
var eventLogText = match.Groups["EventLogMessage"].Value;
if (expectedRegex.IsMatch(eventLogText))
{
count++;
}
}
Assert.Matches(eventLogRegex, builder.ToString());
Assert.True(count > 0, $"'{expectedRegexMatchString}' didn't match any event log messaged");
Assert.True(count < 2, $"'{expectedRegexMatchString}' matched more then one event log message");
}
}
}

View File

@ -28,11 +28,6 @@ namespace Microsoft.AspNetCore.Server.IntegrationTesting
parameters.EnvironmentVariables[DebugEnvironmentVariable] = "console";
}
if (parameters.ApplicationPublisher == null)
{
throw new InvalidOperationException("All tests should use ApplicationPublisher");
}
return IISApplicationDeployerFactory.Create(parameters, LoggerFactory);
}

View File

@ -2,80 +2,75 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
#include "stdafx.h"
#include "file_utility.h"
#include <filesystem>
#include <vector>
#include <string>
#include "hostfxr_utility.h"
#include "Environment.h"
TEST(ParseHostFxrArguments, BasicHostFxrArguments)
{
DWORD retVal = 0;
BSTR* bstrArray;
std::vector<std::wstring> bstrArray;
PCWSTR exeStr = L"C:/Program Files/dotnet.exe";
HRESULT hr = HOSTFXR_UTILITY::ParseHostfxrArguments(
HOSTFXR_UTILITY::ParseHostfxrArguments(
L"exec \"test.dll\"", // args
exeStr, // exe path
L"invalid", // physical path to application
&retVal, // arg count
&bstrArray); // args array.
bstrArray); // args array.
EXPECT_EQ(hr, S_OK);
EXPECT_EQ(DWORD(3), retVal);
ASSERT_STREQ(exeStr, bstrArray[0]);
ASSERT_STREQ(L"exec", bstrArray[1]);
ASSERT_STREQ(L"test.dll", bstrArray[2]);
EXPECT_EQ(3, bstrArray.size());
ASSERT_STREQ(exeStr, bstrArray[0].c_str());
ASSERT_STREQ(L"exec", bstrArray[1].c_str());
ASSERT_STREQ(L"test.dll", bstrArray[2].c_str());
}
TEST(ParseHostFxrArguments, NoExecProvided)
{
DWORD retVal = 0;
BSTR* bstrArray;
std::vector<std::wstring> bstrArray;
PCWSTR exeStr = L"C:/Program Files/dotnet.exe";
HRESULT hr = HOSTFXR_UTILITY::ParseHostfxrArguments(
HOSTFXR_UTILITY::ParseHostfxrArguments(
L"test.dll", // args
exeStr, // exe path
L"ignored", // physical path to application
&retVal, // arg count
&bstrArray); // args array.
bstrArray); // args array.
EXPECT_EQ(hr, S_OK);
EXPECT_EQ(DWORD(2), retVal);
ASSERT_STREQ(exeStr, bstrArray[0]);
ASSERT_STREQ(L"test.dll", bstrArray[1]);
EXPECT_EQ(DWORD(2), bstrArray.size());
ASSERT_STREQ(exeStr, bstrArray[0].c_str());
ASSERT_STREQ(L"test.dll", bstrArray[1].c_str());
}
TEST(ParseHostFxrArguments, ConvertDllToAbsolutePath)
{
DWORD retVal = 0;
BSTR* bstrArray;
std::vector<std::wstring> bstrArray;
PCWSTR exeStr = L"C:/Program Files/dotnet.exe";
HRESULT hr = HOSTFXR_UTILITY::ParseHostfxrArguments(
L"exec \"test.dll\"", // args
// we need to use existing dll so let's use ntdll that we know exists everywhere
auto system32 = Environment::ExpandEnvironmentVariables(L"%WINDIR%\\System32");
HOSTFXR_UTILITY::ParseHostfxrArguments(
L"exec \"ntdll.dll\"", // args
exeStr, // exe path
L"C:/test", // physical path to application
&retVal, // arg count
&bstrArray); // args array.
system32, // physical path to application
bstrArray, // args array.
true); // expandDllPaths
EXPECT_EQ(hr, S_OK);
EXPECT_EQ(DWORD(3), retVal);
ASSERT_STREQ(exeStr, bstrArray[0]);
ASSERT_STREQ(L"exec", bstrArray[1]);
ASSERT_STREQ(L"C:\\test\\test.dll", bstrArray[2]);
EXPECT_EQ(DWORD(3), bstrArray.size());
ASSERT_STREQ(exeStr, bstrArray[0].c_str());
ASSERT_STREQ(L"exec", bstrArray[1].c_str());
ASSERT_STREQ((system32 + L"\\ntdll.dll").c_str(), bstrArray[2].c_str());
}
TEST(ParseHostFxrArguments, ProvideNoArgs_InvalidArgs)
{
DWORD retVal = 0;
BSTR* bstrArray;
std::vector<std::wstring> bstrArray;
PCWSTR exeStr = L"C:/Program Files/dotnet.exe";
HRESULT hr = HOSTFXR_UTILITY::ParseHostfxrArguments(
ASSERT_THROW(HOSTFXR_UTILITY::ParseHostfxrArguments(
L"", // args
exeStr, // exe path
L"ignored", // physical path to application
&retVal, // arg count
&bstrArray); // args array.
EXPECT_EQ(E_INVALIDARG, hr);
bstrArray), // args array.
HOSTFXR_UTILITY::StartupParametersResolutionException);
}
TEST(GetAbsolutePathToDotnetFromProgramFiles, BackupWorks)
@ -118,19 +113,16 @@ TEST(GetAbsolutePathToDotnetFromProgramFiles, BackupWorks)
TEST(GetHostFxrArguments, InvalidParams)
{
DWORD retVal = 0;
BSTR* bstrArray;
STRU struHostFxrDllLocation;
STRU struExeLocation;
std::vector<std::wstring> bstrArray;
std::filesystem::path struHostFxrDllLocation;
std::filesystem::path struExeLocation;
HRESULT hr = HOSTFXR_UTILITY::GetHostFxrParameters(
EXPECT_THROW(HOSTFXR_UTILITY::GetHostFxrParameters(
L"bogus", // processPath
L"", // application physical path, ignored.
L"ignored", //arguments
NULL,
&struExeLocation,
&retVal, // arg count
&bstrArray); // args array.
EXPECT_EQ(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), hr);
struHostFxrDllLocation,
struExeLocation,
bstrArray), // args array.
HOSTFXR_UTILITY::StartupParametersResolutionException);
}

View File

@ -5,6 +5,7 @@ using System;
using System.IO;
using System.Linq;
using System.Net;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Server.IIS.FunctionalTests.Utilities;
using Microsoft.AspNetCore.Server.IntegrationTesting;
@ -40,14 +41,16 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests
}
[ConditionalTheory]
[InlineData("bogus")]
[InlineData("c:\\random files\\dotnet.exe")]
[InlineData(".\\dotnet.exe")]
public async Task InvalidProcessPath_ExpectServerError(string path)
[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)
{
var deploymentParameters = _fixture.GetBaseDeploymentParameters(publish: true);
deploymentParameters.WebConfigActionList.Add(WebConfigHelpers.AddOrModifyAspNetCoreSection("processPath", path));
deploymentParameters.WebConfigActionList.Add(WebConfigHelpers.AddOrModifyAspNetCoreSection("arguments", arguments));
var deploymentResult = await DeployAsync(deploymentParameters);
@ -57,7 +60,7 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests
StopServer();
EventLogHelpers.VerifyEventLogEvent(deploymentResult, TestSink, @"Invalid or unknown processPath provided in web\.config: processPath = '.+', ErrorCode = '0x80070002'\.");
EventLogHelpers.VerifyEventLogEvent(deploymentResult, TestSink, $@"Application '{Regex.Escape(deploymentResult.ContentRoot)}\\' wasn't able to start. {subError}");
}
[ConditionalFact]
@ -136,6 +139,24 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests
Assert.Equal("Hello World", responseText);
}
[ConditionalFact]
public async Task StartsWithPortableAndBootstraperExe()
{
var deploymentParameters = _fixture.GetBaseDeploymentParameters(_fixture.InProcessTestSite, publish: true);
// rest publisher as it doesn't support additional parameters
deploymentParameters.ApplicationPublisher = null;
// ReferenceTestTasks is workaround for https://github.com/dotnet/sdk/issues/2482
deploymentParameters.AdditionalPublishParameters = "-p:RuntimeIdentifier=win7-x64 -p:UseAppHost=true -p:SelfContained=false -p:ReferenceTestTasks=false";
deploymentParameters.RestoreOnPublish = true;
var deploymentResult = await DeployAsync(deploymentParameters);
Assert.True(File.Exists(Path.Combine(deploymentResult.ContentRoot, "InProcessWebSite.exe")));
Assert.False(File.Exists(Path.Combine(deploymentResult.ContentRoot, "hostfxr.dll")));
Assert.Contains("InProcessWebSite.exe", File.ReadAllText(Path.Combine(deploymentResult.ContentRoot, "web.config")));
await deploymentResult.AssertStarts();
}
[ConditionalFact]
public async Task DetectsOveriddenServer()
{