aspnetcore/src/AspNetCoreModuleV2/CommonLib/hostfxr_utility.cpp

876 lines
25 KiB
C++

// 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"
HOSTFXR_UTILITY::HOSTFXR_UTILITY()
{
}
HOSTFXR_UTILITY::~HOSTFXR_UTILITY()
{
}
//
// 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,
HANDLE hEventLog,
_Inout_ STRU* pStruHostFxrDllLocation,
_Out_ DWORD* pdwArgCount,
_Out_ BSTR** ppwzArgv
)
{
HRESULT hr = S_OK;
STRU struDllPath;
STRU struArguments;
STRU struHostFxrPath;
STRU struRuntimeConfigLocation;
DWORD dwPosition;
// Obtain the app name from the processPath section.
if (FAILED(hr = struDllPath.Copy(pwzExeAbsolutePath)))
{
goto Finished;
}
dwPosition = struDllPath.LastIndexOf(L'.', 0);
if (dwPosition == -1)
{
hr = E_FAIL;
goto Finished;
}
hr = UTILITY::ConvertPathToFullPath(L".\\hostfxr.dll", pcwzApplicationPhysicalPath, &struHostFxrPath);
if (FAILED(hr))
{
goto Finished;
}
struDllPath.QueryStr()[dwPosition] = L'\0';
if (FAILED(hr = struDllPath.SyncWithBuffer()))
{
goto Finished;
}
if (!UTILITY::CheckIfFileExists(struHostFxrPath.QueryStr()))
{
// Most likely a full framework app.
// Check that the runtime config file doesn't exist in the folder as another heuristic.
if (FAILED(hr = struRuntimeConfigLocation.Copy(struDllPath)) ||
FAILED(hr = struRuntimeConfigLocation.Append(L".runtimeconfig.json")))
{
goto Finished;
}
if (!UTILITY::CheckIfFileExists(struRuntimeConfigLocation.QueryStr()))
{
hr = E_APPLICATION_ACTIVATION_EXEC_FAILURE;
UTILITY::LogEventF(hEventLog,
EVENTLOG_ERROR_TYPE,
ASPNETCORE_EVENT_INPROCESS_FULL_FRAMEWORK_APP,
ASPNETCORE_EVENT_INPROCESS_FULL_FRAMEWORK_APP_MSG,
pcwzApplicationPhysicalPath,
hr);
}
else
{
// If a runtime config file does exist, report a file not found on the app.exe
hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
UTILITY::LogEventF(hEventLog,
EVENTLOG_ERROR_TYPE,
ASPNETCORE_EVENT_APPLICATION_EXE_NOT_FOUND,
ASPNETCORE_EVENT_APPLICATION_EXE_NOT_FOUND_MSG,
pcwzApplicationPhysicalPath,
hr);
}
goto Finished;
}
if (FAILED(hr = pStruHostFxrDllLocation->Copy(struHostFxrPath)))
{
goto Finished;
}
if (FAILED(hr = struDllPath.Append(L".dll")))
{
goto Finished;
}
if (!UTILITY::CheckIfFileExists(struDllPath.QueryStr()))
{
// Treat access issue as File not found
hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
goto Finished;
}
if (FAILED(hr = struArguments.Copy(struDllPath)) ||
FAILED(hr = struArguments.Append(L" ")) ||
FAILED(hr = struArguments.Append(pcwzArguments)))
{
goto Finished;
}
if (FAILED(hr = ParseHostfxrArguments(
struArguments.QueryStr(),
pwzExeAbsolutePath,
pcwzApplicationPhysicalPath,
hEventLog,
pdwArgCount,
ppwzArgv)))
{
goto Finished;
}
Finished:
return hr;
}
BOOL
HOSTFXR_UTILITY::IsDotnetExecutable(STRU *struExecutablePath)
{
if (struExecutablePath == NULL)
{
return FALSE;
}
return struExecutablePath->EndsWith(L"dotnet.exe") || struExecutablePath->EndsWith(L"dotnet");
}
HRESULT
HOSTFXR_UTILITY::GetHostFxrParameters(
_In_ HANDLE hEventLog,
_In_ PCWSTR pcwzProcessPath,
_In_ PCWSTR pcwzApplicationPhysicalPath,
_In_ PCWSTR pcwzArguments,
_Inout_ STRU *pStruHostFxrDllLocation,
_Inout_ STRU *pStruExeAbsolutePath,
_Out_ DWORD *pdwArgCount,
_Out_ BSTR **pbstrArgv
)
{
HRESULT hr = S_OK;
STRU struSystemPathVariable;
STRU struAbsolutePathToHostFxr;
STRU struAbsolutePathToDotnet;
STRU struEventMsg;
STACK_STRU(struExpandedProcessPath, MAX_PATH);
STACK_STRU(struExpandedArguments, MAX_PATH);
// Copy and Expand the processPath and Arguments.
if (FAILED(hr = struExpandedProcessPath.CopyAndExpandEnvironmentStrings(pcwzProcessPath))
|| FAILED(hr = struExpandedArguments.CopyAndExpandEnvironmentStrings(pcwzArguments)))
{
goto Finished;
}
// Convert the process path an absolute path to our current application directory.
// If the path is already an absolute path, it will be unchanged.
hr = UTILITY::ConvertPathToFullPath(
struExpandedProcessPath.QueryStr(),
pcwzApplicationPhysicalPath,
&struAbsolutePathToDotnet
);
if (FAILED(hr))
{
goto Finished;
}
// Check if the absolute path is to dotnet or not.
if (HOSTFXR_UTILITY::IsDotnetExecutable(&struAbsolutePathToDotnet))
{
//
// The processPath ends with dotnet.exe or dotnet
// like: C:\Program Files\dotnet\dotnet.exe, C:\Program Files\dotnet\dotnet, dotnet.exe, or dotnet.
// Get the absolute path to dotnet. If the path is already an absolute path, it will return that path
//
if (FAILED(hr = HOSTFXR_UTILITY::GetAbsolutePathToDotnet(&struAbsolutePathToDotnet))) // Make sure to append the dotnet.exe path correctly here (pass in regular path)?
{
goto Finished;
}
if (FAILED(hr = GetAbsolutePathToHostFxr(&struAbsolutePathToDotnet, hEventLog, &struAbsolutePathToHostFxr)))
{
goto Finished;
}
if (FAILED(hr = HOSTFXR_UTILITY::ParseHostfxrArguments(
struExpandedArguments.QueryStr(),
struAbsolutePathToDotnet.QueryStr(),
pcwzApplicationPhysicalPath,
hEventLog,
pdwArgCount,
pbstrArgv)))
{
goto Finished;
}
if (FAILED(hr = pStruHostFxrDllLocation->Copy(struAbsolutePathToHostFxr)))
{
goto Finished;
}
if (FAILED(hr = pStruExeAbsolutePath->Copy(struAbsolutePathToDotnet)))
{
goto Finished;
}
}
else
{
//
// 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()))
{
hr = HOSTFXR_UTILITY::GetStandaloneHostfxrParameters(
struAbsolutePathToDotnet.QueryStr(),
pcwzApplicationPhysicalPath,
struExpandedArguments.QueryStr(),
hEventLog,
pStruHostFxrDllLocation,
pdwArgCount,
pbstrArgv);
if (FAILED(hr))
{
goto Finished;
}
if (FAILED(hr = pStruExeAbsolutePath->Copy(struAbsolutePathToDotnet)))
{
goto Finished;
}
}
else
{
//
// If the processPath file does not exist and it doesn't include dotnet.exe or dotnet
// then it is an invalid argument.
//
hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);;
UTILITY::LogEventF(hEventLog,
EVENTLOG_ERROR_TYPE,
ASPNETCORE_EVENT_GENERAL_ERROR_MSG,
ASPNETCORE_EVENT_INVALID_PROCESS_PATH_MSG,
struExpandedProcessPath.QueryStr(),
hr);
}
}
Finished:
return hr;
}
//
// 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
HOSTFXR_UTILITY::ParseHostfxrArguments(
PCWSTR pwzArgumentsFromConfig,
PCWSTR pwzExePath,
PCWSTR pcwzApplicationPhysicalPath,
HANDLE hEventLog,
_Out_ DWORD* pdwArgCount,
_Out_ BSTR** pbstrArgv
)
{
UNREFERENCED_PARAMETER(hEventLog); // TODO use event log to set errors.
DBG_ASSERT(dwArgCount != NULL);
DBG_ASSERT(pwzArgv != NULL);
DBG_ASSERT(pwzExePath != NULL);
HRESULT hr = S_OK;
INT argc = 0;
BSTR* argv = NULL;
LPWSTR* pwzArgs = NULL;
STRU struTempPath;
INT intArgsProcessed = 0;
// 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)
{
hr = E_INVALIDARG;
goto Finished;
}
pwzArgs = CommandLineToArgvW(pwzArgumentsFromConfig, &argc);
if (pwzArgs == NULL)
{
hr = HRESULT_FROM_WIN32(GetLastError());
goto Failure;
}
argv = new BSTR[argc + 1];
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"))
{
if (SUCCEEDED(UTILITY::ConvertPathToFullPath(pwzArgs[intArgsProcessed], pcwzApplicationPhysicalPath, &struTempPath)))
{
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;
}
}
}
*pbstrArgv = argv;
*pdwArgCount = argc + 1;
goto Finished;
Failure:
if (argv != NULL)
{
// 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]);
}
}
delete[] argv;
Finished:
if (pwzArgs != NULL)
{
LocalFree(pwzArgs);
DBG_ASSERT(pwzArgs == NULL);
}
return hr;
}
HRESULT
HOSTFXR_UTILITY::GetAbsolutePathToDotnet(
_Inout_ STRU* pStruAbsolutePathToDotnet
)
{
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;
UTILITY::LogEventF(hEventLog,
EVENTLOG_ERROR_TYPE,
ASPNETCORE_EVENT_HOSTFXR_DIRECTORY_NOT_FOUND,
struEventMsg.QueryStr(),
ASPNETCORE_EVENT_HOSTFXR_DIRECTORY_NOT_FOUND_MSG,
struHostFxrPath.QueryStr(),
hr);
goto Finished;
}
// Find all folders under host\\fxr\\ for version numbers.
hr = struHostFxrSearchExpression.Copy(struHostFxrPath);
if (FAILED(hr))
{
goto Finished;
}
hr = struHostFxrSearchExpression.Append(L"\\*");
if (FAILED(hr))
{
goto Finished;
}
// As we use the logic from core-setup, we are opting to use std here.
HOSTFXR_UTILITY::FindDotNetFolders(struHostFxrSearchExpression.QueryStr(), &vVersionFolders);
if (vVersionFolders.size() == 0)
{
hr = HRESULT_FROM_WIN32(ERROR_BAD_ENVIRONMENT);
UTILITY::LogEventF(hEventLog,
EVENTLOG_ERROR_TYPE,
ASPNETCORE_EVENT_HOSTFXR_DIRECTORY_NOT_FOUND,
ASPNETCORE_EVENT_HOSTFXR_DIRECTORY_NOT_FOUND_MSG,
struHostFxrPath.QueryStr(),
hr);
goto Finished;
}
hr = FindHighestDotNetVersion(vVersionFolders, &struHighestDotnetVersion);
if (FAILED(hr))
{
goto Finished;
}
if (FAILED(hr = struHostFxrPath.Append(L"\\"))
|| FAILED(hr = struHostFxrPath.Append(struHighestDotnetVersion.QueryStr()))
|| FAILED(hr = struHostFxrPath.Append(L"\\hostfxr.dll")))
{
goto Finished;
}
if (!UTILITY::CheckIfFileExists(struHostFxrPath.QueryStr()))
{
// ASPNETCORE_EVENT_HOSTFXR_DLL_NOT_FOUND_MSG
hr = HRESULT_FROM_WIN32(ERROR_FILE_INVALID);
UTILITY::LogEventF(hEventLog,
EVENTLOG_ERROR_TYPE,
ASPNETCORE_EVENT_HOSTFXR_DLL_NOT_FOUND,
ASPNETCORE_EVENT_HOSTFXR_DLL_NOT_FOUND_MSG,
struHostFxrPath.QueryStr(),
hr);
goto Finished;
}
if (FAILED(hr = pStruAbsolutePathToHostfxr->Copy(struHostFxrPath)))
{
goto Finished;
}
Finished:
return hr;
}
//
// 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 };
PROCESS_INFORMATION processInformation = { 0 };
SECURITY_ATTRIBUTES securityAttributes;
CHAR pzFileContents[READ_BUFFER_SIZE];
HANDLE hStdOutReadPipe = 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.lpSecurityDescriptor = NULL;
securityAttributes.bInheritHandle = TRUE;
// Reset the path to dotnet as we will be using whether the string is
// empty or not as state
pStruAbsolutePathToDotnet->Reset();
// Create a read/write pipe that will be used for reading the result of where.exe
if (!CreatePipe(&hStdOutReadPipe, &hStdOutWritePipe, &securityAttributes, 0))
{
hr = HRESULT_FROM_WIN32(GetLastError());
goto Finished;
}
if (!SetHandleInformation(hStdOutReadPipe, HANDLE_FLAG_INHERIT, 0))
{
hr = ERROR_FILE_INVALID;
goto Finished;
}
// Set the stdout and err pipe to the write pipes.
startupInfo.cb = sizeof(startupInfo);
startupInfo.dwFlags |= STARTF_USESTDHANDLES;
startupInfo.hStdOutput = hStdOutWritePipe;
startupInfo.hStdError = hStdOutWritePipe;
// CreateProcess requires a mutable string to be passed to commandline
// See https://blogs.msdn.microsoft.com/oldnewthing/20090601-00/?p=18083/
pwzDotnetName = SysAllocString(L"\"where.exe\" dotnet.exe");
if (pwzDotnetName == NULL)
{
goto Finished;
}
// Create a process to invoke where.exe
fProcessCreationResult = CreateProcessW(NULL,
pwzDotnetName,
NULL,
NULL,
TRUE,
CREATE_NO_WINDOW,
NULL,
NULL,
&startupInfo,
&processInformation
);
if (!fProcessCreationResult)
{
goto Finished;
}
// 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);
hr = HRESULT_FROM_WIN32(ERROR_TIMEOUT);
goto Finished;
}
//
// where.exe will return 0 on success, 1 if the file is not found
// and 2 if there was an error. Check if the exit code is 1 and set
// a new hr result saying it couldn't find dotnet.exe
//
if (!GetExitCodeProcess(processInformation.hProcess, &dwExitCode))
{
goto Finished;
}
//
// In this block, if anything fails, we will goto our fallback of
// looking in C:/Program Files/
//
if (dwExitCode != 0)
{
goto Finished;
}
// Where succeeded.
// Reset file pointer to the beginning of the file.
dwFilePointer = SetFilePointer(hStdOutReadPipe, 0, NULL, FILE_BEGIN);
if (dwFilePointer == INVALID_SET_FILE_POINTER)
{
goto Finished;
}
//
// 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:
if (hStdOutReadPipe != INVALID_HANDLE_VALUE)
{
CloseHandle(hStdOutReadPipe);
}
if (hStdOutWritePipe != INVALID_HANDLE_VALUE)
{
CloseHandle(hStdOutWritePipe);
}
if (processInformation.hProcess != INVALID_HANDLE_VALUE)
{
CloseHandle(processInformation.hProcess);
}
if (processInformation.hThread != INVALID_HANDLE_VALUE)
{
CloseHandle(processInformation.hThread);
}
if (pwzDotnetName != NULL)
{
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;
}
HRESULT
HOSTFXR_UTILITY::FindHighestDotNetVersion(
_In_ std::vector<std::wstring> vFolders,
_Out_ STRU *pstrResult
)
{
HRESULT hr = S_OK;
fx_ver_t max_ver(-1, -1, -1);
for (const auto& dir : vFolders)
{
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);
}
}
hr = pstrResult->Copy(max_ver.as_str().c_str());
// we check FAILED(hr) outside of function
return hr;
}
VOID
HOSTFXR_UTILITY::FindDotNetFolders(
_In_ PCWSTR pszPath,
_Out_ std::vector<std::wstring> *pvFolders
)
{
HANDLE handle = NULL;
WIN32_FIND_DATAW data = { 0 };
handle = FindFirstFileExW(pszPath, FindExInfoStandard, &data, FindExSearchNameMatch, NULL, 0);
if (handle == INVALID_HANDLE_VALUE)
{
return;
}
do
{
std::wstring folder(data.cFileName);
pvFolders->push_back(folder);
} while (FindNextFileW(handle, &data));
FindClose(handle);
}