Support portable.exe apps and better error reporting (#1287)
This commit is contained in:
parent
36e5aceb3c
commit
eebbb6a602
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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">
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
@ -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
|
||||
.
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
{
|
||||
|
|
|
|||
Loading…
Reference in New Issue