Created
August 27, 2024 22:43
-
-
Save fredemmott/4651df860b0ae0cc062020cdd503e5cf to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
diff --git a/.clang-format b/.clang-format | |
index 63d4edf..1cd1c88 100644 | |
--- a/.clang-format | |
+++ b/.clang-format | |
@@ -23,7 +23,10 @@ IncludeCategories: | |
- Regex: '^<Windows\.h>$' | |
Priority: 30 | |
- Regex: '^<Unknwn\.h>' | |
- Priority: 31 | |
+ Priority: 30 | |
+ - Regex: '^<Psapi\.h>' | |
+ Priority: 30 | |
+ SortPriority: 31 | |
- Regex: '^<winrt/.+' | |
Priority: 32 | |
SortPriority: 32 | |
diff --git a/src/windows/wWinMain.cpp b/src/windows/wWinMain.cpp | |
index 2b28c09..27e824f 100644 | |
--- a/src/windows/wWinMain.cpp | |
+++ b/src/windows/wWinMain.cpp | |
@@ -2,9 +2,136 @@ | |
// SPDX-License-Identifier: ISC | |
#include <Windows.h> | |
+#include <Psapi.h> | |
+ | |
+#include <winrt/base.h> | |
+ | |
+#include <filesystem> | |
+#include <format> | |
+#include <memory> | |
#include "GUI.hpp" | |
+static void CheckForUpdates() { | |
+ wchar_t exePathStr[32767]; | |
+ const auto exePathStrLength = GetModuleFileNameExW( | |
+ GetCurrentProcess(), nullptr, exePathStr, std::size(exePathStr)); | |
+ const std::filesystem::path thisExe { | |
+ std::wstring_view {exePathStr, exePathStrLength}}; | |
+ const auto directory = thisExe.parent_path(); | |
+ | |
+ constexpr auto base = L"fredemmott_OpenXR-API-Layers-GUI_Updater"; | |
+ const auto config = directory / std::format(L"{}.json", base); | |
+ const auto updater = directory / std::format(L"{}.exe", base); | |
+ | |
+ if (!std::filesystem::exists(config)) { | |
+ OutputDebugStringW( | |
+ std::format( | |
+ L"Skipping auto-update because `{}` does not exist", config.wstring()) | |
+ .c_str()); | |
+ return; | |
+ } | |
+ if (!std::filesystem::exists(updater)) { | |
+ OutputDebugStringW( | |
+ std::format( | |
+ L"Skipping auto-update because `{}` does not exist", updater.wstring()) | |
+ .c_str()); | |
+ return; | |
+ } | |
+ | |
+ // - We run elevated as the entire point of this program is to write to HKLM | |
+ // - We don't want to elevate the installer unnecessarily | |
+ // | |
+ // So, we get the shell window/process so we can use | |
+ // PROC_THREAD_ATTRIBUTE_PARENT_PROCESS to de-elevate the updater | |
+ | |
+ const auto shellWindow = GetShellWindow(); | |
+ DWORD shellPid {}; | |
+ GetWindowThreadProcessId(shellWindow, &shellPid); | |
+ winrt::handle shellProcess { | |
+ OpenProcess(PROCESS_CREATE_PROCESS | PROCESS_DUP_HANDLE, FALSE, shellPid)}; | |
+ | |
+ SIZE_T threadAttributesSize {}; | |
+ InitializeProcThreadAttributeList(nullptr, 1, 0, &threadAttributesSize); | |
+ auto threadAttributesBuffer = std::make_unique<char[]>(threadAttributesSize); | |
+ auto threadAttributes = reinterpret_cast<PPROC_THREAD_ATTRIBUTE_LIST>( | |
+ threadAttributesBuffer.get()); | |
+ winrt::check_bool(InitializeProcThreadAttributeList( | |
+ threadAttributes, 1, 0, &threadAttributesSize)); | |
+ | |
+ // Need a pointer to the value, not just the value | |
+ HANDLE shellProcessRaw {shellProcess.get()}; | |
+ winrt::check_bool(UpdateProcThreadAttribute( | |
+ threadAttributes, | |
+ 0, | |
+ PROC_THREAD_ATTRIBUTE_PARENT_PROCESS, | |
+ &shellProcessRaw, | |
+ sizeof(HANDLE), | |
+ nullptr, | |
+ nullptr)); | |
+ | |
+ // As we're specifying PROC_THREAD_ATTRIBUTE_PARENT_PROCESS as the shell, we | |
+ // need to duplicate the handle to the shell too, rather than the current | |
+ // process. | |
+ HANDLE thisProcessAsShell {}; | |
+ winrt::check_bool(DuplicateHandle( | |
+ GetCurrentProcess(), | |
+ GetCurrentProcess(), | |
+ shellProcessRaw, | |
+ &thisProcessAsShell, | |
+ PROCESS_TERMINATE | PROCESS_QUERY_LIMITED_INFORMATION, | |
+ /* inherit = */ true, | |
+ 0)); | |
+ | |
+ auto launch = [&]<class... Args>( | |
+ std::wformat_string<Args...> commandLineFmt, | |
+ Args&&... commandLineFmtArgs) { | |
+ auto commandLine | |
+ = std::format(commandLineFmt, std::forward<Args>(commandLineFmtArgs)...); | |
+ | |
+ STARTUPINFOEXW startupInfo { | |
+ .StartupInfo = {sizeof(STARTUPINFOEXW)}, | |
+ .lpAttributeList = threadAttributes, | |
+ }; | |
+ PROCESS_INFORMATION processInfo {}; | |
+ | |
+ winrt::check_bool(CreateProcessW( | |
+ updater.wstring().c_str(), | |
+ commandLine.data(), | |
+ nullptr, | |
+ nullptr, | |
+ /* inherit handles = */ true, | |
+ EXTENDED_STARTUPINFO_PRESENT, | |
+ nullptr, | |
+ directory.wstring().c_str(), | |
+ &startupInfo.StartupInfo, | |
+ &processInfo)); | |
+ WaitForSingleObject(processInfo.hProcess, INFINITE); | |
+ CloseHandle(processInfo.hProcess); | |
+ CloseHandle(processInfo.hThread); | |
+ }; | |
+ | |
+ // launch(L"{} --install --no-scheduled-task --no-autostart", | |
+ // updater.wstring()); | |
+ | |
+ launch( | |
+ L"{} --channel=live --terminate-process-before-update={}", | |
+ updater.wstring(), | |
+ reinterpret_cast<uintptr_t>(thisProcessAsShell)); | |
+ | |
+ // `thisProcessAsShell` is owned by the shell, so we need to duplicate it back | |
+ // with DUPLICATE_CLOSE_SOURCE, then close it again :D | |
+ winrt::handle thisProcess; | |
+ winrt::check_bool(DuplicateHandle( | |
+ shellProcessRaw, | |
+ thisProcessAsShell, | |
+ GetCurrentProcess(), | |
+ thisProcess.put(), | |
+ 0, | |
+ FALSE, | |
+ DUPLICATE_CLOSE_SOURCE)); | |
+} | |
+ | |
// Entrypoint for Windows | |
// | |
// See main.cpp for Linux and MacOS | |
@@ -13,6 +140,8 @@ int WINAPI wWinMain( | |
[[maybe_unused]] HINSTANCE hPrevInstance, | |
[[maybe_unused]] PWSTR pCmdLine, | |
[[maybe_unused]] int nCmdShow) { | |
+ CheckForUpdates(); | |
+ | |
FredEmmott::OpenXRLayers::GUI().Run(); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment