Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
stenzek
GitHub Repository: stenzek/duckstation
Path: blob/master/src/updater/win32_main.cpp
4243 views
1
// SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin <[email protected]>
2
// SPDX-License-Identifier: 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
}
110
111