mirror of
https://github.com/velopack/velopack.git
synced 2025-10-25 15:19:22 +00:00
Add C++ to editorconfig & update formatting. No functional changes.
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
};
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -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"");
|
||||
|
||||
wchar_t installFolder[MAX_PATH];
|
||||
CString fullPath = CString(ourFile);
|
||||
CString pkgName = CString(ourFile + fullPath.ReverseFind(L'\\'));
|
||||
pkgName.Replace(L"DeploymentTool.exe", L"");
|
||||
|
||||
// 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
|
||||
wchar_t installFolder[MAX_PATH];
|
||||
|
||||
// C:\Users\Username\AppData\Local\$pkgName
|
||||
SHGetFolderPath(NULL, CSIDL_LOCAL_APPDATA, NULL, SHGFP_TYPE_CURRENT, installFolder);
|
||||
wcscat(installFolder, L"\\");
|
||||
wcscat(installFolder, pkgName);
|
||||
// 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
|
||||
|
||||
if (directoryExists(installFolder)) {
|
||||
return false;
|
||||
}
|
||||
// C:\Users\Username\AppData\Local\$pkgName
|
||||
SHGetFolderPath(NULL, CSIDL_LOCAL_APPDATA, NULL, SHGFP_TYPE_CURRENT, installFolder);
|
||||
wcscat(installFolder, L"\\");
|
||||
wcscat(installFolder, pkgName);
|
||||
|
||||
// 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;
|
||||
}
|
||||
// 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);
|
||||
|
||||
// None of these exist, we should install
|
||||
return true;
|
||||
if (directoryExists(installFolder)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// None of these exist, we should install
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
#pragma once
|
||||
class MachineInstaller
|
||||
{
|
||||
|
||||
public:
|
||||
static bool ShouldSilentInstall();
|
||||
static bool ShouldSilentInstall();
|
||||
|
||||
};
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
};
|
||||
|
||||
4875
src/Setup/unzip.cpp
4875
src/Setup/unzip.cpp
File diff suppressed because it is too large
Load Diff
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,81 +8,81 @@
|
||||
|
||||
using namespace std;
|
||||
|
||||
wchar_t* FindRootAppDir()
|
||||
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* 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 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;
|
||||
}
|
||||
|
||||
@@ -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() }
|
||||
{}
|
||||
};
|
||||
|
||||
}
|
||||
@@ -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"
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user