CCEngine/CCEngineLoader/UpdateWorker.cpp
2018-05-25 01:06:30 +02:00

133 lines
4.7 KiB
C++

#include "UpdateWorker.h"
#include "CachedDownloader.h"
#include "HTTPClient.h"
#include "JSONModuleDatabase.h"
#include "ModuleManager.h"
struct IUnknown; // Workaround for "combaseapi.h(229): error C2187: syntax error: 'identifier' was unexpected here" when using /permissive-
#include <windows.h>
#include <boost/filesystem.hpp>
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/json_parser.hpp>
#include <fstream>
namespace pt = boost::property_tree;
namespace fs = boost::filesystem;
namespace
{
static const std::string moduleDatabaseFolder = ".inst";
static const std::string updateCacheFolder = ".upd";
static const std::string updateTrashFolder = ".upd/.remove";
void waitUntilProcessTerminates(unsigned long processID)
{
HANDLE hProcess = OpenProcess(SYNCHRONIZE, true, processID);
if (NULL == hProcess)
{
WaitForSingleObject(hProcess, INFINITE);
}
}
void runApp(const std::string& cmdLine, bool waitForProcessToExit)
{
STARTUPINFO si;
PROCESS_INFORMATION pi;
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
ZeroMemory(&pi, sizeof(pi));
// Start the child process.
if (!CreateProcess(NULL, // No module name (use command line)
(LPSTR)cmdLine.c_str(), // Command line
NULL, // Process handle not inheritable
NULL, // Thread handle not inheritable
FALSE, // Set handle inheritance to FALSE
DETACHED_PROCESS, // No creation flags
NULL, // Use parent's environment block
NULL, // Use parent's starting directory
&si, // Pointer to STARTUPINFO structure
&pi) // Pointer to PROCESS_INFORMATION structure
)
{
printf("CreateProcess failed (%d).\n", GetLastError());
return;
}
if (waitForProcessToExit)
{
// Wait until child process exits.
WaitForSingleObject(pi.hProcess, INFINITE);
}
// Close process and thread handles.
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
}
std::vector<ModuleUpdate> loadPendingUpdates(const boost::filesystem::path& appDir)
{
std::vector<ModuleUpdate> result;
boost::filesystem::path path(appDir / updateCacheFolder / ".updates.json");
if (boost::filesystem::is_regular_file(path))
{
pt::ptree root;
pt::read_json(path.string(), root);
deserialize(root, result);
}
return result;
}
}
UpdateWorkerThread::UpdateWorkerThread(const boost::filesystem::path& appDir, unsigned parentProcessId)
: m_appDir(appDir)
, m_parentProcessId(parentProcessId)
{
qRegisterMetaType<UpdateStatus>();
}
void UpdateWorkerThread::run()
{
emit progressChanged(UpdateStatus::loadingUpdateDefinitions, QString("Loading update definitions..."));
std::vector<ModuleUpdate> moduleUpdates = loadPendingUpdates(m_appDir);
HTTPClient httpClient;
CachedDownloader cachedDownloader(m_appDir / updateCacheFolder, httpClient);
JSONModuleDatabase moduleDatabase((m_appDir / moduleDatabaseFolder).string());
ModuleManager moduleManager(m_appDir.string(), moduleDatabase, cachedDownloader, (m_appDir / updateTrashFolder).string());
bool isRestartRequired = false;
for (const auto& update : moduleUpdates)
{
bool restartRequired = update.flag & static_cast<uint32_t>(ModuleUpdateFlags::restartRequired);
isRestartRequired = isRestartRequired || restartRequired;
if (restartRequired && m_parentProcessId)
{
std::cout << "RestartRequired" << std::endl;
emit progressChanged(UpdateStatus::waitingForAppToExit, QString("Waiting for application to exit"));
waitUntilProcessTerminates(m_parentProcessId);
m_parentProcessId = 0;
}
emit progressChanged(UpdateStatus::installingUpdates, QString("Installing update %1...").arg(update.moduleId.c_str()));
moduleManager.applyUpdate(update.moduleId, update);
std::cout << "UpdateApplied " << update.moduleId << " " << update.version << std::endl;
}
std::cout << "Done" << std::endl;
if (isRestartRequired)
{
emit progressChanged(UpdateStatus::restartingApp, QString("Restarting application"));
runApp((m_appDir / "CCEngine.exe").string() + " --updated", false);
}
emit progressChanged(UpdateStatus::done, QString("Updated successfully"));
}