655 lines
20 KiB
C++
655 lines
20 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 "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"
|
|
|
|
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,
|
|
HANDLE hEventLog,
|
|
_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(hEventLog, INPROCESS_FULL_FRAMEWORK_APP, pcwzApplicationPhysicalPath, 0);
|
|
return E_FAIL;
|
|
}
|
|
|
|
EVENTLOG(hEventLog, APPLICATION_EXE_NOT_FOUND, 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,
|
|
hEventLog,
|
|
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
|
|
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;
|
|
|
|
LOG_INFOF("Resolving hostfxr parameters for application: %S arguments: %S path: %S",
|
|
pcwzProcessPath,
|
|
pcwzArguments,
|
|
pcwzApplicationPhysicalPath);
|
|
|
|
const fs::path applicationPhysicalPath = pcwzApplicationPhysicalPath;
|
|
fs::path processPath = Environment::ExpandEnvironmentVariables(pcwzProcessPath);
|
|
std::wstring arguments = Environment::ExpandEnvironmentVariables(pcwzArguments);
|
|
|
|
// Check if the absolute path is to dotnet or not.
|
|
if (IsDotnetExecutable(processPath))
|
|
{
|
|
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())
|
|
{
|
|
hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
|
|
EVENTLOG(hEventLog, INVALID_PROCESS_PATH, processPath.c_str(), hr);
|
|
return hr;
|
|
}
|
|
|
|
processPath = fullProcessPath.value();
|
|
|
|
auto hostFxrPath = GetAbsolutePathToHostFxr(processPath, hEventLog);
|
|
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,
|
|
hEventLog,
|
|
pdwArgCount,
|
|
pbstrArgv));
|
|
|
|
RETURN_IF_FAILED(pStruHostFxrDllLocation->Copy(hostFxrPath->c_str()));
|
|
RETURN_IF_FAILED(pStruExeAbsolutePath->Copy(processPath.c_str()));
|
|
}
|
|
else
|
|
{
|
|
LOG_INFOF("Process path %S is not dotnet, treating application as standalone", processPath.c_str());
|
|
|
|
if (processPath.is_relative())
|
|
{
|
|
processPath = applicationPhysicalPath / processPath;
|
|
}
|
|
|
|
//
|
|
// 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 (is_regular_file(processPath))
|
|
{
|
|
RETURN_IF_FAILED(GetStandaloneHostfxrParameters(
|
|
processPath.c_str(),
|
|
pcwzApplicationPhysicalPath,
|
|
arguments.c_str(),
|
|
hEventLog,
|
|
pStruHostFxrDllLocation,
|
|
pdwArgCount,
|
|
pbstrArgv));
|
|
|
|
RETURN_IF_FAILED(pStruExeAbsolutePath->Copy(processPath.c_str()));
|
|
}
|
|
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);
|
|
EVENTLOG(hEventLog, INVALID_PROCESS_PATH, processPath.c_str(), hr);
|
|
return hr;
|
|
}
|
|
}
|
|
|
|
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
|
|
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(pdwArgCount != NULL);
|
|
DBG_ASSERT(pbstrArgv != NULL);
|
|
DBG_ASSERT(pwzExePath != NULL);
|
|
|
|
HRESULT hr = S_OK;
|
|
INT argc = 0;
|
|
BSTR* argv = NULL;
|
|
LPWSTR* pwzArgs = NULL;
|
|
STRU struTempPath;
|
|
INT intArgsProcessed = 0;
|
|
|
|
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)
|
|
{
|
|
hr = E_INVALIDARG;
|
|
goto Finished;
|
|
}
|
|
|
|
pwzArgs = CommandLineToArgvW(pwzArgumentsFromConfig, &argc);
|
|
|
|
if (pwzArgs == NULL)
|
|
{
|
|
hr = LOG_IF_FAILED(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;
|
|
}
|
|
}
|
|
|
|
LOG_INFOF("Argument[%d] = %S",
|
|
intArgsProcessed + 1,
|
|
argv[intArgsProcessed + 1]);
|
|
}
|
|
|
|
*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);
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
std::optional<fs::path>
|
|
HOSTFXR_UTILITY::GetAbsolutePathToDotnet(
|
|
const fs::path & applicationPath,
|
|
const fs::path & requestedPath
|
|
)
|
|
{
|
|
LOG_INFOF("Resolving absolute path to dotnet.exe from %S", requestedPath.c_str());
|
|
|
|
auto processPath = requestedPath;
|
|
if (processPath.is_relative())
|
|
{
|
|
processPath = applicationPath / processPath;
|
|
}
|
|
|
|
//
|
|
// If we are given an absolute path to dotnet.exe, we are done
|
|
//
|
|
if (is_regular_file(requestedPath))
|
|
{
|
|
LOG_INFOF("Found dotnet.exe at %S", requestedPath.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);
|
|
}
|
|
|
|
// At this point, we are calling where.exe to find dotnet.
|
|
// If we encounter any failures, try getting dotnet.exe from the
|
|
// backup location.
|
|
// Only do it if no path is specified
|
|
if (requestedPath.has_parent_path())
|
|
{
|
|
LOG_INFOF("Absolute path to dotnet.exe was not found at %S", requestedPath.c_str());
|
|
|
|
return std::nullopt;
|
|
}
|
|
|
|
const auto dotnetViaWhere = InvokeWhereToFindDotnet();
|
|
if (dotnetViaWhere.has_value())
|
|
{
|
|
LOG_INFOF("Found dotnet.exe via where.exe invocation at %S", dotnetViaWhere.value().c_str());
|
|
|
|
return dotnetViaWhere;
|
|
}
|
|
|
|
const auto programFilesLocation = GetAbsolutePathToDotnetFromProgramFiles();
|
|
if (programFilesLocation.has_value())
|
|
{
|
|
LOG_INFOF("Found dotnet.exe in Program Files at %S", programFilesLocation.value().c_str());
|
|
|
|
return programFilesLocation;
|
|
}
|
|
|
|
LOG_INFOF("dotnet.exe not found");
|
|
return std::nullopt;
|
|
}
|
|
|
|
std::optional<fs::path>
|
|
HOSTFXR_UTILITY::GetAbsolutePathToHostFxr(
|
|
const fs::path & dotnetPath,
|
|
HANDLE hEventLog
|
|
)
|
|
{
|
|
std::vector<std::wstring> versionFolders;
|
|
const auto hostFxrBase = dotnetPath.parent_path() / "host" / "fxr";
|
|
|
|
LOG_INFOF("Resolving absolute path to hostfxr.dll from %S", dotnetPath.c_str());
|
|
|
|
if (!is_directory(hostFxrBase))
|
|
{
|
|
EVENTLOG(hEventLog, HOSTFXR_DIRECTORY_NOT_FOUND, hostFxrBase.c_str(), HRESULT_FROM_WIN32(ERROR_BAD_ENVIRONMENT));
|
|
|
|
return std::nullopt;
|
|
}
|
|
|
|
auto searchPattern = std::wstring(hostFxrBase) + L"\\*";
|
|
FindDotNetFolders(searchPattern.c_str(), versionFolders);
|
|
|
|
if (versionFolders.empty())
|
|
{
|
|
EVENTLOG(hEventLog, HOSTFXR_DIRECTORY_NOT_FOUND, hostFxrBase.c_str(), HRESULT_FROM_WIN32(ERROR_BAD_ENVIRONMENT));
|
|
|
|
return std::nullopt;
|
|
}
|
|
|
|
const auto highestVersion = FindHighestDotNetVersion(versionFolders);
|
|
const auto hostFxrPath = hostFxrBase / highestVersion / "hostfxr.dll";
|
|
|
|
if (!is_regular_file(hostFxrPath))
|
|
{
|
|
EVENTLOG(hEventLog, HOSTFXR_DLL_NOT_FOUND, hostFxrPath.c_str(), HRESULT_FROM_WIN32(ERROR_FILE_INVALID));
|
|
|
|
return std::nullopt;
|
|
}
|
|
|
|
LOG_INFOF("hostfxr.dll located at %S", hostFxrPath.c_str());
|
|
return std::make_optional(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.
|
|
//
|
|
std::optional<fs::path>
|
|
HOSTFXR_UTILITY::InvokeWhereToFindDotnet()
|
|
{
|
|
HRESULT hr = S_OK;
|
|
// Arguments to call where.exe
|
|
STARTUPINFOW startupInfo = { 0 };
|
|
PROCESS_INFORMATION processInformation = { 0 };
|
|
SECURITY_ATTRIBUTES securityAttributes;
|
|
|
|
CHAR pzFileContents[READ_BUFFER_SIZE];
|
|
HandleWrapper<InvalidHandleTraits> hStdOutReadPipe;
|
|
HandleWrapper<InvalidHandleTraits> hStdOutWritePipe;
|
|
HandleWrapper<InvalidHandleTraits> hProcess;
|
|
HandleWrapper<InvalidHandleTraits> hThread;
|
|
CComBSTR pwzDotnetName = NULL;
|
|
DWORD dwFilePointer;
|
|
BOOL fIsWow64Process;
|
|
BOOL fIsCurrentProcess64Bit;
|
|
DWORD dwExitCode;
|
|
STRU struDotnetSubstring;
|
|
STRU struDotnetLocationsString;
|
|
DWORD dwNumBytesRead;
|
|
DWORD dwBinaryType;
|
|
INT index = 0;
|
|
INT prevIndex = 0;
|
|
std::optional<fs::path> result;
|
|
|
|
// Set the security attributes for the read/write pipe
|
|
securityAttributes.nLength = sizeof(securityAttributes);
|
|
securityAttributes.lpSecurityDescriptor = NULL;
|
|
securityAttributes.bInheritHandle = TRUE;
|
|
|
|
LOG_INFO("Invoking where.exe to find dotnet.exe");
|
|
|
|
// Create a read/write pipe that will be used for reading the result of where.exe
|
|
FINISHED_LAST_ERROR_IF(!CreatePipe(&hStdOutReadPipe, &hStdOutWritePipe, &securityAttributes, 0));
|
|
FINISHED_LAST_ERROR_IF(!SetHandleInformation(hStdOutReadPipe, HANDLE_FLAG_INHERIT, 0));
|
|
|
|
// 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 = L"\"where.exe\" dotnet.exe";
|
|
|
|
// Create a process to invoke where.exe
|
|
FINISHED_LAST_ERROR_IF(!CreateProcessW(NULL,
|
|
pwzDotnetName,
|
|
NULL,
|
|
NULL,
|
|
TRUE,
|
|
CREATE_NO_WINDOW,
|
|
NULL,
|
|
NULL,
|
|
&startupInfo,
|
|
&processInformation
|
|
));
|
|
|
|
// Store handles into wrapper so they get closed automatically
|
|
hProcess = processInformation.hProcess;
|
|
hThread = processInformation.hThread;
|
|
|
|
// Wait for where.exe to return
|
|
WaitForSingleObject(processInformation.hProcess, INFINITE);
|
|
|
|
//
|
|
// 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
|
|
//
|
|
FINISHED_LAST_ERROR_IF (!GetExitCodeProcess(processInformation.hProcess, &dwExitCode));
|
|
|
|
//
|
|
// In this block, if anything fails, we will goto our fallback of
|
|
// looking in C:/Program Files/
|
|
//
|
|
if (dwExitCode != 0)
|
|
{
|
|
FINISHED_IF_FAILED(E_FAIL);
|
|
}
|
|
|
|
// Where succeeded.
|
|
// Reset file pointer to the beginning of the file.
|
|
dwFilePointer = SetFilePointer(hStdOutReadPipe, 0, NULL, FILE_BEGIN);
|
|
if (dwFilePointer == INVALID_SET_FILE_POINTER)
|
|
{
|
|
FINISHED_IF_FAILED(E_FAIL);
|
|
}
|
|
|
|
//
|
|
// 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.
|
|
//
|
|
FINISHED_LAST_ERROR_IF (!ReadFile(hStdOutReadPipe, pzFileContents, READ_BUFFER_SIZE, &dwNumBytesRead, NULL));
|
|
|
|
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.
|
|
FINISHED_IF_FAILED(E_FAIL);
|
|
}
|
|
|
|
FINISHED_IF_FAILED(struDotnetLocationsString.CopyA(pzFileContents, dwNumBytesRead));
|
|
|
|
LOG_INFOF("where.exe invocation returned: %S", struDotnetLocationsString.QueryStr());
|
|
|
|
// Check the bitness of the currently running process
|
|
// matches the dotnet.exe found.
|
|
FINISHED_LAST_ERROR_IF (!IsWow64Process(GetCurrentProcess(), &fIsWow64Process));
|
|
|
|
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;
|
|
}
|
|
|
|
LOG_INFOF("Current process bitness type detected as isX64=%d", fIsCurrentProcess64Bit);
|
|
|
|
while (TRUE)
|
|
{
|
|
index = struDotnetLocationsString.IndexOf(L"\r\n", prevIndex);
|
|
if (index == -1)
|
|
{
|
|
break;
|
|
}
|
|
|
|
FINISHED_IF_FAILED(struDotnetSubstring.Copy(&struDotnetLocationsString.QueryStr()[prevIndex], index - prevIndex));
|
|
// \r\n is two wchars, so add 2 here.
|
|
prevIndex = index + 2;
|
|
|
|
LOG_INFOF("Processing entry %S", struDotnetSubstring.QueryStr());
|
|
|
|
if (LOG_LAST_ERROR_IF(!GetBinaryTypeW(struDotnetSubstring.QueryStr(), &dwBinaryType)))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
LOG_INFOF("Binary type %d", dwBinaryType);
|
|
|
|
if (fIsCurrentProcess64Bit == (dwBinaryType == SCS_64BIT_BINARY))
|
|
{
|
|
// The bitness of dotnet matched with the current worker process bitness.
|
|
return std::make_optional(struDotnetSubstring.QueryStr());
|
|
}
|
|
}
|
|
|
|
Finished:
|
|
return result;
|
|
}
|
|
|
|
std::optional<fs::path>
|
|
HOSTFXR_UTILITY::GetAbsolutePathToDotnetFromProgramFiles()
|
|
{
|
|
const auto programFilesDotnet = fs::path(Environment::ExpandEnvironmentVariables(L"%ProgramFiles%")) / "dotnet" / "dotnet.exe";
|
|
return is_regular_file(programFilesDotnet) ? std::make_optional(programFilesDotnet) : std::nullopt;
|
|
}
|
|
|
|
std::wstring
|
|
HOSTFXR_UTILITY::FindHighestDotNetVersion(
|
|
_In_ std::vector<std::wstring> & vFolders
|
|
)
|
|
{
|
|
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);
|
|
}
|
|
}
|
|
|
|
return max_ver.as_str();
|
|
}
|
|
|
|
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
|
|
{
|
|
pvFolders.emplace_back(data.cFileName);
|
|
} while (FindNextFileW(handle, &data));
|
|
|
|
FindClose(handle);
|
|
}
|