Merge pull request #299 from SHADE-DP/SP3-6-c-scripting

Added scripting quality of life features
This commit is contained in:
XiaoQiDigipen 2023-01-01 12:37:09 +08:00 committed by GitHub
commit a6a0e1588b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 677 additions and 109 deletions

View File

@ -20,10 +20,11 @@ echo "M - SDL"
echo "N - dotnet" echo "N - dotnet"
echo "O - tinyddsloader" echo "O - tinyddsloader"
echo "P - fmod" echo "P - fmod"
echo "Q - vswhere"
echo --------------------------------------------------- echo ---------------------------------------------------
echo. echo.
choice /C ABCDEFGHIJKLMNOP /T 10 /D A choice /C ABCDEFGHIJKLMNOPQ /T 10 /D A
set _e=%ERRORLEVEL% set _e=%ERRORLEVEL%
if %_e%==1 goto VMA if %_e%==1 goto VMA
@ -42,6 +43,7 @@ if %_e%==13 goto SDL
if %_e%==14 goto dotnet if %_e%==14 goto dotnet
if %_e%==15 goto tinyddsloader if %_e%==15 goto tinyddsloader
if %_e%==16 goto fmod if %_e%==16 goto fmod
if %_e%==17 goto vswhere
:VMA :VMA
echo -----------------------VMA---------------------------- echo -----------------------VMA----------------------------
@ -155,6 +157,13 @@ if %_e%==15 (goto :done) else (goto :fmod)
echo --------------------fmod------------------------- echo --------------------fmod-------------------------
rmdir "Dependencies/fmod" /S /Q rmdir "Dependencies/fmod" /S /Q
git clone https://github.com/SHADE-DP/FMOD.git "Dependencies/fmod" git clone https://github.com/SHADE-DP/FMOD.git "Dependencies/fmod"
if %_e%==16 (goto :done) else (goto :vswhere)
:vswhere
echo -----------------------vswhere----------------------------
rmdir "Dependencies/vswhere" /S /Q
mkdir "Dependencies/vswhere"
powershell -Command "& {wget https://github.com/microsoft/vswhere/releases/download/3.1.1/vswhere.exe -OutFile "Dependencies/vswhere/vswhere.exe"}"
:done :done
echo DONE! echo DONE!

View File

@ -16,4 +16,5 @@ IncludeDir["SDL"] = "%{wks.location}\\Dependencies\\SDL"
IncludeDir["VULKAN"] = "$(VULKAN_SDK)" IncludeDir["VULKAN"] = "$(VULKAN_SDK)"
IncludeDir["dotnet"] = "%{wks.location}\\Dependencies\\dotnet" IncludeDir["dotnet"] = "%{wks.location}\\Dependencies\\dotnet"
IncludeDir["tinyddsloader"] = "%{wks.location}\\Dependencies\\tinyddsloader" IncludeDir["tinyddsloader"] = "%{wks.location}\\Dependencies\\tinyddsloader"
IncludeDir["fmod"] = "%{wks.location}\\Dependencies\\fmod" IncludeDir["fmod"] = "%{wks.location}\\Dependencies\\fmod"
IncludeDir["vswhere"] = "%{wks.location}\\Dependencies\\vswhere"

View File

@ -124,7 +124,8 @@ project "SHADE_Engine"
"xcopy /r /y /q \"%{IncludeDir.ModelCompiler}\\bin\\Debug\\ModelCompiler.exe\" \"$(OutDir)\"", "xcopy /r /y /q \"%{IncludeDir.ModelCompiler}\\bin\\Debug\\ModelCompiler.exe\" \"$(OutDir)\"",
"xcopy /r /y /q \"%{IncludeDir.FontCompiler}\\bin\\Debug\\FontCompiler.exe\" \"$(OutDir)\"", "xcopy /r /y /q \"%{IncludeDir.FontCompiler}\\bin\\Debug\\FontCompiler.exe\" \"$(OutDir)\"",
"xcopy /r /y /q \"%{IncludeDir.fmod}\\lib\\fmodL.dll\" \"$(OutDir)\"", "xcopy /r /y /q \"%{IncludeDir.fmod}\\lib\\fmodL.dll\" \"$(OutDir)\"",
"xcopy /r /y /q \"%{IncludeDir.fmod}\\lib\\fmodstudioL.dll\" \"$(OutDir)\"" "xcopy /r /y /q \"%{IncludeDir.fmod}\\lib\\fmodstudioL.dll\" \"$(OutDir)\"",
"xcopy /r /y /q \"%{IncludeDir.vswhere}\\vswhere.exe\" \"$(OutDir)\""
} }
filter "configurations:Release" filter "configurations:Release"
@ -134,7 +135,8 @@ project "SHADE_Engine"
"xcopy /r /y /q \"%{IncludeDir.ModelCompiler}\\bin\\Release\\ModelCompiler.exe\" \"$(OutDir)\"", "xcopy /r /y /q \"%{IncludeDir.ModelCompiler}\\bin\\Release\\ModelCompiler.exe\" \"$(OutDir)\"",
"xcopy /r /y /q \"%{IncludeDir.FontCompiler}\\bin\\Release\\FontCompiler.exe\" \"$(OutDir)\"", "xcopy /r /y /q \"%{IncludeDir.FontCompiler}\\bin\\Release\\FontCompiler.exe\" \"$(OutDir)\"",
"xcopy /r /y /q \"%{IncludeDir.fmod}\\lib\\fmod.dll\" \"$(OutDir)\"", "xcopy /r /y /q \"%{IncludeDir.fmod}\\lib\\fmod.dll\" \"$(OutDir)\"",
"xcopy /r /y /q \"%{IncludeDir.fmod}\\lib\\fmodstudio.dll\" \"$(OutDir)\"" "xcopy /r /y /q \"%{IncludeDir.fmod}\\lib\\fmodstudio.dll\" \"$(OutDir)\"",
"xcopy /r /y /q \"%{IncludeDir.vswhere}\\vswhere.exe\" \"$(OutDir)\""
} }
filter "configurations:Publish" filter "configurations:Publish"

View File

@ -14,6 +14,8 @@
#include "Editor/DragDrop/SHDragDrop.hpp" #include "Editor/DragDrop/SHDragDrop.hpp"
#include "Editor/EditorWindow/MaterialInspector/SHMaterialInspector.h" #include "Editor/EditorWindow/MaterialInspector/SHMaterialInspector.h"
#include "Editor/EditorWindow/SHEditorWindowManager.h" #include "Editor/EditorWindow/SHEditorWindowManager.h"
#include "Scripting/SHVSUtilities.h"
#include "Scripting/SHScriptEngine.h"
namespace SHADE namespace SHADE
{ {
@ -249,6 +251,12 @@ namespace SHADE
matInspector->OpenMaterial(asset->id); matInspector->OpenMaterial(asset->id);
} }
break; break;
case AssetType::SCRIPT:
if(auto scriptEngine = SHSystemManager::GetSystem<SHScriptEngine>())
{
scriptEngine->OpenFile(asset->path);
}
break;
case AssetType::MAX_COUNT: break; case AssetType::MAX_COUNT: break;
default:; default:;
} }

View File

@ -120,6 +120,11 @@ namespace SHADE
auto* scriptEngine = static_cast<SHScriptEngine*>(SHSystemManager::GetSystem<SHScriptEngine>()); auto* scriptEngine = static_cast<SHScriptEngine*>(SHSystemManager::GetSystem<SHScriptEngine>());
scriptEngine->GenerateScriptsCsProjFile(); scriptEngine->GenerateScriptsCsProjFile();
} }
if (ImGui::Selectable("Open Visual Studio Project"))
{
auto* scriptEngine = static_cast<SHScriptEngine*>(SHSystemManager::GetSystem<SHScriptEngine>());
scriptEngine->OpenSolution();
}
ImGui::BeginDisabled(SHSystemManager::GetSystem<SHEditor>()->editorState != SHEditor::State::STOP); ImGui::BeginDisabled(SHSystemManager::GetSystem<SHEditor>()->editorState != SHEditor::State::STOP);
if (ImGui::Selectable("Build Scripts - Debug")) if (ImGui::Selectable("Build Scripts - Debug"))
{ {

View File

@ -28,8 +28,9 @@ of DigiPen Institute of Technology is prohibited.
#include "Physics/System/SHPhysicsSystem.h" #include "Physics/System/SHPhysicsSystem.h"
#include "Physics/SHPhysicsEvents.h" #include "Physics/SHPhysicsEvents.h"
#include "Scene/SHSceneEvents.h" #include "Scene/SHSceneEvents.h"
#include "Assets/SHAssetMacros.h" #include "Assets/SHAssetMacros.h"
#include "Tools/Utilities/SHExecUtilities.h"
#include "SHVSUtilities.h"
namespace SHADE namespace SHADE
{ {
@ -189,12 +190,9 @@ namespace SHADE
oss << "[ScriptEngine] Building " << (debug ? " debug " : "") << "Managed Script Assembly (" << MANAGED_SCRIPT_LIB_NAME << ")!"; oss << "[ScriptEngine] Building " << (debug ? " debug " : "") << "Managed Script Assembly (" << MANAGED_SCRIPT_LIB_NAME << ")!";
SHLOG_INFO(oss.str()); SHLOG_INFO(oss.str());
oss.str(""); oss.str("");
const bool BUILD_SUCCESS = execProcess const auto BUILD_SUCCESS = SHExecUtilties::ExecBlockingProcess(L"C:\\Program Files\\dotnet\\dotnet.exe", generateBuildCommand(debug), false, false);
( SHLOG_WARNING("Build Output: {}", SHStringUtilities::WstrToStr(BUILD_SUCCESS.StdOutput));
L"C:\\Windows\\system32\\cmd.exe", if (BUILD_SUCCESS.Code == 0)
L"/K \"" + generateBuildCommand(debug) + L" & exit\""
) == 0;
if (BUILD_SUCCESS)
{ {
// Copy to built dll to the working directory and replace // Copy to built dll to the working directory and replace
if (!copyFile("./tmp/SHADE_Scripting.dll", "SHADE_Scripting.dll", std::filesystem::copy_options::overwrite_existing)) if (!copyFile("./tmp/SHADE_Scripting.dll", "SHADE_Scripting.dll", std::filesystem::copy_options::overwrite_existing))
@ -238,7 +236,7 @@ namespace SHADE
LoadScriptAssembly(); LoadScriptAssembly();
} }
return BUILD_SUCCESS; return BUILD_SUCCESS.Code == 0;
} }
void SHScriptEngine::GenerateScriptsCsProjFile(const std::filesystem::path& path) const void SHScriptEngine::GenerateScriptsCsProjFile(const std::filesystem::path& path) const
@ -306,6 +304,16 @@ namespace SHADE
file.close(); file.close();
} }
void SHScriptEngine::OpenSolution()
{
SHVSUtilties::OpenProject(CSPROJ_PATH);
}
void SHScriptEngine::OpenFile(const std::filesystem::path& path)
{
SHVSUtilties::OpenFile(CSPROJ_PATH, path);
}
/*-----------------------------------------------------------------------------------*/ /*-----------------------------------------------------------------------------------*/
/* Event Handler Functions */ /* Event Handler Functions */
/*-----------------------------------------------------------------------------------*/ /*-----------------------------------------------------------------------------------*/
@ -610,64 +618,12 @@ namespace SHADE
} }
} }
DWORD SHScriptEngine::execProcess(const std::wstring& path, const std::wstring& args)
{
STARTUPINFOW startInfo;
PROCESS_INFORMATION procInfo;
ZeroMemory(&startInfo, sizeof(startInfo));
ZeroMemory(&procInfo, sizeof(procInfo));
startInfo.cb = sizeof(startInfo);
std::wstring argsWstr = args;
// Start Process
const auto SUCCESS = CreateProcess
(
path.data(), argsWstr.data(),
nullptr, nullptr, false, NULL, nullptr, nullptr,
&startInfo, &procInfo
);
// Error Check
if (!SUCCESS)
{
auto err = GetLastError();
std::ostringstream oss;
oss << "[ScriptEngine] Failed to launch process. Error code: " << std::hex << err
<< " (" << SHStringUtilities::GetWin32ErrorMessage(err) << ")";
throw std::runtime_error(oss.str());
}
// Wait for execution to end
DWORD status;
while (true)
{
const auto EXEC_SUCCESS = GetExitCodeProcess(procInfo.hProcess, &status);
if (!EXEC_SUCCESS)
{
auto err = GetLastError();
std::ostringstream oss;
oss << "[ScriptEngine] Failed to query process. Error code: " << std::hex << err
<< " (" << SHStringUtilities::GetWin32ErrorMessage(err) << ")";
throw std::runtime_error(oss.str());
}
// Break only if process ends
if (status != STILL_ACTIVE)
{
CloseHandle(procInfo.hProcess);
CloseHandle(procInfo.hThread);
return status;
}
}
}
std::wstring SHScriptEngine::generateBuildCommand(bool debug) std::wstring SHScriptEngine::generateBuildCommand(bool debug)
{ {
std::wostringstream oss; std::wostringstream oss;
oss << "dotnet build \"" << SHStringUtilities::StrToWstr(CSPROJ_PATH) << "\" -c "; oss << " build \"" << SHStringUtilities::StrToWstr(std::filesystem::absolute(CSPROJ_PATH).string()) << "\" -c ";
oss << debug ? "Debug" : "Release"; oss << debug ? "Debug" : "Release";
oss << " -o \"./tmp/\" -fl -flp:LogFile=build.log;Verbosity=quiet -r \"win-x64\""; oss << " -o \"./tmp/\" -r \"win-x64\"";
return oss.str(); return oss.str();
} }
} }

View File

@ -1,5 +1,5 @@
/************************************************************************************//*! /************************************************************************************//*!
\file ScriptEngine.h \file SHScriptEngine.h
\author Tng Kah Wei, kahwei.tng, 390009620 \author Tng Kah Wei, kahwei.tng, 390009620
\par email: kahwei.tng\@digipen.edu \par email: kahwei.tng\@digipen.edu
\date Sep 17, 2021 \date Sep 17, 2021
@ -217,6 +217,15 @@ namespace SHADE
/// </summary> /// </summary>
/// <param name="path">File path to the generated file.</param> /// <param name="path">File path to the generated file.</param>
void GenerateScriptsCsProjFile(const std::filesystem::path& path = CSPROJ_PATH) const; void GenerateScriptsCsProjFile(const std::filesystem::path& path = CSPROJ_PATH) const;
/// <summary>
/// Opens the script solution in Visual Studio.
/// </summary>
void OpenSolution();
/// <summary>
/// Opens the file in Visual Studio.
/// </summary>
/// <param name="path"></param>
void OpenFile(const std::filesystem::path& path);
private: private:
/*-----------------------------------------------------------------------------*/ /*-----------------------------------------------------------------------------*/
@ -320,7 +329,6 @@ namespace SHADE
/// <returns> True if the file exists </returns> /// <returns> True if the file exists </returns>
static bool fileExists(const std::filesystem::path& filePath); static bool fileExists(const std::filesystem::path& filePath);
static bool copyFile(const std::filesystem::path& from, const std::filesystem::path& to, const std::filesystem::copy_options options) noexcept; static bool copyFile(const std::filesystem::path& from, const std::filesystem::path& to, const std::filesystem::copy_options options) noexcept;
static DWORD execProcess(const std::wstring& path, const std::wstring& args);
static std::wstring generateBuildCommand(bool debug); static std::wstring generateBuildCommand(bool debug);
}; };
} }

View File

@ -0,0 +1,127 @@
/************************************************************************************//*!
\file SHVSUtilities.cpp
\author Tng Kah Wei, kahwei.tng, 390009620
\par email: kahwei.tng\@digipen.edu
\date Dec 21, 2022
\brief Contains the implementation for SHVSUtilities static class.
Copyright (C) 2022 DigiPen Institute of Technology.
Reproduction or disclosure of this file or its contents without the prior written consent
of DigiPen Institute of Technology is prohibited.
*//*************************************************************************************/
// Precompiled Headers
#include <SHpch.h>
// Primary Header
#include "SHVSUtilities.h"
// Project Headers
#include "Tools/Utilities/SHExecUtilities.h"
namespace SHADE
{
/*-----------------------------------------------------------------------------------*/
/* Static Data Members */
/*-----------------------------------------------------------------------------------*/
std::filesystem::path SHVSUtilties::devEnvPath;
HANDLE SHVSUtilties::devEnvInst = nullptr;
/*-----------------------------------------------------------------------------------*/
/* Usage Functions */
/*-----------------------------------------------------------------------------------*/
void SHVSUtilties::OpenProject(const std::filesystem::path& projPath)
try
{
// Check if there is an instance
bool devEnvActive = false;
if (devEnvInst)
{
DWORD status;
const auto GET_CODE_STATUS = GetExitCodeProcess(devEnvInst, &status);
devEnvActive = GET_CODE_STATUS && status == STILL_ACTIVE;
}
// Reuse the existing instance if there is one
if (devEnvActive)
{
// No need to reopen one
return;
}
else
{
if (devEnvPath.empty())
{
devEnvPath = SHVSUtilties::getDevEnvPath();
}
auto absProjPath = std::filesystem::canonical(projPath);
auto pi = SHExecUtilties::ExecProcess(devEnvPath.generic_wstring(), L" " + absProjPath.generic_wstring());
// Cache the process handle
devEnvInst = pi.hProcess;
}
}
catch (std::exception& e)
{
SHLOG_ERROR("{}", e.what());
SHLOG_ERROR("[SHVSUtilities] Failed to launch Visual Studio.");
}
void SHVSUtilties::OpenFile(const std::filesystem::path& projPath, const std::filesystem::path& path)
try
{
// Check if there is an instance
bool devEnvActive = false;
if (devEnvInst)
{
DWORD status;
const auto GET_CODE_STATUS = GetExitCodeProcess(devEnvInst, &status);
devEnvActive = GET_CODE_STATUS && status == STILL_ACTIVE;
}
auto absPath = std::filesystem::canonical(path);
// Reuse the existing instance if there is one
if (devEnvActive)
{
// Edit the file only
SHExecUtilties::ExecProcess(devEnvPath.generic_wstring(), L" /Edit " + absPath.generic_wstring());
}
else
{
if (devEnvPath.empty())
{
devEnvPath = SHVSUtilties::getDevEnvPath();
}
auto absProjPath = std::filesystem::canonical(projPath);
auto pi = SHExecUtilties::ExecProcess(devEnvPath.generic_wstring(), absProjPath.generic_wstring() + L" " + absPath.generic_wstring());
// Cache the process handle
devEnvInst = pi.hProcess;
}
}
catch (std::exception& e)
{
SHLOG_ERROR("{}", e.what());
SHLOG_ERROR("[SHVSUtilities] Failed to launch Visual Studio.");
}
/*-----------------------------------------------------------------------------------*/
/* Helper Functions */
/*-----------------------------------------------------------------------------------*/
std::filesystem::path SHVSUtilties::getDevEnvPath()
{
#ifdef _PUBLISH
return {}; // Don't do anything if it's a published build
#else
static constexpr int EXCESS_CHARS_COUNT = 2;
const auto RESULT = SHExecUtilties::ExecBlockingPowerShellCommand(L"./vswhere -version \"[15.0,19.0]\" -requires Microsoft.NetCore.Component.DevelopmentTools -find Common7\\\\IDE\\\\devenv.exe | Select-Object -first 1", true, true);
if (RESULT.StdOutput.size() < EXCESS_CHARS_COUNT)
{
SHLOG_ERROR("[SHVSUtilities] Unable to get path to Visual Studio installation. SHVSUtilities will not work.");
return {};
}
return RESULT.StdOutput.substr(0, RESULT.StdOutput.size() - EXCESS_CHARS_COUNT);
#endif
}
}

View File

@ -0,0 +1,61 @@
/************************************************************************************//*!
\file SHVSUtilties.h
\author Tng Kah Wei, kahwei.tng, 390009620
\par email: kahwei.tng\@digipen.edu
\date Dec 21, 2022
\brief Contains the interface for SHVSUtilties class.
Copyright (C) 2022 DigiPen Institute of Technology.
Reproduction or disclosure of this file or its contents without the prior written consent
of DigiPen Institute of Technology is prohibited.
*//*************************************************************************************/
// STL Includes
#include <filesystem>
// External Dependencies
#include <Windows.h>
namespace SHADE
{
/// <summary>
/// Static class containing functions for working with Visual Studio installation and
/// running instances.
/// </summary>
class SH_API SHVSUtilties final
{
public:
/*---------------------------------------------------------------------------------*/
/* Usage Functions */
/*---------------------------------------------------------------------------------*/
/// <summary>
/// Opens the project at the specified path with a new or existing instance of Visual
/// Studio if it exists.
/// </summary>
/// <param name="projPath">Path to the project file to open.</param>
static void OpenProject(const std::filesystem::path& projPath);
/// <summary>
/// Opens the file at the specified path with a new or existing instance of Visual
/// Studio if it exists.
/// </summary>
/// <param name="projPath">Path to the project file to open.</param>
/// <param name="path">Path to the file to open.</param>
static void OpenFile(const std::filesystem::path& projPath, const std::filesystem::path& path);
private:
/*---------------------------------------------------------------------------------*/
/* Constructors/Destructors */
/*---------------------------------------------------------------------------------*/
SHVSUtilties() = delete;
/*---------------------------------------------------------------------------------*/
/* Static Data Members */
/*---------------------------------------------------------------------------------*/
static std::filesystem::path devEnvPath;
static HANDLE devEnvInst;
/*---------------------------------------------------------------------------------*/
/* Helper Functions */
/*---------------------------------------------------------------------------------*/
static std::filesystem::path getDevEnvPath();
};
}

View File

@ -0,0 +1,215 @@
/************************************************************************************//*!
\file SHExecUtilities.cpp
\author Tng Kah Wei, kahwei.tng, 390009620
\par email: kahwei.tng\@digipen.edu
\date Dec 21, 2022
\brief Contains the implementation for SHExecUtilities static class.
Copyright (C) 2022 DigiPen Institute of Technology.
Reproduction or disclosure of this file or its contents without the prior written consent
of DigiPen Institute of Technology is prohibited.
*//*************************************************************************************/
// Precompiled Headers
#include <SHpch.h>
// Primary Header
#include "SHExecUtilities.h"
// Project Includes
#include "SHStringUtilities.h"
#include "SHFileUtilties.h"
namespace SHADE
{
/*-----------------------------------------------------------------------------------*/
/* Process Execution Functions */
/*-----------------------------------------------------------------------------------*/
PROCESS_INFORMATION SHExecUtilties::ExecProcess(const std::wstring& path, const std::wstring& args)
{
return execProcess(path, args, nullptr, nullptr);
}
ExecResult SHExecUtilties::ExecBlockingProcess(const std::wstring& path, const std::wstring& args, bool output, bool errorOutput)
{
// Create pipes for stdout and stderr if specified
HANDLE stdoutReadPipe = nullptr;
HANDLE stdoutWritePipe = nullptr;
HANDLE stderrReadPipe = nullptr;
HANDLE stderrWritePipe = nullptr;
if (output)
{
std::tie(stdoutReadPipe, stdoutWritePipe) = createIoPipes();
}
if (errorOutput)
{
std::tie(stderrReadPipe, stderrWritePipe) = createIoPipes();
}
PROCESS_INFORMATION procInfo = execProcess(path, args, stdoutWritePipe, stderrWritePipe);
// Wait for execution to end
ExecResult result;
while (true)
{
const auto EXEC_SUCCESS = GetExitCodeProcess(procInfo.hProcess, &result.Code);
if (!EXEC_SUCCESS)
{
auto err = GetLastError();
std::ostringstream oss;
oss << "[SHExecUtilties] Failed to query process. Error code: " << std::hex << err
<< " (" << SHStringUtilities::GetWin32ErrorMessage(err) << ")";
throw std::runtime_error(oss.str());
}
// Break only if process ends
if (result.Code != STILL_ACTIVE)
{
// Get stdout/stderror output
if (stdoutReadPipe)
{
readPipeData(stdoutReadPipe, stdoutWritePipe, result.StdOutput);
}
if (stderrReadPipe)
{
readPipeData(stderrReadPipe, stderrWritePipe, result.StdErrOutput);
}
// Close the process and threads for that process
CloseHandle(procInfo.hProcess);
CloseHandle(procInfo.hThread);
// Return results
return result;
}
}
}
/*-----------------------------------------------------------------------------------*/
/* Command Execution Functions */
/*-----------------------------------------------------------------------------------*/
PROCESS_INFORMATION SHExecUtilties::ExecCommand(const std::wstring& command)
{
return ExecProcess
(
L"C:\\Windows\\system32\\cmd.exe",
L"/K \"" + command + L" & exit\""
);
}
ExecResult SHExecUtilties::ExecBlockingCommand(const std::wstring& command, bool output, bool errorOutput)
{
return ExecBlockingProcess
(
L"C:\\Windows\\system32\\cmd.exe",
L"/K \"" + command + L" & exit\"",
output,
errorOutput
);
}
PROCESS_INFORMATION SHExecUtilties::ExecPowerShellCommand(const std::wstring& command)
{
return ExecProcess
(
L"C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe",
L"-Command \"& {" + command + L"} \""
);
}
ExecResult SHExecUtilties::ExecBlockingPowerShellCommand(const std::wstring& command, bool output, bool errorOutput)
{
return ExecBlockingProcess
(
L"C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe",
L"-Command \"& { cd \"" + SHFileUtilities::GetExecDir().generic_wstring() + L"\";" + command + L"} \"",
output,
errorOutput
);
}
PROCESS_INFORMATION SHExecUtilties::execProcess(const std::wstring& path, const std::wstring& args, HANDLE outputWritePipe, HANDLE errorOutputWritePipe)
{
STARTUPINFOW startInfo;
PROCESS_INFORMATION procInfo;
ZeroMemory(&startInfo, sizeof(startInfo));
ZeroMemory(&procInfo, sizeof(procInfo));
startInfo.cb = sizeof(startInfo);
if (outputWritePipe)
{
startInfo.hStdOutput = outputWritePipe;
startInfo.dwFlags |= STARTF_USESTDHANDLES;
startInfo.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
}
if (errorOutputWritePipe)
{
startInfo.hStdError = errorOutputWritePipe;
startInfo.dwFlags |= STARTF_USESTDHANDLES;
startInfo.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
}
std::wstring argsWstr = args;
// Start Process
const auto SUCCESS = CreateProcess
(
path.data(), argsWstr.data(),
nullptr, nullptr, true, NULL, nullptr, nullptr,
&startInfo, &procInfo
);
// Error Check
if (!SUCCESS)
{
auto err = GetLastError();
std::ostringstream oss;
oss << "[SHExecUtilties] Failed to launch process. Error code: " << std::hex << err
<< " (" << SHStringUtilities::GetWin32ErrorMessage(err) << ")";
throw std::runtime_error(oss.str());
}
return procInfo;
}
std::pair<HANDLE, HANDLE> SHExecUtilties::createIoPipes()
{
SECURITY_ATTRIBUTES saAttr;
saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
saAttr.bInheritHandle = true;
saAttr.lpSecurityDescriptor = nullptr;
// First: Read | Second: Write
std::pair<HANDLE, HANDLE> output = { nullptr, nullptr };
if (CreatePipe(&output.first, &output.second, &saAttr, 0))
{
if (!SetHandleInformation(output.first, HANDLE_FLAG_INHERIT, 0))
{
SHLOG_ERROR("[SHExecUtilities] Failed to initialize pipe for process IO.");
CloseHandle(output.first);
CloseHandle(output.second);
output = { nullptr, nullptr };
}
}
else
{
SHLOG_ERROR("[SHExecUtilities] Failed to create pipe for process IO.");
}
return output;
}
void SHExecUtilties::readPipeData(HANDLE readPipe, HANDLE writePipe, std::wstring& output)
{
CloseHandle(writePipe);
LARGE_INTEGER stdoutSize = {};
while (true)
{
std::array<char, 256> buffer{};
DWORD bytesRead = 0;
const auto RESULT = ReadFile(readPipe, buffer.data(), buffer.size(), &bytesRead, nullptr);
if (!RESULT || bytesRead <= 0)
break;
output.insert(output.end(), buffer.data(), buffer.data() + bytesRead);
}
CloseHandle(readPipe);
}
}

View File

@ -0,0 +1,142 @@
/************************************************************************************//*!
\file SHExecUtilties.h
\author Tng Kah Wei, kahwei.tng, 390009620
\par email: kahwei.tng\@digipen.edu
\date Dec 21, 2022
\brief Contains the interface for SHExecUtilities class.
Copyright (C) 2022 DigiPen Institute of Technology.
Reproduction or disclosure of this file or its contents without the prior written consent
of DigiPen Institute of Technology is prohibited.
*//*************************************************************************************/
// STL Includes
#include <string>
// External Dependencies
#include <Windows.h>
namespace SHADE
{
/// <summary>
/// Stores the result of an execution of a process.
/// </summary>
struct SH_API ExecResult final
{
/// <summary>
/// Exit code of the process.
/// </summary>
DWORD Code;
/// <summary>
/// Stored text output from the process.
/// </summary>
std::wstring StdOutput;
/// <summary>
/// Stored error text output from the process.
/// </summary>
std::wstring StdErrOutput;
};
/// <summary>
/// Static class containing functions for executing external processes or commands.
/// </summary>
class SH_API SHExecUtilties final
{
public:
/*---------------------------------------------------------------------------------*/
/* Process Execution Functions */
/*---------------------------------------------------------------------------------*/
/// <summary>
/// Executes a process at the specified path with the specified arguments. This call
/// does not wait for the process to finish executing.
/// </summary>
/// <param name="path">Path to the processs to start.</param>
/// <param name="args">Arguments to pass to the process.</param>
/// <returns>Information about the started process.</returns>
/// <exception cref="std::runtime_error">
/// Thrown if failed to start the process.
/// </exception>
static PROCESS_INFORMATION ExecProcess(const std::wstring& path, const std::wstring& args);
/// <summary>
/// Executes a process at the specified path with the specified arguments and waits
/// for that process to finish executing.
/// </summary>
/// <param name="path">Path to the processs to start.</param>
/// <param name="args">Arguments to pass to the process.</param>
/// <param name="output">If true, stdout will be routed to return.</param>
/// <param name="errorOutput">If true, outstderr will be routed to return.</param>
/// <returns>Information about the process's execution.</returns>
/// <exception cref="std::runtime_error">
/// Thrown if failed to start the process.
/// </exception>
static ExecResult ExecBlockingProcess(const std::wstring& path, const std::wstring& args, bool output = false, bool errorOutput = false);
/*---------------------------------------------------------------------------------*/
/* Command Execution Functions */
/*---------------------------------------------------------------------------------*/
/// <summary>
/// Executes a specified command in cmd.
/// This call does not wait for the command to finish executing.
/// </summary>
/// <param name="command">Command to execute.</param>
/// <returns>
/// Information about the started cmd process that executes the command.
/// </returns>
/// <exception cref="std::runtime_error">
/// Thrown if failed to start the process.
/// </exception>
static PROCESS_INFORMATION ExecCommand(const std::wstring& command);
/// <summary>
/// Executes a specified command in cmd and waits for that process to finish
/// executing.
/// </summary>
/// <param name="command">Command to execute.</param>
/// <param name="output">If true, stdout will be routed to return.</param>
/// <param name="errorOutput">If true, outstderr will be routed to return.</param>
/// <returns>Information about the process's execution.</returns>
/// <exception cref="std::runtime_error">
/// Thrown if failed to start the process.
/// </exception>
static ExecResult ExecBlockingCommand(const std::wstring& command, bool output = false, bool errorOutput = false);
/*---------------------------------------------------------------------------------*/
/* PowerShell Execution Functions */
/*---------------------------------------------------------------------------------*/
/// <summary>
/// Executes a specified command in PowerShell.
/// This call does not wait for the command to finish executing.
/// </summary>
/// <param name="command">Command to execute.</param>
/// <returns>
/// Information about the started cmd process that executes the command.
/// </returns>
/// <exception cref="std::runtime_error">
/// Thrown if failed to start the process.
/// </exception>
static PROCESS_INFORMATION ExecPowerShellCommand(const std::wstring& command);
/// <summary>
/// Executes a specified command in PowerShell and waits for that process to finish
/// executing.
/// </summary>
/// <param name="command">Command to execute.</param>
/// <param name="output">If true, stdout will be routed to return.</param>
/// <param name="errorOutput">If true, outstderr will be routed to return.</param>
/// <returns>Information about the process's execution.</returns>
/// <exception cref="std::runtime_error">
/// Thrown if failed to start the process.
/// </exception>
static ExecResult ExecBlockingPowerShellCommand(const std::wstring& command, bool output, bool errorOutput);
private:
/*---------------------------------------------------------------------------------*/
/* Constructors/Destructors */
/*---------------------------------------------------------------------------------*/
SHExecUtilties() = delete;
/*---------------------------------------------------------------------------------*/
/* Helper Functions */
/*---------------------------------------------------------------------------------*/
static PROCESS_INFORMATION execProcess(const std::wstring& path, const std::wstring& args, HANDLE outputWritePipe, HANDLE errorOutputWritePipe);
static std::pair<HANDLE, HANDLE> createIoPipes();
static void readPipeData(HANDLE readPipe, HANDLE writePipe, std::wstring& output);
};
}

View File

@ -22,9 +22,21 @@ namespace SHADE
{ {
void SHFileUtilities::SetWorkDirToExecDir() void SHFileUtilities::SetWorkDirToExecDir()
{ {
TCHAR currentExecFilePath[MAX_PATH] = { '\0' }; std::filesystem::current_path(GetExecDir());
GetModuleFileName(nullptr, currentExecFilePath, MAX_PATH); }
PathRemoveFileSpec(currentExecFilePath);
std::filesystem::current_path(currentExecFilePath); std::filesystem::path SHFileUtilities::GetExecDir()
{
TCHAR currentExecFilePath[MAX_PATH] = { '\0' };
GetModuleFileName(nullptr, currentExecFilePath, MAX_PATH);
PathRemoveFileSpec(currentExecFilePath);
return std::filesystem::path(currentExecFilePath);
}
std::filesystem::path SHFileUtilities::GetExecPath()
{
TCHAR currentExecFilePath[MAX_PATH] = { '\0' };
GetModuleFileName(nullptr, currentExecFilePath, MAX_PATH);
return std::filesystem::path(currentExecFilePath);
} }
} }

View File

@ -15,24 +15,29 @@ of DigiPen Institute of Technology is prohibited.
namespace SHADE namespace SHADE
{ {
/*!************************************************************************************ /// <summary>
/// Static class that contains functions for working with files and directories.
\class SHFileUtilities /// </summary>
\brief
Static class that contains functions for working with files and directories.
**************************************************************************************/
class SH_API SHFileUtilities class SH_API SHFileUtilities
{ {
public: public:
/*!********************************************************************************** /*---------------------------------------------------------------------------------*/
/* Executable Directory Functions */
\brief /*---------------------------------------------------------------------------------*/
Sets the application's current working directory to the application executable's /// <summary>
directory. /// Sets the application's current working directory to the application executable's
/// directory.
************************************************************************************/ /// </summary>
static void SetWorkDirToExecDir(); static void SetWorkDirToExecDir();
/// <summary>
/// Retrieves the file path to the executable's directory.
/// </summary>
/// <returns>File path to the executable's directory.</returns>
static std::filesystem::path GetExecDir();
/// <summary>
/// Retrieves the file path to the executable.
/// </summary>
/// <returns>File path to the executable.</returns>
static std::filesystem::path GetExecPath();
}; };
} }

View File

@ -26,27 +26,29 @@ namespace SHADE
std::vector<std::wstring> SHStringUtilities::Split(const std::wstring& str, const wchar_t& delim) std::vector<std::wstring> SHStringUtilities::Split(const std::wstring& str, const wchar_t& delim)
{ {
return Split<wchar_t>(str, delim); return Split<wchar_t>(str, delim);
} }
std::string SHStringUtilities::WstrToStr(const std::wstring& wstr) std::string SHStringUtilities::WstrToStr(const std::wstring& wstr)
{ {
static std::vector<char> buffer; static std::vector<char> buffer;
const int STR_SIZE = WideCharToMultiByte(CP_UTF8, 0, wstr.data(), static_cast<int>(wstr.size()), nullptr, 0, nullptr, nullptr) + 1 /* Null Terminator */; buffer.clear();
buffer.resize(STR_SIZE); const int STR_SIZE = WideCharToMultiByte(CP_UTF8, 0, wstr.data(), static_cast<int>(wstr.size()), nullptr, 0, nullptr, nullptr) + 1 /* Null Terminator */;
WideCharToMultiByte(CP_UTF8, 0, wstr.data(), static_cast<int>(wstr.size()), buffer.data(), MAX_PATH, nullptr, nullptr); buffer.resize(STR_SIZE, '\0');
return std::string(buffer.data()); WideCharToMultiByte(CP_UTF8, 0, wstr.data(), static_cast<int>(wstr.size()), buffer.data(), MAX_PATH, nullptr, nullptr);
} return std::string(buffer.data());
std::wstring SHStringUtilities::StrToWstr(const std::string& str) }
{ std::wstring SHStringUtilities::StrToWstr(const std::string& str)
static std::vector<wchar_t> buffer; {
const int WSTR_SIZE = MultiByteToWideChar(CP_UTF8, 0, str.data(), static_cast<int>(str.size()), nullptr, 0) + 1 /* Null Terminator */; static std::vector<wchar_t> buffer;
buffer.resize(WSTR_SIZE); buffer.clear();
MultiByteToWideChar(CP_UTF8, 0, str.data(), static_cast<int>(str.size()), buffer.data(), WSTR_SIZE); const int WSTR_SIZE = MultiByteToWideChar(CP_UTF8, 0, str.data(), static_cast<int>(str.size()), nullptr, 0) + 1 /* Null Terminator */;
return std::wstring(buffer.data()); buffer.resize(WSTR_SIZE, '\0');
} MultiByteToWideChar(CP_UTF8, 0, str.data(), static_cast<int>(str.size()), buffer.data(), WSTR_SIZE);
return std::wstring(buffer.data());
}
std::string SHStringUtilities::GetWin32ErrorMessage(unsigned long errorCode) std::string SHStringUtilities::GetWin32ErrorMessage(unsigned long errorCode)
{ {
return std::system_category().message(errorCode); return std::system_category().message(errorCode);
} }
} }

View File

@ -44,6 +44,10 @@ namespace SHADE
return false; return false;
} }
bool Application::IsEditor::get()
{
return SHSystemManager::GetSystem<SHEditor>() != nullptr;
}
int Application::WindowWidth::get() int Application::WindowWidth::get()
{ {
return SHGraphicsSystemInterface::GetWindowWidth(); return SHGraphicsSystemInterface::GetWindowWidth();
@ -66,6 +70,9 @@ namespace SHADE
/*---------------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------------*/
void Application::Quit() void Application::Quit()
{ {
SHGraphicsSystemInterface::CloseWindow(); if (!IsEditor)
{
SHGraphicsSystemInterface::CloseWindow();
}
} }
} }

View File

@ -43,6 +43,13 @@ namespace SHADE
bool get(); bool get();
} }
/// <summary> /// <summary>
/// True if the engine is running in the editor.
/// </summary>
static property bool IsEditor
{
bool get();
}
/// <summary>
/// Retrieves the designated width of the current window. /// Retrieves the designated width of the current window.
/// </summary> /// </summary>
static property int WindowWidth static property int WindowWidth
@ -71,6 +78,7 @@ namespace SHADE
/*-----------------------------------------------------------------------------*/ /*-----------------------------------------------------------------------------*/
/// <summary> /// <summary>
/// Marks the application to stop at the end of the current frame. /// Marks the application to stop at the end of the current frame.
/// If running in the editor, this function does nothing.
/// </summary> /// </summary>
static void Quit(); static void Quit();
}; };