Add C++ to editorconfig & update formatting. No functional changes.

This commit is contained in:
Caelan Sayler
2021-09-01 16:03:27 +01:00
parent b9ab4a9336
commit 300f06cc82
23 changed files with 4060 additions and 3986 deletions

View File

@@ -123,3 +123,72 @@ insert_final_newline=false
[*.vb]
# Modifier preferences
visual_basic_preferred_modifier_order = Partial,Default,Private,Protected,Public,Friend,NotOverridable,Overridable,MustOverride,Overloads,Overrides,MustInherit,NotInheritable,Static,Shared,Shadows,ReadOnly,WriteOnly,Dim,Const,WithEvents,Widening,Narrowing,Custom,Async:suggestion
[*.{c++,cc,cpp,cppm,cxx,h,h++,hh,hpp,hxx,inl,ipp,ixx,tlh,tli}]
# Visual C++ Code Style settings
cpp_generate_documentation_comments = xml
# Visual C++ Formatting settings
indent_style = space
indent_size = 4
cpp_indent_braces = false
cpp_indent_multi_line_relative_to = innermost_parenthesis
cpp_indent_within_parentheses = indent
cpp_indent_preserve_within_parentheses = true
cpp_indent_case_contents = true
cpp_indent_case_labels = false
cpp_indent_case_contents_when_block = false
cpp_indent_lambda_braces_when_parameter = true
cpp_indent_goto_labels = one_left
cpp_indent_preprocessor = leftmost_column
cpp_indent_access_specifiers = false
cpp_indent_namespace_contents = true
cpp_indent_preserve_comments = false
cpp_new_line_before_open_brace_namespace = new_line
cpp_new_line_before_open_brace_type = new_line
cpp_new_line_before_open_brace_function = new_line
cpp_new_line_before_open_brace_block = same_line
cpp_new_line_before_open_brace_lambda = ignore
cpp_new_line_scope_braces_on_separate_lines = false
cpp_new_line_close_brace_same_line_empty_type = false
cpp_new_line_close_brace_same_line_empty_function = false
cpp_new_line_before_catch = true
cpp_new_line_before_else = true
cpp_new_line_before_while_in_do_while = false
cpp_space_before_function_open_parenthesis = remove
cpp_space_within_parameter_list_parentheses = false
cpp_space_between_empty_parameter_list_parentheses = false
cpp_space_after_keywords_in_control_flow_statements = true
cpp_space_within_control_flow_statement_parentheses = false
cpp_space_before_lambda_open_parenthesis = false
cpp_space_within_cast_parentheses = false
cpp_space_after_cast_close_parenthesis = false
cpp_space_within_expression_parentheses = false
cpp_space_before_block_open_brace = true
cpp_space_between_empty_braces = false
cpp_space_before_initializer_list_open_brace = false
cpp_space_within_initializer_list_braces = true
cpp_space_preserve_in_initializer_list = true
cpp_space_before_open_square_bracket = false
cpp_space_within_square_brackets = false
cpp_space_before_empty_square_brackets = false
cpp_space_between_empty_square_brackets = false
cpp_space_group_square_brackets = true
cpp_space_within_lambda_brackets = false
cpp_space_between_empty_lambda_brackets = false
cpp_space_before_comma = false
cpp_space_after_comma = true
cpp_space_remove_around_member_operators = true
cpp_space_before_inheritance_colon = true
cpp_space_before_constructor_colon = true
cpp_space_remove_before_semicolon = true
cpp_space_after_semicolon = false
cpp_space_remove_around_unary_operator = true
cpp_space_around_binary_operator = insert
cpp_space_around_assignment_operator = insert
cpp_space_pointer_reference_alignment = left
cpp_space_around_ternary_operator = insert
cpp_wrap_preserve_blocks = one_liners

View File

@@ -5,246 +5,244 @@
using std::wstring;
class ATL_NO_VTABLE CDownloadProgressCallback :
public CComObjectRoot,
public IBindStatusCallback
public CComObjectRoot,
public IBindStatusCallback
{
public:
CDownloadProgressCallback()
{
}
CDownloadProgressCallback()
{}
DECLARE_NOT_AGGREGATABLE(CDownloadProgressCallback)
DECLARE_NOT_AGGREGATABLE(CDownloadProgressCallback)
BEGIN_COM_MAP(CDownloadProgressCallback)
COM_INTERFACE_ENTRY(IBindStatusCallback)
END_COM_MAP()
DECLARE_PROTECT_FINAL_CONSTRUCT()
BEGIN_COM_MAP(CDownloadProgressCallback)
COM_INTERFACE_ENTRY(IBindStatusCallback)
END_COM_MAP()
HRESULT FinalConstruct() { return S_OK; }
DECLARE_PROTECT_FINAL_CONSTRUCT()
void FinalRelease()
{}
HRESULT FinalConstruct() { return S_OK; }
void SetProgressDialog(IProgressDialog* pd)
{
m_spProgressDialog = pd;
}
void FinalRelease()
{
}
STDMETHOD(OnProgress)(ULONG ulProgress, ULONG ulProgressMax, ULONG /*ulStatusCode*/, LPCWSTR /*szStatusText*/)
{
if (m_spProgressDialog != nullptr) {
if (m_spProgressDialog->HasUserCancelled()) {
return E_ABORT;
}
void SetProgressDialog(IProgressDialog* pd)
{
m_spProgressDialog = pd;
}
m_spProgressDialog->SetProgress(ulProgress, ulProgressMax);
}
STDMETHOD(OnProgress)(ULONG ulProgress, ULONG ulProgressMax, ULONG /*ulStatusCode*/, LPCWSTR /*szStatusText*/)
{
if (m_spProgressDialog != nullptr) {
if (m_spProgressDialog->HasUserCancelled()) {
return E_ABORT;
}
return S_OK;
}
m_spProgressDialog->SetProgress(ulProgress, ulProgressMax);
}
return S_OK;
}
STDMETHOD(OnStartBinding)(DWORD /*dwReserved*/, IBinding *pBinding) { return E_NOTIMPL; }
STDMETHOD(GetPriority)(LONG *pnPriority) { return E_NOTIMPL; }
STDMETHOD(OnLowResource)(DWORD /*reserved*/) { return E_NOTIMPL; }
STDMETHOD(OnStopBinding)(HRESULT /*hresult*/, LPCWSTR /*szError*/) { return E_NOTIMPL; }
STDMETHOD(GetBindInfo)(DWORD *pgrfBINDF, BINDINFO *pbindInfo) { return E_NOTIMPL; }
STDMETHOD(OnDataAvailable)(DWORD grfBSCF, DWORD dwSize, FORMATETC * /*pformatetc*/, STGMEDIUM *pstgmed) { return E_NOTIMPL; }
STDMETHOD(OnObjectAvailable)(REFIID /*riid*/, IUnknown * /*punk*/) { return E_NOTIMPL; }
STDMETHOD(OnStartBinding)(DWORD /*dwReserved*/, IBinding* pBinding) { return E_NOTIMPL; }
STDMETHOD(GetPriority)(LONG* pnPriority) { return E_NOTIMPL; }
STDMETHOD(OnLowResource)(DWORD /*reserved*/) { return E_NOTIMPL; }
STDMETHOD(OnStopBinding)(HRESULT /*hresult*/, LPCWSTR /*szError*/) { return E_NOTIMPL; }
STDMETHOD(GetBindInfo)(DWORD* pgrfBINDF, BINDINFO* pbindInfo) { return E_NOTIMPL; }
STDMETHOD(OnDataAvailable)(DWORD grfBSCF, DWORD dwSize, FORMATETC* /*pformatetc*/, STGMEDIUM* pstgmed) { return E_NOTIMPL; }
STDMETHOD(OnObjectAvailable)(REFIID /*riid*/, IUnknown* /*punk*/) { return E_NOTIMPL; }
private:
CComPtr<IProgressDialog> m_spProgressDialog;
CComPtr<IProgressDialog> m_spProgressDialog;
};
HRESULT CFxHelper::InstallDotnet(const RUNTIMEINFO* runtime, bool isQuiet)
{
auto runtimeName = wstring(runtime->friendlyName);
auto runtimeUrl = wstring(runtime->installerUrl);
auto runtimeName = wstring(runtime->friendlyName);
auto runtimeUrl = wstring(runtime->installerUrl);
if (!isQuiet) {
CTaskDialog dlg;
TASKDIALOG_BUTTON buttons[] = {
{ 1, L"Install", },
{ 2, L"Cancel", },
};
if (!isQuiet) {
CTaskDialog dlg;
TASKDIALOG_BUTTON buttons[] = {
{ 1, L"Install", },
{ 2, L"Cancel", },
};
wstring txtInstruction = L"Install " + runtimeName;
wstring txtMain = L"This application requires " + runtimeName + L". Click the Install button to get started.";
wstring txtExpanded = L"Clicking install will download the latest version of this operating system component from Microsoft and install it on your PC. Setup can not continue until this is complete.";
wstring txtInstruction = L"Install " + runtimeName;
wstring txtMain = L"This application requires " + runtimeName + L". Click the Install button to get started.";
wstring txtExpanded = L"Clicking install will download the latest version of this operating system component from Microsoft and install it on your PC. Setup can not continue until this is complete.";
dlg.SetButtons(buttons, 2);
dlg.SetMainInstructionText(txtInstruction.c_str());
dlg.SetContentText(txtMain.c_str());
dlg.SetMainIcon(TD_INFORMATION_ICON);
dlg.SetButtons(buttons, 2);
dlg.SetMainInstructionText(txtInstruction.c_str());
dlg.SetContentText(txtMain.c_str());
dlg.SetMainIcon(TD_INFORMATION_ICON);
dlg.SetExpandedInformationText(txtExpanded.c_str());
dlg.SetExpandedInformationText(txtExpanded.c_str());
int nButton;
if (FAILED(dlg.DoModal(::GetActiveWindow(), &nButton)) || nButton != 1) {
return S_FALSE;
}
}
int nButton;
if (FAILED(dlg.DoModal(::GetActiveWindow(), &nButton)) || nButton != 1) {
return S_FALSE;
}
}
HRESULT hr = E_FAIL;
WCHAR szFinalTempFileName[_MAX_PATH] = L"";
CComPtr<IBindStatusCallback> bscb;
CComPtr<IProgressDialog> pd;
SHELLEXECUTEINFO execInfo = { sizeof(execInfo), };
HRESULT hr = E_FAIL;
WCHAR szFinalTempFileName[_MAX_PATH] = L"";
CComPtr<IBindStatusCallback> bscb;
CComPtr<IProgressDialog> pd;
SHELLEXECUTEINFO execInfo = { sizeof(execInfo), };
WCHAR szTempPath[_MAX_PATH];
DWORD dwTempPathResult = GetTempPath(_MAX_PATH, szTempPath);
WCHAR szTempPath[_MAX_PATH];
DWORD dwTempPathResult = GetTempPath(_MAX_PATH, szTempPath);
if (dwTempPathResult == 0) {
hr = AtlHresultFromLastError();
goto out;
} else if (dwTempPathResult > _MAX_PATH) {
hr = DISP_E_BUFFERTOOSMALL;
goto out;
}
if (dwTempPathResult == 0) {
hr = AtlHresultFromLastError();
goto out;
}
else if (dwTempPathResult > _MAX_PATH) {
hr = DISP_E_BUFFERTOOSMALL;
goto out;
}
WCHAR szTempFileName[_MAX_PATH];
if (!GetTempFileName(szTempPath, L"NDP", 0, szTempFileName)) {
hr = AtlHresultFromLastError();
goto out;
}
WCHAR szTempFileName[_MAX_PATH];
if (!GetTempFileName(szTempPath, L"NDP", 0, szTempFileName)) {
hr = AtlHresultFromLastError();
goto out;
}
szTempFileName[_countof(szTempFileName) - 1] = L'\0';
if (wcscpy_s(szFinalTempFileName, _countof(szFinalTempFileName), szTempFileName) != 0) {
hr = E_FAIL;
goto out;
}
szTempFileName[_countof(szTempFileName) - 1] = L'\0';
if (wcscpy_s(szFinalTempFileName, _countof(szFinalTempFileName), szTempFileName) != 0) {
hr = E_FAIL;
goto out;
}
WCHAR* pLastDot = wcsrchr(szFinalTempFileName, L'.');
if (pLastDot == nullptr) {
if (wcscat_s(szFinalTempFileName, _countof(szFinalTempFileName), L".exe") != 0) {
hr = E_FAIL;
goto out;
}
} else {
if (wcscpy_s(pLastDot, _countof(szFinalTempFileName) - (pLastDot - szFinalTempFileName), L".exe") != 0) {
hr = E_FAIL;
goto out;
}
}
WCHAR* pLastDot = wcsrchr(szFinalTempFileName, L'.');
if (pLastDot == nullptr) {
if (wcscat_s(szFinalTempFileName, _countof(szFinalTempFileName), L".exe") != 0) {
hr = E_FAIL;
goto out;
}
}
else {
if (wcscpy_s(pLastDot, _countof(szFinalTempFileName) - (pLastDot - szFinalTempFileName), L".exe") != 0) {
hr = E_FAIL;
goto out;
}
}
if (!MoveFile(szTempFileName, szFinalTempFileName)) {
hr = AtlHresultFromLastError();
goto out;
}
if (!MoveFile(szTempFileName, szFinalTempFileName)) {
hr = AtlHresultFromLastError();
goto out;
}
if (!isQuiet) {
pd.CoCreateInstance(CLSID_ProgressDialog);
if (!isQuiet) {
pd.CoCreateInstance(CLSID_ProgressDialog);
if (pd != nullptr) {
pd->SetTitle(L"Downloading");
pd->SetLine(1, L"Downloading the .NET installer", FALSE, nullptr);
pd->StartProgressDialog(nullptr, nullptr, 0, nullptr);
if (pd != nullptr) {
pd->SetTitle(L"Downloading");
pd->SetLine(1, L"Downloading the .NET installer", FALSE, nullptr);
pd->StartProgressDialog(nullptr, nullptr, 0, nullptr);
CComObject<CDownloadProgressCallback>* bscbObj = nullptr;
if (SUCCEEDED(CComObject<CDownloadProgressCallback>::CreateInstance(&bscbObj))) {
bscbObj->SetProgressDialog(pd);
bscb = bscbObj;
}
}
}
CComObject<CDownloadProgressCallback>* bscbObj = nullptr;
if (SUCCEEDED(CComObject<CDownloadProgressCallback>::CreateInstance(&bscbObj))) {
bscbObj->SetProgressDialog(pd);
bscb = bscbObj;
}
}
}
hr = URLDownloadToFile(nullptr, runtimeUrl.c_str(), szFinalTempFileName, 0, bscb);
if (pd != nullptr) {
pd->StopProgressDialog();
}
if (hr != S_OK) {
goto out;
}
hr = URLDownloadToFile(nullptr, runtimeUrl.c_str(), szFinalTempFileName, 0, bscb);
if (pd != nullptr) {
pd->StopProgressDialog();
}
if (hr != S_OK) {
goto out;
}
execInfo.fMask = SEE_MASK_NOCLOSEPROCESS;
execInfo.lpVerb = L"open";
execInfo.lpFile = szFinalTempFileName;
execInfo.fMask = SEE_MASK_NOCLOSEPROCESS;
execInfo.lpVerb = L"open";
execInfo.lpFile = szFinalTempFileName;
if (isQuiet) {
execInfo.lpParameters = L"/q /norestart";
}
else {
execInfo.lpParameters = L"/passive /norestart /showrmui";
}
if (isQuiet) {
execInfo.lpParameters = L"/q /norestart";
}
else {
execInfo.lpParameters = L"/passive /norestart /showrmui";
}
execInfo.nShow = SW_SHOW;
if (!ShellExecuteEx(&execInfo)) {
hr = AtlHresultFromLastError();
goto out;
}
execInfo.nShow = SW_SHOW;
if (!ShellExecuteEx(&execInfo)) {
hr = AtlHresultFromLastError();
goto out;
}
WaitForSingleObject(execInfo.hProcess, INFINITE);
WaitForSingleObject(execInfo.hProcess, INFINITE);
DWORD exitCode;
if (!GetExitCodeProcess(execInfo.hProcess, &exitCode)) {
hr = AtlHresultFromLastError();
goto out;
}
DWORD exitCode;
if (!GetExitCodeProcess(execInfo.hProcess, &exitCode)) {
hr = AtlHresultFromLastError();
goto out;
}
if (exitCode == 1641 || exitCode == 3010) {
// The framework installer wants a reboot before we can continue
// See https://msdn.microsoft.com/en-us/library/ee942965%28v=vs.110%29.aspx
// hr = HandleRebootRequirement(isQuiet);
// Exit as a failure, so that setup doesn't carry on now
hr = ERROR_SUCCESS_REBOOT_REQUIRED;
}
else {
hr = exitCode != 0 ? E_FAIL : S_OK;
}
if (exitCode == 1641 || exitCode == 3010) {
// The framework installer wants a reboot before we can continue
// See https://msdn.microsoft.com/en-us/library/ee942965%28v=vs.110%29.aspx
// hr = HandleRebootRequirement(isQuiet);
// Exit as a failure, so that setup doesn't carry on now
hr = ERROR_SUCCESS_REBOOT_REQUIRED;
}
else {
hr = exitCode != 0 ? E_FAIL : S_OK;
}
out:
if (execInfo.hProcess != NULL && execInfo.hProcess != INVALID_HANDLE_VALUE) {
CloseHandle(execInfo.hProcess);
}
if (execInfo.hProcess != NULL && execInfo.hProcess != INVALID_HANDLE_VALUE) {
CloseHandle(execInfo.hProcess);
}
if (*szFinalTempFileName != L'\0') {
DeleteFile(szFinalTempFileName);
}
if (*szFinalTempFileName != L'\0') {
DeleteFile(szFinalTempFileName);
}
return hr;
return hr;
}
// Deal with the aftermath of the framework installer telling us that we need to reboot
HRESULT CFxHelper::HandleRebootRequirement(bool isQuiet)
{
if (isQuiet) {
// Don't silently reboot - just error-out
fprintf_s(stderr, "A reboot is required following .NET installation - reboot then run installer again.\n");
return E_FAIL;
}
if (isQuiet) {
// Don't silently reboot - just error-out
fprintf_s(stderr, "A reboot is required following .NET installation - reboot then run installer again.\n");
return E_FAIL;
}
CTaskDialog dlg;
TASKDIALOG_BUTTON buttons[] = {
{ 1, L"Restart Now", },
{ 2, L"Cancel", },
};
CTaskDialog dlg;
TASKDIALOG_BUTTON buttons[] = {
{ 1, L"Restart Now", },
{ 2, L"Cancel", },
};
dlg.SetButtons(buttons, 2);
dlg.SetMainInstructionText(L"Restart System");
dlg.SetContentText(L"To finish installing the .NET Framework, the system now needs to restart. The installation will finish after you restart and log-in again.");
dlg.SetMainIcon(TD_INFORMATION_ICON);
dlg.SetButtons(buttons, 2);
dlg.SetMainInstructionText(L"Restart System");
dlg.SetContentText(L"To finish installing the .NET Framework, the system now needs to restart. The installation will finish after you restart and log-in again.");
dlg.SetMainIcon(TD_INFORMATION_ICON);
dlg.SetExpandedInformationText(L"If you click 'Cancel', you'll need to re-run this setup program yourself, after restarting your system.");
dlg.SetExpandedInformationText(L"If you click 'Cancel', you'll need to re-run this setup program yourself, after restarting your system.");
int nButton;
if (FAILED(dlg.DoModal(::GetActiveWindow(), &nButton)) || nButton != 1) {
return S_FALSE;
}
int nButton;
if (FAILED(dlg.DoModal(::GetActiveWindow(), &nButton)) || nButton != 1) {
return S_FALSE;
}
// We need to set up a runonce entry to restart this installer once the reboot has happened
if (!WriteRunOnceEntry()) {
return E_FAIL;
}
// We need to set up a runonce entry to restart this installer once the reboot has happened
if (!WriteRunOnceEntry()) {
return E_FAIL;
}
// And now, reboot
if (!RebootSystem()) {
return E_FAIL;
}
// And now, reboot
if (!RebootSystem()) {
return E_FAIL;
}
// About to reboot, but just in case...
return S_FALSE;
// About to reboot, but just in case...
return S_FALSE;
}
//
@@ -253,44 +251,44 @@ HRESULT CFxHelper::HandleRebootRequirement(bool isQuiet)
//
bool CFxHelper::WriteRunOnceEntry()
{
ATL::CRegKey key;
ATL::CRegKey key;
if (key.Open(HKEY_CURRENT_USER, L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\RunOnce", KEY_WRITE) != ERROR_SUCCESS) {
return false;
}
if (key.Open(HKEY_CURRENT_USER, L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\RunOnce", KEY_WRITE) != ERROR_SUCCESS) {
return false;
}
TCHAR exePath[MAX_PATH];
GetModuleFileName(NULL, exePath, MAX_PATH);
TCHAR exePath[MAX_PATH];
GetModuleFileName(NULL, exePath, MAX_PATH);
if (key.SetStringValue(L"SquirrelInstall", exePath) != ERROR_SUCCESS) {
return false;
}
if (key.SetStringValue(L"SquirrelInstall", exePath) != ERROR_SUCCESS) {
return false;
}
return true;
return true;
}
bool CFxHelper::RebootSystem()
{
// First we need to enable the SE_SHUTDOWN_NAME privilege
LUID luid;
if (!LookupPrivilegeValue(L"", SE_SHUTDOWN_NAME, &luid)) {
return false;
}
// First we need to enable the SE_SHUTDOWN_NAME privilege
LUID luid;
if (!LookupPrivilegeValue(L"", SE_SHUTDOWN_NAME, &luid)) {
return false;
}
HANDLE hToken = NULL;
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken)) {
return false;
}
HANDLE hToken = NULL;
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken)) {
return false;
}
TOKEN_PRIVILEGES tp;
tp.PrivilegeCount = 1;
tp.Privileges[0].Luid = luid;
tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
if (!AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(tp), NULL, 0)) {
CloseHandle(hToken);
return false;
}
TOKEN_PRIVILEGES tp;
tp.PrivilegeCount = 1;
tp.Privileges[0].Luid = luid;
tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
if (!AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(tp), NULL, 0)) {
CloseHandle(hToken);
return false;
}
// Now we have that privilege, we can ask Windows to restart
return ExitWindowsEx(EWX_REBOOT, 0) != 0;
// Now we have that privilege, we can ask Windows to restart
return ExitWindowsEx(EWX_REBOOT, 0) != 0;
}

View File

@@ -3,10 +3,12 @@
class CFxHelper
{
public:
static HRESULT InstallDotnet(const RUNTIMEINFO* runtime, bool isQuiet);
static HRESULT HandleRebootRequirement(bool isQuiet);
static HRESULT InstallDotnet(const RUNTIMEINFO* runtime, bool isQuiet);
static HRESULT HandleRebootRequirement(bool isQuiet);
private:
static bool WriteRunOnceEntry();
static bool RebootSystem();
static bool WriteRunOnceEntry();
static bool RebootSystem();
};

View File

@@ -12,245 +12,226 @@ void ASSERT(void* obj) {}
// https://stackoverflow.com/a/66238748/184746
IStream* LoadImageFromResource(const wchar_t* resid, const wchar_t* restype)
{
IStream* pStream = nullptr;
HGLOBAL hGlobal = nullptr;
IStream* pStream = nullptr;
HGLOBAL hGlobal = nullptr;
HINSTANCE hInst = GetModuleHandle(NULL);
HRSRC hrsrc = FindResourceW(hInst, resid, restype); // get the handle to the resource
if (hrsrc)
{
DWORD dwResourceSize = SizeofResource(hInst, hrsrc);
if (dwResourceSize > 0)
{
HGLOBAL hGlobalResource = LoadResource(hInst, hrsrc); // load it
if (hGlobalResource)
{
void* imagebytes = LockResource(hGlobalResource); // get a pointer to the file bytes
HINSTANCE hInst = GetModuleHandle(NULL);
HRSRC hrsrc = FindResourceW(hInst, resid, restype); // get the handle to the resource
if (hrsrc) {
DWORD dwResourceSize = SizeofResource(hInst, hrsrc);
if (dwResourceSize > 0) {
HGLOBAL hGlobalResource = LoadResource(hInst, hrsrc); // load it
if (hGlobalResource) {
void* imagebytes = LockResource(hGlobalResource); // get a pointer to the file bytes
// copy image bytes into a real hglobal memory handle
hGlobal = ::GlobalAlloc(GHND, dwResourceSize);
if (hGlobal)
{
void* pBuffer = ::GlobalLock(hGlobal);
if (pBuffer)
{
memcpy(pBuffer, imagebytes, dwResourceSize);
HRESULT hr = CreateStreamOnHGlobal(hGlobal, TRUE, &pStream);
if (SUCCEEDED(hr))
{
// pStream now owns the global handle and will invoke GlobalFree on release
hGlobal = nullptr;
}
}
}
}
}
}
// copy image bytes into a real hglobal memory handle
hGlobal = ::GlobalAlloc(GHND, dwResourceSize);
if (hGlobal) {
void* pBuffer = ::GlobalLock(hGlobal);
if (pBuffer) {
memcpy(pBuffer, imagebytes, dwResourceSize);
HRESULT hr = CreateStreamOnHGlobal(hGlobal, TRUE, &pStream);
if (SUCCEEDED(hr)) {
// pStream now owns the global handle and will invoke GlobalFree on release
hGlobal = nullptr;
}
}
}
}
}
}
if (hGlobal)
{
GlobalFree(hGlobal);
hGlobal = nullptr;
}
if (hGlobal) {
GlobalFree(hGlobal);
hGlobal = nullptr;
}
return pStream;
return pStream;
}
ImageEx::ImageEx(const wchar_t* resid, const wchar_t* restype)
{
Initialize();
Initialize();
auto stream = LoadImageFromResource(resid, restype);
if (stream)
{
nativeImage = NULL;
lastResult = DllExports::GdipLoadImageFromStreamICM(stream, &nativeImage);
m_bIsInitialized = true;
TestForAnimatedGIF();
stream->Release();
}
auto stream = LoadImageFromResource(resid, restype);
if (stream) {
nativeImage = NULL;
lastResult = DllExports::GdipLoadImageFromStreamICM(stream, &nativeImage);
m_bIsInitialized = true;
TestForAnimatedGIF();
stream->Release();
}
}
ImageEx::~ImageEx()
{
Destroy();
Destroy();
}
bool ImageEx::InitAnimation(HWND hWnd, CPoint pt)
{
m_hWnd = hWnd;
m_pt = pt;
m_hWnd = hWnd;
m_pt = pt;
if (!m_bIsInitialized) {
TRACE(_T("GIF not initialized\n"));
return false;
};
if (!m_bIsInitialized)
{
TRACE(_T("GIF not initialized\n"));
return false;
};
if (IsAnimatedGIF()) {
if (m_hThread == NULL) {
if (IsAnimatedGIF())
{
if (m_hThread == NULL)
{
unsigned int nTID = 0;
unsigned int nTID = 0;
m_hThread = (HANDLE)_beginthreadex(NULL, 0, _ThreadAnimationProc, this, CREATE_SUSPENDED, &nTID);
m_hThread = (HANDLE)_beginthreadex(NULL, 0, _ThreadAnimationProc, this, CREATE_SUSPENDED, &nTID);
if (!m_hThread) {
TRACE(_T("Couldn't start a GIF animation thread\n"));
return true;
}
else
ResumeThread(m_hThread);
}
}
if (!m_hThread)
{
TRACE(_T("Couldn't start a GIF animation thread\n"));
return true;
}
else
ResumeThread(m_hThread);
}
}
return false;
return false;
}
CSize ImageEx::GetSize()
{
return CSize(GetWidth(), GetHeight());
return CSize(GetWidth(), GetHeight());
}
bool ImageEx::TestForAnimatedGIF()
{
UINT count = 0;
count = GetFrameDimensionsCount();
GUID* pDimensionIDs = new GUID[count];
UINT count = 0;
count = GetFrameDimensionsCount();
GUID* pDimensionIDs = new GUID[count];
// Get the list of frame dimensions from the Image object.
GetFrameDimensionsList(pDimensionIDs, count);
// Get the list of frame dimensions from the Image object.
GetFrameDimensionsList(pDimensionIDs, count);
// Get the number of frames in the first dimension.
m_nFrameCount = GetFrameCount(&pDimensionIDs[0]);
// Get the number of frames in the first dimension.
m_nFrameCount = GetFrameCount(&pDimensionIDs[0]);
// Assume that the image has a property item of type PropertyItemEquipMake.
// Get the size of that property item.
int nSize = GetPropertyItemSize(PropertyTagFrameDelay);
// Assume that the image has a property item of type PropertyItemEquipMake.
// Get the size of that property item.
int nSize = GetPropertyItemSize(PropertyTagFrameDelay);
// Allocate a buffer to receive the property item.
m_pPropertyItem = (PropertyItem*)malloc(nSize);
// Allocate a buffer to receive the property item.
m_pPropertyItem = (PropertyItem*)malloc(nSize);
GetPropertyItem(PropertyTagFrameDelay, nSize, m_pPropertyItem);
GetPropertyItem(PropertyTagFrameDelay, nSize, m_pPropertyItem);
delete pDimensionIDs;
delete pDimensionIDs;
return m_nFrameCount > 1;
return m_nFrameCount > 1;
}
void ImageEx::Initialize()
{
m_nFramePosition = 0;
m_nFrameCount = 0;
lastResult = InvalidParameter;
m_hThread = NULL;
m_bIsInitialized = false;
m_pPropertyItem = NULL;
m_nFramePosition = 0;
m_nFrameCount = 0;
lastResult = InvalidParameter;
m_hThread = NULL;
m_bIsInitialized = false;
m_pPropertyItem = NULL;
#ifdef INDIGO_CTRL_PROJECT
m_hInst = _Module.GetResourceInstance();
m_hInst = _Module.GetResourceInstance();
#else
m_hInst = GetModuleHandle(NULL);// AfxGetResourceHandle();
m_hInst = GetModuleHandle(NULL);// AfxGetResourceHandle();
#endif
m_bPause = false;
m_hExitEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
m_hPause = CreateEvent(NULL, TRUE, TRUE, NULL);
m_bPause = false;
m_hExitEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
m_hPause = CreateEvent(NULL, TRUE, TRUE, NULL);
}
UINT WINAPI ImageEx::_ThreadAnimationProc(LPVOID pParam)
{
ASSERT(pParam);
ImageEx* pImage = reinterpret_cast<ImageEx*> (pParam);
pImage->ThreadAnimation();
ASSERT(pParam);
ImageEx* pImage = reinterpret_cast<ImageEx*> (pParam);
pImage->ThreadAnimation();
return 0;
return 0;
}
void ImageEx::ThreadAnimation()
{
m_nFramePosition = 0;
m_nFramePosition = 0;
bool bExit = false;
while (bExit == false)
{
bExit = DrawFrameGIF();
}
bool bExit = false;
while (bExit == false) {
bExit = DrawFrameGIF();
}
}
bool ImageEx::DrawFrameGIF()
{
::WaitForSingleObject(m_hPause, INFINITE);
::WaitForSingleObject(m_hPause, INFINITE);
GUID pageGuid = FrameDimensionTime;
GUID pageGuid = FrameDimensionTime;
long hmWidth = GetWidth();
long hmHeight = GetHeight();
long hmWidth = GetWidth();
long hmHeight = GetHeight();
HDC hDC = GetDC(m_hWnd);
if (hDC)
{
Graphics graphics(hDC);
graphics.DrawImage(this, m_pt.x, m_pt.y, hmWidth, hmHeight);
ReleaseDC(m_hWnd, hDC);
}
HDC hDC = GetDC(m_hWnd);
if (hDC) {
Graphics graphics(hDC);
graphics.DrawImage(this, m_pt.x, m_pt.y, hmWidth, hmHeight);
ReleaseDC(m_hWnd, hDC);
}
SelectActiveFrame(&pageGuid, m_nFramePosition++);
SelectActiveFrame(&pageGuid, m_nFramePosition++);
if (m_nFramePosition == m_nFrameCount)
m_nFramePosition = 0;
if (m_nFramePosition == m_nFrameCount)
m_nFramePosition = 0;
long lPause = ((long*)m_pPropertyItem->value)[m_nFramePosition] * 10;
DWORD dwErr = WaitForSingleObject(m_hExitEvent, lPause);
return dwErr == WAIT_OBJECT_0;
long lPause = ((long*)m_pPropertyItem->value)[m_nFramePosition] * 10;
DWORD dwErr = WaitForSingleObject(m_hExitEvent, lPause);
return dwErr == WAIT_OBJECT_0;
}
void ImageEx::SetPause(bool bPause)
{
if (!IsAnimatedGIF())
return;
if (!IsAnimatedGIF())
return;
if (bPause && !m_bPause)
{
::ResetEvent(m_hPause);
}
else
{
if (bPause && !m_bPause) {
::ResetEvent(m_hPause);
}
else {
if (m_bPause && !bPause)
{
::SetEvent(m_hPause);
}
}
if (m_bPause && !bPause) {
::SetEvent(m_hPause);
}
}
m_bPause = bPause;
m_bPause = bPause;
}
void ImageEx::Destroy()
{
if (m_hThread)
{
// If pause un pause
SetPause(false);
if (m_hThread) {
// If pause un pause
SetPause(false);
SetEvent(m_hExitEvent);
WaitForSingleObject(m_hThread, INFINITE);
}
SetEvent(m_hExitEvent);
WaitForSingleObject(m_hThread, INFINITE);
}
CloseHandle(m_hThread);
CloseHandle(m_hExitEvent);
CloseHandle(m_hPause);
CloseHandle(m_hThread);
CloseHandle(m_hExitEvent);
CloseHandle(m_hPause);
free(m_pPropertyItem);
free(m_pPropertyItem);
m_pPropertyItem = NULL;
m_hThread = NULL;
m_hExitEvent = NULL;
m_hPause = NULL;
m_pPropertyItem = NULL;
m_hThread = NULL;
m_hExitEvent = NULL;
m_hPause = NULL;
}

View File

@@ -17,36 +17,36 @@ class ImageEx : public Image
{
public:
ImageEx(const wchar_t* resid, const wchar_t* restype);
~ImageEx();
ImageEx(const wchar_t* resid, const wchar_t* restype);
~ImageEx();
public:
CSize GetSize();
bool IsAnimatedGIF() { return m_nFrameCount > 1; }
void SetPause(bool bPause);
bool IsPaused() { return m_bPause; }
bool InitAnimation(HWND hWnd, CPoint pt);
void Destroy();
CSize GetSize();
bool IsAnimatedGIF() { return m_nFrameCount > 1; }
void SetPause(bool bPause);
bool IsPaused() { return m_bPause; }
bool InitAnimation(HWND hWnd, CPoint pt);
void Destroy();
protected:
bool TestForAnimatedGIF();
void Initialize();
bool DrawFrameGIF();
bool TestForAnimatedGIF();
void Initialize();
bool DrawFrameGIF();
void ThreadAnimation();
static UINT WINAPI _ThreadAnimationProc(LPVOID pParam);
void ThreadAnimation();
static UINT WINAPI _ThreadAnimationProc(LPVOID pParam);
HANDLE m_hThread;
HANDLE m_hPause;
HANDLE m_hExitEvent;
HINSTANCE m_hInst;
HWND m_hWnd;
UINT m_nFrameCount;
UINT m_nFramePosition;
bool m_bIsInitialized;
bool m_bPause;
PropertyItem* m_pPropertyItem;
CPoint m_pt;
HANDLE m_hThread;
HANDLE m_hPause;
HANDLE m_hExitEvent;
HINSTANCE m_hInst;
HWND m_hWnd;
UINT m_nFrameCount;
UINT m_nFramePosition;
bool m_bIsInitialized;
bool m_bPause;
PropertyItem* m_pPropertyItem;
CPoint m_pt;
};

View File

@@ -4,64 +4,65 @@
#include "resource.h"
#include <sddl.h>
bool directoryExists(wchar_t* path) {
DWORD dwResult = GetFileAttributes(path);
bool directoryExists(wchar_t* path)
{
DWORD dwResult = GetFileAttributes(path);
if (dwResult != INVALID_FILE_ATTRIBUTES) {
return true;
}
if (dwResult != INVALID_FILE_ATTRIBUTES) {
return true;
}
// NB: The directory could exist but we can't access it, let's check
DWORD dwLastError = GetLastError();
if (dwLastError == ERROR_FILE_NOT_FOUND) return false;
if (dwLastError == ERROR_PATH_NOT_FOUND) return false;
// NB: The directory could exist but we can't access it, let's check
DWORD dwLastError = GetLastError();
if (dwLastError == ERROR_FILE_NOT_FOUND) return false;
if (dwLastError == ERROR_PATH_NOT_FOUND) return false;
return true;
return true;
}
bool MachineInstaller::ShouldSilentInstall()
{
// Figure out the package name from our own EXE name
// The name consist of [$pkgName]DeploymentTool.exe
wchar_t ourFile[MAX_PATH];
HMODULE hMod = GetModuleHandle(NULL);
GetModuleFileName(hMod, ourFile, _countof(ourFile));
// Figure out the package name from our own EXE name
// The name consist of [$pkgName]DeploymentTool.exe
wchar_t ourFile[MAX_PATH];
HMODULE hMod = GetModuleHandle(NULL);
GetModuleFileName(hMod, ourFile, _countof(ourFile));
CString fullPath = CString(ourFile);
CString pkgName = CString(ourFile + fullPath.ReverseFind(L'\\'));
pkgName.Replace(L"DeploymentTool.exe", L"");
CString fullPath = CString(ourFile);
CString pkgName = CString(ourFile + fullPath.ReverseFind(L'\\'));
pkgName.Replace(L"DeploymentTool.exe", L"");
wchar_t installFolder[MAX_PATH];
wchar_t installFolder[MAX_PATH];
// NB: Users often get into the sitch where they install the MSI, then try to
// install the standalone package on top of that. In previous versions we tried
// to detect if the app was properly installed, but now we're taking the much
// more conservative approach, that if the package dir exists in any way, we're
// bailing out
// NB: Users often get into the sitch where they install the MSI, then try to
// install the standalone package on top of that. In previous versions we tried
// to detect if the app was properly installed, but now we're taking the much
// more conservative approach, that if the package dir exists in any way, we're
// bailing out
// C:\Users\Username\AppData\Local\$pkgName
SHGetFolderPath(NULL, CSIDL_LOCAL_APPDATA, NULL, SHGFP_TYPE_CURRENT, installFolder);
wcscat(installFolder, L"\\");
wcscat(installFolder, pkgName);
// C:\Users\Username\AppData\Local\$pkgName
SHGetFolderPath(NULL, CSIDL_LOCAL_APPDATA, NULL, SHGFP_TYPE_CURRENT, installFolder);
wcscat(installFolder, L"\\");
wcscat(installFolder, pkgName);
if (directoryExists(installFolder)) {
return false;
}
if (directoryExists(installFolder)) {
return false;
}
// C:\ProgramData\$pkgName\$username
wchar_t username[512];
DWORD unamesize = _countof(username);
SHGetFolderPath(NULL, CSIDL_COMMON_APPDATA, NULL, SHGFP_TYPE_CURRENT, installFolder);
GetUserName(username, &unamesize);
wcscat(installFolder, L"\\");
wcscat(installFolder, pkgName);
wcscat(installFolder, L"\\");
wcscat(installFolder, username);
// C:\ProgramData\$pkgName\$username
wchar_t username[512];
DWORD unamesize = _countof(username);
SHGetFolderPath(NULL, CSIDL_COMMON_APPDATA, NULL, SHGFP_TYPE_CURRENT, installFolder);
GetUserName(username, &unamesize);
wcscat(installFolder, L"\\");
wcscat(installFolder, pkgName);
wcscat(installFolder, L"\\");
wcscat(installFolder, username);
if (directoryExists(installFolder)) {
return false;
}
if (directoryExists(installFolder)) {
return false;
}
// None of these exist, we should install
return true;
// None of these exist, we should install
return true;
}

View File

@@ -1,6 +1,8 @@
#pragma once
class MachineInstaller
{
public:
static bool ShouldSilentInstall();
static bool ShouldSilentInstall();
};

View File

@@ -9,104 +9,104 @@ using std::vector;
RUNTIMEINFO supported_runtimes[] =
{
// net45 through net46 is supported on Vista SP2 and newer
// net45 through net46 is supported on Vista SP2 and newer
{
_WIN32_WINNT_VISTA, 2,
L"net45", L".NET Framework 4.5",
L"http://go.microsoft.com/fwlink/?LinkId=397707",
378389
},
{
_WIN32_WINNT_VISTA, 2,
L"net45", L".NET Framework 4.5",
L"http://go.microsoft.com/fwlink/?LinkId=397707",
378389
},
{
_WIN32_WINNT_VISTA, 2,
L"net451", L".NET Framework 4.5.1",
L"http://go.microsoft.com/fwlink/?LinkId=397707",
378675
},
{
_WIN32_WINNT_VISTA, 2,
L"net451", L".NET Framework 4.5.1",
L"http://go.microsoft.com/fwlink/?LinkId=397707",
378675
},
{
_WIN32_WINNT_VISTA, 2,
L"net452", L".NET Framework 4.5.2",
L"http://go.microsoft.com/fwlink/?LinkId=397707",
379893
},
{
_WIN32_WINNT_VISTA, 2,
L"net452", L".NET Framework 4.5.2",
L"http://go.microsoft.com/fwlink/?LinkId=397707",
379893
},
// net461 through net48 supports Windows 7 and newer
// net461 through net48 supports Windows 7 and newer
{
_WIN32_WINNT_WIN7, 0,
L"net46", L".NET Framework 4.6",
L"http://go.microsoft.com/fwlink/?LinkId=780596",
393295
},
{
_WIN32_WINNT_WIN7, 0,
L"net46", L".NET Framework 4.6",
L"http://go.microsoft.com/fwlink/?LinkId=780596",
393295
},
{
_WIN32_WINNT_WIN7, 0,
L"net461", L".NET Framework 4.6.1",
L"http://go.microsoft.com/fwlink/?LinkId=780596",
394254
},
{
_WIN32_WINNT_WIN7, 0,
L"net461", L".NET Framework 4.6.1",
L"http://go.microsoft.com/fwlink/?LinkId=780596",
394254
},
{
_WIN32_WINNT_WIN7, 0,
L"net462", L".NET Framework 4.6.2",
L"http://go.microsoft.com/fwlink/?LinkId=780596",
394802
},
{
_WIN32_WINNT_WIN7, 0,
L"net462", L".NET Framework 4.6.2",
L"http://go.microsoft.com/fwlink/?LinkId=780596",
394802
},
{
_WIN32_WINNT_WIN7, 0,
L"net47", L".NET Framework 4.7",
L"http://go.microsoft.com/fwlink/?LinkId=863262",
460798
},
{
_WIN32_WINNT_WIN7, 0,
L"net47", L".NET Framework 4.7",
L"http://go.microsoft.com/fwlink/?LinkId=863262",
460798
},
{
_WIN32_WINNT_WIN7, 0,
L"net471", L".NET Framework 4.7.1",
L"http://go.microsoft.com/fwlink/?LinkId=863262",
461308
},
{
_WIN32_WINNT_WIN7, 0,
L"net471", L".NET Framework 4.7.1",
L"http://go.microsoft.com/fwlink/?LinkId=863262",
461308
},
{
_WIN32_WINNT_WIN7, 0,
L"net472", L".NET Framework 4.7.2",
L"http://go.microsoft.com/fwlink/?LinkId=863262",
461808
},
{
_WIN32_WINNT_WIN7, 0,
L"net472", L".NET Framework 4.7.2",
L"http://go.microsoft.com/fwlink/?LinkId=863262",
461808
},
{
_WIN32_WINNT_WIN7, 0,
L"net48", L".NET Framework 4.8",
L"https://go.microsoft.com/fwlink/?LinkId=2085155",
528040
},
{
_WIN32_WINNT_WIN7, 0,
L"net48", L".NET Framework 4.8",
L"https://go.microsoft.com/fwlink/?LinkId=2085155",
528040
},
// dotnet core is supported on Windows 7 SP1 and newer.
// update this list periodically from https://dotnet.microsoft.com/download/dotnet
// we could add support for 2.0/2.1/2.2 but since those runtimes didn't ship with desktop support it is probably not needed.
// dotnet core is supported on Windows 7 SP1 and newer.
// update this list periodically from https://dotnet.microsoft.com/download/dotnet
// we could add support for 2.0/2.1/2.2 but since those runtimes didn't ship with desktop support it is probably not needed.
{
_WIN32_WINNT_WIN7, 1,
L"netcoreapp3", L".NET Core 3.0.3",
L"https://download.visualstudio.microsoft.com/download/pr/c525a2bb-6e98-4e6e-849e-45241d0db71c/d21612f02b9cae52fa50eb54de905986/windowsdesktop-runtime-3.0.3-win-x64.exe",
0, L"WindowsDesktop.App 3.0"
},
{
_WIN32_WINNT_WIN7, 1,
L"netcoreapp3", L".NET Core 3.0.3",
L"https://download.visualstudio.microsoft.com/download/pr/c525a2bb-6e98-4e6e-849e-45241d0db71c/d21612f02b9cae52fa50eb54de905986/windowsdesktop-runtime-3.0.3-win-x64.exe",
0, L"WindowsDesktop.App 3.0"
},
{
_WIN32_WINNT_WIN7, 1,
L"netcoreapp31", L".NET Core 3.1.18",
L"https://download.visualstudio.microsoft.com/download/pr/aa240732-82d7-4bd1-9701-e4014d7dc735/41a460f5e9c343f7b9207f64ab0e4c90/windowsdesktop-runtime-3.1.18-win-x64.exe",
0, L"WindowsDesktop.App 3.1"
},
{
_WIN32_WINNT_WIN7, 1,
L"netcoreapp31", L".NET Core 3.1.18",
L"https://download.visualstudio.microsoft.com/download/pr/aa240732-82d7-4bd1-9701-e4014d7dc735/41a460f5e9c343f7b9207f64ab0e4c90/windowsdesktop-runtime-3.1.18-win-x64.exe",
0, L"WindowsDesktop.App 3.1"
},
{
_WIN32_WINNT_WIN7, 1,
L"net5", L".NET 5.0.9",
L"https://download.visualstudio.microsoft.com/download/pr/8bc41df1-cbb4-4da6-944f-6652378e9196/1014aacedc80bbcc030dabb168d2532f/windowsdesktop-runtime-5.0.9-win-x64.exe",
0, L"WindowsDesktop.App 5.0"
},
{
_WIN32_WINNT_WIN7, 1,
L"net5", L".NET 5.0.9",
L"https://download.visualstudio.microsoft.com/download/pr/8bc41df1-cbb4-4da6-944f-6652378e9196/1014aacedc80bbcc030dabb168d2532f/windowsdesktop-runtime-5.0.9-win-x64.exe",
0, L"WindowsDesktop.App 5.0"
},
};
@@ -114,126 +114,123 @@ RUNTIMEINFO supported_runtimes[] =
const RUNTIMEINFO* GetRuntimeByName(wstring name)
{
for (int i = 0; i < NUM_RUNTIMES; i++)
{
const RUNTIMEINFO* item = &supported_runtimes[i];
auto itemName = wstring(item->name);
for (int i = 0; i < NUM_RUNTIMES; i++) {
const RUNTIMEINFO* item = &supported_runtimes[i];
auto itemName = wstring(item->name);
if (name == itemName)
return item;
}
if (name == itemName)
return item;
}
return nullptr;
return nullptr;
}
bool IsRuntimeSupported(const RUNTIMEINFO* runtime)
{
return IsWindowsVersionOrGreater(HIBYTE(runtime->minOS), LOBYTE(runtime->minOS), runtime->minSP);
return IsWindowsVersionOrGreater(HIBYTE(runtime->minOS), LOBYTE(runtime->minOS), runtime->minSP);
}
static const wchar_t* ndpPath = L"SOFTWARE\\Microsoft\\NET Framework Setup\\NDP\\v4\\Full";
bool IsFullNetFrameworkInstalled(DWORD requiredVersion)
{
ATL::CRegKey key;
ATL::CRegKey key;
if (key.Open(HKEY_LOCAL_MACHINE, ndpPath, KEY_READ) != ERROR_SUCCESS) {
return false;
}
if (key.Open(HKEY_LOCAL_MACHINE, ndpPath, KEY_READ) != ERROR_SUCCESS) {
return false;
}
DWORD dwReleaseInfo = 0;
if (key.QueryDWORDValue(L"Release", dwReleaseInfo) != ERROR_SUCCESS ||
dwReleaseInfo < requiredVersion) {
return false;
}
DWORD dwReleaseInfo = 0;
if (key.QueryDWORDValue(L"Release", dwReleaseInfo) != ERROR_SUCCESS ||
dwReleaseInfo < requiredVersion) {
return false;
}
return true;
return true;
}
// TODO this is extremely messy, it should certainly be re-written.
wstring exec(const char* cmd) {
char buffer[128];
string result = "";
FILE* pipe = _popen(cmd, "r");
if (!pipe)
return L"";
try {
while (fgets(buffer, sizeof buffer, pipe) != NULL) {
result += buffer;
}
}
catch (...) {
_pclose(pipe);
return L"";
}
_pclose(pipe);
wstring exec(const char* cmd)
{
char buffer[128];
string result = "";
FILE* pipe = _popen(cmd, "r");
if (!pipe)
return L"";
try {
while (fgets(buffer, sizeof buffer, pipe) != NULL) {
result += buffer;
}
}
catch (...) {
_pclose(pipe);
return L"";
}
_pclose(pipe);
// https://stackoverflow.com/a/8969776/184746
std::wstring wsTmp(result.begin(), result.end());
return wsTmp;
// https://stackoverflow.com/a/8969776/184746
std::wstring wsTmp(result.begin(), result.end());
return wsTmp;
}
bool IsDotNetCoreInstalled(wstring searchString)
{
// it is possible to parse this registry entry, but it only returns the newest version
// and it might be necessary to install an older version of the runtime if it's not installed,
// so we need a full list of installed runtimes.
// static const wchar_t* dncPath = L"SOFTWARE\\dotnet\\Setup\\InstalledVersions";
// it is possible to parse this registry entry, but it only returns the newest version
// and it might be necessary to install an older version of the runtime if it's not installed,
// so we need a full list of installed runtimes.
// static const wchar_t* dncPath = L"SOFTWARE\\dotnet\\Setup\\InstalledVersions";
// note, dotnet cli will only return x64 results.
//auto runtimes = exec("dotnet --list-runtimes");
auto runtimes = exec("dotnet --info");
return runtimes.find(searchString) != std::string::npos;
// note, dotnet cli will only return x64 results.
//auto runtimes = exec("dotnet --list-runtimes");
auto runtimes = exec("dotnet --info");
return runtimes.find(searchString) != std::string::npos;
}
bool IsRuntimeInstalled(const RUNTIMEINFO* runtime)
{
if (runtime->fxReleaseVersion > 0)
{
return IsFullNetFrameworkInstalled(runtime->fxReleaseVersion);
}
else
{
return IsDotNetCoreInstalled(wstring(runtime->dncRuntimeVersionName));
}
if (runtime->fxReleaseVersion > 0) {
return IsFullNetFrameworkInstalled(runtime->fxReleaseVersion);
}
else {
return IsDotNetCoreInstalled(wstring(runtime->dncRuntimeVersionName));
}
}
int ParseRuntimeString(std::wstring version, vector<const RUNTIMEINFO*>& runtimes)
{
// split version string by comma
int ret = S_OK;
wstring temp;
std::wstringstream wss(version);
while (std::getline(wss, temp, L','))
{
const RUNTIMEINFO* rt = GetRuntimeByName(temp);
if (rt != nullptr)
runtimes.push_back(rt);
else
ret = S_FALSE;
}
return ret;
// split version string by comma
int ret = S_OK;
wstring temp;
std::wstringstream wss(version);
while (std::getline(wss, temp, L',')) {
const RUNTIMEINFO* rt = GetRuntimeByName(temp);
if (rt != nullptr)
runtimes.push_back(rt);
else
ret = S_FALSE;
}
return ret;
}
vector<const RUNTIMEINFO*> GetRequiredRuntimes()
{
vector<const RUNTIMEINFO*> runtimes;
vector<const RUNTIMEINFO*> runtimes;
// get comma-delimited version string from exe resources
wchar_t* versionFlag = (wchar_t*)LoadResource(NULL, FindResource(NULL, (LPCWSTR)IDR_FX_VERSION_FLAG, L"FLAGS"));
if (versionFlag == nullptr)
return runtimes;
// get comma-delimited version string from exe resources
wchar_t* versionFlag = (wchar_t*)LoadResource(NULL, FindResource(NULL, (LPCWSTR)IDR_FX_VERSION_FLAG, L"FLAGS"));
if (versionFlag == nullptr)
return runtimes;
wstring version(versionFlag);
if (version.length() == 0)
return runtimes;
wstring version(versionFlag);
if (version.length() == 0)
return runtimes;
ParseRuntimeString(version, runtimes);
return runtimes;
ParseRuntimeString(version, runtimes);
return runtimes;
}
int VerifyRuntimeString(std::wstring version)
{
vector<const RUNTIMEINFO*> runtimes;
return ParseRuntimeString(version, runtimes);
vector<const RUNTIMEINFO*> runtimes;
return ParseRuntimeString(version, runtimes);
}

View File

@@ -5,13 +5,13 @@
typedef struct
{
WORD minOS;
WORD minSP;
wchar_t name[32];
wchar_t friendlyName[32];
wchar_t installerUrl[256];
DWORD fxReleaseVersion;
wchar_t dncRuntimeVersionName[32];
WORD minOS;
WORD minSP;
wchar_t name[32];
wchar_t friendlyName[32];
wchar_t installerUrl[256];
DWORD fxReleaseVersion;
wchar_t dncRuntimeVersionName[32];
} RUNTIMEINFO;
const RUNTIMEINFO* GetRuntimeByName(std::wstring name);

View File

@@ -45,28 +45,23 @@ void CSplashWnd::SetImage(const wchar_t* resid, const wchar_t* restype)
void CSplashWnd::Show()
{
if (m_hThread == NULL)
{
if (m_hThread == NULL) {
m_hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
m_hThread = (HANDLE)_beginthreadex(NULL, 0, SplashThreadProc, static_cast<LPVOID>(this), 0, &m_ThreadId);
if (WaitForSingleObject(m_hEvent, 5000) == WAIT_TIMEOUT)
{
if (WaitForSingleObject(m_hEvent, 5000) == WAIT_TIMEOUT) {
OutputDebugString(L"Error starting SplashThread\n");
}
}
else
{
else {
PostThreadMessage(m_ThreadId, WM_ACTIVATE, WA_CLICKACTIVE, 0);
}
}
void CSplashWnd::Hide()
{
if (m_hThread)
{
if (m_hThread) {
PostThreadMessage(m_ThreadId, WM_QUIT, 0, 0);
if (WaitForSingleObject(m_hThread, 9000) == WAIT_TIMEOUT)
{
if (WaitForSingleObject(m_hThread, 9000) == WAIT_TIMEOUT) {
::TerminateThread(m_hThread, 2222);
}
CloseHandle(m_hThread);
@@ -101,8 +96,7 @@ unsigned int __stdcall CSplashWnd::SplashThreadProc(void* lpParameter)
wndcls.lpszClassName = L"SplashWnd";
wndcls.hIcon = LoadIcon(wndcls.hInstance, MAKEINTRESOURCE(IDI_APPLICATION));
if (!RegisterClass(&wndcls))
{
if (!RegisterClass(&wndcls)) {
if (GetLastError() != 0x00000582) // already registered)
{
OutputDebugString(L"Unable to register class SplashWnd\n");
@@ -118,13 +112,11 @@ unsigned int __stdcall CSplashWnd::SplashThreadProc(void* lpParameter)
::GetCursorPos(&point);
hMonitor = ::MonitorFromPoint(point, MONITOR_DEFAULTTONEAREST);
if (::GetMonitorInfo(hMonitor, &mi))
{
if (::GetMonitorInfo(hMonitor, &mi)) {
rcArea.left = (mi.rcMonitor.right + mi.rcMonitor.left - static_cast<long>(pThis->m_pImage->GetWidth())) / 2;
rcArea.top = (mi.rcMonitor.top + mi.rcMonitor.bottom - static_cast<long>(pThis->m_pImage->GetHeight())) / 2;
}
else
{
else {
SystemParametersInfo(SPI_GETWORKAREA, NULL, &rcArea, NULL);
rcArea.left = (rcArea.right + rcArea.left - pThis->m_pImage->GetWidth()) / 2;
rcArea.top = (rcArea.top + rcArea.bottom - pThis->m_pImage->GetHeight()) / 2;
@@ -141,8 +133,7 @@ unsigned int __stdcall CSplashWnd::SplashThreadProc(void* lpParameter)
wndcls.hInstance,
NULL);
if (!pThis->m_hSplashWnd)
{
if (!pThis->m_hSplashWnd) {
OutputDebugString(L"Unable to create SplashWnd\n");
return 0;
}
@@ -159,16 +150,13 @@ unsigned int __stdcall CSplashWnd::SplashThreadProc(void* lpParameter)
PeekMessage(&msg, NULL, 0, 0, 0); // invoke creating message queue
SetEvent(pThis->m_hEvent);
while ((bRet = GetMessage(&msg, NULL, 0, 0)) != 0)
{
while ((bRet = GetMessage(&msg, NULL, 0, 0)) != 0) {
if (msg.message == WM_QUIT) break;
if (bRet == -1)
{
if (bRet == -1) {
// handle the error and possibly exit
}
else
{
else {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
@@ -181,24 +169,19 @@ unsigned int __stdcall CSplashWnd::SplashThreadProc(void* lpParameter)
LRESULT CALLBACK CSplashWnd::SplashWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
CSplashWnd* pInstance = reinterpret_cast<CSplashWnd*>(GetWindowLongPtr(hwnd, GWL_USERDATA));
if (pInstance == NULL)
{
if (pInstance == NULL) {
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
switch (uMsg)
{
switch (uMsg) {
case WM_PAINT:
{
if (pInstance->m_pImage)
{
if (pInstance->m_pImage->IsAnimatedGIF())
{
if (pInstance->m_pImage) {
if (pInstance->m_pImage->IsAnimatedGIF()) {
// do nothing, the gif will be drawn by it's own thread.
}
else
{
else {
Gdiplus::Graphics gdip(hwnd);
gdip.DrawImage(pInstance->m_pImage, 0, 0, pInstance->m_pImage->GetWidth(), pInstance->m_pImage->GetHeight());
}
@@ -216,8 +199,7 @@ LRESULT CALLBACK CSplashWnd::SplashWndProc(HWND hwnd, UINT uMsg, WPARAM wParam,
case WM_MOUSEMOVE:
{
if (GetCapture() == hwnd)
{
if (GetCapture() == hwnd) {
RECT rcWnd;
GetWindowRect(hwnd, &rcWnd);

View File

@@ -24,19 +24,18 @@ private:
CSplashWnd& operator=(const CSplashWnd&) {};
protected:
HANDLE m_hThread;
unsigned int m_ThreadId;
HANDLE m_hEvent;
ImageEx* m_pImage;
HWND m_hSplashWnd;
HWND m_hParentWnd;
POINT m_ptMouseDown;
HANDLE m_hThread;
unsigned int m_ThreadId;
HANDLE m_hEvent;
ImageEx* m_pImage;
HWND m_hSplashWnd;
HWND m_hParentWnd;
POINT m_ptMouseDown;
public:
CSplashWnd(HWND hParent = NULL);
~CSplashWnd();
void SetImage(const wchar_t* resid, const wchar_t* restype);
void SetWindowName(const wchar_t* windowName);
void Show();
void Hide();

View File

@@ -6,296 +6,297 @@
void CUpdateRunner::DisplayErrorMessage(CString& errorMessage, wchar_t* logFile)
{
CTaskDialog dlg;
TASKDIALOG_BUTTON buttons[] = {
{ 1, L"Open Setup Log", },
{ 2, L"Close", },
};
CTaskDialog dlg;
TASKDIALOG_BUTTON buttons[] = {
{ 1, L"Open Setup Log", },
{ 2, L"Close", },
};
// TODO: Something about contacting support?
if (logFile == NULL) {
dlg.SetButtons(&buttons[1], 1, 1);
} else {
dlg.SetButtons(buttons, 2, 1);
}
// TODO: Something about contacting support?
if (logFile == NULL) {
dlg.SetButtons(&buttons[1], 1, 1);
}
else {
dlg.SetButtons(buttons, 2, 1);
}
dlg.SetMainInstructionText(L"Installation has failed");
dlg.SetContentText(errorMessage);
dlg.SetMainIcon(TD_ERROR_ICON);
dlg.SetMainInstructionText(L"Installation has failed");
dlg.SetContentText(errorMessage);
dlg.SetMainIcon(TD_ERROR_ICON);
int nButton;
int nButton;
if (FAILED(dlg.DoModal(::GetActiveWindow(), &nButton))) {
return;
}
if (FAILED(dlg.DoModal(::GetActiveWindow(), &nButton))) {
return;
}
if (nButton == 1 && logFile != NULL) {
ShellExecute(NULL, NULL, logFile, NULL, NULL, SW_SHOW);
}
if (nButton == 1 && logFile != NULL) {
ShellExecute(NULL, NULL, logFile, NULL, NULL, SW_SHOW);
}
}
HRESULT CUpdateRunner::AreWeUACElevated()
{
HANDLE hProcess = GetCurrentProcess();
HANDLE hToken = 0;
HRESULT hr;
HANDLE hProcess = GetCurrentProcess();
HANDLE hToken = 0;
HRESULT hr;
if (!OpenProcessToken(hProcess, TOKEN_QUERY, &hToken)) {
hr = HRESULT_FROM_WIN32(GetLastError());
goto out;
}
if (!OpenProcessToken(hProcess, TOKEN_QUERY, &hToken)) {
hr = HRESULT_FROM_WIN32(GetLastError());
goto out;
}
TOKEN_ELEVATION_TYPE elevType;
DWORD dontcare;
if (!GetTokenInformation(hToken, TokenElevationType, &elevType, sizeof(TOKEN_ELEVATION_TYPE), &dontcare)) {
hr = HRESULT_FROM_WIN32(GetLastError());
goto out;
}
TOKEN_ELEVATION_TYPE elevType;
DWORD dontcare;
if (!GetTokenInformation(hToken, TokenElevationType, &elevType, sizeof(TOKEN_ELEVATION_TYPE), &dontcare)) {
hr = HRESULT_FROM_WIN32(GetLastError());
goto out;
}
hr = (elevType == TokenElevationTypeFull ? S_OK : S_FALSE);
hr = (elevType == TokenElevationTypeFull ? S_OK : S_FALSE);
out:
if (hToken) {
CloseHandle(hToken);
}
if (hToken) {
CloseHandle(hToken);
}
return hr;
return hr;
}
HRESULT FindDesktopFolderView(REFIID riid, void **ppv)
HRESULT FindDesktopFolderView(REFIID riid, void** ppv)
{
HRESULT hr;
HRESULT hr;
CComPtr<IShellWindows> spShellWindows;
spShellWindows.CoCreateInstance(CLSID_ShellWindows);
CComPtr<IShellWindows> spShellWindows;
spShellWindows.CoCreateInstance(CLSID_ShellWindows);
CComVariant vtLoc(CSIDL_DESKTOP);
CComVariant vtEmpty;
long lhwnd;
CComPtr<IDispatch> spdisp;
CComVariant vtLoc(CSIDL_DESKTOP);
CComVariant vtEmpty;
long lhwnd;
CComPtr<IDispatch> spdisp;
hr = spShellWindows->FindWindowSW(
&vtLoc, &vtEmpty,
SWC_DESKTOP, &lhwnd, SWFO_NEEDDISPATCH, &spdisp);
if (FAILED(hr)) return hr;
hr = spShellWindows->FindWindowSW(
&vtLoc, &vtEmpty,
SWC_DESKTOP, &lhwnd, SWFO_NEEDDISPATCH, &spdisp);
if (FAILED(hr)) return hr;
CComPtr<IShellBrowser> spBrowser;
hr = CComQIPtr<IServiceProvider>(spdisp)->QueryService(SID_STopLevelBrowser, IID_PPV_ARGS(&spBrowser));
if (FAILED(hr)) return hr;
CComPtr<IShellBrowser> spBrowser;
hr = CComQIPtr<IServiceProvider>(spdisp)->QueryService(SID_STopLevelBrowser, IID_PPV_ARGS(&spBrowser));
if (FAILED(hr)) return hr;
CComPtr<IShellView> spView;
hr = spBrowser->QueryActiveShellView(&spView);
if (FAILED(hr)) return hr;
CComPtr<IShellView> spView;
hr = spBrowser->QueryActiveShellView(&spView);
if (FAILED(hr)) return hr;
hr = spView->QueryInterface(riid, ppv);
if (FAILED(hr)) return hr;
hr = spView->QueryInterface(riid, ppv);
if (FAILED(hr)) return hr;
return S_OK;
return S_OK;
}
HRESULT GetDesktopAutomationObject(REFIID riid, void **ppv)
HRESULT GetDesktopAutomationObject(REFIID riid, void** ppv)
{
HRESULT hr;
HRESULT hr;
CComPtr<IShellView> spsv;
hr = FindDesktopFolderView(IID_PPV_ARGS(&spsv));
if (FAILED(hr)) return hr;
CComPtr<IShellView> spsv;
hr = FindDesktopFolderView(IID_PPV_ARGS(&spsv));
if (FAILED(hr)) return hr;
CComPtr<IDispatch> spdispView;
hr = spsv->GetItemObject(SVGIO_BACKGROUND, IID_PPV_ARGS(&spdispView));
if (FAILED(hr)) return hr;
CComPtr<IDispatch> spdispView;
hr = spsv->GetItemObject(SVGIO_BACKGROUND, IID_PPV_ARGS(&spdispView));
if (FAILED(hr)) return hr;
return spdispView->QueryInterface(riid, ppv);
return spdispView->QueryInterface(riid, ppv);
}
HRESULT CUpdateRunner::ShellExecuteFromExplorer(LPWSTR pszFile, LPWSTR pszParameters)
{
HRESULT hr;
HRESULT hr;
CComPtr<IShellFolderViewDual> spFolderView;
hr = GetDesktopAutomationObject(IID_PPV_ARGS(&spFolderView));
if (FAILED(hr)) return hr;
CComPtr<IShellFolderViewDual> spFolderView;
hr = GetDesktopAutomationObject(IID_PPV_ARGS(&spFolderView));
if (FAILED(hr)) return hr;
CComPtr<IDispatch> spdispShell;
hr = spFolderView->get_Application(&spdispShell);
if (FAILED(hr)) return hr;
CComPtr<IDispatch> spdispShell;
hr = spFolderView->get_Application(&spdispShell);
if (FAILED(hr)) return hr;
return CComQIPtr<IShellDispatch2>(spdispShell)->ShellExecute(
CComBSTR(pszFile),
CComVariant(pszParameters ? pszParameters : L""),
CComVariant(L""),
CComVariant(L""),
CComVariant(SW_SHOWDEFAULT));
return CComQIPtr<IShellDispatch2>(spdispShell)->ShellExecute(
CComBSTR(pszFile),
CComVariant(pszParameters ? pszParameters : L""),
CComVariant(L""),
CComVariant(L""),
CComVariant(SW_SHOWDEFAULT));
}
bool CUpdateRunner::DirectoryExists(wchar_t* szPath)
{
DWORD dwAttrib = GetFileAttributes(szPath);
DWORD dwAttrib = GetFileAttributes(szPath);
return (dwAttrib != INVALID_FILE_ATTRIBUTES &&
(dwAttrib & FILE_ATTRIBUTE_DIRECTORY));
return (dwAttrib != INVALID_FILE_ATTRIBUTES &&
(dwAttrib & FILE_ATTRIBUTE_DIRECTORY));
}
bool CUpdateRunner::DirectoryIsWritable(wchar_t * szPath)
bool CUpdateRunner::DirectoryIsWritable(wchar_t* szPath)
{
wchar_t szTempFileName[MAX_PATH];
UINT uRetVal = GetTempFileNameW(szPath, L"Squirrel", 0, szTempFileName);
if (uRetVal == 0) {
return false;
}
DeleteFile(szTempFileName);
return true;
wchar_t szTempFileName[MAX_PATH];
UINT uRetVal = GetTempFileNameW(szPath, L"Squirrel", 0, szTempFileName);
if (uRetVal == 0) {
return false;
}
DeleteFile(szTempFileName);
return true;
}
int CUpdateRunner::ExtractUpdaterAndRun(wchar_t* lpCommandLine, bool useFallbackDir, std::function<void()>& callback)
{
PROCESS_INFORMATION pi = { 0 };
STARTUPINFO si = { 0 };
CResource zipResource;
wchar_t targetDir[MAX_PATH] = { 0 };
wchar_t logFile[MAX_PATH];
PROCESS_INFORMATION pi = { 0 };
STARTUPINFO si = { 0 };
CResource zipResource;
wchar_t targetDir[MAX_PATH] = { 0 };
wchar_t logFile[MAX_PATH];
DWORD dwExitCode = 0;
std::vector<CString> to_delete;
std::vector<CString> to_delete;
wchar_t* envSquirrelTemp = _wgetenv(L"SQUIRREL_TEMP");
if (envSquirrelTemp &&
DirectoryExists(envSquirrelTemp) &&
DirectoryIsWritable(envSquirrelTemp) &&
!PathIsUNCW(envSquirrelTemp)) {
_swprintf_c(targetDir, _countof(targetDir), L"%s", envSquirrelTemp);
goto gotADir;
}
wchar_t* envSquirrelTemp = _wgetenv(L"SQUIRREL_TEMP");
if (envSquirrelTemp &&
DirectoryExists(envSquirrelTemp) &&
DirectoryIsWritable(envSquirrelTemp) &&
!PathIsUNCW(envSquirrelTemp)) {
_swprintf_c(targetDir, _countof(targetDir), L"%s", envSquirrelTemp);
goto gotADir;
}
if (!useFallbackDir) {
SHGetFolderPath(NULL, CSIDL_LOCAL_APPDATA, NULL, SHGFP_TYPE_CURRENT, targetDir);
goto gotADir;
}
if (!useFallbackDir) {
SHGetFolderPath(NULL, CSIDL_LOCAL_APPDATA, NULL, SHGFP_TYPE_CURRENT, targetDir);
goto gotADir;
}
wchar_t username[512];
wchar_t appDataDir[MAX_PATH];
ULONG unameSize = _countof(username);
wchar_t username[512];
wchar_t appDataDir[MAX_PATH];
ULONG unameSize = _countof(username);
SHGetFolderPath(NULL, CSIDL_COMMON_APPDATA, NULL, SHGFP_TYPE_CURRENT, appDataDir);
GetUserName(username, &unameSize);
SHGetFolderPath(NULL, CSIDL_COMMON_APPDATA, NULL, SHGFP_TYPE_CURRENT, appDataDir);
GetUserName(username, &unameSize);
_swprintf_c(targetDir, _countof(targetDir), L"%s\\%s", appDataDir, username);
_swprintf_c(targetDir, _countof(targetDir), L"%s\\%s", appDataDir, username);
if (!CreateDirectory(targetDir, NULL) && GetLastError() != ERROR_ALREADY_EXISTS) {
wchar_t err[4096];
_swprintf_c(err, _countof(err), L"Unable to write to %s - IT policies may be restricting access to this folder", targetDir);
if (!CreateDirectory(targetDir, NULL) && GetLastError() != ERROR_ALREADY_EXISTS) {
wchar_t err[4096];
_swprintf_c(err, _countof(err), L"Unable to write to %s - IT policies may be restricting access to this folder", targetDir);
callback();
DisplayErrorMessage(CString(err), NULL);
return -1;
}
DisplayErrorMessage(CString(err), NULL);
return -1;
}
gotADir:
wcscat_s(targetDir, _countof(targetDir), L"\\SquirrelTemp");
wcscat_s(targetDir, _countof(targetDir), L"\\SquirrelTemp");
if (!CreateDirectory(targetDir, NULL) && GetLastError() != ERROR_ALREADY_EXISTS) {
wchar_t err[4096];
_swprintf_c(err, _countof(err), L"Unable to write to %s - IT policies may be restricting access to this folder", targetDir);
if (!CreateDirectory(targetDir, NULL) && GetLastError() != ERROR_ALREADY_EXISTS) {
wchar_t err[4096];
_swprintf_c(err, _countof(err), L"Unable to write to %s - IT policies may be restricting access to this folder", targetDir);
if (useFallbackDir) {
if (useFallbackDir) {
callback();
DisplayErrorMessage(CString(err), NULL);
}
}
goto failedExtract;
}
goto failedExtract;
}
swprintf_s(logFile, L"%s\\SquirrelSetup.log", targetDir);
swprintf_s(logFile, L"%s\\SquirrelSetup.log", targetDir);
if (!zipResource.Load(L"DATA", IDR_UPDATE_ZIP)) {
goto failedExtract;
}
if (!zipResource.Load(L"DATA", IDR_UPDATE_ZIP)) {
goto failedExtract;
}
DWORD dwSize = zipResource.GetSize();
if (dwSize < 0x100) {
goto failedExtract;
}
DWORD dwSize = zipResource.GetSize();
if (dwSize < 0x100) {
goto failedExtract;
}
BYTE* pData = (BYTE*)zipResource.Lock();
HZIP zipFile = OpenZip(pData, dwSize, NULL);
SetUnzipBaseDir(zipFile, targetDir);
BYTE* pData = (BYTE*)zipResource.Lock();
HZIP zipFile = OpenZip(pData, dwSize, NULL);
SetUnzipBaseDir(zipFile, targetDir);
// NB: This library is kind of a disaster
ZRESULT zr;
int index = 0;
do {
ZIPENTRY zentry;
wchar_t targetFile[MAX_PATH];
// NB: This library is kind of a disaster
ZRESULT zr;
int index = 0;
do {
ZIPENTRY zentry;
wchar_t targetFile[MAX_PATH];
zr = GetZipItem(zipFile, index, &zentry);
if (zr != ZR_OK && zr != ZR_MORE) {
break;
}
zr = GetZipItem(zipFile, index, &zentry);
if (zr != ZR_OK && zr != ZR_MORE) {
break;
}
// NB: UnzipItem won't overwrite data, we need to do it ourselves
swprintf_s(targetFile, L"%s\\%s", targetDir, zentry.name);
DeleteFile(targetFile);
// NB: UnzipItem won't overwrite data, we need to do it ourselves
swprintf_s(targetFile, L"%s\\%s", targetDir, zentry.name);
DeleteFile(targetFile);
if (UnzipItem(zipFile, index, zentry.name) != ZR_OK) break;
to_delete.push_back(CString(targetFile));
index++;
} while (zr == ZR_MORE || zr == ZR_OK);
if (UnzipItem(zipFile, index, zentry.name) != ZR_OK) break;
to_delete.push_back(CString(targetFile));
index++;
} while (zr == ZR_MORE || zr == ZR_OK);
CloseZip(zipFile);
zipResource.Release();
CloseZip(zipFile);
zipResource.Release();
// nfi if the zip extract actually worked, check for Update.exe
wchar_t updateExePath[MAX_PATH];
swprintf_s(updateExePath, L"%s\\%s", targetDir, L"Update.exe");
// nfi if the zip extract actually worked, check for Update.exe
wchar_t updateExePath[MAX_PATH];
swprintf_s(updateExePath, L"%s\\%s", targetDir, L"Update.exe");
if (GetFileAttributes(updateExePath) == INVALID_FILE_ATTRIBUTES) {
goto failedExtract;
}
if (GetFileAttributes(updateExePath) == INVALID_FILE_ATTRIBUTES) {
goto failedExtract;
}
// Run Update.exe
si.cb = sizeof(STARTUPINFO);
si.wShowWindow = SW_SHOW;
si.dwFlags = STARTF_USESHOWWINDOW;
// Run Update.exe
si.cb = sizeof(STARTUPINFO);
si.wShowWindow = SW_SHOW;
si.dwFlags = STARTF_USESHOWWINDOW;
if (!lpCommandLine || wcsnlen_s(lpCommandLine, MAX_PATH) < 1) {
lpCommandLine = L"";
}
if (!lpCommandLine || wcsnlen_s(lpCommandLine, MAX_PATH) < 1) {
lpCommandLine = L"";
}
wchar_t cmd[MAX_PATH];
swprintf_s(cmd, L"\"%s\" --install . %s", updateExePath, lpCommandLine);
wchar_t cmd[MAX_PATH];
swprintf_s(cmd, L"\"%s\" --install . %s", updateExePath, lpCommandLine);
if (!CreateProcess(NULL, cmd, NULL, NULL, false, 0, NULL, targetDir, &si, &pi)) {
goto failedExtract;
}
if (!CreateProcess(NULL, cmd, NULL, NULL, false, 0, NULL, targetDir, &si, &pi)) {
goto failedExtract;
}
WaitForSingleObject(pi.hProcess, INFINITE);
WaitForSingleObject(pi.hProcess, INFINITE);
if (!GetExitCodeProcess(pi.hProcess, &dwExitCode)) {
dwExitCode = (DWORD)-1;
}
if (!GetExitCodeProcess(pi.hProcess, &dwExitCode)) {
dwExitCode = (DWORD)-1;
}
if (dwExitCode != 0) {
if (dwExitCode != 0) {
callback();
DisplayErrorMessage(CString(
L"There was an error while installing the application. "
L"Check the setup log for more information and contact the author."), logFile);
}
DisplayErrorMessage(CString(
L"There was an error while installing the application. "
L"Check the setup log for more information and contact the author."), logFile);
}
for (unsigned int i = 0; i < to_delete.size(); i++) {
DeleteFile(to_delete[i]);
}
for (unsigned int i = 0; i < to_delete.size(); i++) {
DeleteFile(to_delete[i]);
}
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
return (int) dwExitCode;
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
return (int)dwExitCode;
failedExtract:
if (!useFallbackDir) {
// Take another pass at it, using C:\ProgramData instead
return ExtractUpdaterAndRun(lpCommandLine, true, callback);
}
if (!useFallbackDir) {
// Take another pass at it, using C:\ProgramData instead
return ExtractUpdaterAndRun(lpCommandLine, true, callback);
}
callback();
DisplayErrorMessage(CString(L"Failed to extract installer"), NULL);
return (int) dwExitCode;
return (int)dwExitCode;
}

View File

@@ -5,10 +5,11 @@ class CUpdateRunner
{
public:
static void DisplayErrorMessage(CString& errorMessage, wchar_t* logFile);
static HRESULT AreWeUACElevated();
static HRESULT ShellExecuteFromExplorer(LPWSTR pszFile, LPWSTR pszParameters);
static bool DirectoryExists(wchar_t* szPath);
static bool DirectoryIsWritable(wchar_t* szPath);
static int ExtractUpdaterAndRun(wchar_t* lpCommandLine, bool useFallbackDir, std::function<void()>& callback);
static void DisplayErrorMessage(CString& errorMessage, wchar_t* logFile);
static HRESULT AreWeUACElevated();
static HRESULT ShellExecuteFromExplorer(LPWSTR pszFile, LPWSTR pszParameters);
static bool DirectoryExists(wchar_t* szPath);
static bool DirectoryIsWritable(wchar_t* szPath);
static int ExtractUpdaterAndRun(wchar_t* lpCommandLine, bool useFallbackDir, std::function<void()>& callback);
};

File diff suppressed because it is too large Load Diff

View File

@@ -18,18 +18,19 @@ typedef DWORD ZRESULT;
// return codes from any of the zip functions. Listed later.
typedef struct
{ int index; // index of this file within the zip
TCHAR name[MAX_PATH]; // filename within the zip
DWORD attr; // attributes, as in GetFileAttributes.
FILETIME atime,ctime,mtime;// access, create, modify filetimes
long comp_size; // sizes of item, compressed and uncompressed. These
long unc_size; // may be -1 if not yet known (e.g. being streamed in)
{
int index; // index of this file within the zip
TCHAR name[MAX_PATH]; // filename within the zip
DWORD attr; // attributes, as in GetFileAttributes.
FILETIME atime, ctime, mtime;// access, create, modify filetimes
long comp_size; // sizes of item, compressed and uncompressed. These
long unc_size; // may be -1 if not yet known (e.g. being streamed in)
} ZIPENTRY;
HZIP OpenZip(const TCHAR *fn, const char *password);
HZIP OpenZip(void *z,unsigned int len, const char *password);
HZIP OpenZipHandle(HANDLE h, const char *password);
HZIP OpenZip(const TCHAR* fn, const char* password);
HZIP OpenZip(void* z, unsigned int len, const char* password);
HZIP OpenZipHandle(HANDLE h, const char* password);
// OpenZip - opens a zip file and returns a handle with which you can
// subsequently examine its contents. You can open a zip file from:
// from a pipe: OpenZipHandle(hpipe_read,0);
@@ -46,7 +47,7 @@ HZIP OpenZipHandle(HANDLE h, const char *password);
// but for real windows, the zip makes its own copy of your handle, so you
// can close yours anytime.
ZRESULT GetZipItem(HZIP hz, int index, ZIPENTRY *ze);
ZRESULT GetZipItem(HZIP hz, int index, ZIPENTRY* ze);
// GetZipItem - call this to get information about an item in the zip.
// If index is -1 and the file wasn't opened through a pipe,
// then it returns information about the whole zipfile
@@ -61,14 +62,14 @@ ZRESULT GetZipItem(HZIP hz, int index, ZIPENTRY *ze);
// then then comp_size and sometimes unc_size as well may not be known until
// after the item has been unzipped.
ZRESULT FindZipItem(HZIP hz, const TCHAR *name, bool ic, int *index, ZIPENTRY *ze);
ZRESULT FindZipItem(HZIP hz, const TCHAR* name, bool ic, int* index, ZIPENTRY* ze);
// FindZipItem - finds an item by name. ic means 'insensitive to case'.
// It returns the index of the item, and returns information about it.
// If nothing was found, then index is set to -1 and the function returns
// an error code.
ZRESULT UnzipItem(HZIP hz, int index, const TCHAR *fn);
ZRESULT UnzipItem(HZIP hz, int index, void *z,unsigned int len);
ZRESULT UnzipItem(HZIP hz, int index, const TCHAR* fn);
ZRESULT UnzipItem(HZIP hz, int index, void* z, unsigned int len);
ZRESULT UnzipItemHandle(HZIP hz, int index, HANDLE h);
// UnzipItem - given an index to an item, unzips it. You can unzip to:
// to a pipe: UnzipItemHandle(hz,i, hpipe_write);
@@ -85,7 +86,7 @@ ZRESULT UnzipItemHandle(HZIP hz, int index, HANDLE h);
// If you unzip a directory with ZIP_FILENAME, then the directory gets created.
// If you unzip it to a handle or a memory block, then nothing gets created
// and it emits 0 bytes.
ZRESULT SetUnzipBaseDir(HZIP hz, const TCHAR *dir);
ZRESULT SetUnzipBaseDir(HZIP hz, const TCHAR* dir);
// if unzipping to a filename, and it's a relative filename, then it will be relative to here.
// (defaults to current-directory).
@@ -93,7 +94,7 @@ ZRESULT SetUnzipBaseDir(HZIP hz, const TCHAR *dir);
ZRESULT CloseZip(HZIP hz);
// CloseZip - the zip handle must be closed with this function.
unsigned int FormatZipMessage(ZRESULT code, TCHAR *buf,unsigned int len);
unsigned int FormatZipMessage(ZRESULT code, TCHAR* buf, unsigned int len);
// FormatZipMessage - given an error code, formats it as a string.
// It returns the length of the error message. If buf/len points
// to a real buffer, then it also writes as much as possible into there.
@@ -199,7 +200,7 @@ unsigned int FormatZipMessage(ZRESULT code, TCHAR *buf,unsigned int len);
// one or the other of them based on a dynamic choice. If the header file
// for only one is present, then we will bind to that particular one.
ZRESULT CloseZipU(HZIP hz);
unsigned int FormatZipMessageU(ZRESULT code, TCHAR *buf,unsigned int len);
unsigned int FormatZipMessageU(ZRESULT code, TCHAR* buf, unsigned int len);
bool IsZipHandleU(HZIP hz);
#ifdef _zip_H
#undef CloseZip

View File

@@ -22,156 +22,146 @@ typedef BOOL(WINAPI* SetDefaultDllDirectoriesFunction)(DWORD DirectoryFlags);
// If we pre-load them with an absolute path then we are good.
void PreloadLibs()
{
wchar_t sys32Folder[MAX_PATH];
GetSystemDirectory(sys32Folder, MAX_PATH);
wchar_t sys32Folder[MAX_PATH];
GetSystemDirectory(sys32Folder, MAX_PATH);
std::wstring version = (std::wstring(sys32Folder) + L"\\version.dll");
std::wstring logoncli = (std::wstring(sys32Folder) + L"\\logoncli.dll");
std::wstring sspicli = (std::wstring(sys32Folder) + L"\\sspicli.dll");
std::wstring version = (std::wstring(sys32Folder) + L"\\version.dll");
std::wstring logoncli = (std::wstring(sys32Folder) + L"\\logoncli.dll");
std::wstring sspicli = (std::wstring(sys32Folder) + L"\\sspicli.dll");
LoadLibrary(version.c_str());
LoadLibrary(logoncli.c_str());
LoadLibrary(sspicli.c_str());
LoadLibrary(version.c_str());
LoadLibrary(logoncli.c_str());
LoadLibrary(sspicli.c_str());
}
void MitigateDllHijacking()
{
// Set the default DLL lookup directory to System32 for ourselves and kernel32.dll
SetDefaultDllDirectories(LOAD_LIBRARY_SEARCH_SYSTEM32);
// Set the default DLL lookup directory to System32 for ourselves and kernel32.dll
SetDefaultDllDirectories(LOAD_LIBRARY_SEARCH_SYSTEM32);
HMODULE hKernel32 = LoadLibrary(L"kernel32.dll");
ATLASSERT(hKernel32 != NULL);
HMODULE hKernel32 = LoadLibrary(L"kernel32.dll");
ATLASSERT(hKernel32 != NULL);
SetDefaultDllDirectoriesFunction pfn = (SetDefaultDllDirectoriesFunction)GetProcAddress(hKernel32, "SetDefaultDllDirectories");
if (pfn) { (*pfn)(LOAD_LIBRARY_SEARCH_SYSTEM32); }
SetDefaultDllDirectoriesFunction pfn = (SetDefaultDllDirectoriesFunction)GetProcAddress(hKernel32, "SetDefaultDllDirectories");
if (pfn) { (*pfn)(LOAD_LIBRARY_SEARCH_SYSTEM32); }
PreloadLibs();
PreloadLibs();
}
int APIENTRY wWinMain(
_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPWSTR lpCmdLine,
_In_ int nCmdShow)
_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPWSTR lpCmdLine,
_In_ int nCmdShow)
{
MitigateDllHijacking();
MitigateDllHijacking();
int exitCode = -1;
CString cmdLine(lpCmdLine);
int exitCode = -1;
CString cmdLine(lpCmdLine);
// see if the requested framework(s) are supported and exit.
int chkFrameworkIdx = cmdLine.Find(L"--checkFramework");
if (chkFrameworkIdx >= 0) {
return VerifyRuntimeString(std::wstring(cmdLine).substr(chkFrameworkIdx + 17));
}
// see if the requested framework(s) are supported and exit.
int chkFrameworkIdx = cmdLine.Find(L"--checkFramework");
if (chkFrameworkIdx >= 0)
return VerifyRuntimeString(std::wstring(cmdLine).substr(chkFrameworkIdx + 17));
if (cmdLine.Find(L"--checkInstall") >= 0) {
// If we're already installed, exit as fast as possible
if (!MachineInstaller::ShouldSilentInstall()) {
return 0;
}
if (cmdLine.Find(L"--checkInstall") >= 0) {
// If we're already installed, exit as fast as possible
if (!MachineInstaller::ShouldSilentInstall())
return 0;
// Make sure update.exe gets silent
wcscat(lpCmdLine, L" --silent");
}
// Make sure update.exe gets silent
wcscat(lpCmdLine, L" --silent");
}
// Initialize COM
HRESULT hr = ::CoInitialize(NULL);
ATLASSERT(SUCCEEDED(hr));
AtlInitCommonControls(ICC_COOL_CLASSES | ICC_BAR_CLASSES);
// Initialize COM
HRESULT hr = ::CoInitialize(NULL);
ATLASSERT(SUCCEEDED(hr));
AtlInitCommonControls(ICC_COOL_CLASSES | ICC_BAR_CLASSES);
// Initialize ATL
_Module = new CAppModule();
hr = _Module->Init(NULL, hInstance);
// Initialize ATL
_Module = new CAppModule();
hr = _Module->Init(NULL, hInstance);
// Initialize GDI
ULONG_PTR gdiplusToken;
Gdiplus::GdiplusStartupInput gdiplusStartupInput;
Gdiplus::GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
CSplashWnd splash;
// Initialize GDI
ULONG_PTR gdiplusToken;
Gdiplus::GdiplusStartupInput gdiplusStartupInput;
Gdiplus::GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
CSplashWnd splash;
std::function<void()> closeSplashFn([&]() { splash.Hide(); });
bool isQuiet = (cmdLine.Find(L"-s") >= 0);
bool weAreUACElevated = CUpdateRunner::AreWeUACElevated() == S_OK;
bool attemptingToRerun = (cmdLine.Find(L"--rerunningWithoutUAC") >= 0);
bool isQuiet = (cmdLine.Find(L"-s") >= 0);
bool weAreUACElevated = CUpdateRunner::AreWeUACElevated() == S_OK;
bool attemptingToRerun = (cmdLine.Find(L"--rerunningWithoutUAC") >= 0);
// todo: make min windows version configurable (eg, > windows 10 18362)
auto runtimes = GetRequiredRuntimes();
// todo: make min windows version configurable (eg, > windows 10 18362)
auto runtimes = GetRequiredRuntimes();
if (weAreUACElevated && attemptingToRerun) {
CUpdateRunner::DisplayErrorMessage(CString(L"Please re-run this installer as a normal user instead of \"Run as Administrator\"."), NULL);
exitCode = E_FAIL;
goto out;
}
if (weAreUACElevated && attemptingToRerun) {
CUpdateRunner::DisplayErrorMessage(CString(L"Please re-run this installer as a normal user instead of \"Run as Administrator\"."), NULL);
exitCode = E_FAIL;
goto out;
}
bool rebootRequired = false;
bool rebootRequired = false;
// check all required runtimes against the current operating system version
for (auto rt : runtimes)
{
if (!IsRuntimeSupported(rt))
{
// Explain this as nicely as possible and give up.
MessageBox(0L, L"This program cannot run on this computer; it requires a later version of Windows.", L"Incompatible Operating System", 0);
exitCode = E_FAIL;
goto out;
}
}
// check all required runtimes against the current operating system version
for (auto rt : runtimes) {
if (!IsRuntimeSupported(rt)) {
// Explain this as nicely as possible and give up.
MessageBox(0L, L"This program cannot run on this computer; it requires a later version of Windows.", L"Incompatible Operating System", 0);
exitCode = E_FAIL;
goto out;
}
}
// install any missing runtimes
for (auto rt : runtimes)
{
if (!IsRuntimeInstalled(rt))
{
HRESULT hr = CFxHelper::InstallDotnet(rt, isQuiet);
if (hr == ERROR_SUCCESS_REBOOT_REQUIRED)
{
// we will reboot after installing all required runtimes.
rebootRequired = true;
}
else if (FAILED(hr))
{
exitCode = hr; // #yolo
CUpdateRunner::DisplayErrorMessage(CString(L"Failed to install .NET, you can try installing it manually."), NULL);
goto out;
}
// S_FALSE isn't failure, but we still shouldn't try to install
else if (hr != S_OK)
{
exitCode = 0;
goto out;
}
}
}
// install any missing runtimes
for (auto rt : runtimes) {
if (!IsRuntimeInstalled(rt)) {
HRESULT hr = CFxHelper::InstallDotnet(rt, isQuiet);
if (hr == ERROR_SUCCESS_REBOOT_REQUIRED) {
// we will reboot after installing all required runtimes.
rebootRequired = true;
}
else if (FAILED(hr)) {
exitCode = hr; // #yolo
CUpdateRunner::DisplayErrorMessage(CString(L"Failed to install .NET, you can try installing it manually."), NULL);
goto out;
}
// S_FALSE isn't failure, but we still shouldn't try to install
else if (hr != S_OK) {
exitCode = 0;
goto out;
}
}
}
// if any runtimes indicated a reboot is required, let's do that now
if (rebootRequired)
{
exitCode = CFxHelper::HandleRebootRequirement(isQuiet);
goto out;
}
// if any runtimes indicated a reboot is required, let's do that now
if (rebootRequired) {
exitCode = CFxHelper::HandleRebootRequirement(isQuiet);
goto out;
}
// If we're UAC-elevated, we shouldn't be because it will give us permissions
// problems later. Just silently rerun ourselves.
if (weAreUACElevated) {
wchar_t buf[4096];
HMODULE hMod = GetModuleHandle(NULL);
GetModuleFileNameW(hMod, buf, 4096);
wcscat(lpCmdLine, L" --rerunningWithoutUAC");
// If we're UAC-elevated, we shouldn't be because it will give us permissions
// problems later. Just silently rerun ourselves.
if (weAreUACElevated) {
wchar_t buf[4096];
HMODULE hMod = GetModuleHandle(NULL);
GetModuleFileNameW(hMod, buf, 4096);
wcscat(lpCmdLine, L" --rerunningWithoutUAC");
CUpdateRunner::ShellExecuteFromExplorer(buf, lpCmdLine);
exitCode = 0;
goto out;
}
CUpdateRunner::ShellExecuteFromExplorer(buf, lpCmdLine);
exitCode = 0;
goto out;
}
splash.SetImage(MAKEINTRESOURCE(IDR_SPLASH_IMG), L"DATA");
splash.Show();
splash.SetImage(MAKEINTRESOURCE(IDR_SPLASH_IMG), L"DATA");
splash.Show();
// run updater
exitCode = CUpdateRunner::ExtractUpdaterAndRun(lpCmdLine, false, closeSplashFn);
// run updater
exitCode = CUpdateRunner::ExtractUpdaterAndRun(lpCmdLine, false, closeSplashFn);
out:
splash.Hide();
_Module->Term();
return exitCode;
splash.Hide();
_Module->Term();
return exitCode;
}

View File

@@ -31,82 +31,91 @@ SOFTWARE.
using namespace std;
namespace version {
namespace version
{
namespace {
namespace
{
// Compare normal version identifiers.
int compare_normal(const Version_data& l, const Version_data& r) {
if (l.major > r.major) return 1;
if (l.major < r.major) return -1;
if (l.minor > r.minor) return 1;
if (l.minor < r.minor) return -1;
if (l.patch > r.patch) return 1;
if (l.patch < r.patch) return -1;
return 0;
}
// Compare normal version identifiers.
int compare_normal(const Version_data& l, const Version_data& r)
{
if (l.major > r.major) return 1;
if (l.major < r.major) return -1;
if (l.minor > r.minor) return 1;
if (l.minor < r.minor) return -1;
if (l.patch > r.patch) return 1;
if (l.patch < r.patch) return -1;
return 0;
}
// Compare alphanumeric prerelease identifiers.
inline int cmp_alnum_prerel_ids(const string& l, const string& r) {
auto cmp = l.compare(r);
if (cmp == 0) {
return cmp;
} else {
return cmp > 0 ? 1 : -1;
}
}
// Compare alphanumeric prerelease identifiers.
inline int cmp_alnum_prerel_ids(const string& l, const string& r)
{
auto cmp = l.compare(r);
if (cmp == 0) {
return cmp;
}
else {
return cmp > 0 ? 1 : -1;
}
}
// Compare numeric prerelease identifiers.
inline int cmp_num_prerel_ids(const string& l, const string& r) {
long long li = stoll(l);
long long ri = stoll(r);
if (li == ri) return 0;
return li > ri ? 1 : -1;
}
// Compare numeric prerelease identifiers.
inline int cmp_num_prerel_ids(const string& l, const string& r)
{
long long li = stoll(l);
long long ri = stoll(r);
if (li == ri) return 0;
return li > ri ? 1 : -1;
}
using Prerel_type_pair = pair<Id_type, Id_type>;
using Prerel_id_comparator = function<int(const string&, const string&)>;
const map<Prerel_type_pair, Prerel_id_comparator> comparators = {
{ { Id_type::alnum, Id_type::alnum }, cmp_alnum_prerel_ids },
{ { Id_type::alnum, Id_type::num }, [](const string&, const string&) {return 1;} },
{ { Id_type::num, Id_type::alnum }, [](const string&, const string&) {return -1;} },
{ { Id_type::num, Id_type::num }, cmp_num_prerel_ids }
};
using Prerel_type_pair = pair<Id_type, Id_type>;
using Prerel_id_comparator = function<int(const string&, const string&)>;
const map<Prerel_type_pair, Prerel_id_comparator> comparators = {
{ { Id_type::alnum, Id_type::alnum }, cmp_alnum_prerel_ids },
{ { Id_type::alnum, Id_type::num }, [](const string&, const string&) {return 1;} },
{ { Id_type::num, Id_type::alnum }, [](const string&, const string&) {return -1;} },
{ { Id_type::num, Id_type::num }, cmp_num_prerel_ids }
};
// Compare prerelease identifiers based on their types.
inline int compare_prerel_identifiers(const Prerelease_identifier& l, const Prerelease_identifier& r) {
auto cmp = comparators.at({ l.second, r.second });
return cmp(l.first, r.first);
}
// Compare prerelease identifiers based on their types.
inline int compare_prerel_identifiers(const Prerelease_identifier& l, const Prerelease_identifier& r)
{
auto cmp = comparators.at({ l.second, r.second });
return cmp(l.first, r.first);
}
inline int cmp_rel_prerel(const Prerelease_identifiers& l, const Prerelease_identifiers& r) {
if (l.empty() && !r.empty()) return 1;
if (r.empty() && !l.empty()) return -1;
return 0;
}
}
inline int cmp_rel_prerel(const Prerelease_identifiers& l, const Prerelease_identifiers& r)
{
if (l.empty() && !r.empty()) return 1;
if (r.empty() && !l.empty()) return -1;
return 0;
}
}
int Semver200_comparator::compare(const Version_data& l, const Version_data& r) const {
// Compare normal version components.
int cmp = compare_normal(l, r);
if (cmp != 0) return cmp;
int Semver200_comparator::compare(const Version_data& l, const Version_data& r) const
{
// Compare normal version components.
int cmp = compare_normal(l, r);
if (cmp != 0) return cmp;
// Compare if one version is release and the other prerelease - release is always higher.
cmp = cmp_rel_prerel(l.prerelease_ids, r.prerelease_ids);
if (cmp != 0) return cmp;
// Compare if one version is release and the other prerelease - release is always higher.
cmp = cmp_rel_prerel(l.prerelease_ids, r.prerelease_ids);
if (cmp != 0) return cmp;
// Compare prerelease by looking at each identifier: numeric ones are compared as numbers,
// alphanum as ASCII strings.
auto shorter = min(l.prerelease_ids.size(), r.prerelease_ids.size());
for (size_t i = 0; i < shorter; i++) {
cmp = compare_prerel_identifiers(l.prerelease_ids[i], r.prerelease_ids[i]);
if (cmp != 0) return cmp;
}
// Compare prerelease by looking at each identifier: numeric ones are compared as numbers,
// alphanum as ASCII strings.
auto shorter = min(l.prerelease_ids.size(), r.prerelease_ids.size());
for (size_t i = 0; i < shorter; i++) {
cmp = compare_prerel_identifiers(l.prerelease_ids[i], r.prerelease_ids[i]);
if (cmp != 0) return cmp;
}
// Prerelease identifiers are the same, to the length of the shorter version string;
// if they are the same length, then versions are equal, otherwise, longer one wins.
if (l.prerelease_ids.size() == r.prerelease_ids.size()) return 0;
return l.prerelease_ids.size() > r.prerelease_ids.size() ? 1 : -1;
}
// Prerelease identifiers are the same, to the length of the shorter version string;
// if they are the same length, then versions are equal, otherwise, longer one wins.
if (l.prerelease_ids.size() == r.prerelease_ids.size()) return 0;
return l.prerelease_ids.size() > r.prerelease_ids.size() ? 1 : -1;
}
}

View File

@@ -35,176 +35,190 @@ SOFTWARE.
using namespace std;
namespace version {
namespace version
{
namespace {
enum class Parser_state {
major, minor, patch, prerelease, build
};
namespace
{
enum class Parser_state
{
major, minor, patch, prerelease, build
};
using Validator = function<void(const string&, const char)>;
using State_transition_hook = function<void(string&)>;
/// State transition is described by a character that triggers it, a state to transition to and
/// optional hook to be invoked on transition.
using Transition = tuple<const char, Parser_state, State_transition_hook>;
using Transitions = vector<Transition>;
using State = tuple<Transitions, string&, Validator>;
using State_machine = map<Parser_state, State>;
using Validator = function<void(const string&, const char)>;
using State_transition_hook = function<void(string&)>;
/// State transition is described by a character that triggers it, a state to transition to and
/// optional hook to be invoked on transition.
using Transition = tuple<const char, Parser_state, State_transition_hook>;
using Transitions = vector<Transition>;
using State = tuple<Transitions, string&, Validator>;
using State_machine = map<Parser_state, State>;
// Ranges of characters allowed in prerelease and build identifiers.
const vector<pair<char, char>> allowed_prerel_id_chars = {
{ '0', '9' },{ 'A','Z' },{ 'a','z' },{ '-','-' }
};
// Ranges of characters allowed in prerelease and build identifiers.
const vector<pair<char, char>> allowed_prerel_id_chars = {
{ '0', '9' },{ 'A','Z' },{ 'a','z' },{ '-','-' }
};
inline Transition mkx(const char c, Parser_state p, State_transition_hook pth) {
return make_tuple(c, p, pth);
}
inline Transition mkx(const char c, Parser_state p, State_transition_hook pth)
{
return make_tuple(c, p, pth);
}
/// Advance parser state machine by a single step.
/**
Perform single step of parser state machine: if character matches one from transition tables -
trigger transition to next state; otherwise, validate if current token is in legal state
(throw Parse_error if not) and then add character to current token; State transition includes
preparing various vars for next state and invoking state transition hook (if specified) which is
where whole tokens are validated.
*/
inline void process_char(const char c, Parser_state& cstate, Parser_state& pstate,
const Transitions& transitions, string& target, Validator validate) {
for (const auto& transition : transitions) {
if (c == get<0>(transition)) {
if (get<2>(transition)) get<2>(transition)(target);
pstate = cstate;
cstate = get<1>(transition);
return;
}
}
validate(target, c);
target.push_back(c);
}
/// Advance parser state machine by a single step.
/**
Perform single step of parser state machine: if character matches one from transition tables -
trigger transition to next state; otherwise, validate if current token is in legal state
(throw Parse_error if not) and then add character to current token; State transition includes
preparing various vars for next state and invoking state transition hook (if specified) which is
where whole tokens are validated.
*/
inline void process_char(const char c, Parser_state& cstate, Parser_state& pstate,
const Transitions& transitions, string& target, Validator validate)
{
for (const auto& transition : transitions) {
if (c == get<0>(transition)) {
if (get<2>(transition)) get<2>(transition)(target);
pstate = cstate;
cstate = get<1>(transition);
return;
}
}
validate(target, c);
target.push_back(c);
}
/// Validate normal (major, minor, patch) version components.
inline void normal_version_validator(const string& tgt, const char c) {
if (c < '0' || c > '9') throw Parse_error("invalid character encountered: " + string(1, c));
if (tgt.compare(0, 1, "0") == 0) throw Parse_error("leading 0 not allowed");
}
/// Validate normal (major, minor, patch) version components.
inline void normal_version_validator(const string& tgt, const char c)
{
if (c < '0' || c > '9') throw Parse_error("invalid character encountered: " + string(1, c));
if (tgt.compare(0, 1, "0") == 0) throw Parse_error("leading 0 not allowed");
}
/// Validate that prerelease and build version identifiers are comprised of allowed chars only.
inline void prerelease_version_validator(const string&, const char c) {
bool res = false;
for (const auto& r : allowed_prerel_id_chars) {
res |= (c >= r.first && c <= r.second);
}
if (!res)
throw Parse_error("invalid character encountered: " + string(1, c));
}
/// Validate that prerelease and build version identifiers are comprised of allowed chars only.
inline void prerelease_version_validator(const string&, const char c)
{
bool res = false;
for (const auto& r : allowed_prerel_id_chars) {
res |= (c >= r.first && c <= r.second);
}
if (!res)
throw Parse_error("invalid character encountered: " + string(1, c));
}
inline bool is_identifier_numeric(const string& id) {
return id.find_first_not_of("0123456789") == string::npos;
}
inline bool is_identifier_numeric(const string& id)
{
return id.find_first_not_of("0123456789") == string::npos;
}
inline bool check_for_leading_0(const string& str) {
return str.length() > 1 && str[0] == '0';
}
inline bool check_for_leading_0(const string& str)
{
return str.length() > 1 && str[0] == '0';
}
/// Validate every individual prerelease identifier, determine it's type and add it to collection.
void prerelease_hook_impl(string& id, Prerelease_identifiers& prerelease) {
if (id.empty()) throw Parse_error("version identifier cannot be empty");
Id_type t = Id_type::alnum;
if (is_identifier_numeric(id)) {
t = Id_type::num;
if (check_for_leading_0(id)) {
throw Parse_error("numeric identifiers cannot have leading 0");
}
}
prerelease.push_back(Prerelease_identifier(id, t));
id.clear();
}
/// Validate every individual prerelease identifier, determine it's type and add it to collection.
void prerelease_hook_impl(string& id, Prerelease_identifiers& prerelease)
{
if (id.empty()) throw Parse_error("version identifier cannot be empty");
Id_type t = Id_type::alnum;
if (is_identifier_numeric(id)) {
t = Id_type::num;
if (check_for_leading_0(id)) {
throw Parse_error("numeric identifiers cannot have leading 0");
}
}
prerelease.push_back(Prerelease_identifier(id, t));
id.clear();
}
/// Validate every individual build identifier and add it to collection.
void build_hook_impl(string& id, Parser_state& pstate, Build_identifiers& build,
std::string& prerelease_id, Prerelease_identifiers& prerelease) {
// process last token left from parsing prerelease data
if (pstate == Parser_state::prerelease) prerelease_hook_impl(prerelease_id, prerelease);
if (id.empty()) throw Parse_error("version identifier cannot be empty");
build.push_back(id);
id.clear();
}
/// Validate every individual build identifier and add it to collection.
void build_hook_impl(string& id, Parser_state& pstate, Build_identifiers& build,
std::string& prerelease_id, Prerelease_identifiers& prerelease)
{
// process last token left from parsing prerelease data
if (pstate == Parser_state::prerelease) prerelease_hook_impl(prerelease_id, prerelease);
if (id.empty()) throw Parse_error("version identifier cannot be empty");
build.push_back(id);
id.clear();
}
}
}
/// Parse semver 2.0.0-compatible string to Version_data structure.
/**
Version text parser is implemented as a state machine. In each step one successive character from version
string is consumed and is either added to current token or triggers state transition. Hooks can be
injected into state transitions for validation/customization purposes.
*/
Version_data Semver200_parser::parse(const string& s) const {
string major;
string minor;
string patch;
string prerelease_id;
string build_id;
Prerelease_identifiers prerelease;
Build_identifiers build;
Parser_state cstate{ Parser_state::major };
Parser_state pstate;
/// Parse semver 2.0.0-compatible string to Version_data structure.
/**
Version text parser is implemented as a state machine. In each step one successive character from version
string is consumed and is either added to current token or triggers state transition. Hooks can be
injected into state transitions for validation/customization purposes.
*/
Version_data Semver200_parser::parse(const string& s) const
{
string major;
string minor;
string patch;
string prerelease_id;
string build_id;
Prerelease_identifiers prerelease;
Build_identifiers build;
Parser_state cstate{ Parser_state::major };
Parser_state pstate;
auto prerelease_hook = [&](string& id) {
prerelease_hook_impl(id, prerelease);
};
auto prerelease_hook = [&](string& id) {
prerelease_hook_impl(id, prerelease);
};
auto build_hook = [&](string& id) {
build_hook_impl(id, pstate, build, prerelease_id, prerelease);
};
auto build_hook = [&](string& id) {
build_hook_impl(id, pstate, build, prerelease_id, prerelease);
};
// State transition tables
auto major_trans = {
mkx('.', Parser_state::minor, {})
};
auto minor_trans = {
mkx('.', Parser_state::patch, {})
};
auto patch_trans = {
mkx('-', Parser_state::prerelease, {}),
mkx('+', Parser_state::build, {})
};
auto prerelease_trans = {
// When identifier separator (.) is found, stay in the same state but invoke hook
// in order to process each individual identifier separately.
mkx('.', Parser_state::prerelease, prerelease_hook),
mkx('+', Parser_state::build, {})
};
auto build_trans = {
// Same stay-in-the-same-state-but-invoke-hook trick from above.
mkx('.', Parser_state::build, build_hook)
};
// State transition tables
auto major_trans = {
mkx('.', Parser_state::minor, {})
};
auto minor_trans = {
mkx('.', Parser_state::patch, {})
};
auto patch_trans = {
mkx('-', Parser_state::prerelease, {}),
mkx('+', Parser_state::build, {})
};
auto prerelease_trans = {
// When identifier separator (.) is found, stay in the same state but invoke hook
// in order to process each individual identifier separately.
mkx('.', Parser_state::prerelease, prerelease_hook),
mkx('+', Parser_state::build, {})
};
auto build_trans = {
// Same stay-in-the-same-state-but-invoke-hook trick from above.
mkx('.', Parser_state::build, build_hook)
};
State_machine state_machine = {
{Parser_state::major, State{major_trans, major, normal_version_validator}},
{Parser_state::minor, State{minor_trans, minor, normal_version_validator}},
{Parser_state::patch, State{patch_trans, patch, normal_version_validator}},
{Parser_state::prerelease, State{prerelease_trans, prerelease_id, prerelease_version_validator}},
{Parser_state::build, State{build_trans, build_id, prerelease_version_validator}}
};
State_machine state_machine = {
{Parser_state::major, State{major_trans, major, normal_version_validator}},
{Parser_state::minor, State{minor_trans, minor, normal_version_validator}},
{Parser_state::patch, State{patch_trans, patch, normal_version_validator}},
{Parser_state::prerelease, State{prerelease_trans, prerelease_id, prerelease_version_validator}},
{Parser_state::build, State{build_trans, build_id, prerelease_version_validator}}
};
// Main loop.
for (const auto& c : s) {
auto state = state_machine.at(cstate);
process_char(c, cstate, pstate, get<0>(state), get<1>(state), get<2>(state));
}
// Main loop.
for (const auto& c : s) {
auto state = state_machine.at(cstate);
process_char(c, cstate, pstate, get<0>(state), get<1>(state), get<2>(state));
}
// Trigger appropriate hooks in order to process last token, because no state transition was
// triggered for it.
if (cstate == Parser_state::prerelease) {
prerelease_hook(prerelease_id);
} else if (cstate == Parser_state::build) {
build_hook(build_id);
}
// Trigger appropriate hooks in order to process last token, because no state transition was
// triggered for it.
if (cstate == Parser_state::prerelease) {
prerelease_hook(prerelease_id);
}
else if (cstate == Parser_state::build) {
build_hook(build_id);
}
try {
return Version_data{ stoi(major), stoi(minor), stoi(patch), prerelease, build };
} catch (invalid_argument& ex) {
throw Parse_error(ex.what());
}
}
try {
return Version_data{ stoi(major), stoi(minor), stoi(patch), prerelease, build };
}
catch (invalid_argument& ex) {
throw Parse_error(ex.what());
}
}
}

View File

@@ -10,79 +10,79 @@ using namespace std;
wchar_t* FindRootAppDir()
{
wchar_t* ourDirectory = new wchar_t[MAX_PATH];
wchar_t* ourDirectory = new wchar_t[MAX_PATH];
GetModuleFileName(GetModuleHandle(NULL), ourDirectory, MAX_PATH);
wchar_t* lastSlash = wcsrchr(ourDirectory, L'\\');
if (!lastSlash) {
delete[] ourDirectory;
return NULL;
}
GetModuleFileName(GetModuleHandle(NULL), ourDirectory, MAX_PATH);
wchar_t* lastSlash = wcsrchr(ourDirectory, L'\\');
if (!lastSlash) {
delete[] ourDirectory;
return NULL;
}
// Null-terminate the string at the slash so now it's a directory
*lastSlash = 0x0;
return ourDirectory;
// Null-terminate the string at the slash so now it's a directory
*lastSlash = 0x0;
return ourDirectory;
}
wchar_t* FindOwnExecutableName()
{
wchar_t* ourDirectory = new wchar_t[MAX_PATH];
wchar_t* ourDirectory = new wchar_t[MAX_PATH];
GetModuleFileName(GetModuleHandle(NULL), ourDirectory, MAX_PATH);
wchar_t* lastSlash = wcsrchr(ourDirectory, L'\\');
if (!lastSlash) {
delete[] ourDirectory;
return NULL;
}
GetModuleFileName(GetModuleHandle(NULL), ourDirectory, MAX_PATH);
wchar_t* lastSlash = wcsrchr(ourDirectory, L'\\');
if (!lastSlash) {
delete[] ourDirectory;
return NULL;
}
wchar_t* ret = _wcsdup(lastSlash + 1);
delete[] ourDirectory;
return ret;
wchar_t* ret = _wcsdup(lastSlash + 1);
delete[] ourDirectory;
return ret;
}
std::wstring FindLatestAppDir()
{
std::wstring ourDir;
ourDir.assign(FindRootAppDir());
std::wstring ourDir;
ourDir.assign(FindRootAppDir());
ourDir += L"\\app-*";
ourDir += L"\\app-*";
WIN32_FIND_DATA fileInfo = { 0 };
HANDLE hFile = FindFirstFile(ourDir.c_str(), &fileInfo);
if (hFile == INVALID_HANDLE_VALUE) {
return NULL;
}
WIN32_FIND_DATA fileInfo = { 0 };
HANDLE hFile = FindFirstFile(ourDir.c_str(), &fileInfo);
if (hFile == INVALID_HANDLE_VALUE) {
return NULL;
}
version::Semver200_version acc("0.0.0");
std::wstring acc_s;
version::Semver200_version acc("0.0.0");
std::wstring acc_s;
do {
std::wstring appVer = fileInfo.cFileName;
appVer = appVer.substr(4); // Skip 'app-'
if (!(fileInfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
continue;
}
do {
std::wstring appVer = fileInfo.cFileName;
appVer = appVer.substr(4); // Skip 'app-'
if (!(fileInfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
continue;
}
std::string s(appVer.begin(), appVer.end());
std::string s(appVer.begin(), appVer.end());
version::Semver200_version thisVer(s);
version::Semver200_version thisVer(s);
if (thisVer > acc) {
acc = thisVer;
acc_s = appVer;
}
} while (FindNextFile(hFile, &fileInfo));
if (thisVer > acc) {
acc = thisVer;
acc_s = appVer;
}
} while (FindNextFile(hFile, &fileInfo));
if (acc == version::Semver200_version("0.0.0")) {
return NULL;
}
if (acc == version::Semver200_version("0.0.0")) {
return NULL;
}
ourDir.assign(FindRootAppDir());
std::wstringstream ret;
ret << ourDir << L"\\app-" << acc_s;
ourDir.assign(FindRootAppDir());
std::wstringstream ret;
ret << ourDir << L"\\app-" << acc_s;
FindClose(hFile);
return ret.str();
FindClose(hFile);
return ret.str();
}
int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
@@ -90,31 +90,31 @@ int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
_In_ LPWSTR lpCmdLine,
_In_ int nCmdShow)
{
std::wstring appName;
appName.assign(FindOwnExecutableName());
std::wstring appName;
appName.assign(FindOwnExecutableName());
std::wstring workingDir(FindLatestAppDir());
std::wstring fullPath(workingDir + L"\\" + appName);
std::wstring workingDir(FindLatestAppDir());
std::wstring fullPath(workingDir + L"\\" + appName);
STARTUPINFO si = { 0 };
PROCESS_INFORMATION pi = { 0 };
STARTUPINFO si = { 0 };
PROCESS_INFORMATION pi = { 0 };
si.cb = sizeof(si);
si.dwFlags = STARTF_USESHOWWINDOW;
si.wShowWindow = nCmdShow;
si.cb = sizeof(si);
si.dwFlags = STARTF_USESHOWWINDOW;
si.wShowWindow = nCmdShow;
std::wstring cmdLine(L"\"");
cmdLine += fullPath;
cmdLine += L"\" ";
cmdLine += lpCmdLine;
std::wstring cmdLine(L"\"");
cmdLine += fullPath;
cmdLine += L"\" ";
cmdLine += lpCmdLine;
wchar_t* lpCommandLine = _wcsdup(cmdLine.c_str());
wchar_t* lpCurrentDirectory = _wcsdup(workingDir.c_str());
if (!CreateProcess(NULL, lpCommandLine, NULL, NULL, true, 0, NULL, lpCurrentDirectory, &si, &pi)) {
return -1;
}
wchar_t* lpCommandLine = _wcsdup(cmdLine.c_str());
wchar_t* lpCurrentDirectory = _wcsdup(workingDir.c_str());
if (!CreateProcess(NULL, lpCommandLine, NULL, NULL, true, 0, NULL, lpCurrentDirectory, &si, &pi)) {
return -1;
}
AllowSetForegroundWindow(pi.dwProcessId);
WaitForInputIdle(pi.hProcess, 5 * 1000);
return 0;
AllowSetForegroundWindow(pi.dwProcessId);
WaitForInputIdle(pi.hProcess, 5 * 1000);
return 0;
}

View File

@@ -26,26 +26,32 @@ SOFTWARE.
#include "version.h"
namespace version {
namespace version
{
/// Parse string into Version_data structure according to semantic versioning 2.0.0 rules.
struct Semver200_parser {
Version_data parse(const std::string&) const;
};
/// Parse string into Version_data structure according to semantic versioning 2.0.0 rules.
struct Semver200_parser
{
Version_data parse(const std::string&) const;
};
/// Compare Version_data to another using semantic versioning 2.0.0 rules.
struct Semver200_comparator {
int compare(const Version_data&, const Version_data&) const;
};
/// Compare Version_data to another using semantic versioning 2.0.0 rules.
struct Semver200_comparator
{
int compare(const Version_data&, const Version_data&) const;
};
/// Concrete version class that binds all semver 2.0.0 functionality together.
class Semver200_version : public Basic_version<Semver200_parser, Semver200_comparator> {
public:
Semver200_version()
: Basic_version{ Semver200_parser(), Semver200_comparator() } {}
/// Concrete version class that binds all semver 2.0.0 functionality together.
class Semver200_version : public Basic_version<Semver200_parser, Semver200_comparator>
{
public:
Semver200_version()
: Basic_version{ Semver200_parser(), Semver200_comparator() }
{}
Semver200_version(const std::string& v)
: Basic_version{ v, Semver200_parser(), Semver200_comparator() } {}
};
Semver200_version(const std::string& v)
: Basic_version{ v, Semver200_parser(), Semver200_comparator() }
{}
};
}

View File

@@ -28,129 +28,134 @@ SOFTWARE.
#include <string>
#include <vector>
namespace version {
namespace version
{
/// Any error in parsing or validation of version string will result in Parse_error exception being thrown.
class Parse_error : public std::runtime_error {
using std::runtime_error::runtime_error;
};
/// Any error in parsing or validation of version string will result in Parse_error exception being thrown.
class Parse_error : public std::runtime_error
{
using std::runtime_error::runtime_error;
};
/// Type of prerelease identifier: alphanumeric or numeric.
/**
Type of identifier affects comparison: alphanumeric identifiers are compared as ASCII strings, while
numeric identifiers are compared as numbers.
*/
enum class Id_type {
alnum, ///< Identifier is alphanumerical
num ///< Identifier is numeric
};
/// Type of prerelease identifier: alphanumeric or numeric.
/**
Type of identifier affects comparison: alphanumeric identifiers are compared as ASCII strings, while
numeric identifiers are compared as numbers.
*/
enum class Id_type
{
alnum, ///< Identifier is alphanumerical
num ///< Identifier is numeric
};
/// Container for prerelease identifier value and it's type.
/**
Prerelease version string consist of an optional series of dot-separated identifiers.
These identifiers can be either numerical or alphanumerical.
This structure describes one such identifier.
*/
using Prerelease_identifier = std::pair<std::string, Id_type>;
/// Container for prerelease identifier value and it's type.
/**
Prerelease version string consist of an optional series of dot-separated identifiers.
These identifiers can be either numerical or alphanumerical.
This structure describes one such identifier.
*/
using Prerelease_identifier = std::pair<std::string, Id_type>;
/// Container for all prerelease identifiers for a given version string.
using Prerelease_identifiers = std::vector<Prerelease_identifier>;
/// Container for all prerelease identifiers for a given version string.
using Prerelease_identifiers = std::vector<Prerelease_identifier>;
/// Build identifier is arbitrary string with no special meaning with regards to version precedence.
using Build_identifier = std::string;
/// Build identifier is arbitrary string with no special meaning with regards to version precedence.
using Build_identifier = std::string;
/// Container for all build identifiers of a given version string.
using Build_identifiers = std::vector<Build_identifier>;
/// Container for all build identifiers of a given version string.
using Build_identifiers = std::vector<Build_identifier>;
/// Description of version broken into parts, as per semantic versioning specification.
struct Version_data {
int major; ///< Major version, change only on incompatible API modifications.
int minor; ///< Minor version, change on backwards-compatible API modifications.
int patch; ///< Patch version, change only on bugfixes.
/// Description of version broken into parts, as per semantic versioning specification.
struct Version_data
{
int major; ///< Major version, change only on incompatible API modifications.
int minor; ///< Minor version, change on backwards-compatible API modifications.
int patch; ///< Patch version, change only on bugfixes.
/// Optional series of prerelease identifiers.
Prerelease_identifiers prerelease_ids;
/// Optional series of prerelease identifiers.
Prerelease_identifiers prerelease_ids;
/// Optional series of build identifiers.
Build_identifiers build_ids;
};
/// Optional series of build identifiers.
Build_identifiers build_ids;
};
// Forward declaration required for operators' template declarations.
template<typename Parser, typename Comparator>
class Basic_version;
// Forward declaration required for operators' template declarations.
template<typename Parser, typename Comparator>
class Basic_version;
/// Test if left-hand version operand is of lower precedence than the right-hand version.
template<typename Parser, typename Comparator>
bool operator<(const Basic_version<Parser, Comparator>&,
const Basic_version<Parser, Comparator>&);
/// Test if left-hand version operand is of lower precedence than the right-hand version.
template<typename Parser, typename Comparator>
bool operator<(const Basic_version<Parser, Comparator>&,
const Basic_version<Parser, Comparator>&);
/// Test if left-hand version operand if of equal precedence as the right-hand version.
template<typename Parser, typename Comparator>
bool operator==(const Basic_version<Parser, Comparator>&,
const Basic_version<Parser, Comparator>&);
/// Test if left-hand version operand if of equal precedence as the right-hand version.
template<typename Parser, typename Comparator>
bool operator==(const Basic_version<Parser, Comparator>&,
const Basic_version<Parser, Comparator>&);
/// Output version object to stream using standard semver format (X.Y.Z-PR+B).
template<typename Parser, typename Comparator>
std::ostream& operator<<(std::ostream&,
const Basic_version<Parser, Comparator>&);
/// Output version object to stream using standard semver format (X.Y.Z-PR+B).
template<typename Parser, typename Comparator>
std::ostream& operator<<(std::ostream&,
const Basic_version<Parser, Comparator>&);
/// Test if left-hand version and right-hand version are of different precedence.
template<typename Parser, typename Comparator>
bool operator!=(const Basic_version<Parser, Comparator>&,
const Basic_version<Parser, Comparator>&);
/// Test if left-hand version and right-hand version are of different precedence.
template<typename Parser, typename Comparator>
bool operator!=(const Basic_version<Parser, Comparator>&,
const Basic_version<Parser, Comparator>&);
/// Test if left-hand version operand is of higher precedence than the right-hand version.
template<typename Parser, typename Comparator>
bool operator>(const Basic_version<Parser, Comparator>&,
const Basic_version<Parser, Comparator>&);
/// Test if left-hand version operand is of higher precedence than the right-hand version.
template<typename Parser, typename Comparator>
bool operator>(const Basic_version<Parser, Comparator>&,
const Basic_version<Parser, Comparator>&);
/// Test if left-hand version operand is of higher or equal precedence as the right-hand version.
template<typename Parser, typename Comparator>
bool operator>=(const Basic_version<Parser, Comparator>&,
const Basic_version<Parser, Comparator>&);
/// Test if left-hand version operand is of higher or equal precedence as the right-hand version.
template<typename Parser, typename Comparator>
bool operator>=(const Basic_version<Parser, Comparator>&,
const Basic_version<Parser, Comparator>&);
/// Test if left-hand version operand is of lower or equal precedence as the right-hand version.
template<typename Parser, typename Comparator>
bool operator<=(const Basic_version<Parser, Comparator>&,
const Basic_version<Parser, Comparator>&);
/// Test if left-hand version operand is of lower or equal precedence as the right-hand version.
template<typename Parser, typename Comparator>
bool operator<=(const Basic_version<Parser, Comparator>&,
const Basic_version<Parser, Comparator>&);
/// Base class for various version parsing and precedence ordering schemes.
/**
Basic_version class describes general version object without prescribing parsing,
validation and comparison rules. These rules are implemented by supplied Parser and
Comparator objects.
*/
template<typename Parser, typename Comparator>
class Basic_version {
public:
/// Construct Basic_version object using Parser object to parse default ("0.0.0") version string and Comparator for comparison.
Basic_version(Parser, Comparator);
/// Base class for various version parsing and precedence ordering schemes.
/**
Basic_version class describes general version object without prescribing parsing,
validation and comparison rules. These rules are implemented by supplied Parser and
Comparator objects.
*/
template<typename Parser, typename Comparator>
class Basic_version
{
public:
/// Construct Basic_version object using Parser object to parse default ("0.0.0") version string and Comparator for comparison.
Basic_version(Parser, Comparator);
/// Construct Basic_version object using Parser to parse supplied version string and Comparator for comparison.
Basic_version(const std::string&, Parser, Comparator);
/// Construct Basic_version object using Parser to parse supplied version string and Comparator for comparison.
Basic_version(const std::string&, Parser, Comparator);
/// Construct Basic_version by copying data from another one.
Basic_version(const Basic_version&);
/// Construct Basic_version by copying data from another one.
Basic_version(const Basic_version&);
/// Copy version data from another Basic_version to this one.
Basic_version& operator=(const Basic_version&);
/// Copy version data from another Basic_version to this one.
Basic_version& operator=(const Basic_version&);
int major() const; ///< Get major version.
int minor() const; ///< Get minor version.
int patch() const; ///< Get patch version.
const std::string prerelease() const; ///< Get prerelease version string.
const std::string build() const; ///< Get build version string.
int major() const; ///< Get major version.
int minor() const; ///< Get minor version.
int patch() const; ///< Get patch version.
const std::string prerelease() const; ///< Get prerelease version string.
const std::string build() const; ///< Get build version string.
friend bool operator< <>(const Basic_version&, const Basic_version&);
friend bool operator== <>(const Basic_version&, const Basic_version&);
friend std::ostream& operator<< <>(std::ostream&s, const Basic_version&);
friend bool operator< <>(const Basic_version&, const Basic_version&);
friend bool operator== <>(const Basic_version&, const Basic_version&);
friend std::ostream& operator<< <>(std::ostream& s, const Basic_version&);
private:
Parser parser_;
Comparator comparator_;
Version_data ver_;
};
private:
Parser parser_;
Comparator comparator_;
Version_data ver_;
};
}
#include "version.inl"

View File

@@ -13,171 +13,171 @@ using namespace std;
BOOL CALLBACK EnumResLangProc(HMODULE hModule, LPCTSTR lpszType, LPCTSTR lpszName, WORD wIDLanguage, LONG_PTR lParam)
{
HANDLE hUpdate = (HANDLE)lParam;
HRSRC hFindItAgain = FindResourceEx(hModule, lpszType, lpszName, wIDLanguage);
HANDLE hUpdate = (HANDLE)lParam;
HRSRC hFindItAgain = FindResourceEx(hModule, lpszType, lpszName, wIDLanguage);
HGLOBAL hGlobal = LoadResource(hModule, hFindItAgain);
if (!hGlobal) return true;
HGLOBAL hGlobal = LoadResource(hModule, hFindItAgain);
if (!hGlobal) return true;
UpdateResource(hUpdate, lpszType, lpszName, wIDLanguage, LockResource(hGlobal), SizeofResource(hModule, hFindItAgain));
return true;
UpdateResource(hUpdate, lpszType, lpszName, wIDLanguage, LockResource(hGlobal), SizeofResource(hModule, hFindItAgain));
return true;
}
BOOL CALLBACK EnumResNameProc(HMODULE hModule, LPCTSTR lpszType, LPTSTR lpszName, LONG_PTR lParam)
{
HANDLE hUpdate = (HANDLE)lParam;
HANDLE hUpdate = (HANDLE)lParam;
EnumResourceLanguages(hModule, lpszType, lpszName, EnumResLangProc, (LONG_PTR)hUpdate);
return true;
EnumResourceLanguages(hModule, lpszType, lpszName, EnumResLangProc, (LONG_PTR)hUpdate);
return true;
}
BOOL CALLBACK EnumResTypeProc(HMODULE hMod, LPTSTR lpszType, LONG_PTR lParam)
{
std::vector<wchar_t*>* typeList = (std::vector<wchar_t*>*)lParam;
if (IS_INTRESOURCE(lpszType)) {
typeList->push_back(lpszType);
}
else {
typeList->push_back(_wcsdup(lpszType));
}
std::vector<wchar_t*>* typeList = (std::vector<wchar_t*>*)lParam;
if (IS_INTRESOURCE(lpszType)) {
typeList->push_back(lpszType);
}
else {
typeList->push_back(_wcsdup(lpszType));
}
return true;
return true;
}
int CopyResourcesToStubExecutable(wchar_t* src, wchar_t* dest)
{
HMODULE hSrc = LoadLibraryEx(src, NULL, LOAD_LIBRARY_AS_DATAFILE);
if (!hSrc) return GetLastError();
HMODULE hSrc = LoadLibraryEx(src, NULL, LOAD_LIBRARY_AS_DATAFILE);
if (!hSrc) return GetLastError();
HANDLE hUpdate = BeginUpdateResource(dest, true);
if (!hUpdate) return GetLastError();
HANDLE hUpdate = BeginUpdateResource(dest, true);
if (!hUpdate) return GetLastError();
std::vector<wchar_t*> typeList;
EnumResourceTypes(hSrc, EnumResTypeProc, (LONG_PTR)&typeList);
std::vector<wchar_t*> typeList;
EnumResourceTypes(hSrc, EnumResTypeProc, (LONG_PTR)&typeList);
for (auto& type : typeList) {
EnumResourceNames(hSrc, type, EnumResNameProc, (LONG_PTR)hUpdate);
}
for (auto& type : typeList) {
EnumResourceNames(hSrc, type, EnumResNameProc, (LONG_PTR)hUpdate);
}
EndUpdateResource(hUpdate, false);
return 0;
EndUpdateResource(hUpdate, false);
return 0;
}
int LoadFileIntoMemory(wstring fpath, BYTE** pBuf, int* cBuf)
{
HANDLE hFile = CreateFile(fpath.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
if (hFile == INVALID_HANDLE_VALUE) {
printf("Can't open file\n");
return E_FAIL;
}
HANDLE hFile = CreateFile(fpath.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
if (hFile == INVALID_HANDLE_VALUE) {
printf("Can't open file\n");
return E_FAIL;
}
BY_HANDLE_FILE_INFORMATION fileInfo;
if (!GetFileInformationByHandle(hFile, &fileInfo)) {
printf("Can't read file handle\n");
return E_FAIL;
}
BY_HANDLE_FILE_INFORMATION fileInfo;
if (!GetFileInformationByHandle(hFile, &fileInfo)) {
printf("Can't read file handle\n");
return E_FAIL;
}
*cBuf = fileInfo.nFileSizeLow;
*pBuf = new BYTE[fileInfo.nFileSizeLow + 0x1000];
*cBuf = fileInfo.nFileSizeLow;
*pBuf = new BYTE[fileInfo.nFileSizeLow + 0x1000];
BYTE* pCurrent = *pBuf;
DWORD dwBytesRead;
BYTE* pCurrent = *pBuf;
DWORD dwBytesRead;
printf("Starting to read file!\n");
do {
if (!ReadFile(hFile, pCurrent, 0x1000, &dwBytesRead, NULL)) {
printf("Failed to read file! 0x%u\n", GetLastError());
return E_FAIL;
}
printf("Starting to read file!\n");
do {
if (!ReadFile(hFile, pCurrent, 0x1000, &dwBytesRead, NULL)) {
printf("Failed to read file! 0x%u\n", GetLastError());
return E_FAIL;
}
pCurrent += dwBytesRead;
} while (dwBytesRead > 0);
pCurrent += dwBytesRead;
} while (dwBytesRead > 0);
return S_OK;
return S_OK;
}
int fail()
{
printf("Usage: WriteZipToSetup [Setup.exe template] [Zip File]\n");
return -1;
printf("Usage: WriteZipToSetup [Setup.exe template] [Zip File]\n");
return -1;
}
int wmain(int argc, wchar_t* argv[])
{
// short circuit exit for stub executable
if (argc > 1 && wcscmp(argv[1], L"--copy-stub-resources") == 0) {
if (argc != 4) return fail();
return CopyResourcesToStubExecutable(argv[2], argv[3]);
}
// short circuit exit for stub executable
if (argc > 1 && wcscmp(argv[1], L"--copy-stub-resources") == 0) {
if (argc != 4) return fail();
return CopyResourcesToStubExecutable(argv[2], argv[3]);
}
// parse command line arguments
const flags::args args(argc, argv);
const auto& parg = args.positional();
if (parg.size() != 2) {
return fail();
}
const auto setupFile = wstring(parg[0]);
const auto zipFile = wstring(parg[1]);
const auto requiredFramework = args.get<wstring>(L"set-required-framework");
const auto splashImage = args.get<wstring>(L"set-splash");
// parse command line arguments
const flags::args args(argc, argv);
const auto& parg = args.positional();
if (parg.size() != 2) {
return fail();
}
const auto setupFile = wstring(parg[0]);
const auto zipFile = wstring(parg[1]);
const auto requiredFramework = args.get<wstring>(L"set-required-framework");
const auto splashImage = args.get<wstring>(L"set-splash");
wprintf(L"Setup: %s, Zip: %s\n", setupFile.c_str(), zipFile.c_str());
wprintf(L"Setup: %s, Zip: %s\n", setupFile.c_str(), zipFile.c_str());
// Read the entire zip file into memory, yolo
BYTE *pZipBuf, *pSplashBuf;
int cZipBuf, cSplashBuf;
// Read the entire zip file into memory, yolo
BYTE* pZipBuf, * pSplashBuf;
int cZipBuf, cSplashBuf;
if (FAILED(LoadFileIntoMemory(zipFile, &pZipBuf, &cZipBuf))) {
printf("Couldn't read zip file.\n");
return fail();
}
if (FAILED(LoadFileIntoMemory(zipFile, &pZipBuf, &cZipBuf))) {
printf("Couldn't read zip file.\n");
return fail();
}
printf("Updating Resource!\n");
HANDLE hRes = BeginUpdateResource(setupFile.c_str(), false);
if (!hRes) {
printf("Couldn't open setup.exe for writing\n");
return fail();
}
printf("Updating Resource!\n");
HANDLE hRes = BeginUpdateResource(setupFile.c_str(), false);
if (!hRes) {
printf("Couldn't open setup.exe for writing\n");
return fail();
}
if (!UpdateResource(hRes, L"DATA", MAKEINTRESOURCE(IDR_UPDATE_ZIP), RESOURCE_LANG, pZipBuf, cZipBuf)) {
printf("Failed to update zip resource\n");
return fail();
}
if (!UpdateResource(hRes, L"DATA", MAKEINTRESOURCE(IDR_UPDATE_ZIP), RESOURCE_LANG, pZipBuf, cZipBuf)) {
printf("Failed to update zip resource\n");
return fail();
}
if (requiredFramework.has_value()) {
wstring sReq = requiredFramework.value();
LPVOID pReq = &sReq[0];
if (!UpdateResource(hRes, L"FLAGS", MAKEINTRESOURCE(IDR_FX_VERSION_FLAG), RESOURCE_LANG, pReq, (sReq.length() + 1) * sizeof(wchar_t))) {
printf("Failed to update required version resource\n");
return fail();
}
}
if (requiredFramework.has_value()) {
wstring sReq = requiredFramework.value();
LPVOID pReq = &sReq[0];
if (!UpdateResource(hRes, L"FLAGS", MAKEINTRESOURCE(IDR_FX_VERSION_FLAG), RESOURCE_LANG, pReq, (sReq.length() + 1) * sizeof(wchar_t))) {
printf("Failed to update required version resource\n");
return fail();
}
}
if (splashImage.has_value()) {
if (FAILED(LoadFileIntoMemory(splashImage.value(), &pSplashBuf, &cSplashBuf))) {
printf("Couldn't read splash image.\n");
return fail();
}
if (splashImage.has_value()) {
if (FAILED(LoadFileIntoMemory(splashImage.value(), &pSplashBuf, &cSplashBuf))) {
printf("Couldn't read splash image.\n");
return fail();
}
if (!UpdateResource(hRes, L"DATA", MAKEINTRESOURCE(IDR_SPLASH_IMG), RESOURCE_LANG, pSplashBuf, cSplashBuf)) {
printf("Failed to update splash resource\n");
return fail();
}
}
else {
if (!UpdateResource(hRes, L"DATA", MAKEINTRESOURCE(IDR_SPLASH_IMG), RESOURCE_LANG, pSplashBuf, cSplashBuf)) {
printf("Failed to update splash resource\n");
return fail();
}
}
else {
// if the user hasn't given us a splash screen, let's remove the default (there will be no splash at all)
if (!UpdateResource(hRes, L"DATA", MAKEINTRESOURCE(IDR_SPLASH_IMG), RESOURCE_LANG, 0, 0)) {
printf("Failed to update splash resource\n");
return fail();
}
}
if (!UpdateResource(hRes, L"DATA", MAKEINTRESOURCE(IDR_SPLASH_IMG), RESOURCE_LANG, 0, 0)) {
printf("Failed to update splash resource\n");
return fail();
}
}
printf("Finished!\n");
if (!EndUpdateResource(hRes, false)) {
printf("Failed to update resource\n");
return fail();
}
printf("Finished!\n");
if (!EndUpdateResource(hRes, false)) {
printf("Failed to update resource\n");
return fail();
}
printf("It worked!\n");
return 0;
printf("It worked!\n");
return 0;
}

View File

@@ -11,8 +11,10 @@
#include <unordered_map>
#include <vector>
namespace flags {
namespace detail {
namespace flags
{
namespace detail
{
using argument_map =
std::unordered_map<std::wstring_view, std::optional<std::wstring_view>>;
@@ -21,8 +23,10 @@ namespace flags {
// * If the token does not begin with a -, it will be considered a value for the
// previous option. If there was no previous option, it will be considered a
// positional argument.
struct parser {
parser(const int argc, wchar_t** argv) {
struct parser
{
parser(const int argc, wchar_t** argv)
{
for (int i = 1; i < argc; ++i) {
churn(argv[i]);
}
@@ -32,22 +36,26 @@ namespace flags {
parser& operator=(const parser&) = delete;
const argument_map& options() const { return options_; }
const std::vector<std::wstring_view>& positional_arguments() const {
const std::vector<std::wstring_view>& positional_arguments() const
{
return positional_arguments_;
}
private:
// Advance the state machine for the current token.
void churn(const std::wstring_view& item) {
void churn(const std::wstring_view& item)
{
item.at(0) == '-' ? on_option(item) : on_value(item);
}
// Consumes the current option if there is one.
void flush() {
void flush()
{
if (current_option_) on_value();
}
void on_option(const std::wstring_view& option) {
void on_option(const std::wstring_view& option)
{
// Consume the current_option and reassign it to the new option while
// removing all leading dashes.
flush();
@@ -64,7 +72,8 @@ namespace flags {
}
}
void on_value(const std::optional<std::wstring_view>& value = std::nullopt) {
void on_value(const std::optional<std::wstring_view>& value = std::nullopt)
{
// If there's not an option preceding the value, it's a positional argument.
if (!current_option_) {
if (value) positional_arguments_.emplace_back(*value);
@@ -82,7 +91,8 @@ namespace flags {
// If a key exists, return an optional populated with its value.
inline std::optional<std::wstring_view> get_value(
const argument_map& options, const std::wstring_view& option) {
const argument_map& options, const std::wstring_view& option)
{
if (const auto it = options.find(option); it != options.end()) {
return it->second;
}
@@ -94,7 +104,8 @@ namespace flags {
// nullopt.
template <class T>
std::optional<T> get(const argument_map& options,
const std::wstring_view& option) {
const std::wstring_view& option)
{
if (const auto view = get_value(options, option)) {
if (T value; std::istringstream(std::wstring(*view)) >> value) return value;
}
@@ -104,13 +115,15 @@ namespace flags {
// Since the values are already stored as strings, there's no need to use `>>`.
template <>
inline std::optional<std::wstring_view> get(const argument_map& options,
const std::wstring_view& option) {
const std::wstring_view& option)
{
return get_value(options, option);
}
template <>
inline std::optional<std::wstring> get(const argument_map& options,
const std::wstring_view& option) {
const std::wstring_view& option)
{
if (const auto view = get<std::wstring_view>(options, option)) {
return std::wstring(*view);
}
@@ -123,7 +136,8 @@ namespace flags {
constexpr std::array<const wchar_t*, 5> falsities{ {L"0", L"n", L"no", L"f", L"false"} };
template <>
inline std::optional<bool> get(const argument_map& options,
const std::wstring_view& option) {
const std::wstring_view& option)
{
if (const auto value = get_value(options, option)) {
return std::none_of(falsities.begin(), falsities.end(),
[&value](auto falsity) { return *value == falsity; });
@@ -137,7 +151,8 @@ namespace flags {
// nullopt.
template <class T>
std::optional<T> get(const std::vector<std::wstring_view>& positional_arguments,
size_t positional_index) {
size_t positional_index)
{
if (positional_index < positional_arguments.size()) {
if (T value; std::istringstream(
std::wstring(positional_arguments[positional_index])) >>
@@ -151,7 +166,8 @@ namespace flags {
template <>
inline std::optional<std::wstring_view> get(
const std::vector<std::wstring_view>& positional_arguments,
size_t positional_index) {
size_t positional_index)
{
if (positional_index < positional_arguments.size()) {
return positional_arguments[positional_index];
}
@@ -161,7 +177,8 @@ namespace flags {
template <>
inline std::optional<std::wstring> get(
const std::vector<std::wstring_view>& positional_arguments,
size_t positional_index) {
size_t positional_index)
{
if (positional_index < positional_arguments.size()) {
return std::wstring(positional_arguments[positional_index]);
}
@@ -169,30 +186,36 @@ namespace flags {
}
} // namespace detail
struct args {
struct args
{
args(const int argc, wchar_t** argv) : parser_(argc, argv) {}
template <class T>
std::optional<T> get(const std::wstring_view& option) const {
std::optional<T> get(const std::wstring_view& option) const
{
return detail::get<T>(parser_.options(), option);
}
template <class T>
T get(const std::wstring_view& option, T&& default_value) const {
T get(const std::wstring_view& option, T&& default_value) const
{
return get<T>(option).value_or(default_value);
}
template <class T>
std::optional<T> get(size_t positional_index) const {
std::optional<T> get(size_t positional_index) const
{
return detail::get<T>(parser_.positional_arguments(), positional_index);
}
template <class T>
T get(size_t positional_index, T&& default_value) const {
T get(size_t positional_index, T&& default_value) const
{
return get<T>(positional_index).value_or(default_value);
}
const std::vector<std::wstring_view>& positional() const {
const std::vector<std::wstring_view>& positional() const
{
return parser_.positional_arguments();
}