From da4bd8ccf3a9a4884e48546c54975152db755b2b Mon Sep 17 00:00:00 2001 From: Nick D'Ademo Date: Thu, 15 Sep 2016 10:41:20 +0200 Subject: [PATCH 1/9] Handle connection closed events during download (#114) There are cases where INTERNET_STATUS_CONNECTION_CLOSED events could be triggered during a download transfer (due to network connectivity issues). Previously, WinSparkle would erroneously report the download as successfully completed due to the fact that InternetReadFileEx would return TRUE with 'dwBufferLength' set to zero after the connection closed event. This commit aims to gracefully handle these events (by throwing an exception) via doing the following: - When an INTERNET_STATUS_CONNECTION_CLOSED event is triggered, immediately close the connection handle so that future API calls will return failure (e.g. ERROR_INVALID_HANDLE). - If the download has completed (indicated by 'dwBufferLength' being zero), check that the connection handle is still valid. This detects the case when the INTERNET_STATUS_CONNECTION_CLOSED event is received and the connection handle is closed during a call to InternetReadFileEx. --- src/download.cpp | 31 ++++++++++++++++++++++++------- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/src/download.cpp b/src/download.cpp index 7c40402c..c4d28736 100644 --- a/src/download.cpp +++ b/src/download.cpp @@ -51,12 +51,7 @@ struct InetHandle ~InetHandle() { - if (m_handle) - { - if (m_callback) - InternetSetStatusCallback(m_handle, NULL); - InternetCloseHandle(m_handle); - } + Close(); } void SetStatusCallback(INTERNET_STATUS_CALLBACK callback) @@ -65,6 +60,17 @@ struct InetHandle InternetSetStatusCallback(m_handle, m_callback); } + void Close() + { + if (m_handle) + { + if (m_callback) + InternetSetStatusCallback(m_handle, NULL); + InternetCloseHandle(m_handle); + m_handle = NULL; + } + } + operator HINTERNET() const { return m_handle; } HINTERNET m_handle; @@ -142,6 +148,10 @@ void CALLBACK DownloadInternetStatusCallback(_In_ HINTERNET hInternet, case INTERNET_STATUS_REQUEST_COMPLETE: context->eventRequestComplete.Signal(); break; + + case INTERNET_STATUS_CONNECTION_CLOSED: + context->conn->Close(); + break; } } @@ -297,7 +307,14 @@ void DownloadFile(const std::string& url, IDownloadSink *sink, Thread *onThread, } if (ibuf.dwBufferLength == 0) - break; // all of the file was downloaded + { + // This check is required in case the INTERNET_STATUS_CONNECTION_CLOSED event was + // received (and the handle was closed) during the call to InternetReadFileEx() + if (!conn) + throw Win32Exception(); + else + break; // all of the file was downloaded + } sink->Add(ibuf.lpvBuffer, ibuf.dwBufferLength); } From 4b5acfb6418d395af8309bb36cd571627fed616b Mon Sep 17 00:00:00 2001 From: Jared Burkeen Date: Tue, 20 Sep 2016 10:20:50 -0400 Subject: [PATCH 2/9] UX changes, don't use caching for loading release notes, check for updates in the background --- src/dll_api.cpp | 93 ++++++++++--------------------- src/ui.cpp | 17 +++--- src/updatechecker.cpp | 126 +++++++++++++++++++++++++++--------------- src/updatechecker.h | 4 +- 4 files changed, 122 insertions(+), 118 deletions(-) diff --git a/src/dll_api.cpp b/src/dll_api.cpp index b665f618..35af0ae0 100644 --- a/src/dll_api.cpp +++ b/src/dll_api.cpp @@ -46,70 +46,35 @@ extern "C" WIN_SPARKLE_API void __cdecl win_sparkle_init() { - try - { - // finish initialization - if (!Settings::GetLanguage().IsOk()) - { - LANGID lang = 0; - if (IsWindowsVistaOrGreater()) - { - auto f_GetThreadUILanguage = LOAD_DYNAMIC_FUNC(GetThreadUILanguage, kernel32); - if (f_GetThreadUILanguage) - lang = f_GetThreadUILanguage(); - } - if (PRIMARYLANGID(lang) == 0) - { - lang = LANGIDFROMLCID(GetThreadLocale()); - } - if (PRIMARYLANGID(lang) != 0) - Settings::SetLanguage(lang); - } - - // first things first - UpdateDownloader::CleanLeftovers(); - - // check for updates - bool checkUpdates; - if ( Settings::ReadConfigValue("CheckForUpdates", checkUpdates) ) - { - if ( checkUpdates ) - { - static const time_t ONE_DAY = 60*60*24; - - time_t lastCheck = 0; - Settings::ReadConfigValue("LastCheckTime", lastCheck); - const time_t currentTime = time(NULL); - - // Only check for updates in reasonable intervals: - const int interval = win_sparkle_get_update_check_interval(); - if ( currentTime - lastCheck >= interval ) - { - // Run the check in background. Only show UI if updates - // are available. - UpdateChecker *check = new UpdateChecker(); - check->Start(); - } - } - } - else // not yet configured - { - bool didRunOnce; - Settings::ReadConfigValue("DidRunOnce", didRunOnce, false); - if ( !didRunOnce ) - { - // Do nothing on the first execution of the app, for better - // first-time impression. - Settings::WriteConfigValue("DidRunOnce", true); - } - else - { - // Only when the app is launched for the second time, ask the - // user for their permission to check for updates. - UI::AskForPermission(); - } - } - } + try + { + // finish initialization + if (!Settings::GetLanguage().IsOk()) + { + LANGID lang = 0; + if (IsWindowsVistaOrGreater()) + { + auto f_GetThreadUILanguage = LOAD_DYNAMIC_FUNC(GetThreadUILanguage, kernel32); + if (f_GetThreadUILanguage) + lang = f_GetThreadUILanguage(); + } + if (PRIMARYLANGID(lang) == 0) + { + lang = LANGIDFROMLCID(GetThreadLocale()); + } + if (PRIMARYLANGID(lang) != 0) + Settings::SetLanguage(lang); + } + + // first things first + UpdateDownloader::CleanLeftovers(); + + // Run the check in background. Only show UI if updates + // are available. + UpdateChecker *check = new UpdateChecker(); + check->Start(); + } + CATCH_ALL_EXCEPTIONS } diff --git a/src/ui.cpp b/src/ui.cpp index b8cbdb11..17cd8420 100644 --- a/src/ui.cpp +++ b/src/ui.cpp @@ -320,13 +320,11 @@ class WinSparkleDialog : public wxDialog WinSparkleDialog::WinSparkleDialog() : wxDialog(NULL, wxID_ANY, _("Software Update"), wxDefaultPosition, wxDefaultSize, - wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER | wxDIALOG_NO_PARENT) + wxDEFAULT_DIALOG_STYLE | wxDIALOG_NO_PARENT) { wxSize dpi = wxClientDC(this).GetPPI(); m_scaleFactor = dpi.y / 96.0; - SetIcon(LoadNamedIcon(UI::GetDllHINSTANCE(), L"UpdateAvailable", GetSystemMetrics(SM_CXSMICON))); - wxSizer *topSizer = new wxBoxSizer(wxHORIZONTAL); // Load the dialog box icon: the first 48x48 application icon will be loaded, if available, @@ -388,7 +386,6 @@ void WinSparkleDialog::SetHeadingFont(wxWindow *win) // 9pt is base font, 12pt is for "Main instructions". See // http://msdn.microsoft.com/en-us/library/aa511282%28v=MSDN.10%29.aspx f.SetPointSize(f.GetPointSize() + 3); - win->SetForegroundColour(wxColour(0x00, 0x33, 0x99)); } else // Windows XP/2000 { @@ -543,7 +540,7 @@ UpdateDialog::UpdateDialog() ); m_updateButtonsSizer->Add ( - m_installButton = new wxButton(this, ID_INSTALL, _("Install update")), + m_installButton = new wxButton(this, ID_INSTALL, _("Download")), wxSizerFlags() ); m_buttonSizer->Add(m_updateButtonsSizer, wxSizerFlags(1)); @@ -1003,10 +1000,14 @@ void UpdateDialog::ShowReleaseNotes(const Appcast& info) if( !info.ReleaseNotesURL.empty() ) { - m_webBrowser->Navigate + VARIANT varFlags; + varFlags.vt = VT_I4; + varFlags.llVal = (navNoHistory | navNoReadFromCache | navNoWriteToCache); + + m_webBrowser->Navigate ( wxBasicString(info.ReleaseNotesURL), - NULL, // Flags + &varFlags, // Flags NULL, // TargetFrameName NULL, // PostData NULL // Headers @@ -1071,7 +1072,7 @@ void UpdateDialog::ShowReleaseNotes(const Appcast& info) } } - SetWindowStyleFlag(wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER); + SetWindowStyleFlag(wxDEFAULT_DIALOG_STYLE); } diff --git a/src/updatechecker.cpp b/src/updatechecker.cpp index 437b990a..925558ff 100644 --- a/src/updatechecker.cpp +++ b/src/updatechecker.cpp @@ -36,6 +36,7 @@ #include #include #include +#include using namespace std; @@ -219,52 +220,80 @@ UpdateChecker::UpdateChecker(): Thread("WinSparkle updates check") void UpdateChecker::Run() { - // no initialization to do, so signal readiness immediately - SignalReady(); - - try - { - const std::string url = Settings::GetAppcastURL(); - if ( url.empty() ) - throw std::runtime_error("Appcast URL not specified."); - CheckForInsecureURL(url, "appcast feed"); - - StringDownloadSink appcast_xml; - DownloadFile(url, &appcast_xml, this, GetAppcastDownloadFlags()); - - Appcast appcast = Appcast::Load(appcast_xml.data); - if (!appcast.ReleaseNotesURL.empty()) - CheckForInsecureURL(appcast.ReleaseNotesURL, "release notes"); - if (!appcast.DownloadURL.empty()) - CheckForInsecureURL(appcast.DownloadURL, "update file"); - - Settings::WriteConfigValue("LastCheckTime", time(NULL)); - - const std::string currentVersion = - WideToAnsi(Settings::GetAppBuildVersion()); - - // Check if our version is out of date. - if ( !appcast.IsValid() || CompareVersions(currentVersion, appcast.Version) >= 0 ) - { - // The same or newer version is already installed. - UI::NotifyNoUpdates(ShouldAutomaticallyInstall()); - return; - } - - // Check if the user opted to ignore this particular version. - if ( ShouldSkipUpdate(appcast) ) - { - UI::NotifyNoUpdates(ShouldAutomaticallyInstall()); - return; - } + while (1) + { + // no initialization to do, so signal readiness immediately + SignalReady(); + + bool checkUpdates; + if (Settings::ReadConfigValue("CheckForUpdates", checkUpdates)) + { + if (checkUpdates) + { + static const time_t ONE_DAY = 60 * 60 * 24; + + time_t lastCheck = 0; + Settings::ReadConfigValue("LastCheckTime", lastCheck); + const time_t currentTime = time(NULL); + + // Only check for updates in reasonable intervals: + const int interval = win_sparkle_get_update_check_interval(); + if (currentTime - lastCheck >= interval) + { + UpdateCheckWorker(); + } + } + } + // Check every 5 minutes + Sleep(300000); + } +} - UI::NotifyUpdateAvailable(appcast, ShouldAutomaticallyInstall()); - } - catch ( ... ) - { - UI::NotifyUpdateError(); - throw; - } +void UpdateChecker::UpdateCheckWorker() +{ + try + { + const std::string url = Settings::GetAppcastURL(); + if (url.empty()) + throw std::runtime_error("Appcast URL not specified."); + CheckForInsecureURL(url, "appcast feed"); + + StringDownloadSink appcast_xml; + DownloadFile(url, &appcast_xml, this, GetAppcastDownloadFlags()); + + Appcast appcast = Appcast::Load(appcast_xml.data); + if (!appcast.ReleaseNotesURL.empty()) + CheckForInsecureURL(appcast.ReleaseNotesURL, "release notes"); + if (!appcast.DownloadURL.empty()) + CheckForInsecureURL(appcast.DownloadURL, "update file"); + + Settings::WriteConfigValue("LastCheckTime", time(NULL)); + + const std::string currentVersion = + WideToAnsi(Settings::GetAppBuildVersion()); + + // Check if our version is out of date. + if (!appcast.IsValid() || CompareVersions(currentVersion, appcast.Version) >= 0) + { + // The same or newer version is already installed. + UI::NotifyNoUpdates(ShouldAutomaticallyInstall()); + return; + } + + // Check if the user opted to ignore this particular version. + if (ShouldSkipUpdate(appcast)) + { + UI::NotifyNoUpdates(ShouldAutomaticallyInstall()); + return; + } + + UI::NotifyUpdateAvailable(appcast, ShouldAutomaticallyInstall()); + } + catch (...) + { + UI::NotifyUpdateError(); + throw; + } } bool UpdateChecker::ShouldSkipUpdate(const Appcast& appcast) const @@ -302,4 +331,11 @@ bool ManualUpdateChecker::ShouldSkipUpdate(const Appcast&) const return false; } +void ManualUpdateChecker::Run() +{ + // no initialization to do, so signal readiness immediately + SignalReady(); + + UpdateCheckWorker(); +} } // namespace winsparkle diff --git a/src/updatechecker.h b/src/updatechecker.h index 1009e395..74e1ec0b 100644 --- a/src/updatechecker.h +++ b/src/updatechecker.h @@ -70,7 +70,8 @@ class UpdateChecker : public Thread virtual bool ShouldAutomaticallyInstall() const { return false; } protected: - virtual void Run(); + void Run(); + virtual void UpdateCheckWorker(); virtual bool IsJoinable() const { return false; } }; @@ -87,6 +88,7 @@ class ManualUpdateChecker : public UpdateChecker protected: virtual int GetAppcastDownloadFlags() const; virtual bool ShouldSkipUpdate(const Appcast& appcast) const; + void Run(); }; From 125cc73cd5ad16ba861f5aa2c8f207aaa978baa1 Mon Sep 17 00:00:00 2001 From: Jared Burkeen Date: Mon, 26 Sep 2016 04:30:36 -0400 Subject: [PATCH 3/9] Change sleep time to update check interval --- src/updatechecker.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/updatechecker.cpp b/src/updatechecker.cpp index 925558ff..1c30e329 100644 --- a/src/updatechecker.cpp +++ b/src/updatechecker.cpp @@ -230,8 +230,6 @@ void UpdateChecker::Run() { if (checkUpdates) { - static const time_t ONE_DAY = 60 * 60 * 24; - time_t lastCheck = 0; Settings::ReadConfigValue("LastCheckTime", lastCheck); const time_t currentTime = time(NULL); @@ -245,7 +243,7 @@ void UpdateChecker::Run() } } // Check every 5 minutes - Sleep(300000); + Sleep(win_sparkle_get_update_check_interval()); } } From c69d80455c500c3cc0846a2b90cdced49e858f4d Mon Sep 17 00:00:00 2001 From: Jared Burkeen Date: Mon, 3 Oct 2016 11:48:34 -0400 Subject: [PATCH 4/9] UX updates --- src/ui.cpp | 25 +++++-------------------- 1 file changed, 5 insertions(+), 20 deletions(-) diff --git a/src/ui.cpp b/src/ui.cpp index 17cd8420..c496f11d 100644 --- a/src/ui.cpp +++ b/src/ui.cpp @@ -750,27 +750,12 @@ void UpdateDialog::StateNoUpdateFound(bool installAutomatically) LayoutChangesGuard guard(this); - m_heading->SetLabel(_("You're up to date!")); - - wxString msg; - try - { - msg = wxString::Format - ( - _("%s %s is currently the newest version available."), - Settings::GetAppName(), - Settings::GetAppVersion() - ); - } - catch ( std::exception& ) - { - // GetAppVersion() may fail - msg = "Error: Updates checking not properly configured."; - } + m_heading->SetLabel(_("You are up to date!")); + wxString msg = _("There are no new versions available at this time."); SetMessage(msg); - m_closeButton->SetLabel(_("Close")); + m_closeButton->SetLabel(_("OK")); m_closeButton->SetDefault(); EnablePulsing(false); @@ -851,8 +836,8 @@ void UpdateDialog::StateUpdateAvailable(const Appcast& info, bool installAutomat ( wxString::Format ( - _("%s %s is now available (you have %s). Would you like to download it now?"), - appname, ver_new, ver_my + _("You are currently using %s version %s.\nWould you like to download our new version (%s) now?"), + appname, ver_my, ver_new ), showRelnotes ? RELNOTES_WIDTH : MESSAGE_AREA_WIDTH ); From 9fe3222acd848636c5f30319957536fc0555b1dd Mon Sep 17 00:00:00 2001 From: Jared Burkeen Date: Tue, 4 Oct 2016 09:29:00 -0400 Subject: [PATCH 5/9] UX updates --- src/ui.cpp | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/src/ui.cpp b/src/ui.cpp index c496f11d..f5e8932e 100644 --- a/src/ui.cpp +++ b/src/ui.cpp @@ -401,8 +401,8 @@ void WinSparkleDialog::SetHeadingFont(wxWindow *win) Window for communicating with the user *--------------------------------------------------------------------------*/ -const int ID_SKIP_VERSION = wxNewId(); -const int ID_REMIND_LATER = wxNewId(); +const int ID_REMIND_HOUR = wxNewId(); +const int ID_REMIND_TOMORROW = wxNewId(); const int ID_INSTALL = wxNewId(); const int ID_RUN_INSTALLER = wxNewId(); @@ -432,8 +432,8 @@ class UpdateDialog : public WinSparkleDialog void OnCloseButton(wxCommandEvent& event); void OnClose(wxCloseEvent& event); - void OnSkipVersion(wxCommandEvent&); - void OnRemindLater(wxCommandEvent&); + void OnRemindOneHour(wxCommandEvent&); + void OnRemindTomorrow(wxCommandEvent&); void OnInstall(wxCommandEvent&); void OnRunInstaller(wxCommandEvent&); @@ -529,20 +529,21 @@ UpdateDialog::UpdateDialog() m_updateButtonsSizer = new wxBoxSizer(wxHORIZONTAL); m_updateButtonsSizer->Add ( - new wxButton(this, ID_SKIP_VERSION, _("Skip this version")), + new wxButton(this, ID_REMIND_HOUR, _("Remind me in an hour")), wxSizerFlags().Border(wxRIGHT, PX(20)) ); - m_updateButtonsSizer->AddStretchSpacer(1); m_updateButtonsSizer->Add ( - new wxButton(this, ID_REMIND_LATER, _("Remind me later")), + new wxButton(this, ID_REMIND_TOMORROW, _("Remind me tomorrow")), wxSizerFlags().Border(wxRIGHT, PX(10)) ); - m_updateButtonsSizer->Add + m_updateButtonsSizer->AddStretchSpacer(1); + m_updateButtonsSizer->Add ( m_installButton = new wxButton(this, ID_INSTALL, _("Download")), wxSizerFlags() ); + m_installButton->SetFocus(); m_buttonSizer->Add(m_updateButtonsSizer, wxSizerFlags(1)); m_closeButtonSizer = new wxBoxSizer(wxHORIZONTAL); @@ -569,8 +570,8 @@ UpdateDialog::UpdateDialog() Bind(wxEVT_CLOSE_WINDOW, &UpdateDialog::OnClose, this); Bind(wxEVT_TIMER, &UpdateDialog::OnTimer, this); Bind(wxEVT_COMMAND_BUTTON_CLICKED, &UpdateDialog::OnCloseButton, this, wxID_CANCEL); - Bind(wxEVT_COMMAND_BUTTON_CLICKED, &UpdateDialog::OnSkipVersion, this, ID_SKIP_VERSION); - Bind(wxEVT_COMMAND_BUTTON_CLICKED, &UpdateDialog::OnRemindLater, this, ID_REMIND_LATER); + Bind(wxEVT_COMMAND_BUTTON_CLICKED, &UpdateDialog::OnRemindOneHour, this, ID_REMIND_HOUR); + Bind(wxEVT_COMMAND_BUTTON_CLICKED, &UpdateDialog::OnRemindTomorrow, this, ID_REMIND_TOMORROW); Bind(wxEVT_COMMAND_BUTTON_CLICKED, &UpdateDialog::OnInstall, this, ID_INSTALL); Bind(wxEVT_COMMAND_BUTTON_CLICKED, &UpdateDialog::OnRunInstaller, this, ID_RUN_INSTALLER); } @@ -626,17 +627,18 @@ void UpdateDialog::OnClose(wxCloseEvent&) } -void UpdateDialog::OnSkipVersion(wxCommandEvent&) +void UpdateDialog::OnRemindOneHour(wxCommandEvent&) { - Settings::WriteConfigValue("SkipThisVersion", m_appcast.Version); + static const int ONE_HOUR_IN_SECONDS = 3600; + Settings::WriteConfigValue("UpdateInterval", ONE_HOUR_IN_SECONDS); Close(); } -void UpdateDialog::OnRemindLater(wxCommandEvent&) +void UpdateDialog::OnRemindTomorrow(wxCommandEvent&) { - // Just abort the update. Next time it's scheduled to run, - // the user will be prompted. + static const int ONE_DAY_IN_SECONDS = 86400; + Settings::WriteConfigValue("UpdateInterval", ONE_DAY_IN_SECONDS); Close(); } From 124175c7eae71240306afa5983c747db1a094a49 Mon Sep 17 00:00:00 2001 From: Jared Burkeen Date: Tue, 20 Sep 2016 10:20:50 -0400 Subject: [PATCH 6/9] UX changes, don't use caching for loading release notes, check for updates in the background --- src/dll_api.cpp | 93 ++++++++++--------------------- src/ui.cpp | 17 +++--- src/updatechecker.cpp | 126 +++++++++++++++++++++++++++--------------- src/updatechecker.h | 4 +- 4 files changed, 122 insertions(+), 118 deletions(-) diff --git a/src/dll_api.cpp b/src/dll_api.cpp index b665f618..35af0ae0 100644 --- a/src/dll_api.cpp +++ b/src/dll_api.cpp @@ -46,70 +46,35 @@ extern "C" WIN_SPARKLE_API void __cdecl win_sparkle_init() { - try - { - // finish initialization - if (!Settings::GetLanguage().IsOk()) - { - LANGID lang = 0; - if (IsWindowsVistaOrGreater()) - { - auto f_GetThreadUILanguage = LOAD_DYNAMIC_FUNC(GetThreadUILanguage, kernel32); - if (f_GetThreadUILanguage) - lang = f_GetThreadUILanguage(); - } - if (PRIMARYLANGID(lang) == 0) - { - lang = LANGIDFROMLCID(GetThreadLocale()); - } - if (PRIMARYLANGID(lang) != 0) - Settings::SetLanguage(lang); - } - - // first things first - UpdateDownloader::CleanLeftovers(); - - // check for updates - bool checkUpdates; - if ( Settings::ReadConfigValue("CheckForUpdates", checkUpdates) ) - { - if ( checkUpdates ) - { - static const time_t ONE_DAY = 60*60*24; - - time_t lastCheck = 0; - Settings::ReadConfigValue("LastCheckTime", lastCheck); - const time_t currentTime = time(NULL); - - // Only check for updates in reasonable intervals: - const int interval = win_sparkle_get_update_check_interval(); - if ( currentTime - lastCheck >= interval ) - { - // Run the check in background. Only show UI if updates - // are available. - UpdateChecker *check = new UpdateChecker(); - check->Start(); - } - } - } - else // not yet configured - { - bool didRunOnce; - Settings::ReadConfigValue("DidRunOnce", didRunOnce, false); - if ( !didRunOnce ) - { - // Do nothing on the first execution of the app, for better - // first-time impression. - Settings::WriteConfigValue("DidRunOnce", true); - } - else - { - // Only when the app is launched for the second time, ask the - // user for their permission to check for updates. - UI::AskForPermission(); - } - } - } + try + { + // finish initialization + if (!Settings::GetLanguage().IsOk()) + { + LANGID lang = 0; + if (IsWindowsVistaOrGreater()) + { + auto f_GetThreadUILanguage = LOAD_DYNAMIC_FUNC(GetThreadUILanguage, kernel32); + if (f_GetThreadUILanguage) + lang = f_GetThreadUILanguage(); + } + if (PRIMARYLANGID(lang) == 0) + { + lang = LANGIDFROMLCID(GetThreadLocale()); + } + if (PRIMARYLANGID(lang) != 0) + Settings::SetLanguage(lang); + } + + // first things first + UpdateDownloader::CleanLeftovers(); + + // Run the check in background. Only show UI if updates + // are available. + UpdateChecker *check = new UpdateChecker(); + check->Start(); + } + CATCH_ALL_EXCEPTIONS } diff --git a/src/ui.cpp b/src/ui.cpp index b8cbdb11..17cd8420 100644 --- a/src/ui.cpp +++ b/src/ui.cpp @@ -320,13 +320,11 @@ class WinSparkleDialog : public wxDialog WinSparkleDialog::WinSparkleDialog() : wxDialog(NULL, wxID_ANY, _("Software Update"), wxDefaultPosition, wxDefaultSize, - wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER | wxDIALOG_NO_PARENT) + wxDEFAULT_DIALOG_STYLE | wxDIALOG_NO_PARENT) { wxSize dpi = wxClientDC(this).GetPPI(); m_scaleFactor = dpi.y / 96.0; - SetIcon(LoadNamedIcon(UI::GetDllHINSTANCE(), L"UpdateAvailable", GetSystemMetrics(SM_CXSMICON))); - wxSizer *topSizer = new wxBoxSizer(wxHORIZONTAL); // Load the dialog box icon: the first 48x48 application icon will be loaded, if available, @@ -388,7 +386,6 @@ void WinSparkleDialog::SetHeadingFont(wxWindow *win) // 9pt is base font, 12pt is for "Main instructions". See // http://msdn.microsoft.com/en-us/library/aa511282%28v=MSDN.10%29.aspx f.SetPointSize(f.GetPointSize() + 3); - win->SetForegroundColour(wxColour(0x00, 0x33, 0x99)); } else // Windows XP/2000 { @@ -543,7 +540,7 @@ UpdateDialog::UpdateDialog() ); m_updateButtonsSizer->Add ( - m_installButton = new wxButton(this, ID_INSTALL, _("Install update")), + m_installButton = new wxButton(this, ID_INSTALL, _("Download")), wxSizerFlags() ); m_buttonSizer->Add(m_updateButtonsSizer, wxSizerFlags(1)); @@ -1003,10 +1000,14 @@ void UpdateDialog::ShowReleaseNotes(const Appcast& info) if( !info.ReleaseNotesURL.empty() ) { - m_webBrowser->Navigate + VARIANT varFlags; + varFlags.vt = VT_I4; + varFlags.llVal = (navNoHistory | navNoReadFromCache | navNoWriteToCache); + + m_webBrowser->Navigate ( wxBasicString(info.ReleaseNotesURL), - NULL, // Flags + &varFlags, // Flags NULL, // TargetFrameName NULL, // PostData NULL // Headers @@ -1071,7 +1072,7 @@ void UpdateDialog::ShowReleaseNotes(const Appcast& info) } } - SetWindowStyleFlag(wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER); + SetWindowStyleFlag(wxDEFAULT_DIALOG_STYLE); } diff --git a/src/updatechecker.cpp b/src/updatechecker.cpp index 437b990a..925558ff 100644 --- a/src/updatechecker.cpp +++ b/src/updatechecker.cpp @@ -36,6 +36,7 @@ #include #include #include +#include using namespace std; @@ -219,52 +220,80 @@ UpdateChecker::UpdateChecker(): Thread("WinSparkle updates check") void UpdateChecker::Run() { - // no initialization to do, so signal readiness immediately - SignalReady(); - - try - { - const std::string url = Settings::GetAppcastURL(); - if ( url.empty() ) - throw std::runtime_error("Appcast URL not specified."); - CheckForInsecureURL(url, "appcast feed"); - - StringDownloadSink appcast_xml; - DownloadFile(url, &appcast_xml, this, GetAppcastDownloadFlags()); - - Appcast appcast = Appcast::Load(appcast_xml.data); - if (!appcast.ReleaseNotesURL.empty()) - CheckForInsecureURL(appcast.ReleaseNotesURL, "release notes"); - if (!appcast.DownloadURL.empty()) - CheckForInsecureURL(appcast.DownloadURL, "update file"); - - Settings::WriteConfigValue("LastCheckTime", time(NULL)); - - const std::string currentVersion = - WideToAnsi(Settings::GetAppBuildVersion()); - - // Check if our version is out of date. - if ( !appcast.IsValid() || CompareVersions(currentVersion, appcast.Version) >= 0 ) - { - // The same or newer version is already installed. - UI::NotifyNoUpdates(ShouldAutomaticallyInstall()); - return; - } - - // Check if the user opted to ignore this particular version. - if ( ShouldSkipUpdate(appcast) ) - { - UI::NotifyNoUpdates(ShouldAutomaticallyInstall()); - return; - } + while (1) + { + // no initialization to do, so signal readiness immediately + SignalReady(); + + bool checkUpdates; + if (Settings::ReadConfigValue("CheckForUpdates", checkUpdates)) + { + if (checkUpdates) + { + static const time_t ONE_DAY = 60 * 60 * 24; + + time_t lastCheck = 0; + Settings::ReadConfigValue("LastCheckTime", lastCheck); + const time_t currentTime = time(NULL); + + // Only check for updates in reasonable intervals: + const int interval = win_sparkle_get_update_check_interval(); + if (currentTime - lastCheck >= interval) + { + UpdateCheckWorker(); + } + } + } + // Check every 5 minutes + Sleep(300000); + } +} - UI::NotifyUpdateAvailable(appcast, ShouldAutomaticallyInstall()); - } - catch ( ... ) - { - UI::NotifyUpdateError(); - throw; - } +void UpdateChecker::UpdateCheckWorker() +{ + try + { + const std::string url = Settings::GetAppcastURL(); + if (url.empty()) + throw std::runtime_error("Appcast URL not specified."); + CheckForInsecureURL(url, "appcast feed"); + + StringDownloadSink appcast_xml; + DownloadFile(url, &appcast_xml, this, GetAppcastDownloadFlags()); + + Appcast appcast = Appcast::Load(appcast_xml.data); + if (!appcast.ReleaseNotesURL.empty()) + CheckForInsecureURL(appcast.ReleaseNotesURL, "release notes"); + if (!appcast.DownloadURL.empty()) + CheckForInsecureURL(appcast.DownloadURL, "update file"); + + Settings::WriteConfigValue("LastCheckTime", time(NULL)); + + const std::string currentVersion = + WideToAnsi(Settings::GetAppBuildVersion()); + + // Check if our version is out of date. + if (!appcast.IsValid() || CompareVersions(currentVersion, appcast.Version) >= 0) + { + // The same or newer version is already installed. + UI::NotifyNoUpdates(ShouldAutomaticallyInstall()); + return; + } + + // Check if the user opted to ignore this particular version. + if (ShouldSkipUpdate(appcast)) + { + UI::NotifyNoUpdates(ShouldAutomaticallyInstall()); + return; + } + + UI::NotifyUpdateAvailable(appcast, ShouldAutomaticallyInstall()); + } + catch (...) + { + UI::NotifyUpdateError(); + throw; + } } bool UpdateChecker::ShouldSkipUpdate(const Appcast& appcast) const @@ -302,4 +331,11 @@ bool ManualUpdateChecker::ShouldSkipUpdate(const Appcast&) const return false; } +void ManualUpdateChecker::Run() +{ + // no initialization to do, so signal readiness immediately + SignalReady(); + + UpdateCheckWorker(); +} } // namespace winsparkle diff --git a/src/updatechecker.h b/src/updatechecker.h index 1009e395..74e1ec0b 100644 --- a/src/updatechecker.h +++ b/src/updatechecker.h @@ -70,7 +70,8 @@ class UpdateChecker : public Thread virtual bool ShouldAutomaticallyInstall() const { return false; } protected: - virtual void Run(); + void Run(); + virtual void UpdateCheckWorker(); virtual bool IsJoinable() const { return false; } }; @@ -87,6 +88,7 @@ class ManualUpdateChecker : public UpdateChecker protected: virtual int GetAppcastDownloadFlags() const; virtual bool ShouldSkipUpdate(const Appcast& appcast) const; + void Run(); }; From bf731ac22253cabcdeaaf73a73b65611175d490e Mon Sep 17 00:00:00 2001 From: Jared Burkeen Date: Mon, 26 Sep 2016 04:30:36 -0400 Subject: [PATCH 7/9] Change sleep time to update check interval --- src/updatechecker.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/updatechecker.cpp b/src/updatechecker.cpp index 925558ff..1c30e329 100644 --- a/src/updatechecker.cpp +++ b/src/updatechecker.cpp @@ -230,8 +230,6 @@ void UpdateChecker::Run() { if (checkUpdates) { - static const time_t ONE_DAY = 60 * 60 * 24; - time_t lastCheck = 0; Settings::ReadConfigValue("LastCheckTime", lastCheck); const time_t currentTime = time(NULL); @@ -245,7 +243,7 @@ void UpdateChecker::Run() } } // Check every 5 minutes - Sleep(300000); + Sleep(win_sparkle_get_update_check_interval()); } } From 7410e64672de3766099535447415cacab6d6fcaf Mon Sep 17 00:00:00 2001 From: Jared Burkeen Date: Mon, 3 Oct 2016 11:48:34 -0400 Subject: [PATCH 8/9] UX updates --- src/ui.cpp | 25 +++++-------------------- 1 file changed, 5 insertions(+), 20 deletions(-) diff --git a/src/ui.cpp b/src/ui.cpp index 17cd8420..c496f11d 100644 --- a/src/ui.cpp +++ b/src/ui.cpp @@ -750,27 +750,12 @@ void UpdateDialog::StateNoUpdateFound(bool installAutomatically) LayoutChangesGuard guard(this); - m_heading->SetLabel(_("You're up to date!")); - - wxString msg; - try - { - msg = wxString::Format - ( - _("%s %s is currently the newest version available."), - Settings::GetAppName(), - Settings::GetAppVersion() - ); - } - catch ( std::exception& ) - { - // GetAppVersion() may fail - msg = "Error: Updates checking not properly configured."; - } + m_heading->SetLabel(_("You are up to date!")); + wxString msg = _("There are no new versions available at this time."); SetMessage(msg); - m_closeButton->SetLabel(_("Close")); + m_closeButton->SetLabel(_("OK")); m_closeButton->SetDefault(); EnablePulsing(false); @@ -851,8 +836,8 @@ void UpdateDialog::StateUpdateAvailable(const Appcast& info, bool installAutomat ( wxString::Format ( - _("%s %s is now available (you have %s). Would you like to download it now?"), - appname, ver_new, ver_my + _("You are currently using %s version %s.\nWould you like to download our new version (%s) now?"), + appname, ver_my, ver_new ), showRelnotes ? RELNOTES_WIDTH : MESSAGE_AREA_WIDTH ); From 1bc2ce40876d290f5e6087126a8600f4bc1d3471 Mon Sep 17 00:00:00 2001 From: Jared Burkeen Date: Tue, 4 Oct 2016 09:29:00 -0400 Subject: [PATCH 9/9] UX updates --- src/ui.cpp | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/src/ui.cpp b/src/ui.cpp index c496f11d..f5e8932e 100644 --- a/src/ui.cpp +++ b/src/ui.cpp @@ -401,8 +401,8 @@ void WinSparkleDialog::SetHeadingFont(wxWindow *win) Window for communicating with the user *--------------------------------------------------------------------------*/ -const int ID_SKIP_VERSION = wxNewId(); -const int ID_REMIND_LATER = wxNewId(); +const int ID_REMIND_HOUR = wxNewId(); +const int ID_REMIND_TOMORROW = wxNewId(); const int ID_INSTALL = wxNewId(); const int ID_RUN_INSTALLER = wxNewId(); @@ -432,8 +432,8 @@ class UpdateDialog : public WinSparkleDialog void OnCloseButton(wxCommandEvent& event); void OnClose(wxCloseEvent& event); - void OnSkipVersion(wxCommandEvent&); - void OnRemindLater(wxCommandEvent&); + void OnRemindOneHour(wxCommandEvent&); + void OnRemindTomorrow(wxCommandEvent&); void OnInstall(wxCommandEvent&); void OnRunInstaller(wxCommandEvent&); @@ -529,20 +529,21 @@ UpdateDialog::UpdateDialog() m_updateButtonsSizer = new wxBoxSizer(wxHORIZONTAL); m_updateButtonsSizer->Add ( - new wxButton(this, ID_SKIP_VERSION, _("Skip this version")), + new wxButton(this, ID_REMIND_HOUR, _("Remind me in an hour")), wxSizerFlags().Border(wxRIGHT, PX(20)) ); - m_updateButtonsSizer->AddStretchSpacer(1); m_updateButtonsSizer->Add ( - new wxButton(this, ID_REMIND_LATER, _("Remind me later")), + new wxButton(this, ID_REMIND_TOMORROW, _("Remind me tomorrow")), wxSizerFlags().Border(wxRIGHT, PX(10)) ); - m_updateButtonsSizer->Add + m_updateButtonsSizer->AddStretchSpacer(1); + m_updateButtonsSizer->Add ( m_installButton = new wxButton(this, ID_INSTALL, _("Download")), wxSizerFlags() ); + m_installButton->SetFocus(); m_buttonSizer->Add(m_updateButtonsSizer, wxSizerFlags(1)); m_closeButtonSizer = new wxBoxSizer(wxHORIZONTAL); @@ -569,8 +570,8 @@ UpdateDialog::UpdateDialog() Bind(wxEVT_CLOSE_WINDOW, &UpdateDialog::OnClose, this); Bind(wxEVT_TIMER, &UpdateDialog::OnTimer, this); Bind(wxEVT_COMMAND_BUTTON_CLICKED, &UpdateDialog::OnCloseButton, this, wxID_CANCEL); - Bind(wxEVT_COMMAND_BUTTON_CLICKED, &UpdateDialog::OnSkipVersion, this, ID_SKIP_VERSION); - Bind(wxEVT_COMMAND_BUTTON_CLICKED, &UpdateDialog::OnRemindLater, this, ID_REMIND_LATER); + Bind(wxEVT_COMMAND_BUTTON_CLICKED, &UpdateDialog::OnRemindOneHour, this, ID_REMIND_HOUR); + Bind(wxEVT_COMMAND_BUTTON_CLICKED, &UpdateDialog::OnRemindTomorrow, this, ID_REMIND_TOMORROW); Bind(wxEVT_COMMAND_BUTTON_CLICKED, &UpdateDialog::OnInstall, this, ID_INSTALL); Bind(wxEVT_COMMAND_BUTTON_CLICKED, &UpdateDialog::OnRunInstaller, this, ID_RUN_INSTALLER); } @@ -626,17 +627,18 @@ void UpdateDialog::OnClose(wxCloseEvent&) } -void UpdateDialog::OnSkipVersion(wxCommandEvent&) +void UpdateDialog::OnRemindOneHour(wxCommandEvent&) { - Settings::WriteConfigValue("SkipThisVersion", m_appcast.Version); + static const int ONE_HOUR_IN_SECONDS = 3600; + Settings::WriteConfigValue("UpdateInterval", ONE_HOUR_IN_SECONDS); Close(); } -void UpdateDialog::OnRemindLater(wxCommandEvent&) +void UpdateDialog::OnRemindTomorrow(wxCommandEvent&) { - // Just abort the update. Next time it's scheduled to run, - // the user will be prompted. + static const int ONE_DAY_IN_SECONDS = 86400; + Settings::WriteConfigValue("UpdateInterval", ONE_DAY_IN_SECONDS); Close(); }