diff --git a/.gitignore b/.gitignore
index bce07a9..54d1f29 100644
--- a/.gitignore
+++ b/.gitignore
@@ -6,3 +6,5 @@
/Debug/
/libModuleManagerTest/Debug/
/Release/
+/CCEngineLoader/Debug/
+/CCEngineLoader/Release/
diff --git a/CCEngine/CCEngine.vcxproj b/CCEngine/CCEngine.vcxproj
index 6b75436..978d624 100644
--- a/CCEngine/CCEngine.vcxproj
+++ b/CCEngine/CCEngine.vcxproj
@@ -21,9 +21,11 @@
+
+
diff --git a/CCEngine/CCEngine.vcxproj.filters b/CCEngine/CCEngine.vcxproj.filters
index 251b858..68d0bdc 100644
--- a/CCEngine/CCEngine.vcxproj.filters
+++ b/CCEngine/CCEngine.vcxproj.filters
@@ -17,10 +17,16 @@
src
+
+ src
+
src
+
+ src
+
\ No newline at end of file
diff --git a/CCEngine/src/CCEngine.cpp b/CCEngine/src/CCEngine.cpp
index 3678448..7913f7f 100644
--- a/CCEngine/src/CCEngine.cpp
+++ b/CCEngine/src/CCEngine.cpp
@@ -1,9 +1,11 @@
+#include "CachedDownloader.h"
#include "SystemParamsProvider_win.h"
#include "HTTPClient.h"
#include "LicenseClient.h"
#include "CCServer.h"
#include "ModuleManager.h"
#include "JSONModuleDatabase.h"
+#include "ProcessUtils.h"
#include
@@ -11,6 +13,11 @@
//------------------------------------------------------------------------------
+namespace
+{
+ static const std::string updateCacheFolder = ".upd";
+}
+
int main(int argc, char* argv[])
{
auto appDir = boost::filesystem::system_complete(argv[0]).parent_path();
@@ -69,19 +76,41 @@ int main(int argc, char* argv[])
std::cin >> what;
if (what == 'y')
{
- ModuleManager moduleManager(appDir.string(), moduleDatabase, httpClient);
- for (const auto& update : moduleUpdates)
{
- restartRequired = restartRequired || (update.flag & static_cast(ModuleUpdateFlags::restartRequired));
- moduleManager.applyUpdate(update.moduleId, update);
- }
- }
- }
+ CachedDownloader cachedDownloader(appDir / updateCacheFolder, httpClient);
+ for (const auto& update : moduleUpdates)
+ {
+ cachedDownloader.download(update.updateUri);
+ }
- if (restartRequired)
- {
- std::cout << "Restart required" << std::endl;
- return 0;
+ std::ofstream os((appDir / updateCacheFolder / ".updates.json").native());
+ ::serialize(os, moduleUpdates);
+ }
+
+ std::ostringstream command;
+ command << (appDir / "CCEngineLoader.exe") << " " << getCurrentProcessId();
+
+ auto stdOutPipe = createAnonymousPipe(false, true);
+ auto updProcess = createPipedProcess(command.str(), nullptr, std::move(stdOutPipe));
+ while (1)
+ {
+ auto line = updProcess.readLine();
+ std::cout << line << std::endl;
+ if (line == "RestartRequired")
+ {
+ // Exit the current application to allow updater to continue
+ return 0;
+ }
+ }
+
+// ModuleManager moduleManager(appDir.string(), moduleDatabase, httpClient);
+// for (const auto& update : moduleUpdates)
+// {
+// restartRequired = restartRequired || (update.flag & static_cast(ModuleUpdateFlags::restartRequired));
+// moduleManager.applyUpdate(update.moduleId, update);
+// }
+
+ }
}
auto docRoot = appDir / "data";
diff --git a/CCEngine/src/ProcessUtils.cpp b/CCEngine/src/ProcessUtils.cpp
new file mode 100644
index 0000000..ab08ce9
--- /dev/null
+++ b/CCEngine/src/ProcessUtils.cpp
@@ -0,0 +1,294 @@
+#include "ProcessUtils.h"
+
+#include
+
+#include
+#include
+
+namespace detail
+{
+ class AnonymousPipeImpl
+ {
+ public:
+ AnonymousPipeImpl()
+ {
+ m_secAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
+ }
+
+ virtual ~AnonymousPipeImpl()
+ {
+ CloseHandle(m_readEnd);
+ CloseHandle(m_writeEnd);
+ }
+
+ public:
+ HANDLE& readEnd() { return m_readEnd; }
+ HANDLE& writeEnd() { return m_writeEnd; }
+ SECURITY_ATTRIBUTES& secAttr() { return m_secAttr; }
+
+ public:
+ size_t write(const char* data, size_t size)
+ {
+ DWORD written = 0;
+ auto success = WriteFile(m_writeEnd, data, size, &written, NULL);
+ if (!success)
+ {
+ throw std::runtime_error("Could not write pipe");
+ }
+ return static_cast(written);
+ }
+
+ size_t read(char* data, size_t size)
+ {
+ DWORD bytesRead = 0;
+ auto success = ReadFile(m_readEnd, data, size, &bytesRead, NULL);
+ if (!success)
+ {
+ throw std::runtime_error("Could not read pipe");
+ }
+ return static_cast(bytesRead);
+ }
+
+ private:
+ SECURITY_ATTRIBUTES m_secAttr = { 0 };
+ HANDLE m_readEnd = nullptr;
+ HANDLE m_writeEnd = nullptr;
+ };
+
+ std::unique_ptr createAnonymousPipe(bool readEndInheritable, bool writeEndInheritable)
+ {
+ auto pipe = std::make_unique();
+
+ bool inheritable = readEndInheritable || writeEndInheritable;
+ pipe->secAttr().bInheritHandle = inheritable;
+ pipe->secAttr().lpSecurityDescriptor = NULL;
+
+ if (!CreatePipe(&pipe->readEnd(), &pipe->writeEnd(), &pipe->secAttr(), 0))
+ {
+ throw std::runtime_error("Could not create pipe");
+ }
+
+ if (inheritable && !readEndInheritable)
+ {
+ if (!SetHandleInformation(pipe->readEnd(), HANDLE_FLAG_INHERIT, 0))
+ {
+ throw std::runtime_error("Setting pipe flags failed");
+ }
+ }
+
+ if (inheritable && !writeEndInheritable)
+ {
+ if (!SetHandleInformation(pipe->writeEnd(), HANDLE_FLAG_INHERIT, 0))
+ {
+ throw std::runtime_error("Setting pipe flags failed");
+ }
+ }
+
+ return pipe;
+ }
+}
+
+namespace detail
+{
+
+class ProcessHandleImpl
+{
+public:
+ explicit ProcessHandleImpl(HANDLE processHandle, HANDLE threadHandle = nullptr)
+ : m_processHandle(processHandle)
+ , m_threadHandle(threadHandle)
+ {}
+ ProcessHandleImpl(const ProcessHandleImpl&) = delete;
+ ProcessHandleImpl& operator= (const ProcessHandleImpl&) = delete;
+
+ virtual ~ProcessHandleImpl()
+ {
+ CloseHandle(m_processHandle);
+ CloseHandle(m_threadHandle);
+ }
+
+public:
+ bool waitForExit(const std::chrono::milliseconds& ms)
+ {
+ DWORD ret = WaitForSingleObject(m_processHandle, static_cast(ms.count()));
+ if (ret == WAIT_OBJECT_0)
+ {
+ return true;
+ }
+ if (ret == WAIT_TIMEOUT)
+ {
+ return false;
+ }
+ throw std::runtime_error("Wait for exit failed");
+ }
+
+ void waitForExit()
+ {
+ DWORD ret = WaitForSingleObject(m_processHandle, INFINITE);
+ if (ret != WAIT_OBJECT_0)
+ {
+ throw std::runtime_error("Inifinite wait for exit failed");
+ }
+ }
+
+ bool isRunning()
+ {
+ return !waitForExit(std::chrono::milliseconds(0));
+ }
+
+ int exitCode()
+ {
+ DWORD exitCode;
+ auto result = GetExitCodeProcess(m_processHandle, &exitCode);
+ if (!result)
+ {
+ throw std::runtime_error("Could not get exit code");
+ }
+ return exitCode;
+ }
+private:
+ HANDLE m_processHandle = nullptr;
+ HANDLE m_threadHandle = nullptr;
+};
+
+std::unique_ptr openProcess(DWORD processId)
+{
+ auto retVal = OpenProcess(SYNCHRONIZE, false, processId);
+ if (retVal == nullptr)
+ {
+ return nullptr;
+ }
+
+ return std::make_unique(retVal);
+}
+
+/** Create a child process that uses the previously created pipes for STDIN and STDOUT. */
+std::unique_ptr createPipedProcess(const std::string& commandLine, HANDLE childPipeReadEnd, HANDLE childPipeWriteEnd)
+{
+ // Set up members of the STARTUPINFO structure.
+ // This structure specifies the STDIN and STDOUT handles for redirection.
+ PROCESS_INFORMATION piProcInfo = { 0 };
+ STARTUPINFO siStartInfo = { 0 };
+ siStartInfo.cb = sizeof(STARTUPINFO);
+ siStartInfo.hStdError = childPipeWriteEnd;
+ siStartInfo.hStdOutput = childPipeWriteEnd;
+ siStartInfo.hStdInput = childPipeReadEnd;
+ siStartInfo.dwFlags |= STARTF_USESTDHANDLES;
+
+ // Create the child process.
+
+ bool success = CreateProcess(NULL,
+ (LPSTR)commandLine.c_str(), // command line
+ NULL, // process security attributes
+ NULL, // primary thread security attributes
+ TRUE, // handles are inherited
+ 0, // creation flags
+ NULL, // use parent's environment
+ NULL, // use parent's current directory
+ &siStartInfo, // STARTUPINFO pointer
+ &piProcInfo); // receives PROCESS_INFORMATION
+
+ // If an error occurs, exit the application.
+ if (!success)
+ {
+ throw std::runtime_error("Could not create process");
+ }
+
+ return std::make_unique(piProcInfo.hProcess, piProcInfo.hThread);
+}
+
+}
+
+ProcessHandle::ProcessHandle(std::unique_ptr initVal)
+ : m_impl(std::move(initVal))
+{}
+
+ProcessHandle::~ProcessHandle()
+{
+}
+
+bool ProcessHandle::waitForExit(const std::chrono::milliseconds& ms)
+{
+ return m_impl->waitForExit(ms);
+}
+
+void ProcessHandle::waitForExit()
+{
+ return m_impl->waitForExit();
+}
+
+int ProcessHandle::exitCode()
+{
+ return m_impl->exitCode();
+}
+
+AnonymousPipe::AnonymousPipe(std::unique_ptr initVal)
+ : m_impl(std::move(initVal))
+{
+}
+
+AnonymousPipe::~AnonymousPipe()
+{
+}
+
+size_t AnonymousPipe::write(const char* data, size_t size)
+{
+ return m_impl->write(data, size);
+}
+
+size_t AnonymousPipe::read(char* data, size_t size)
+{
+ return m_impl->read(data, size);
+}
+
+std::unique_ptr createAnonymousPipe(bool inheritableReadEnd, bool inheritableWriteEnd)
+{
+ return std::make_unique(detail::createAnonymousPipe(inheritableReadEnd, inheritableWriteEnd));
+}
+
+ProcessHandle openProcess(uint32_t processId)
+{
+ return { detail::openProcess(processId) };
+}
+
+PipedProcessHandle createPipedProcess(const std::string & commandLine, std::unique_ptr stdinPipe, std::unique_ptr stdoutPipe)
+{
+ auto hisWriteEnd = stdoutPipe ? stdoutPipe->_impl().writeEnd() : nullptr;
+ auto hisReadEnd = stdinPipe ? stdinPipe->_impl().readEnd() : nullptr;
+ auto processHandle = detail::createPipedProcess(commandLine, hisReadEnd, hisWriteEnd);
+
+ return {
+ std::make_unique(std::move(processHandle)),
+ std::move(stdinPipe),
+ std::move(stdoutPipe)};
+}
+
+void PipedProcessHandle::writeLine(const std::string& line)
+{
+ stdinPipe->write(line.data(), line.size());
+ std::ostringstream ss;
+ ss << std::endl;
+ auto tmpstr = ss.str();
+ stdinPipe->write(tmpstr.data(), tmpstr.size());
+}
+
+std::string PipedProcessHandle::readLine()
+{
+ std::ostringstream ss;
+ char buf[1] = { 0 };
+ stdoutPipe->read(buf, 1);
+ while (buf[0] != '\n')
+ {
+ if (buf[0] != '\r') {
+ ss.write(buf, 1);
+ }
+ stdoutPipe->read(buf, 1);
+ }
+
+ return ss.str();
+}
+
+unsigned long getCurrentProcessId()
+{
+ return static_cast(GetCurrentProcessId());
+}
diff --git a/CCEngine/src/ProcessUtils.h b/CCEngine/src/ProcessUtils.h
new file mode 100644
index 0000000..655c148
--- /dev/null
+++ b/CCEngine/src/ProcessUtils.h
@@ -0,0 +1,62 @@
+#pragma once
+
+#include
+#include
+#include
+
+namespace detail
+{
+ class AnonymousPipeImpl;
+ class ProcessHandleImpl;
+}
+
+class AnonymousPipe
+{
+public:
+ AnonymousPipe(std::unique_ptr initVal);
+ virtual ~AnonymousPipe();
+
+public:
+ size_t write(const char* data, size_t size);
+ size_t read(char *data, size_t size);
+
+ detail::AnonymousPipeImpl& _impl() { return (*m_impl); };
+private:
+ std::unique_ptr m_impl;
+};
+
+std::unique_ptr createAnonymousPipe(bool inheritableReadEnd, bool inheritableWriteEnd);
+
+class ProcessHandle
+{
+public:
+ ProcessHandle(std::unique_ptr initVal);
+ virtual ~ProcessHandle();
+
+public:
+ bool waitForExit(const std::chrono::milliseconds& ms);
+ void waitForExit();
+ int exitCode();
+
+private:
+ std::unique_ptr m_impl;
+};
+
+ProcessHandle openProcess(uint32_t processId);
+
+struct PipedProcessHandle
+{
+ void writeLine(const std::string& line);
+ std::string readLine();
+
+ std::unique_ptr processHandle;
+ std::unique_ptr stdinPipe;
+ std::unique_ptr stdoutPipe;
+};
+
+PipedProcessHandle createPipedProcess(
+ const std::string& commandLine,
+ std::unique_ptr stdinPipe,
+ std::unique_ptr stdoutPipe);
+
+unsigned long getCurrentProcessId();
\ No newline at end of file
diff --git a/CCEngineLoader/CCEngineLoader.cpp b/CCEngineLoader/CCEngineLoader.cpp
index b45833d..a7004c2 100644
Binary files a/CCEngineLoader/CCEngineLoader.cpp and b/CCEngineLoader/CCEngineLoader.cpp differ
diff --git a/CCEngineLoader/CCEngineLoader.vcxproj b/CCEngineLoader/CCEngineLoader.vcxproj
index 74f3478..d22b103 100644
--- a/CCEngineLoader/CCEngineLoader.vcxproj
+++ b/CCEngineLoader/CCEngineLoader.vcxproj
@@ -30,27 +30,27 @@
Application
true
v141
- Unicode
+ MultiByte
Application
false
v141
true
- Unicode
+ MultiByte
Application
true
v141
- Unicode
+ MultiByte
Application
false
v141
true
- Unicode
+ MultiByte
@@ -94,12 +94,13 @@
- Use
+ NotUsing
Level3
Disabled
true
WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)
true
+ $(ProjectDir)..\..\libLicenseClient\api;%(AdditionalIncludeDirectories)
Console
@@ -108,12 +109,13 @@
- Use
+ NotUsing
Level3
Disabled
true
_DEBUG;_CONSOLE;%(PreprocessorDefinitions)
true
+ $(ProjectDir)..\..\libLicenseClient\api;%(AdditionalIncludeDirectories)
Console
@@ -122,7 +124,7 @@
- Use
+ NotUsing
Level3
MaxSpeed
true
@@ -130,6 +132,7 @@
true
WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)
true
+ $(ProjectDir)..\..\libLicenseClient\api;%(AdditionalIncludeDirectories)
Console
@@ -140,7 +143,7 @@
- Use
+ NotUsing
Level3
MaxSpeed
true
@@ -148,6 +151,7 @@
true
NDEBUG;_CONSOLE;%(PreprocessorDefinitions)
true
+ $(ProjectDir)..\..\libLicenseClient\api;%(AdditionalIncludeDirectories)
Console
@@ -169,6 +173,11 @@
Create
+
+
+ {51345e59-83e5-4389-93a9-0131b40522b7}
+
+