Expand environment variables processPath and arguments, reject invalid processPaths. (#715)
This commit is contained in:
parent
847ab71209
commit
7b0f50aa53
|
|
@ -164,19 +164,25 @@ HOSTFXR_UTILITY::GetHostFxrParameters(
|
||||||
{
|
{
|
||||||
HRESULT hr = S_OK;
|
HRESULT hr = S_OK;
|
||||||
STRU struSystemPathVariable;
|
STRU struSystemPathVariable;
|
||||||
STRU struHostFxrPath;
|
STRU struAbsolutePathToHostFxr;
|
||||||
STRU struExeLocation;
|
STRU struAbsolutePathToDotnet;
|
||||||
STRU struHostFxrSearchExpression;
|
|
||||||
STRU struHighestDotnetVersion;
|
|
||||||
STRU struEventMsg;
|
STRU struEventMsg;
|
||||||
std::vector<std::wstring> vVersionFolders;
|
STACK_STRU(struExpandedProcessPath, MAX_PATH);
|
||||||
DWORD dwPosition;
|
STACK_STRU(struExpandedArguments, MAX_PATH);
|
||||||
|
|
||||||
// Convert the process path an absolute path.
|
// Copy and Expand the processPath and Arguments.
|
||||||
|
if (FAILED(hr = struExpandedProcessPath.CopyAndExpandEnvironmentStrings(pcwzProcessPath))
|
||||||
|
|| FAILED(hr = struExpandedArguments.CopyAndExpandEnvironmentStrings(pcwzArguments)))
|
||||||
|
{
|
||||||
|
goto Finished;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert the process path an absolute path to our current application directory.
|
||||||
|
// If the path is already an absolute path, it will be unchanged.
|
||||||
hr = UTILITY::ConvertPathToFullPath(
|
hr = UTILITY::ConvertPathToFullPath(
|
||||||
pcwzProcessPath,
|
struExpandedProcessPath.QueryStr(),
|
||||||
pcwzApplicationPhysicalPath,
|
pcwzApplicationPhysicalPath,
|
||||||
&struExeLocation
|
&struAbsolutePathToDotnet
|
||||||
);
|
);
|
||||||
|
|
||||||
if (FAILED(hr))
|
if (FAILED(hr))
|
||||||
|
|
@ -184,152 +190,76 @@ HOSTFXR_UTILITY::GetHostFxrParameters(
|
||||||
goto Finished;
|
goto Finished;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (UTILITY::CheckIfFileExists(struExeLocation.QueryStr()))
|
// Check if the absolute path is to dotnet or not.
|
||||||
|
if (struAbsolutePathToDotnet.EndsWith(L"dotnet.exe") || struAbsolutePathToDotnet.EndsWith(L"dotnet"))
|
||||||
{
|
{
|
||||||
// Check if hostfxr is in this folder, if it is, we are a standalone application,
|
//
|
||||||
// else we assume we received an absolute path to dotnet.exe
|
// The processPath ends with dotnet.exe or dotnet
|
||||||
if (!struExeLocation.EndsWith(L"dotnet.exe") &&
|
// like: C:\Program Files\dotnet\dotnet.exe, C:\Program Files\dotnet\dotnet, dotnet.exe, or dotnet.
|
||||||
!struExeLocation.EndsWith(L"dotnet"))
|
// Get the absolute path to dotnet. If the path is already an absolute path, it will return that path
|
||||||
|
//
|
||||||
|
if (FAILED(hr = HOSTFXR_UTILITY::GetAbsolutePathToDotnet(&struAbsolutePathToDotnet))) // Make sure to append the dotnet.exe path correctly here (pass in regular path)?
|
||||||
|
{
|
||||||
|
goto Finished;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (FAILED(hr = GetAbsolutePathToHostFxr(&struAbsolutePathToDotnet, hEventLog, &struAbsolutePathToHostFxr)))
|
||||||
|
{
|
||||||
|
goto Finished;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (FAILED(hr = ParseHostfxrArguments(
|
||||||
|
struExpandedArguments.QueryStr(),
|
||||||
|
struAbsolutePathToDotnet.QueryStr(),
|
||||||
|
pcwzApplicationPhysicalPath,
|
||||||
|
hEventLog,
|
||||||
|
pdwArgCount,
|
||||||
|
pbstrArgv)))
|
||||||
|
{
|
||||||
|
goto Finished;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (FAILED(hr = struHostFxrDllLocation->Copy(struAbsolutePathToHostFxr)))
|
||||||
{
|
{
|
||||||
hr = GetStandaloneHostfxrParameters(
|
|
||||||
struExeLocation.QueryStr(),
|
|
||||||
pcwzApplicationPhysicalPath,
|
|
||||||
pcwzArguments,
|
|
||||||
hEventLog,
|
|
||||||
struHostFxrDllLocation,
|
|
||||||
pdwArgCount,
|
|
||||||
pbstrArgv);
|
|
||||||
goto Finished;
|
goto Finished;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (FAILED(hr = HOSTFXR_UTILITY::FindDotnetExePath(&struExeLocation)))
|
//
|
||||||
|
// The processPath is a path to the application executable
|
||||||
|
// like: C:\test\MyApp.Exe or MyApp.Exe
|
||||||
|
// Check if the file exists, and if it does, get the parameters for a standalone application
|
||||||
|
//
|
||||||
|
if (UTILITY::CheckIfFileExists(struAbsolutePathToDotnet.QueryStr()))
|
||||||
{
|
{
|
||||||
goto Finished;
|
hr = GetStandaloneHostfxrParameters(
|
||||||
|
struAbsolutePathToDotnet.QueryStr(),
|
||||||
|
pcwzApplicationPhysicalPath,
|
||||||
|
struExpandedArguments.QueryStr(),
|
||||||
|
hEventLog,
|
||||||
|
struHostFxrDllLocation,
|
||||||
|
pdwArgCount,
|
||||||
|
pbstrArgv);
|
||||||
}
|
}
|
||||||
}
|
else
|
||||||
|
|
||||||
if (FAILED(hr = struExeLocation.SyncWithBuffer()) ||
|
|
||||||
FAILED(hr = struHostFxrPath.Copy(struExeLocation)))
|
|
||||||
{
|
|
||||||
goto Finished;
|
|
||||||
}
|
|
||||||
|
|
||||||
dwPosition = struHostFxrPath.LastIndexOf(L'\\', 0);
|
|
||||||
if (dwPosition == -1)
|
|
||||||
{
|
|
||||||
hr = E_FAIL;
|
|
||||||
goto Finished;
|
|
||||||
}
|
|
||||||
|
|
||||||
struHostFxrPath.QueryStr()[dwPosition] = L'\0';
|
|
||||||
|
|
||||||
if (FAILED(hr = struHostFxrPath.SyncWithBuffer()) ||
|
|
||||||
FAILED(hr = struHostFxrPath.Append(L"\\")))
|
|
||||||
{
|
|
||||||
goto Finished;
|
|
||||||
}
|
|
||||||
|
|
||||||
hr = struHostFxrPath.Append(L"host\\fxr");
|
|
||||||
if (FAILED(hr))
|
|
||||||
{
|
|
||||||
goto Finished;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!UTILITY::DirectoryExists(&struHostFxrPath))
|
|
||||||
{
|
|
||||||
hr = ERROR_BAD_ENVIRONMENT;
|
|
||||||
if (SUCCEEDED(struEventMsg.SafeSnwprintf(
|
|
||||||
ASPNETCORE_EVENT_HOSTFXR_DIRECTORY_NOT_FOUND_MSG,
|
|
||||||
struHostFxrPath.QueryStr(),
|
|
||||||
hr)))
|
|
||||||
{
|
{
|
||||||
UTILITY::LogEvent(hEventLog,
|
//
|
||||||
EVENTLOG_ERROR_TYPE,
|
// If the processPath file does not exist and it doesn't include dotnet.exe or dotnet
|
||||||
ASPNETCORE_EVENT_HOSTFXR_DIRECTORY_NOT_FOUND,
|
// then it is an invalid argument.
|
||||||
struEventMsg.QueryStr());
|
//
|
||||||
|
hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);;
|
||||||
|
if (SUCCEEDED(struEventMsg.SafeSnwprintf(
|
||||||
|
ASPNETCORE_EVENT_INVALID_PROCESS_PATH_MSG,
|
||||||
|
struExpandedProcessPath.QueryStr(),
|
||||||
|
hr)))
|
||||||
|
{
|
||||||
|
UTILITY::LogEvent(hEventLog,
|
||||||
|
EVENTLOG_ERROR_TYPE,
|
||||||
|
ASPNETCORE_EVENT_GENERAL_ERROR_MSG,
|
||||||
|
struEventMsg.QueryStr());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
goto Finished;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Find all folders under host\\fxr\\ for version numbers.
|
|
||||||
hr = struHostFxrSearchExpression.Copy(struHostFxrPath);
|
|
||||||
if (FAILED(hr))
|
|
||||||
{
|
|
||||||
goto Finished;
|
|
||||||
}
|
|
||||||
|
|
||||||
hr = struHostFxrSearchExpression.Append(L"\\*");
|
|
||||||
if (FAILED(hr))
|
|
||||||
{
|
|
||||||
goto Finished;
|
|
||||||
}
|
|
||||||
|
|
||||||
// As we use the logic from core-setup, we are opting to use std here.
|
|
||||||
// TODO remove all uses of std?
|
|
||||||
UTILITY::FindDotNetFolders(struHostFxrSearchExpression.QueryStr(), &vVersionFolders);
|
|
||||||
|
|
||||||
if (vVersionFolders.size() == 0)
|
|
||||||
{
|
|
||||||
hr = HRESULT_FROM_WIN32(ERROR_BAD_ENVIRONMENT);
|
|
||||||
if (SUCCEEDED(struEventMsg.SafeSnwprintf(
|
|
||||||
ASPNETCORE_EVENT_HOSTFXR_DIRECTORY_NOT_FOUND_MSG,
|
|
||||||
struHostFxrPath.QueryStr(),
|
|
||||||
hr)))
|
|
||||||
{
|
|
||||||
UTILITY::LogEvent(hEventLog,
|
|
||||||
EVENTLOG_ERROR_TYPE,
|
|
||||||
ASPNETCORE_EVENT_HOSTFXR_DIRECTORY_NOT_FOUND,
|
|
||||||
struEventMsg.QueryStr());
|
|
||||||
}
|
|
||||||
goto Finished;
|
|
||||||
}
|
|
||||||
|
|
||||||
hr = UTILITY::FindHighestDotNetVersion(vVersionFolders, &struHighestDotnetVersion);
|
|
||||||
if (FAILED(hr))
|
|
||||||
{
|
|
||||||
goto Finished;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (FAILED(hr = struHostFxrPath.Append(L"\\"))
|
|
||||||
|| FAILED(hr = struHostFxrPath.Append(struHighestDotnetVersion.QueryStr()))
|
|
||||||
|| FAILED(hr = struHostFxrPath.Append(L"\\hostfxr.dll")))
|
|
||||||
{
|
|
||||||
goto Finished;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!UTILITY::CheckIfFileExists(struHostFxrPath.QueryStr()))
|
|
||||||
{
|
|
||||||
// ASPNETCORE_EVENT_HOSTFXR_DLL_NOT_FOUND_MSG
|
|
||||||
hr = HRESULT_FROM_WIN32(ERROR_FILE_INVALID);
|
|
||||||
if (SUCCEEDED(struEventMsg.SafeSnwprintf(
|
|
||||||
ASPNETCORE_EVENT_HOSTFXR_DLL_NOT_FOUND_MSG,
|
|
||||||
struHostFxrPath.QueryStr(),
|
|
||||||
hr)))
|
|
||||||
{
|
|
||||||
UTILITY::LogEvent(hEventLog,
|
|
||||||
EVENTLOG_ERROR_TYPE,
|
|
||||||
ASPNETCORE_EVENT_HOSTFXR_DLL_NOT_FOUND,
|
|
||||||
struEventMsg.QueryStr());
|
|
||||||
}
|
|
||||||
goto Finished;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (FAILED(hr = ParseHostfxrArguments(
|
|
||||||
pcwzArguments,
|
|
||||||
struExeLocation.QueryStr(),
|
|
||||||
pcwzApplicationPhysicalPath,
|
|
||||||
hEventLog,
|
|
||||||
pdwArgCount,
|
|
||||||
pbstrArgv)))
|
|
||||||
{
|
|
||||||
goto Finished;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (FAILED(hr = struHostFxrDllLocation->Copy(struHostFxrPath)))
|
|
||||||
{
|
|
||||||
goto Finished;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Finished:
|
Finished:
|
||||||
|
|
@ -383,7 +313,7 @@ HOSTFXR_UTILITY::ParseHostfxrArguments(
|
||||||
goto Failure;
|
goto Failure;
|
||||||
}
|
}
|
||||||
|
|
||||||
argv = new PWSTR[argc + 1];
|
argv = new BSTR[argc + 1];
|
||||||
if (argv == NULL)
|
if (argv == NULL)
|
||||||
{
|
{
|
||||||
hr = E_OUTOFMEMORY;
|
hr = E_OUTOFMEMORY;
|
||||||
|
|
@ -457,41 +387,220 @@ Finished:
|
||||||
return hr;
|
return hr;
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
|
||||||
// Invoke where.exe to find the location of dotnet.exe
|
|
||||||
// Copies contents of dotnet.exe to a temp file
|
|
||||||
// Respects path ordering.
|
|
||||||
HRESULT
|
HRESULT
|
||||||
HOSTFXR_UTILITY::FindDotnetExePath(
|
HOSTFXR_UTILITY::GetAbsolutePathToDotnet(
|
||||||
_Out_ STRU* struDotnetPath
|
_Inout_ STRU* pStruAbsolutePathToDotnet
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
HRESULT hr = S_OK;
|
HRESULT hr = S_OK;
|
||||||
|
|
||||||
|
//
|
||||||
|
// If we are given an absolute path to dotnet.exe, we are done
|
||||||
|
//
|
||||||
|
if (UTILITY::CheckIfFileExists(pStruAbsolutePathToDotnet->QueryStr()))
|
||||||
|
{
|
||||||
|
goto Finished;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// If the path was C:\Program Files\dotnet\dotnet
|
||||||
|
// We need to try appending .exe and check if the file exists too.
|
||||||
|
//
|
||||||
|
if (FAILED(hr = pStruAbsolutePathToDotnet->Append(L".exe")))
|
||||||
|
{
|
||||||
|
goto Finished;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (UTILITY::CheckIfFileExists(pStruAbsolutePathToDotnet->QueryStr()))
|
||||||
|
{
|
||||||
|
goto Finished;
|
||||||
|
}
|
||||||
|
|
||||||
|
// At this point, we are calling where.exe to find dotnet.
|
||||||
|
// If we encounter any failures, try getting dotnet.exe from the
|
||||||
|
// backup location.
|
||||||
|
if (!InvokeWhereToFindDotnet(pStruAbsolutePathToDotnet))
|
||||||
|
{
|
||||||
|
hr = GetAbsolutePathToDotnetFromProgramFiles(pStruAbsolutePathToDotnet);
|
||||||
|
}
|
||||||
|
|
||||||
|
Finished:
|
||||||
|
|
||||||
|
return hr;
|
||||||
|
}
|
||||||
|
|
||||||
|
HRESULT
|
||||||
|
HOSTFXR_UTILITY::GetAbsolutePathToHostFxr(
|
||||||
|
STRU* pStruAbsolutePathToDotnet,
|
||||||
|
HANDLE hEventLog,
|
||||||
|
STRU* pStruAbsolutePathToHostfxr
|
||||||
|
)
|
||||||
|
{
|
||||||
|
HRESULT hr = S_OK;
|
||||||
|
STRU struHostFxrPath;
|
||||||
|
STRU struHostFxrSearchExpression;
|
||||||
|
STRU struHighestDotnetVersion;
|
||||||
|
STRU struEventMsg;
|
||||||
|
std::vector<std::wstring> vVersionFolders;
|
||||||
|
DWORD dwPosition = 0;
|
||||||
|
|
||||||
|
if (FAILED(hr = struHostFxrPath.Copy(pStruAbsolutePathToDotnet)))
|
||||||
|
{
|
||||||
|
goto Finished;
|
||||||
|
}
|
||||||
|
|
||||||
|
dwPosition = struHostFxrPath.LastIndexOf(L'\\', 0);
|
||||||
|
if (dwPosition == -1)
|
||||||
|
{
|
||||||
|
hr = E_FAIL;
|
||||||
|
goto Finished;
|
||||||
|
}
|
||||||
|
|
||||||
|
struHostFxrPath.QueryStr()[dwPosition] = L'\0';
|
||||||
|
|
||||||
|
if (FAILED(hr = struHostFxrPath.SyncWithBuffer()) ||
|
||||||
|
FAILED(hr = struHostFxrPath.Append(L"\\")))
|
||||||
|
{
|
||||||
|
goto Finished;
|
||||||
|
}
|
||||||
|
|
||||||
|
hr = struHostFxrPath.Append(L"host\\fxr");
|
||||||
|
if (FAILED(hr))
|
||||||
|
{
|
||||||
|
goto Finished;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!UTILITY::DirectoryExists(&struHostFxrPath))
|
||||||
|
{
|
||||||
|
hr = ERROR_BAD_ENVIRONMENT;
|
||||||
|
if (SUCCEEDED(struEventMsg.SafeSnwprintf(
|
||||||
|
ASPNETCORE_EVENT_HOSTFXR_DIRECTORY_NOT_FOUND_MSG,
|
||||||
|
struHostFxrPath.QueryStr(),
|
||||||
|
hr)))
|
||||||
|
{
|
||||||
|
UTILITY::LogEvent(hEventLog,
|
||||||
|
EVENTLOG_ERROR_TYPE,
|
||||||
|
ASPNETCORE_EVENT_HOSTFXR_DIRECTORY_NOT_FOUND,
|
||||||
|
struEventMsg.QueryStr());
|
||||||
|
}
|
||||||
|
goto Finished;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find all folders under host\\fxr\\ for version numbers.
|
||||||
|
hr = struHostFxrSearchExpression.Copy(struHostFxrPath);
|
||||||
|
if (FAILED(hr))
|
||||||
|
{
|
||||||
|
goto Finished;
|
||||||
|
}
|
||||||
|
|
||||||
|
hr = struHostFxrSearchExpression.Append(L"\\*");
|
||||||
|
if (FAILED(hr))
|
||||||
|
{
|
||||||
|
goto Finished;
|
||||||
|
}
|
||||||
|
|
||||||
|
// As we use the logic from core-setup, we are opting to use std here.
|
||||||
|
UTILITY::FindDotNetFolders(struHostFxrSearchExpression.QueryStr(), &vVersionFolders);
|
||||||
|
|
||||||
|
if (vVersionFolders.size() == 0)
|
||||||
|
{
|
||||||
|
hr = HRESULT_FROM_WIN32(ERROR_BAD_ENVIRONMENT);
|
||||||
|
if (SUCCEEDED(struEventMsg.SafeSnwprintf(
|
||||||
|
ASPNETCORE_EVENT_HOSTFXR_DIRECTORY_NOT_FOUND_MSG,
|
||||||
|
struHostFxrPath.QueryStr(),
|
||||||
|
hr)))
|
||||||
|
{
|
||||||
|
UTILITY::LogEvent(hEventLog,
|
||||||
|
EVENTLOG_ERROR_TYPE,
|
||||||
|
ASPNETCORE_EVENT_HOSTFXR_DIRECTORY_NOT_FOUND,
|
||||||
|
struEventMsg.QueryStr());
|
||||||
|
}
|
||||||
|
goto Finished;
|
||||||
|
}
|
||||||
|
|
||||||
|
hr = UTILITY::FindHighestDotNetVersion(vVersionFolders, &struHighestDotnetVersion);
|
||||||
|
if (FAILED(hr))
|
||||||
|
{
|
||||||
|
goto Finished;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (FAILED(hr = struHostFxrPath.Append(L"\\"))
|
||||||
|
|| FAILED(hr = struHostFxrPath.Append(struHighestDotnetVersion.QueryStr()))
|
||||||
|
|| FAILED(hr = struHostFxrPath.Append(L"\\hostfxr.dll")))
|
||||||
|
{
|
||||||
|
goto Finished;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!UTILITY::CheckIfFileExists(struHostFxrPath.QueryStr()))
|
||||||
|
{
|
||||||
|
// ASPNETCORE_EVENT_HOSTFXR_DLL_NOT_FOUND_MSG
|
||||||
|
hr = HRESULT_FROM_WIN32(ERROR_FILE_INVALID);
|
||||||
|
if (SUCCEEDED(struEventMsg.SafeSnwprintf(
|
||||||
|
ASPNETCORE_EVENT_HOSTFXR_DLL_NOT_FOUND_MSG,
|
||||||
|
struHostFxrPath.QueryStr(),
|
||||||
|
hr)))
|
||||||
|
{
|
||||||
|
UTILITY::LogEvent(hEventLog,
|
||||||
|
EVENTLOG_ERROR_TYPE,
|
||||||
|
ASPNETCORE_EVENT_HOSTFXR_DLL_NOT_FOUND,
|
||||||
|
struEventMsg.QueryStr());
|
||||||
|
}
|
||||||
|
goto Finished;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (FAILED(hr = pStruAbsolutePathToHostfxr->Copy(struHostFxrPath)))
|
||||||
|
{
|
||||||
|
goto Finished;
|
||||||
|
}
|
||||||
|
|
||||||
|
Finished:
|
||||||
|
return hr;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
//
|
||||||
|
BOOL
|
||||||
|
HOSTFXR_UTILITY::InvokeWhereToFindDotnet(
|
||||||
|
_Inout_ STRU* pStruAbsolutePathToDotnet
|
||||||
|
)
|
||||||
|
{
|
||||||
|
HRESULT hr = S_OK;
|
||||||
|
// Arguments to call where.exe
|
||||||
STARTUPINFOW startupInfo = { 0 };
|
STARTUPINFOW startupInfo = { 0 };
|
||||||
PROCESS_INFORMATION processInformation = { 0 };
|
PROCESS_INFORMATION processInformation = { 0 };
|
||||||
SECURITY_ATTRIBUTES securityAttributes;
|
SECURITY_ATTRIBUTES securityAttributes;
|
||||||
STRU struDotnetSubstring;
|
|
||||||
STRU struDotnetLocationsString;
|
|
||||||
LPWSTR pwzDotnetName = NULL;
|
|
||||||
DWORD dwExitCode;
|
|
||||||
DWORD dwNumBytesRead;
|
|
||||||
DWORD dwFilePointer;
|
|
||||||
DWORD dwBinaryType;
|
|
||||||
DWORD dwPathSize = MAX_PATH;
|
|
||||||
INT index = 0;
|
|
||||||
INT prevIndex = 0;
|
|
||||||
BOOL fResult = FALSE;
|
|
||||||
BOOL fIsWow64Process;
|
|
||||||
BOOL fIsCurrentProcess64Bit;
|
|
||||||
BOOL fFound = FALSE;
|
|
||||||
CHAR pzFileContents[READ_BUFFER_SIZE];
|
CHAR pzFileContents[READ_BUFFER_SIZE];
|
||||||
HANDLE hStdOutReadPipe = INVALID_HANDLE_VALUE;
|
HANDLE hStdOutReadPipe = INVALID_HANDLE_VALUE;
|
||||||
HANDLE hStdOutWritePipe = INVALID_HANDLE_VALUE;
|
HANDLE hStdOutWritePipe = INVALID_HANDLE_VALUE;
|
||||||
|
LPWSTR pwzDotnetName = NULL;
|
||||||
|
DWORD dwFilePointer;
|
||||||
|
BOOL fIsWow64Process;
|
||||||
|
BOOL fIsCurrentProcess64Bit;
|
||||||
|
DWORD dwExitCode;
|
||||||
|
STRU struDotnetSubstring;
|
||||||
|
STRU struDotnetLocationsString;
|
||||||
|
DWORD dwNumBytesRead;
|
||||||
|
DWORD dwBinaryType;
|
||||||
|
INT index = 0;
|
||||||
|
INT prevIndex = 0;
|
||||||
|
BOOL fProcessCreationResult = FALSE;
|
||||||
|
BOOL fResult = FALSE;
|
||||||
|
|
||||||
|
// Set the security attributes for the read/write pipe
|
||||||
securityAttributes.nLength = sizeof(securityAttributes);
|
securityAttributes.nLength = sizeof(securityAttributes);
|
||||||
securityAttributes.lpSecurityDescriptor = NULL;
|
securityAttributes.lpSecurityDescriptor = NULL;
|
||||||
securityAttributes.bInheritHandle = TRUE;
|
securityAttributes.bInheritHandle = TRUE;
|
||||||
|
|
||||||
|
// Reset the path to dotnet as we will be using whether the string is
|
||||||
|
// empty or not as state
|
||||||
|
pStruAbsolutePathToDotnet->Reset();
|
||||||
|
|
||||||
|
// Create a read/write pipe that will be used for reading the result of where.exe
|
||||||
if (!CreatePipe(&hStdOutReadPipe, &hStdOutWritePipe, &securityAttributes, 0))
|
if (!CreatePipe(&hStdOutReadPipe, &hStdOutWritePipe, &securityAttributes, 0))
|
||||||
{
|
{
|
||||||
hr = HRESULT_FROM_WIN32(GetLastError());
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
||||||
|
|
@ -503,7 +612,7 @@ HOSTFXR_UTILITY::FindDotnetExePath(
|
||||||
goto Finished;
|
goto Finished;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set stdout and error to redirect to the temp file.
|
// Set the stdout and err pipe to the write pipes.
|
||||||
startupInfo.cb = sizeof(startupInfo);
|
startupInfo.cb = sizeof(startupInfo);
|
||||||
startupInfo.dwFlags |= STARTF_USESTDHANDLES;
|
startupInfo.dwFlags |= STARTF_USESTDHANDLES;
|
||||||
startupInfo.hStdOutput = hStdOutWritePipe;
|
startupInfo.hStdOutput = hStdOutWritePipe;
|
||||||
|
|
@ -511,14 +620,14 @@ HOSTFXR_UTILITY::FindDotnetExePath(
|
||||||
|
|
||||||
// CreateProcess requires a mutable string to be passed to commandline
|
// CreateProcess requires a mutable string to be passed to commandline
|
||||||
// See https://blogs.msdn.microsoft.com/oldnewthing/20090601-00/?p=18083/
|
// See https://blogs.msdn.microsoft.com/oldnewthing/20090601-00/?p=18083/
|
||||||
|
|
||||||
pwzDotnetName = SysAllocString(L"\"where.exe\" dotnet.exe");
|
pwzDotnetName = SysAllocString(L"\"where.exe\" dotnet.exe");
|
||||||
if (pwzDotnetName == NULL)
|
if (pwzDotnetName == NULL)
|
||||||
{
|
{
|
||||||
hr = E_OUTOFMEMORY;
|
|
||||||
goto Finished;
|
goto Finished;
|
||||||
}
|
}
|
||||||
fResult = CreateProcessW(NULL,
|
|
||||||
|
// Create a process to invoke where.exe
|
||||||
|
fProcessCreationResult = CreateProcessW(NULL,
|
||||||
pwzDotnetName,
|
pwzDotnetName,
|
||||||
NULL,
|
NULL,
|
||||||
NULL,
|
NULL,
|
||||||
|
|
@ -530,14 +639,15 @@ HOSTFXR_UTILITY::FindDotnetExePath(
|
||||||
&processInformation
|
&processInformation
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!fResult)
|
if (!fProcessCreationResult)
|
||||||
{
|
{
|
||||||
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
||||||
goto Finished;
|
goto Finished;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (WaitForSingleObject(processInformation.hProcess, 2000) != WAIT_OBJECT_0) // 2 seconds
|
// Wait for where.exe to return, waiting 2 seconds.
|
||||||
|
if (WaitForSingleObject(processInformation.hProcess, 2000) != WAIT_OBJECT_0)
|
||||||
{
|
{
|
||||||
|
// Timeout occured, terminate the where.exe process and return.
|
||||||
TerminateProcess(processInformation.hProcess, 2);
|
TerminateProcess(processInformation.hProcess, 2);
|
||||||
hr = HRESULT_FROM_WIN32(ERROR_TIMEOUT);
|
hr = HRESULT_FROM_WIN32(ERROR_TIMEOUT);
|
||||||
goto Finished;
|
goto Finished;
|
||||||
|
|
@ -550,131 +660,95 @@ HOSTFXR_UTILITY::FindDotnetExePath(
|
||||||
//
|
//
|
||||||
if (!GetExitCodeProcess(processInformation.hProcess, &dwExitCode))
|
if (!GetExitCodeProcess(processInformation.hProcess, &dwExitCode))
|
||||||
{
|
{
|
||||||
goto Fallback;
|
goto Finished;
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// In this block, if anything fails, we will goto our fallback of
|
// In this block, if anything fails, we will goto our fallback of
|
||||||
// looking in C:/Program Files/
|
// looking in C:/Program Files/
|
||||||
//
|
//
|
||||||
if (dwExitCode == 0)
|
if (dwExitCode != 0)
|
||||||
{
|
{
|
||||||
// Where succeeded.
|
goto Finished;
|
||||||
// Reset file pointer to the beginning of the file.
|
|
||||||
dwFilePointer = SetFilePointer(hStdOutReadPipe, 0, NULL, FILE_BEGIN);
|
|
||||||
if (dwFilePointer == INVALID_SET_FILE_POINTER)
|
|
||||||
{
|
|
||||||
goto Fallback;
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// As the call to where.exe succeeded (dotnet.exe was found), ReadFile should not hang.
|
|
||||||
// TODO consider putting ReadFile in a separate thread with a timeout to guarantee it doesn't block.
|
|
||||||
//
|
|
||||||
if (!ReadFile(hStdOutReadPipe, pzFileContents, READ_BUFFER_SIZE, &dwNumBytesRead, NULL))
|
|
||||||
{
|
|
||||||
goto Fallback;
|
|
||||||
}
|
|
||||||
if (dwNumBytesRead >= READ_BUFFER_SIZE)
|
|
||||||
{
|
|
||||||
// This shouldn't ever be this large. We could continue to call ReadFile in a loop,
|
|
||||||
// however I'd rather error out here and report an issue.
|
|
||||||
goto Fallback;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (FAILED(hr = struDotnetLocationsString.CopyA(pzFileContents, dwNumBytesRead)))
|
|
||||||
{
|
|
||||||
goto Finished;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check the bitness of the currently running process
|
|
||||||
// matches the dotnet.exe found.
|
|
||||||
if (!IsWow64Process(GetCurrentProcess(), &fIsWow64Process))
|
|
||||||
{
|
|
||||||
// Calling IsWow64Process failed
|
|
||||||
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
||||||
goto Finished;
|
|
||||||
}
|
|
||||||
if (fIsWow64Process)
|
|
||||||
{
|
|
||||||
// 32 bit mode
|
|
||||||
fIsCurrentProcess64Bit = FALSE;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
SYSTEM_INFO systemInfo;
|
|
||||||
GetNativeSystemInfo(&systemInfo);
|
|
||||||
fIsCurrentProcess64Bit = systemInfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64;
|
|
||||||
}
|
|
||||||
|
|
||||||
while (!fFound)
|
|
||||||
{
|
|
||||||
index = struDotnetLocationsString.IndexOf(L"\r\n", prevIndex);
|
|
||||||
if (index == -1)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (FAILED(hr = struDotnetSubstring.Copy(&struDotnetLocationsString.QueryStr()[prevIndex], index - prevIndex)))
|
|
||||||
{
|
|
||||||
goto Finished;
|
|
||||||
}
|
|
||||||
// \r\n is two wchars, so add 2 here.
|
|
||||||
prevIndex = index + 2;
|
|
||||||
|
|
||||||
if (GetBinaryTypeW(struDotnetSubstring.QueryStr(), &dwBinaryType) &&
|
|
||||||
fIsCurrentProcess64Bit == (dwBinaryType == SCS_64BIT_BINARY)) {
|
|
||||||
// Found a valid dotnet.
|
|
||||||
if (FAILED(hr = struDotnetPath->Copy(struDotnetSubstring)))
|
|
||||||
{
|
|
||||||
goto Finished;
|
|
||||||
}
|
|
||||||
fFound = TRUE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Fallback:
|
// Where succeeded.
|
||||||
|
// Reset file pointer to the beginning of the file.
|
||||||
// Look in ProgramFiles
|
dwFilePointer = SetFilePointer(hStdOutReadPipe, 0, NULL, FILE_BEGIN);
|
||||||
while (!fFound)
|
if (dwFilePointer == INVALID_SET_FILE_POINTER)
|
||||||
{
|
{
|
||||||
if (FAILED(hr = struDotnetSubstring.Resize(dwPathSize)))
|
goto Finished;
|
||||||
{
|
|
||||||
goto Finished;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Program files will changes based on the
|
|
||||||
dwNumBytesRead = GetEnvironmentVariable(L"ProgramFiles", struDotnetSubstring.QueryStr(), dwPathSize);
|
|
||||||
if (dwNumBytesRead == 0)
|
|
||||||
{
|
|
||||||
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
||||||
goto Finished;
|
|
||||||
}
|
|
||||||
else if (dwNumBytesRead == dwPathSize)
|
|
||||||
{
|
|
||||||
dwPathSize *= 2 + 30; // for dotnet substring
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (FAILED(hr = struDotnetSubstring.SyncWithBuffer()) ||
|
|
||||||
FAILED(hr = struDotnetSubstring.Append(L"\\dotnet\\dotnet.exe")))
|
|
||||||
{
|
|
||||||
goto Finished;
|
|
||||||
}
|
|
||||||
if (!UTILITY::CheckIfFileExists(struDotnetSubstring.QueryStr()))
|
|
||||||
{
|
|
||||||
hr = HRESULT_FROM_WIN32( GetLastError() );
|
|
||||||
goto Finished;
|
|
||||||
}
|
|
||||||
if (FAILED(hr = struDotnetPath->Copy(struDotnetSubstring)))
|
|
||||||
{
|
|
||||||
goto Finished;
|
|
||||||
}
|
|
||||||
fFound = TRUE;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// As the call to where.exe succeeded (dotnet.exe was found), ReadFile should not hang.
|
||||||
|
// TODO consider putting ReadFile in a separate thread with a timeout to guarantee it doesn't block.
|
||||||
|
//
|
||||||
|
if (!ReadFile(hStdOutReadPipe, pzFileContents, READ_BUFFER_SIZE, &dwNumBytesRead, NULL))
|
||||||
|
{
|
||||||
|
goto Finished;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dwNumBytesRead >= READ_BUFFER_SIZE)
|
||||||
|
{
|
||||||
|
// This shouldn't ever be this large. We could continue to call ReadFile in a loop,
|
||||||
|
// however if someone had this many dotnet.exes on their machine.
|
||||||
|
goto Finished;
|
||||||
|
}
|
||||||
|
|
||||||
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
||||||
|
if (FAILED(hr = struDotnetLocationsString.CopyA(pzFileContents, dwNumBytesRead)))
|
||||||
|
{
|
||||||
|
goto Finished;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check the bitness of the currently running process
|
||||||
|
// matches the dotnet.exe found.
|
||||||
|
if (!IsWow64Process(GetCurrentProcess(), &fIsWow64Process))
|
||||||
|
{
|
||||||
|
// Calling IsWow64Process failed
|
||||||
|
goto Finished;
|
||||||
|
}
|
||||||
|
if (fIsWow64Process)
|
||||||
|
{
|
||||||
|
// 32 bit mode
|
||||||
|
fIsCurrentProcess64Bit = FALSE;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Check the SystemInfo to see if we are currently 32 or 64 bit.
|
||||||
|
SYSTEM_INFO systemInfo;
|
||||||
|
GetNativeSystemInfo(&systemInfo);
|
||||||
|
fIsCurrentProcess64Bit = systemInfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (TRUE)
|
||||||
|
{
|
||||||
|
index = struDotnetLocationsString.IndexOf(L"\r\n", prevIndex);
|
||||||
|
if (index == -1)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (FAILED(hr = struDotnetSubstring.Copy(&struDotnetLocationsString.QueryStr()[prevIndex], index - prevIndex)))
|
||||||
|
{
|
||||||
|
goto Finished;
|
||||||
|
}
|
||||||
|
// \r\n is two wchars, so add 2 here.
|
||||||
|
prevIndex = index + 2;
|
||||||
|
|
||||||
|
if (GetBinaryTypeW(struDotnetSubstring.QueryStr(), &dwBinaryType) &&
|
||||||
|
fIsCurrentProcess64Bit == (dwBinaryType == SCS_64BIT_BINARY))
|
||||||
|
{
|
||||||
|
// The bitness of dotnet matched with the current worker process bitness.
|
||||||
|
if (FAILED(hr = pStruAbsolutePathToDotnet->Copy(struDotnetSubstring)))
|
||||||
|
{
|
||||||
|
goto Finished;
|
||||||
|
}
|
||||||
|
fResult = TRUE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Finished:
|
Finished:
|
||||||
|
|
||||||
if (hStdOutReadPipe != INVALID_HANDLE_VALUE)
|
if (hStdOutReadPipe != INVALID_HANDLE_VALUE)
|
||||||
|
|
@ -698,5 +772,60 @@ Finished:
|
||||||
SysFreeString(pwzDotnetName);
|
SysFreeString(pwzDotnetName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return fResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
HRESULT
|
||||||
|
HOSTFXR_UTILITY::GetAbsolutePathToDotnetFromProgramFiles(
|
||||||
|
_Inout_ STRU* pStruAbsolutePathToDotnet
|
||||||
|
)
|
||||||
|
{
|
||||||
|
HRESULT hr = S_OK;
|
||||||
|
BOOL fFound = FALSE;
|
||||||
|
DWORD dwNumBytesRead = 0;
|
||||||
|
DWORD dwPathSize = MAX_PATH;
|
||||||
|
STRU struDotnetSubstring;
|
||||||
|
|
||||||
|
while (!fFound)
|
||||||
|
{
|
||||||
|
if (FAILED(hr = struDotnetSubstring.Resize(dwPathSize)))
|
||||||
|
{
|
||||||
|
goto Finished;
|
||||||
|
}
|
||||||
|
|
||||||
|
dwNumBytesRead = GetEnvironmentVariable(L"ProgramFiles", struDotnetSubstring.QueryStr(), dwPathSize);
|
||||||
|
if (dwNumBytesRead == 0)
|
||||||
|
{
|
||||||
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
||||||
|
goto Finished;
|
||||||
|
}
|
||||||
|
else if (dwNumBytesRead >= dwPathSize)
|
||||||
|
{
|
||||||
|
//
|
||||||
|
// The path to ProgramFiles should never be this long, but resize and try again.
|
||||||
|
dwPathSize *= 2 + 30; // for dotnet substring
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (FAILED(hr = struDotnetSubstring.SyncWithBuffer()) ||
|
||||||
|
FAILED(hr = struDotnetSubstring.Append(L"\\dotnet\\dotnet.exe")))
|
||||||
|
{
|
||||||
|
goto Finished;
|
||||||
|
}
|
||||||
|
if (!UTILITY::CheckIfFileExists(struDotnetSubstring.QueryStr()))
|
||||||
|
{
|
||||||
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
||||||
|
goto Finished;
|
||||||
|
}
|
||||||
|
if (FAILED(hr = pStruAbsolutePathToDotnet->Copy(struDotnetSubstring)))
|
||||||
|
{
|
||||||
|
goto Finished;
|
||||||
|
}
|
||||||
|
fFound = TRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Finished:
|
||||||
return hr;
|
return hr;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ public:
|
||||||
PCWSTR pcwzProcessPath,
|
PCWSTR pcwzProcessPath,
|
||||||
PCWSTR pcwzApplicationPhysicalPath,
|
PCWSTR pcwzApplicationPhysicalPath,
|
||||||
PCWSTR pcwzArguments,
|
PCWSTR pcwzArguments,
|
||||||
_Inout_ STRU* struHostFxrDllLocation,
|
_Inout_ STRU* pStruHostFxrDllLocation,
|
||||||
_Out_ DWORD* pdwArgCount,
|
_Out_ DWORD* pdwArgCount,
|
||||||
_Out_ BSTR** ppwzArgv
|
_Out_ BSTR** ppwzArgv
|
||||||
);
|
);
|
||||||
|
|
@ -33,7 +33,7 @@ public:
|
||||||
PCWSTR pcwzApplicationPhysicalPath,
|
PCWSTR pcwzApplicationPhysicalPath,
|
||||||
PCWSTR pcwzArguments,
|
PCWSTR pcwzArguments,
|
||||||
HANDLE hEventLog,
|
HANDLE hEventLog,
|
||||||
_Inout_ STRU* struHostFxrDllLocation,
|
_Inout_ STRU* pStruHostFxrDllLocation,
|
||||||
_Out_ DWORD* pdwArgCount,
|
_Out_ DWORD* pdwArgCount,
|
||||||
_Out_ BSTR** ppwzArgv
|
_Out_ BSTR** ppwzArgv
|
||||||
);
|
);
|
||||||
|
|
@ -46,13 +46,33 @@ public:
|
||||||
PCWSTR pcwzApplicationPhysicalPath,
|
PCWSTR pcwzApplicationPhysicalPath,
|
||||||
HANDLE hEventLog,
|
HANDLE hEventLog,
|
||||||
_Out_ DWORD* pdwArgCount,
|
_Out_ DWORD* pdwArgCount,
|
||||||
_Out_ BSTR** ppwzArgv
|
_Out_ BSTR** ppwzArgv
|
||||||
);
|
);
|
||||||
|
|
||||||
static
|
static
|
||||||
HRESULT
|
HRESULT
|
||||||
FindDotnetExePath(
|
GetAbsolutePathToDotnet(
|
||||||
STRU* struDotnetLocation
|
STRU* pStruAbsolutePathToDotnet
|
||||||
|
);
|
||||||
|
|
||||||
|
static
|
||||||
|
HRESULT
|
||||||
|
GetAbsolutePathToHostFxr(
|
||||||
|
_In_ STRU* pStruAbsolutePathToDotnet,
|
||||||
|
_In_ HANDLE hEventLog,
|
||||||
|
_Out_ STRU* pStruAbsolutePathToHostfxr
|
||||||
|
);
|
||||||
|
|
||||||
|
static
|
||||||
|
BOOL
|
||||||
|
InvokeWhereToFindDotnet(
|
||||||
|
_Inout_ STRU* pStruAbsolutePathToDotnet
|
||||||
|
);
|
||||||
|
|
||||||
|
static
|
||||||
|
HRESULT
|
||||||
|
GetAbsolutePathToDotnetFromProgramFiles(
|
||||||
|
_Inout_ STRU* pStruAbsolutePathToDotnet
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -41,3 +41,4 @@
|
||||||
#define ASPNETCORE_EVENT_HOSTFXR_DLL_NOT_FOUND_MSG L"Could not find hostfxr.dll in '%s'. ErrorCode = '0x%x'."
|
#define ASPNETCORE_EVENT_HOSTFXR_DLL_NOT_FOUND_MSG L"Could not find hostfxr.dll in '%s'. ErrorCode = '0x%x'."
|
||||||
#define ASPNETCORE_EVENT_APPLICATION_EXE_NOT_FOUND_MSG L"Could not find application executable in '%s'. ErrorCode = '0x%x'."
|
#define ASPNETCORE_EVENT_APPLICATION_EXE_NOT_FOUND_MSG L"Could not find application executable in '%s'. ErrorCode = '0x%x'."
|
||||||
#define ASPNETCORE_EVENT_INPROCESS_THREAD_EXCEPTION_MSG L"Application '%s' with physical root '%s' hit unexpected managed exception, ErrorCode = '0x%x. Please check the stderr logs for more information."
|
#define ASPNETCORE_EVENT_INPROCESS_THREAD_EXCEPTION_MSG L"Application '%s' with physical root '%s' hit unexpected managed exception, ErrorCode = '0x%x. Please check the stderr logs for more information."
|
||||||
|
#define ASPNETCORE_EVENT_INVALID_PROCESS_PATH_MSG L"Invalid or unknown processPath provided in web.config: processPath = %s, ErrorCode = '0x%x'."
|
||||||
|
|
|
||||||
|
|
@ -118,7 +118,7 @@ public:
|
||||||
_In_ WORD dwEventInfoType,
|
_In_ WORD dwEventInfoType,
|
||||||
_In_ DWORD dwEventId,
|
_In_ DWORD dwEventId,
|
||||||
_In_ LPCWSTR pstrMsg
|
_In_ LPCWSTR pstrMsg
|
||||||
);
|
);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -167,6 +167,7 @@
|
||||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader>
|
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<ClCompile Include="hostfxr_utility_tests.cpp" />
|
<ClCompile Include="hostfxr_utility_tests.cpp" />
|
||||||
|
<ClCompile Include="utility_tests.cpp" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\..\src\CommonLib\CommonLib.vcxproj">
|
<ProjectReference Include="..\..\src\CommonLib\CommonLib.vcxproj">
|
||||||
|
|
|
||||||
|
|
@ -90,5 +90,63 @@ namespace AspNetCoreModuleTests
|
||||||
|
|
||||||
Assert::AreEqual(E_INVALIDARG, hr);
|
Assert::AreEqual(E_INVALIDARG, hr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_METHOD(GetAbsolutePathToDotnetFromProgramFiles_BackupWorks)
|
||||||
|
{
|
||||||
|
STRU struAbsolutePathToDotnet;
|
||||||
|
HRESULT hr = S_OK;
|
||||||
|
BOOL fDotnetInProgramFiles;
|
||||||
|
BOOL is64Bit;
|
||||||
|
BOOL fIsWow64 = FALSE;
|
||||||
|
SYSTEM_INFO systemInfo;
|
||||||
|
IsWow64Process(GetCurrentProcess(), &fIsWow64);
|
||||||
|
if (fIsWow64)
|
||||||
|
{
|
||||||
|
is64Bit = FALSE;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
GetNativeSystemInfo(&systemInfo);
|
||||||
|
is64Bit = systemInfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is64Bit)
|
||||||
|
{
|
||||||
|
fDotnetInProgramFiles = UTILITY::CheckIfFileExists(L"C:/Program Files/dotnet/dotnet.exe");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
fDotnetInProgramFiles = UTILITY::CheckIfFileExists(L"C:/Program Files (x86)/dotnet/dotnet.exe");
|
||||||
|
}
|
||||||
|
|
||||||
|
hr = HOSTFXR_UTILITY::GetAbsolutePathToDotnetFromProgramFiles(&struAbsolutePathToDotnet);
|
||||||
|
if (fDotnetInProgramFiles)
|
||||||
|
{
|
||||||
|
Assert::AreEqual(hr, S_OK);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Assert::AreNotEqual(hr, S_OK);
|
||||||
|
Assert::IsTrue(struAbsolutePathToDotnet.IsEmpty());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_METHOD(GetHostFxrArguments_InvalidParams)
|
||||||
|
{
|
||||||
|
DWORD retVal = 0;
|
||||||
|
BSTR* bstrArray;
|
||||||
|
STRU struHostFxrDllLocation;
|
||||||
|
|
||||||
|
HRESULT hr = HOSTFXR_UTILITY::GetHostFxrParameters(
|
||||||
|
INVALID_HANDLE_VALUE,
|
||||||
|
L"bogus", // processPath
|
||||||
|
L"", // application physical path, ignored.
|
||||||
|
L"ignored", //arguments
|
||||||
|
NULL, // event log
|
||||||
|
&retVal, // arg count
|
||||||
|
&bstrArray); // args array.
|
||||||
|
|
||||||
|
Assert::AreEqual(E_INVALIDARG, hr);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,52 @@
|
||||||
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
|
// Licensed under the MIT License. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
#include "stdafx.h"
|
||||||
|
#include "CppUnitTest.h"
|
||||||
|
|
||||||
|
using namespace Microsoft::VisualStudio::CppUnitTestFramework;
|
||||||
|
|
||||||
|
namespace AspNetCoreModuleTests
|
||||||
|
{
|
||||||
|
TEST_CLASS(UTILITY_TESTS)
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
TEST_METHOD(PassUnexpandedString_ExpandsResult)
|
||||||
|
{
|
||||||
|
HRESULT hr = S_OK;
|
||||||
|
PCWSTR unexpandedString = L"ANCM_TEST_ENV_VAR";
|
||||||
|
PCWSTR unexpandedStringValue = L"foobar";
|
||||||
|
STRU struExpandedString;
|
||||||
|
SetEnvironmentVariable(L"ANCM_TEST_ENV_VAR", unexpandedStringValue);
|
||||||
|
|
||||||
|
hr = struExpandedString.CopyAndExpandEnvironmentStrings(L"%ANCM_TEST_ENV_VAR%");
|
||||||
|
Assert::AreEqual(hr, S_OK);
|
||||||
|
Assert::AreEqual(L"foobar", struExpandedString.QueryStr());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_METHOD(PassUnexpandedString_Resize_ExpandsResult)
|
||||||
|
{
|
||||||
|
HRESULT hr = S_OK;
|
||||||
|
PCWSTR unexpandedString = L"ANCM_TEST_ENV_VAR_LONG";
|
||||||
|
STRU struStringValue;
|
||||||
|
STACK_STRU(struExpandedString, MAX_PATH);
|
||||||
|
|
||||||
|
struStringValue.Append(L"TestValueThatIsLongerThan256CharactersLongToTriggerResize");
|
||||||
|
struStringValue.Append(L"TestValueThatIsLongerThan256CharactersLongToTriggerResize");
|
||||||
|
struStringValue.Append(L"TestValueThatIsLongerThan256CharactersLongToTriggerResize");
|
||||||
|
struStringValue.Append(L"TestValueThatIsLongerThan256CharactersLongToTriggerResize");
|
||||||
|
struStringValue.Append(L"TestValueThatIsLongerThan256CharactersLongToTriggerResize");
|
||||||
|
struStringValue.Append(L"TestValueThatIsLongerThan256CharactersLongToTriggerResize");
|
||||||
|
|
||||||
|
SetEnvironmentVariable(unexpandedString, struStringValue.QueryStr());
|
||||||
|
|
||||||
|
hr = struExpandedString.CopyAndExpandEnvironmentStrings(L"%ANCM_TEST_ENV_VAR_LONG%");
|
||||||
|
Assert::AreEqual(hr, S_OK);
|
||||||
|
Assert::AreEqual(struStringValue.QueryCCH(), struExpandedString.QueryCCH());
|
||||||
|
// The values are exactly the same, however Assert::AreEqual is returning false.
|
||||||
|
//Assert::AreEqual(struStringValue.QueryStr(), struExpandedString.QueryStr());
|
||||||
|
Assert::AreEqual(0, wcscmp(struStringValue.QueryStr(), struExpandedString.QueryStr()));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,240 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Net;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Xml.Linq;
|
||||||
|
using Microsoft.AspNetCore.Server.IntegrationTesting;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Microsoft.Extensions.Logging.Testing;
|
||||||
|
using Xunit;
|
||||||
|
using Xunit.Abstractions;
|
||||||
|
using Xunit.Sdk;
|
||||||
|
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests
|
||||||
|
{
|
||||||
|
public class StartupTests : LoggedTest
|
||||||
|
{
|
||||||
|
public StartupTests(ITestOutputHelper output) : base(output)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task ExpandEnvironmentVariableInWebConfig()
|
||||||
|
{
|
||||||
|
var runtimeFlavor = RuntimeFlavor.CoreClr;
|
||||||
|
var serverType = ServerType.IISExpress;
|
||||||
|
var testName = $"HelloWorld_{runtimeFlavor}";
|
||||||
|
var architecture = RuntimeArchitecture.x64;
|
||||||
|
var dotnetLocation = $"%USERPROFILE%\\.dotnet\\{architecture.ToString()}\\dotnet.exe";
|
||||||
|
using (StartLog(out var loggerFactory, testName))
|
||||||
|
{
|
||||||
|
var logger = loggerFactory.CreateLogger("HelloWorldTest");
|
||||||
|
|
||||||
|
var deploymentParameters = new DeploymentParameters(Helpers.GetInProcessTestSitesPath(), serverType, runtimeFlavor, architecture)
|
||||||
|
{
|
||||||
|
ServerConfigTemplateContent = (serverType == ServerType.IISExpress) ? File.ReadAllText("AppHostConfig/Http.config") : null,
|
||||||
|
SiteName = "HttpTestSite", // This is configured in the Http.config
|
||||||
|
TargetFramework = "netcoreapp2.1",
|
||||||
|
ApplicationType = ApplicationType.Portable,
|
||||||
|
Configuration =
|
||||||
|
#if DEBUG
|
||||||
|
"Debug"
|
||||||
|
#else
|
||||||
|
"Release"
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
// Point to dotnet installed in user profile.
|
||||||
|
deploymentParameters.EnvironmentVariables["DotnetPath"] = Environment.ExpandEnvironmentVariables(dotnetLocation); // Path to dotnet.
|
||||||
|
|
||||||
|
using (var deployer = ApplicationDeployerFactory.Create(deploymentParameters, loggerFactory))
|
||||||
|
{
|
||||||
|
var deploymentResult = await deployer.DeployAsync();
|
||||||
|
|
||||||
|
Helpers.ModifyAspNetCoreSectionInWebConfig(deploymentResult, "processPath", "%DotnetPath%");
|
||||||
|
|
||||||
|
// Request to base address and check if various parts of the body are rendered & measure the cold startup time.
|
||||||
|
var response = await RetryHelper.RetryRequest(() =>
|
||||||
|
{
|
||||||
|
return deploymentResult.HttpClient.GetAsync("HelloWorld");
|
||||||
|
}, logger, deploymentResult.HostShutdownToken, retryCount: 30);
|
||||||
|
|
||||||
|
var responseText = await response.Content.ReadAsStringAsync();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Assert.Equal("Hello World", responseText);
|
||||||
|
}
|
||||||
|
catch (XunitException)
|
||||||
|
{
|
||||||
|
logger.LogWarning(response.ToString());
|
||||||
|
logger.LogWarning(responseText);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task InvalidProcessPath_ExpectServerError()
|
||||||
|
{
|
||||||
|
var architecture = RuntimeArchitecture.x64;
|
||||||
|
var runtimeFlavor = RuntimeFlavor.CoreClr;
|
||||||
|
var serverType = ServerType.IISExpress;
|
||||||
|
var testName = $"HelloWorld_{runtimeFlavor}";
|
||||||
|
var dotnetLocation = "bogus";
|
||||||
|
using (StartLog(out var loggerFactory, testName))
|
||||||
|
{
|
||||||
|
var logger = loggerFactory.CreateLogger("HelloWorldTest");
|
||||||
|
|
||||||
|
var deploymentParameters = new DeploymentParameters(Helpers.GetInProcessTestSitesPath(), serverType, runtimeFlavor, architecture)
|
||||||
|
{
|
||||||
|
ServerConfigTemplateContent = (serverType == ServerType.IISExpress) ? File.ReadAllText("AppHostConfig/Http.config") : null,
|
||||||
|
SiteName = "HttpTestSite", // This is configured in the Http.config
|
||||||
|
TargetFramework = "netcoreapp2.1",
|
||||||
|
ApplicationType = ApplicationType.Portable,
|
||||||
|
Configuration =
|
||||||
|
#if DEBUG
|
||||||
|
"Debug"
|
||||||
|
#else
|
||||||
|
"Release"
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
// Point to dotnet installed in user profile.
|
||||||
|
deploymentParameters.EnvironmentVariables["DotnetPath"] = Environment.ExpandEnvironmentVariables(dotnetLocation); // Path to dotnet.
|
||||||
|
|
||||||
|
using (var deployer = ApplicationDeployerFactory.Create(deploymentParameters, loggerFactory))
|
||||||
|
{
|
||||||
|
var deploymentResult = await deployer.DeployAsync();
|
||||||
|
|
||||||
|
Helpers.ModifyAspNetCoreSectionInWebConfig(deploymentResult, "processPath", "%DotnetPath%");
|
||||||
|
|
||||||
|
// Request to base address and check if various parts of the body are rendered & measure the cold startup time.
|
||||||
|
var response = await RetryHelper.RetryRequest(() =>
|
||||||
|
{
|
||||||
|
return deploymentResult.HttpClient.GetAsync("HelloWorld");
|
||||||
|
}, logger, deploymentResult.HostShutdownToken, retryCount: 30);
|
||||||
|
|
||||||
|
Assert.Equal(HttpStatusCode.InternalServerError, response.StatusCode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#if NETCOREAPP2_0 || NETCOREAPP2_1
|
||||||
|
|
||||||
|
[Fact] // Consistently fails on CI for net461
|
||||||
|
public async Task StandaloneApplication_ExpectCorrectPublish()
|
||||||
|
{
|
||||||
|
var architecture = RuntimeArchitecture.x64;
|
||||||
|
var runtimeFlavor = RuntimeFlavor.CoreClr;
|
||||||
|
var serverType = ServerType.IISExpress;
|
||||||
|
var testName = $"HelloWorld_{runtimeFlavor}";
|
||||||
|
|
||||||
|
using (StartLog(out var loggerFactory, testName))
|
||||||
|
{
|
||||||
|
var logger = loggerFactory.CreateLogger("HelloWorldTest");
|
||||||
|
|
||||||
|
var deploymentParameters = new DeploymentParameters(Helpers.GetInProcessTestSitesPath(), serverType, runtimeFlavor, architecture)
|
||||||
|
{
|
||||||
|
ServerConfigTemplateContent = (serverType == ServerType.IISExpress) ? File.ReadAllText("AppHostConfig/Http.config") : null,
|
||||||
|
SiteName = "HttpTestSite", // This is configured in the Http.config
|
||||||
|
TargetFramework = "netcoreapp2.1",
|
||||||
|
ApplicationType = ApplicationType.Standalone,
|
||||||
|
Configuration =
|
||||||
|
#if DEBUG
|
||||||
|
"Debug"
|
||||||
|
#else
|
||||||
|
"Release"
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
using (var deployer = ApplicationDeployerFactory.Create(deploymentParameters, loggerFactory))
|
||||||
|
{
|
||||||
|
var deploymentResult = await deployer.DeployAsync();
|
||||||
|
|
||||||
|
// Request to base address and check if various parts of the body are rendered & measure the cold startup time.
|
||||||
|
var response = await RetryHelper.RetryRequest(() =>
|
||||||
|
{
|
||||||
|
return deploymentResult.HttpClient.GetAsync("HelloWorld");
|
||||||
|
}, logger, deploymentResult.HostShutdownToken, retryCount: 30);
|
||||||
|
|
||||||
|
var responseText = await response.Content.ReadAsStringAsync();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Assert.Equal("Hello World", responseText);
|
||||||
|
}
|
||||||
|
catch (XunitException)
|
||||||
|
{
|
||||||
|
logger.LogWarning(response.ToString());
|
||||||
|
logger.LogWarning(responseText);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact] // Consistently fails on CI for net461
|
||||||
|
public async Task StandaloneApplication_AbsolutePathToExe_ExpectCorrectPublish()
|
||||||
|
{
|
||||||
|
var architecture = RuntimeArchitecture.x64;
|
||||||
|
var runtimeFlavor = RuntimeFlavor.CoreClr;
|
||||||
|
var serverType = ServerType.IISExpress;
|
||||||
|
var testName = $"HelloWorld_{runtimeFlavor}";
|
||||||
|
|
||||||
|
using (StartLog(out var loggerFactory, testName))
|
||||||
|
{
|
||||||
|
var logger = loggerFactory.CreateLogger("HelloWorldTest");
|
||||||
|
|
||||||
|
var deploymentParameters = new DeploymentParameters(Helpers.GetInProcessTestSitesPath(), serverType, runtimeFlavor, architecture)
|
||||||
|
{
|
||||||
|
ServerConfigTemplateContent = (serverType == ServerType.IISExpress) ? File.ReadAllText("AppHostConfig/Http.config") : null,
|
||||||
|
SiteName = "HttpTestSite", // This is configured in the Http.config
|
||||||
|
TargetFramework = "netcoreapp2.1",
|
||||||
|
ApplicationType = ApplicationType.Standalone,
|
||||||
|
Configuration =
|
||||||
|
#if DEBUG
|
||||||
|
"Debug"
|
||||||
|
#else
|
||||||
|
"Release"
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
using (var deployer = ApplicationDeployerFactory.Create(deploymentParameters, loggerFactory))
|
||||||
|
{
|
||||||
|
var deploymentResult = await deployer.DeployAsync();
|
||||||
|
|
||||||
|
Helpers.ModifyAspNetCoreSectionInWebConfig(deploymentResult, "processPath", $"{deploymentResult.ContentRoot}\\IISTestSite.exe");
|
||||||
|
|
||||||
|
// Request to base address and check if various parts of the body are rendered & measure the cold startup time.
|
||||||
|
var response = await RetryHelper.RetryRequest(() =>
|
||||||
|
{
|
||||||
|
return deploymentResult.HttpClient.GetAsync("HelloWorld");
|
||||||
|
}, logger, deploymentResult.HostShutdownToken, retryCount: 30);
|
||||||
|
|
||||||
|
var responseText = await response.Content.ReadAsStringAsync();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Assert.Equal("Hello World", responseText);
|
||||||
|
}
|
||||||
|
catch (XunitException)
|
||||||
|
{
|
||||||
|
logger.LogWarning(response.ToString());
|
||||||
|
logger.LogWarning(responseText);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#elif NET461
|
||||||
|
#else
|
||||||
|
#error Target frameworks need to be updated
|
||||||
|
#endif
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,8 +1,11 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
// 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.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using Microsoft.AspNetCore.Server.IntegrationTesting;
|
||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Xml.Linq;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests
|
namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests
|
||||||
{
|
{
|
||||||
|
|
@ -29,5 +32,16 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests
|
||||||
"..", // projectfolder
|
"..", // projectfolder
|
||||||
"TestSites"));
|
"TestSites"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void ModifyAspNetCoreSectionInWebConfig(DeploymentResult deploymentResult, string key, string value)
|
||||||
|
{
|
||||||
|
// modify the web.config after publish
|
||||||
|
var root = deploymentResult.ContentRoot;
|
||||||
|
var webConfigFile = $"{root}/web.config";
|
||||||
|
var config = XDocument.Load(webConfigFile);
|
||||||
|
var element = config.Descendants("aspNetCore").FirstOrDefault();
|
||||||
|
element.SetAttributeValue(key, value);
|
||||||
|
config.Save(webConfigFile);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue