[impl] Delegate update job to child process
This commit is contained in:
parent
fe7c1bb368
commit
4eac37cfae
2
.gitignore
vendored
2
.gitignore
vendored
@ -6,3 +6,5 @@
|
||||
/Debug/
|
||||
/libModuleManagerTest/Debug/
|
||||
/Release/
|
||||
/CCEngineLoader/Debug/
|
||||
/CCEngineLoader/Release/
|
||||
|
||||
@ -21,9 +21,11 @@
|
||||
<ItemGroup>
|
||||
<ClCompile Include="src\CCEngine.cpp" />
|
||||
<ClCompile Include="src\CCServer.cpp" />
|
||||
<ClCompile Include="src\ProcessUtils.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="src\CCServer.h" />
|
||||
<ClInclude Include="src\ProcessUtils.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\libLicenseClient\libLicenseClient.vcxproj">
|
||||
|
||||
@ -17,10 +17,16 @@
|
||||
<ClCompile Include="src\CCServer.cpp">
|
||||
<Filter>src</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="src\ProcessUtils.cpp">
|
||||
<Filter>src</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="src\CCServer.h">
|
||||
<Filter>src</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="src\ProcessUtils.h">
|
||||
<Filter>src</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@ -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 <boost/filesystem.hpp>
|
||||
|
||||
@ -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<uint32_t>(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<uint32_t>(ModuleUpdateFlags::restartRequired));
|
||||
// moduleManager.applyUpdate(update.moduleId, update);
|
||||
// }
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
auto docRoot = appDir / "data";
|
||||
|
||||
294
CCEngine/src/ProcessUtils.cpp
Normal file
294
CCEngine/src/ProcessUtils.cpp
Normal file
@ -0,0 +1,294 @@
|
||||
#include "ProcessUtils.h"
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
|
||||
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<size_t>(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<size_t>(bytesRead);
|
||||
}
|
||||
|
||||
private:
|
||||
SECURITY_ATTRIBUTES m_secAttr = { 0 };
|
||||
HANDLE m_readEnd = nullptr;
|
||||
HANDLE m_writeEnd = nullptr;
|
||||
};
|
||||
|
||||
std::unique_ptr<AnonymousPipeImpl> createAnonymousPipe(bool readEndInheritable, bool writeEndInheritable)
|
||||
{
|
||||
auto pipe = std::make_unique<AnonymousPipeImpl>();
|
||||
|
||||
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<DWORD>(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<ProcessHandleImpl> openProcess(DWORD processId)
|
||||
{
|
||||
auto retVal = OpenProcess(SYNCHRONIZE, false, processId);
|
||||
if (retVal == nullptr)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return std::make_unique<ProcessHandleImpl>(retVal);
|
||||
}
|
||||
|
||||
/** Create a child process that uses the previously created pipes for STDIN and STDOUT. */
|
||||
std::unique_ptr<ProcessHandleImpl> 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<ProcessHandleImpl>(piProcInfo.hProcess, piProcInfo.hThread);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
ProcessHandle::ProcessHandle(std::unique_ptr<detail::ProcessHandleImpl> 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<detail::AnonymousPipeImpl> 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<AnonymousPipe> createAnonymousPipe(bool inheritableReadEnd, bool inheritableWriteEnd)
|
||||
{
|
||||
return std::make_unique<AnonymousPipe>(detail::createAnonymousPipe(inheritableReadEnd, inheritableWriteEnd));
|
||||
}
|
||||
|
||||
ProcessHandle openProcess(uint32_t processId)
|
||||
{
|
||||
return { detail::openProcess(processId) };
|
||||
}
|
||||
|
||||
PipedProcessHandle createPipedProcess(const std::string & commandLine, std::unique_ptr<AnonymousPipe> stdinPipe, std::unique_ptr<AnonymousPipe> 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<ProcessHandle>(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<unsigned long>(GetCurrentProcessId());
|
||||
}
|
||||
62
CCEngine/src/ProcessUtils.h
Normal file
62
CCEngine/src/ProcessUtils.h
Normal file
@ -0,0 +1,62 @@
|
||||
#pragma once
|
||||
|
||||
#include <chrono>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
namespace detail
|
||||
{
|
||||
class AnonymousPipeImpl;
|
||||
class ProcessHandleImpl;
|
||||
}
|
||||
|
||||
class AnonymousPipe
|
||||
{
|
||||
public:
|
||||
AnonymousPipe(std::unique_ptr<detail::AnonymousPipeImpl> 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<detail::AnonymousPipeImpl> m_impl;
|
||||
};
|
||||
|
||||
std::unique_ptr<AnonymousPipe> createAnonymousPipe(bool inheritableReadEnd, bool inheritableWriteEnd);
|
||||
|
||||
class ProcessHandle
|
||||
{
|
||||
public:
|
||||
ProcessHandle(std::unique_ptr<detail::ProcessHandleImpl> initVal);
|
||||
virtual ~ProcessHandle();
|
||||
|
||||
public:
|
||||
bool waitForExit(const std::chrono::milliseconds& ms);
|
||||
void waitForExit();
|
||||
int exitCode();
|
||||
|
||||
private:
|
||||
std::unique_ptr<detail::ProcessHandleImpl> m_impl;
|
||||
};
|
||||
|
||||
ProcessHandle openProcess(uint32_t processId);
|
||||
|
||||
struct PipedProcessHandle
|
||||
{
|
||||
void writeLine(const std::string& line);
|
||||
std::string readLine();
|
||||
|
||||
std::unique_ptr<ProcessHandle> processHandle;
|
||||
std::unique_ptr<AnonymousPipe> stdinPipe;
|
||||
std::unique_ptr<AnonymousPipe> stdoutPipe;
|
||||
};
|
||||
|
||||
PipedProcessHandle createPipedProcess(
|
||||
const std::string& commandLine,
|
||||
std::unique_ptr<AnonymousPipe> stdinPipe,
|
||||
std::unique_ptr<AnonymousPipe> stdoutPipe);
|
||||
|
||||
unsigned long getCurrentProcessId();
|
||||
Binary file not shown.
@ -30,27 +30,27 @@
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v141</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v141</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v141</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v141</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
@ -94,12 +94,13 @@
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<PrecompiledHeader>NotUsing</PrecompiledHeader>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<AdditionalIncludeDirectories>$(ProjectDir)..\..\libLicenseClient\api;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
@ -108,12 +109,13 @@
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<PrecompiledHeader>NotUsing</PrecompiledHeader>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<AdditionalIncludeDirectories>$(ProjectDir)..\..\libLicenseClient\api;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
@ -122,7 +124,7 @@
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<PrecompiledHeader>NotUsing</PrecompiledHeader>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
@ -130,6 +132,7 @@
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<AdditionalIncludeDirectories>$(ProjectDir)..\..\libLicenseClient\api;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
@ -140,7 +143,7 @@
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<PrecompiledHeader>NotUsing</PrecompiledHeader>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
@ -148,6 +151,7 @@
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<AdditionalIncludeDirectories>$(ProjectDir)..\..\libLicenseClient\api;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
@ -169,6 +173,11 @@
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\libLicenseClient\libLicenseClient.vcxproj">
|
||||
<Project>{51345e59-83e5-4389-93a9-0131b40522b7}</Project>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user