win32_main.cpp (3527B)
1 // SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin <stenzek@gmail.com> 2 // SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0) 3 4 #include "updater.h" 5 #include "win32_progress_callback.h" 6 7 #include "common/file_system.h" 8 #include "common/log.h" 9 #include "common/path.h" 10 #include "common/scoped_guard.h" 11 #include "common/string_util.h" 12 #include "common/windows_headers.h" 13 14 #include <combaseapi.h> 15 #include <shellapi.h> 16 17 static void WaitForProcessToExit(int process_id) 18 { 19 HANDLE hProcess = OpenProcess(SYNCHRONIZE, FALSE, process_id); 20 if (!hProcess) 21 return; 22 23 WaitForSingleObject(hProcess, INFINITE); 24 CloseHandle(hProcess); 25 } 26 27 int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nShowCmd) 28 { 29 Win32ProgressCallback progress; 30 31 const bool com_initialized = SUCCEEDED(CoInitializeEx(nullptr, COINIT_MULTITHREADED)); 32 const ScopedGuard com_guard = [com_initialized]() { 33 if (com_initialized) 34 CoUninitialize(); 35 }; 36 37 int argc = 0; 38 LPWSTR* argv = CommandLineToArgvW(lpCmdLine, &argc); 39 if (!argv || argc <= 0) 40 { 41 progress.ModalError("Failed to parse command line."); 42 return 1; 43 } 44 if (argc != 4) 45 { 46 progress.ModalError("Expected 4 arguments: parent process id, output directory, update zip, program to " 47 "launch.\n\nThis program is not intended to be run manually, please use the Qt frontend and " 48 "click Help->Check for Updates."); 49 LocalFree(argv); 50 return 1; 51 } 52 53 const int parent_process_id = StringUtil::FromChars<int>(StringUtil::WideStringToUTF8String(argv[0])).value_or(0); 54 std::string destination_directory = Path::ToNativePath(StringUtil::WideStringToUTF8String(argv[1])); 55 std::string staging_directory = Path::Combine(destination_directory, "UPDATE_STAGING"); 56 std::string zip_path = Path::ToNativePath(StringUtil::WideStringToUTF8String(argv[2])); 57 std::wstring program_to_launch(argv[3]); 58 LocalFree(argv); 59 60 if (parent_process_id <= 0 || destination_directory.empty() || zip_path.empty() || program_to_launch.empty()) 61 { 62 progress.ModalError("One or more parameters is empty."); 63 return 1; 64 } 65 66 Log::SetFileOutputParams(true, Path::Combine(destination_directory, "updater.log").c_str()); 67 68 progress.FormatStatusText("Waiting for parent process {} to exit...", parent_process_id); 69 WaitForProcessToExit(parent_process_id); 70 71 Updater updater(&progress); 72 if (!updater.Initialize(std::move(staging_directory), std::move(destination_directory))) 73 { 74 progress.ModalError("Failed to initialize updater."); 75 return 1; 76 } 77 78 if (!updater.OpenUpdateZip(zip_path.c_str())) 79 { 80 progress.FormatModalError("Could not open update zip '{}'. Update not installed.", zip_path); 81 return 1; 82 } 83 84 if (!updater.PrepareStagingDirectory()) 85 { 86 progress.ModalError("Failed to prepare staging directory. Update not installed."); 87 return 1; 88 } 89 90 if (!updater.StageUpdate()) 91 { 92 progress.ModalError("Failed to stage update. Update not installed."); 93 return 1; 94 } 95 96 if (!updater.CommitUpdate()) 97 { 98 progress.ModalError( 99 "Failed to commit update. Your installation may be corrupted, please re-download a fresh version from GitHub."); 100 return 1; 101 } 102 103 updater.CleanupStagingDirectory(); 104 updater.RemoveUpdateZip(); 105 106 progress.FormatInformation("Launching '{}'...", StringUtil::WideStringToUTF8String(program_to_launch)); 107 ShellExecuteW(nullptr, L"open", program_to_launch.c_str(), L"-updatecleanup", nullptr, SW_SHOWNORMAL); 108 return 0; 109 }