diff --git a/SHADE_Engine/src/Scripting/SHDotNetRuntime.cpp b/SHADE_Engine/src/Scripting/SHDotNetRuntime.cpp index 955a8474..98a49ada 100644 --- a/SHADE_Engine/src/Scripting/SHDotNetRuntime.cpp +++ b/SHADE_Engine/src/Scripting/SHDotNetRuntime.cpp @@ -21,6 +21,8 @@ of DigiPen Institute of Technology is prohibited. // External Dependencies #include // PathRemoveFileSpecA #include "Tools/Logger/SHLogger.h" +#include "Tools/Utilities/SHExecUtilities.h" +#include "Tools/Utilities/SHStringUtilities.h" namespace SHADE { @@ -59,6 +61,10 @@ namespace SHADE if (initialised) throw std::runtime_error("[DotNetRuntime] Failed to initialise as it was already initialised or was deinitialised into an invalid state."); + const auto DOT_NET_PATH = getDotNetRuntimePath(); + if (!DOT_NET_PATH) + throw std::runtime_error("[DotNetRuntime] .NET Runtime installation is missing."); + // Get the current executable directory std::string runtimePath(MAX_PATH, '\0'); GetModuleFileNameA(nullptr, runtimePath.data(), MAX_PATH); @@ -70,7 +76,7 @@ namespace SHADE if (coreClr == nullptr) { // Construct the CoreCLR path - std::string coreClrPath(runtimePath); // Works + std::string coreClrPath("C:\\Program Files\\dotnet\\shared\\Microsoft.NETCore.App\\5.0.17"); // Works coreClrPath += "\\coreclr.dll"; // Load the CoreCLR DLL @@ -94,7 +100,7 @@ namespace SHADE // trusted system assemblies (similar to the .NET Framework GAC). // For this host (as with most), assemblies next to CoreCLR will // be included in the TPA list - std::string tpaList = buildTpaList(runtimePath); + std::string tpaList = buildTpaList(DOT_NET_PATH->string()); // Define CoreCLR properties std::array propertyKeys = @@ -195,4 +201,82 @@ namespace SHADE throw std::runtime_error(oss.str()); } } + std::optional SHDotNetRuntime::getDotNetRuntimePath() + { + // Check if dotnet is installed + const auto DOT_NET_PATH = std::filesystem::path(L"C:\\Program Files\\dotnet\\dotnet.exe"); + if (!std::filesystem::exists(DOT_NET_PATH)) + return {}; + + // Execute dotnet to search for + const auto RUNTIME_SEARCH = SHExecUtilties::ExecBlockingProcess(DOT_NET_PATH.wstring(), L" --list-runtimes", true, false); + if (RUNTIME_SEARCH.Code != 0) + return {}; + + // Remove all except Microsoft.NETCore.App entries + auto runtimeEntries = SHStringUtilities::Split(RUNTIME_SEARCH.StdOutput, L'\n'); + std::erase_if(runtimeEntries, [](const std::wstring& str) + { + return str.find(L".NETCore.App") == std::wstring::npos; + }); + + // Early exit if there is none + if (runtimeEntries.empty()) + return {}; + + // Select the highest version + std::wstring highestVersion; + int major = 0; int minor = 0; int revision = 0; + for (const auto& entry : runtimeEntries) + { + auto start = entry.find_first_of(L"1234567890"); + auto end = entry.find_last_of(L"1234567890"); + + // No version string found + if (start == std::wstring::npos) + continue; + + // Grab the version string and tokenize it to parts + const std::wstring VER_STR = entry.substr(start, end - start + 1); + const auto VERSION_TOKENS = SHStringUtilities::Split(VER_STR, L'.'); + + // Invalid version string + if (VERSION_TOKENS.size() < 3) + continue; + + // Get version strings + const auto thisMajor = stoi(VERSION_TOKENS[0]); + const auto thisMinor = stoi(VERSION_TOKENS[1]); + const auto thisRevision = stoi(VERSION_TOKENS[2]); + + if (major > thisRevision) + continue; + if (minor > thisMinor) + continue; + if (revision > thisRevision) + continue; + + major = thisMajor; + minor = thisMinor; + revision = thisRevision; + highestVersion = VER_STR; + } + + // No suitable version found + if (highestVersion.empty()) + return {}; + + // Extract path string + const std::wstring& ENTRY = runtimeEntries[0]; + const auto START = ENTRY.find_first_of(L'['); + const auto END = ENTRY.find_last_of(L']'); + // - Can't extract string + if (START == std::wstring::npos || END == std::wstring::npos) + return {}; + // - Extract string + const std::wstring DIR_PATH = ENTRY.substr(START + 1, END - START - 1); + + // Form resulting path based on found version + return std::filesystem::path(DIR_PATH) / highestVersion; + } } diff --git a/SHADE_Engine/src/Scripting/SHDotNetRuntime.h b/SHADE_Engine/src/Scripting/SHDotNetRuntime.h index efb9e54b..ad8cd283 100644 --- a/SHADE_Engine/src/Scripting/SHDotNetRuntime.h +++ b/SHADE_Engine/src/Scripting/SHDotNetRuntime.h @@ -201,6 +201,19 @@ namespace SHADE */ /***********************************************************************************/ static void throwIfFailed(const std::string& errMsg, int resultCode); + /***********************************************************************************/ + /*! + + \brief + Searches for the dotnet runtime binary folder and returns it if it exists. + If multiple versions are present, the latest version will be returned. + + \return + The path to the dotnet runtime binary folder. Null if none was found. + + */ + /***********************************************************************************/ + static std::optional getDotNetRuntimePath(); }; }