diff --git a/GlosSITarget/AppLauncher.cpp b/GlosSITarget/AppLauncher.cpp index af74b70..363e22b 100644 --- a/GlosSITarget/AppLauncher.cpp +++ b/GlosSITarget/AppLauncher.cpp @@ -20,6 +20,7 @@ limitations under the License. #ifdef _WIN32 #include #include +#include #endif #include "Settings.h" @@ -47,26 +48,30 @@ void AppLauncher::launchApp(const std::wstring& path, const std::wstring& args) void AppLauncher::update() { - if (process_check_clock_.getElapsedTime().asSeconds() > 1 && !logged_process_died_) { + if (process_check_clock_.getElapsedTime().asMilliseconds() > 250) { #ifdef _WIN32 - if (process_info.dwProcessId > 0) { - if (!IsProcessRunning(process_info.dwProcessId)) { - spdlog::info("Launched App with PID \"{}\" died", process_info.dwProcessId); - if (Settings::launch.closeOnExit) { - spdlog::info("Configured to close on exit. Shutting down.."); - shutdown_(); - } - logged_process_died_ = true; + if (launched_pid_ > 0) { + if (Settings::launch.waitForChildProcs) { + getChildPids(launched_pid_); } - } - if (uwp_pid_ > 0) { - if (!IsProcessRunning(uwp_pid_)) { - spdlog::info("Launched App with PID \"{}\" died", uwp_pid_); - if (Settings::launch.closeOnExit) { + if (!IsProcessRunning(launched_pid_)) { + spdlog::info("Launched App with PID \"{}\" died", launched_pid_); + if (Settings::launch.closeOnExit && !Settings::launch.waitForChildProcs) { spdlog::info("Configured to close on exit. Shutting down..."); shutdown_(); } - logged_process_died_ = true; + launched_pid_ = 0; + } + } + if (Settings::launch.waitForChildProcs) { + std::erase_if(child_pids_, [](auto pid) { + const auto running = IsProcessRunning(pid); + spdlog::info("Child process with PID \"{}\" died", pid); + return !running; + }); + if (Settings::launch.closeOnExit && child_pids_.empty() && launched_pid_ == 0) { + spdlog::info("Configured to close on all children exit. Shutting down..."); + shutdown_(); } } #endif @@ -83,6 +88,7 @@ void AppLauncher::close() } #endif } + #ifdef _WIN32 bool AppLauncher::IsProcessRunning(DWORD pid) { @@ -93,6 +99,21 @@ bool AppLauncher::IsProcessRunning(DWORD pid) CloseHandle(process); return ret == WAIT_TIMEOUT; } + +void AppLauncher::getChildPids(DWORD parent_pid) +{ + HANDLE hp = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); + PROCESSENTRY32 pe = {0}; + pe.dwSize = sizeof(PROCESSENTRY32); + if (Process32First(hp, &pe)) { + do { + if (pe.th32ParentProcessID == parent_pid) { + child_pids_.insert(pe.th32ProcessID); + } + } while (Process32Next(hp, &pe)); + } + CloseHandle(hp); +} #endif #ifdef _WIN32 @@ -147,6 +168,7 @@ void AppLauncher::launchWin32App(const std::wstring& path, const std::wstring& a &process_info)) { //spdlog::info(L"Started Program: \"{}\" in directory: \"{}\"", native_seps_path, launch_dir); spdlog::info(L"Started Program: \"{}\"", native_seps_path); + launched_pid_ = process_info.dwProcessId; } else { //spdlog::error(L"Couldn't start program: \"{}\" in directory: \"{}\"", native_seps_path, launch_dir); @@ -177,7 +199,7 @@ void AppLauncher::launchUWPApp(const LPCWSTR package_full_name, const std::wstri } // Launch the app - result = sp_app_activation_manager->ActivateApplication(package_full_name, args.empty() ? nullptr : args.data(), AO_NONE, &uwp_pid_); + result = sp_app_activation_manager->ActivateApplication(package_full_name, args.empty() ? nullptr : args.data(), AO_NONE, &launched_pid_); if (!SUCCEEDED(result)) { spdlog::error("ActivateApplication failed: Code {}", result); } else { diff --git a/GlosSITarget/AppLauncher.h b/GlosSITarget/AppLauncher.h index 657e503..7fcc568 100644 --- a/GlosSITarget/AppLauncher.h +++ b/GlosSITarget/AppLauncher.h @@ -21,6 +21,7 @@ limitations under the License. #endif #include #include +#include #include class AppLauncher { @@ -35,10 +36,10 @@ private: std::function shutdown_; sf::Clock process_check_clock_; - bool logged_process_died_ = false; #ifdef _WIN32 static bool IsProcessRunning(DWORD pid); + void getChildPids(DWORD parent_pid); // Valve also hooks "CreateProcess" // Unpatch that so that launched programs don't also get hooked... @@ -48,6 +49,7 @@ private: void launchUWPApp(LPCWSTR package_full_name, const std::wstring& args = L""); STARTUPINFO info{sizeof(info)}; PROCESS_INFORMATION process_info{}; - DWORD uwp_pid_ = 0; + DWORD launched_pid_ = 0; + std::unordered_set child_pids_; #endif }; diff --git a/GlosSITarget/Resource.rc b/GlosSITarget/Resource.rc index 0568210..8a537cf 100644 --- a/GlosSITarget/Resource.rc +++ b/GlosSITarget/Resource.rc @@ -51,8 +51,8 @@ END // VS_VERSION_INFO VERSIONINFO - FILEVERSION 0,0,2,003003750647 - PRODUCTVERSION 0,0,2,003003750647 + FILEVERSION 0,0,2,005001092093 + PRODUCTVERSION 0,0,2,005001092093 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -69,12 +69,12 @@ BEGIN BEGIN VALUE "CompanyName", "Peter Repukat - FlatspotSoftware" VALUE "FileDescription", "GlosSI - SteamTarget" - VALUE "FileVersion", "0.0.2.0-3-g375c647" + VALUE "FileVersion", "0.0.2.0-5-g1b92093" VALUE "InternalName", "GlosSITarget" VALUE "LegalCopyright", "Copyright (C) 2021 Peter Repukat - FlatspotSoftware" VALUE "OriginalFilename", "GlosSITarget.exe" VALUE "ProductName", "GlosSI" - VALUE "ProductVersion", "0.0.2.0-3-g375c647" + VALUE "ProductVersion", "0.0.2.0-5-g1b92093" END END BLOCK "VarFileInfo" @@ -631,6 +631,10 @@ END + + + + diff --git a/GlosSITarget/Settings.h b/GlosSITarget/Settings.h index c565f98..72aba06 100644 --- a/GlosSITarget/Settings.h +++ b/GlosSITarget/Settings.h @@ -29,6 +29,7 @@ inline struct Launch { std::wstring launchPath; std::wstring launchAppArgs; bool closeOnExit = true; + bool waitForChildProcs = false; bool isUWP = false; } launch; @@ -108,6 +109,7 @@ inline void Parse(std::string arg1) safeWStringParse(launchconf, "launchPath", launch.launchPath); safeWStringParse(launchconf, "launchAppArgs", launch.launchAppArgs); safeParseValue(launchconf, "closeOnExit", launch.closeOnExit); + safeParseValue(launchconf, "waitForChildProcs", launch.waitForChildProcs); } if (auto devconf = json["devices"]; devconf.is_object()) {