| // Copyright 2007-2009 Google Inc. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| // ======================================================================== |
| |
| |
| #include "omaha/worker/ui.h" |
| |
| #include "base/basictypes.h" |
| #include "omaha/common/const_addresses.h" |
| #include "omaha/common/constants.h" |
| #include "omaha/common/debug.h" |
| #include "omaha/common/error.h" |
| #include "omaha/common/exception_barrier.h" |
| #include "omaha/common/logging.h" |
| #include "omaha/common/omaha_version.h" |
| #include "omaha/common/system_info.h" |
| #include "omaha/common/vistautil.h" |
| #include "omaha/goopdate/goopdate_utils.h" |
| #include "omaha/goopdate/ui_displayed_event.h" |
| #include "omaha/worker/ui_ctls.h" |
| #include "omaha/worker/worker_metrics.h" |
| |
| namespace omaha { |
| |
| InstallStoppedWnd::InstallStoppedWnd(CMessageLoop* message_loop, HWND parent) |
| : message_loop_(message_loop), |
| parent_(parent) { |
| CORE_LOG(L3, (_T("[InstallStoppedWnd::InstallStoppedWnd]"))); |
| ASSERT1(message_loop); |
| ASSERT1(::IsWindow(parent)); |
| } |
| |
| InstallStoppedWnd::~InstallStoppedWnd() { |
| CORE_LOG(L3, (_T("[InstallStoppedWnd::~InstallStoppedWnd]"))); |
| if (IsWindow()) { |
| VERIFY1(SUCCEEDED(CloseWindow())); |
| } |
| } |
| |
| // Enables the parent window and destroys this window. |
| // Enabling the parent window before destroying this one causes the parent |
| // window to get the focus and avoids a visible momentary lack of focus if we |
| // instead call SetFocus for the parent after the window is destroyed. |
| HRESULT InstallStoppedWnd::CloseWindow() { |
| ASSERT1(IsWindow()); |
| VERIFY1(::EnableWindow(parent_, true)); |
| |
| return DestroyWindow() ? S_OK : HRESULTFromLastError(); |
| } |
| |
| // Disables the parent window. |
| LRESULT InstallStoppedWnd::OnInitDialog(UINT, |
| WPARAM, |
| LPARAM, |
| BOOL& handled) { // NOLINT |
| VERIFY1(!::EnableWindow(parent_, false)); |
| VERIFY1(message_loop_->AddMessageFilter(this)); |
| handled = true; |
| return 1; |
| } |
| |
| LRESULT InstallStoppedWnd::OnClickButton(WORD, |
| WORD id, |
| HWND, |
| BOOL& handled) { // NOLINT |
| ExceptionBarrier eb; |
| CORE_LOG(L3, (_T("[InstallStoppedWnd::OnClickButton]"))); |
| ASSERT1(id == IDOK || id == IDCANCEL); |
| VERIFY1(::PostMessage(parent_, WM_INSTALL_STOPPED, id, 0)); |
| handled = true; |
| return 0; |
| } |
| |
| LRESULT InstallStoppedWnd::OnDestroy(UINT, |
| WPARAM, |
| LPARAM, |
| BOOL& handled) { // NOLINT |
| VERIFY1(message_loop_->RemoveMessageFilter(this)); |
| handled = true; |
| return 0; |
| } |
| |
| ProgressWnd::ProgressWnd(CMessageLoop* message_loop, HWND parent) |
| : message_loop_(message_loop), |
| parent_(parent), |
| thread_id_(::GetCurrentThreadId()), |
| cur_state_(STATE_INIT), |
| is_close_enabled_(true), |
| events_sink_(NULL), |
| is_machine_(false), |
| product_guid_(GUID_NULL) { |
| ASSERT1(message_loop); |
| CORE_LOG(L3, (_T("[ProgressWnd::ProgressWnd]"))); |
| } |
| |
| ProgressWnd::~ProgressWnd() { |
| CORE_LOG(L3, (_T("[ProgressWnd::~ProgressWnd]"))); |
| ASSERT1(thread_id_ == ::GetCurrentThreadId()); |
| ASSERT1(!IsWindow()); |
| cur_state_ = STATE_END; |
| } |
| |
| HRESULT ProgressWnd::Initialize() { |
| CORE_LOG(L3, (_T("[ProgressWnd::Initialize]"))); |
| cur_state_ = STATE_INIT; |
| ASSERT1(thread_id_ == ::GetCurrentThreadId()); |
| |
| // For the InitCommonControlsEx call to succeed on XP a manifest is |
| // needed to declare "Microsoft.Windows.Common-Controls" as a dependent |
| // assembly. Further work may be needed to ensure W2K compatibility. |
| INITCOMMONCONTROLSEX init_ctrls = { sizeof(INITCOMMONCONTROLSEX), 0 }; |
| ASSERT1(init_ctrls.dwSize == sizeof(init_ctrls)); |
| init_ctrls.dwICC = ICC_STANDARD_CLASSES | ICC_PROGRESS_CLASS; |
| if (!::InitCommonControlsEx(&init_ctrls)) { |
| // In case of XP RTM and XP SP1, InitCommonControlsEx is failing, |
| // however the UI initializes fine and works correctly. Because of this |
| // we only log the failure and do not error out. |
| HRESULT hr = HRESULTFromLastError(); |
| CORE_LOG(LEVEL_ERROR, (_T("[InitCommonControlsEx failed][0x%08x]"), hr)); |
| ASSERT1(hr == 0x80070582); |
| } |
| |
| if (!Create(parent_)) { |
| CORE_LOG(LEVEL_ERROR, (_T("[Failed to create the window]"))); |
| return GOOPDATE_E_UI_INTERNAL_ERROR; |
| } |
| VERIFY1(message_loop_->AddMessageFilter(this)); |
| return S_OK; |
| } |
| |
| HRESULT ProgressWnd::GetWindowTitle(TCHAR* title, int size) { |
| CORE_LOG(L3, (_T("[ProgressWnd::GetWindowTitle]"))); |
| ASSERT1(size); |
| if (!size) return E_INVALIDARG; |
| |
| // The text is truncated if there is no enough space in the buffer. |
| int result = GetWindowText(title, size); |
| CORE_LOG(L4, (_T("[title=%s]"), title)); |
| return result ? S_OK : HRESULTFromLastError(); |
| } |
| |
| HRESULT ProgressWnd::SetWindowTitle(const TCHAR* title) { |
| CORE_LOG(L3, (_T("[ProgressWnd::SetWindowTitle][%s]"), title)); |
| return SetWindowText(title) ? S_OK : HRESULTFromLastError(); |
| } |
| |
| LRESULT ProgressWnd::OnInitDialog(UINT, |
| WPARAM, |
| LPARAM, |
| BOOL& handled) { // NOLINT |
| ExceptionBarrier eb; |
| CORE_LOG(L3, (_T("[ProgressWnd::OnInitDialog]"))); |
| handled = true; |
| ASSERT1(!product_name_.IsEmpty()); |
| CString s; |
| s.FormatMessage(IDS_WINDOW_TITLE, product_name_); |
| VERIFY1(SetWindowText(s)); |
| pause_resume_text_.reset(new StaticEx); |
| pause_resume_text_->SubclassWindow(GetDlgItem(IDC_PAUSE_RESUME_TEXT)); |
| VERIFY1(CenterWindow(NULL)); |
| |
| VERIFY1(s.LoadString(IDS_INITIALIZING)); |
| VERIFY1(::SetWindowText(GetDlgItem(IDC_INSTALLER_STATE_TEXT), s)); |
| VERIFY1(SUCCEEDED(SetMarqueeMode(true))); |
| VERIFY1(SUCCEEDED(ChangeControlState())); |
| VERIFY1(SUCCEEDED(SetWindowIcon())); |
| |
| metrics_timer_.reset(new HighresTimer); |
| return 1; // Let the system set the focus. |
| } |
| |
| LRESULT ProgressWnd::OnClose(UINT, |
| WPARAM, |
| LPARAM, |
| BOOL& handled) { // NOLINT |
| ExceptionBarrier eb; |
| CORE_LOG(L3, (_T("[ProgressWnd::OnClose]"))); |
| |
| ++metric_worker_ui_click_x; |
| |
| MaybeCloseWindow(); |
| handled = true; |
| return 0; |
| } |
| |
| HRESULT ProgressWnd::CloseWindow() { |
| HRESULT hr = DestroyWindow() ? S_OK : HRESULTFromLastError(); |
| if (events_sink_) { |
| events_sink_->DoClose(); |
| } |
| return hr; |
| } |
| |
| void ProgressWnd::MaybeRequestExitProcess() { |
| CORE_LOG(L3, (_T("[ProgressWnd::MaybeRequestExitProcess]"))); |
| if (cur_state_ != STATE_COMPLETE_SUCCESS && |
| cur_state_ != STATE_COMPLETE_ERROR && |
| cur_state_ != STATE_COMPLETE_RESTART_BROWSER && |
| cur_state_ != STATE_COMPLETE_RESTART_ALL_BROWSERS && |
| cur_state_ != STATE_COMPLETE_REBOOT) { |
| return; |
| } |
| |
| RequestExitProcess(); |
| } |
| |
| void ProgressWnd::RequestExitProcess() { |
| CORE_LOG(L3, (_T("[ProgressWnd::RequestExitProcess]"))); |
| ::PostQuitMessage(0); |
| } |
| |
| bool ProgressWnd::MaybeCloseWindow() { |
| if (cur_state_ != STATE_COMPLETE_SUCCESS && |
| cur_state_ != STATE_COMPLETE_ERROR && |
| cur_state_ != STATE_COMPLETE_RESTART_BROWSER && |
| cur_state_ != STATE_COMPLETE_RESTART_ALL_BROWSERS && |
| cur_state_ != STATE_COMPLETE_REBOOT) { |
| // The UI is not in final state: ask the user to proceed with closing it. |
| // A modal dialog opens and sends a message back to this window to |
| // communicate the user decision. |
| install_stopped_wnd_.reset(new InstallStoppedWnd(message_loop_, *this)); |
| HWND hwnd = install_stopped_wnd_->Create(*this); |
| if (hwnd) { |
| CString title; |
| VERIFY1(title.LoadString(IDS_INSTALLATION_STOPPED_WINDOW_TITLE)); |
| VERIFY1(install_stopped_wnd_->SetWindowText(title)); |
| |
| CString button_text; |
| VERIFY1(button_text.LoadString(IDS_RESUME_INSTALLATION)); |
| VERIFY1(::SetWindowText( |
| install_stopped_wnd_->GetDlgItem(IDOK), button_text)); |
| |
| VERIFY1(button_text.LoadString(IDS_CANCEL_INSTALLATION)); |
| VERIFY1(::SetWindowText( |
| install_stopped_wnd_->GetDlgItem(IDCANCEL), button_text)); |
| |
| CString s; |
| s.FormatMessage(IDS_INSTALL_STOPPED, product_name_); |
| VERIFY1(::SetWindowText( |
| install_stopped_wnd_->GetDlgItem(IDC_INSTALL_STOPPED_TEXT), s)); |
| |
| VERIFY1(install_stopped_wnd_->CenterWindow(*this)); |
| VERIFY1(!install_stopped_wnd_->ShowWindow(SW_SHOWDEFAULT)); |
| return false; |
| } else { |
| ASSERT1(false); |
| } |
| } |
| |
| // The UI is in a final state or the dialog box failed to open: proceed with |
| // closing the UI. |
| VERIFY1(SUCCEEDED(CloseWindow())); |
| return true; |
| } |
| |
| LRESULT ProgressWnd::OnNCDestroy(UINT, |
| WPARAM, |
| LPARAM, |
| BOOL& handled) { // NOLINT |
| ExceptionBarrier eb; |
| CORE_LOG(L3, (_T("[ProgressWnd::OnNCDestroy]"))); |
| VERIFY1(message_loop_->RemoveMessageFilter(this)); |
| MaybeRequestExitProcess(); |
| handled = false; // Let ATL default processing handle the WM_NCDESTROY. |
| return 0; |
| } |
| |
| LRESULT ProgressWnd::OnClickedButton(WORD, |
| WORD id, |
| HWND, |
| BOOL& handled) { // NOLINT |
| ExceptionBarrier eb; |
| CORE_LOG(L3, (_T("[ProgressWnd::OnClickedButton]"))); |
| ASSERT1(id == IDC_BUTTON1 || id == IDC_BUTTON2 || id == IDC_CLOSE); |
| handled = true; |
| DestroyWindow(); |
| |
| if (events_sink_) { |
| #pragma warning(push) |
| // C4061: enumerator 'xxx' in switch of enum 'yyy' is not explicitly handled by |
| // a case label. |
| #pragma warning(disable : 4061) |
| |
| switch (id) { |
| case IDC_BUTTON1: |
| switch (cur_state_) { |
| case STATE_COMPLETE_RESTART_BROWSER: |
| ++metric_worker_ui_restart_browser_now_click; |
| events_sink_->DoRestartBrowsers(); |
| break; |
| case STATE_COMPLETE_RESTART_ALL_BROWSERS: |
| ++metric_worker_ui_restart_all_browsers_now_click; |
| events_sink_->DoRestartBrowsers(); |
| break; |
| case STATE_COMPLETE_REBOOT: |
| ++metric_worker_ui_reboot_now_click; |
| events_sink_->DoReboot(); |
| break; |
| default: |
| ASSERT1(false); |
| } |
| break; |
| case IDC_BUTTON2: |
| switch (cur_state_) { |
| case STATE_COMPLETE_RESTART_BROWSER: |
| case STATE_COMPLETE_RESTART_ALL_BROWSERS: |
| case STATE_COMPLETE_REBOOT: |
| break; |
| default: |
| ASSERT1(false); |
| } |
| break; |
| case IDC_CLOSE: |
| switch (cur_state_) { |
| case STATE_COMPLETE_SUCCESS: |
| case STATE_COMPLETE_ERROR: |
| break; |
| default: |
| ASSERT1(false); |
| } |
| break; |
| default: |
| ASSERT1(false); |
| } |
| |
| #pragma warning(pop) |
| |
| events_sink_->DoClose(); |
| } |
| return 0; |
| } |
| |
| // Called when esc key is hit. |
| // If close is disabled, does nothing because we don't want the window to close. |
| LRESULT ProgressWnd::OnCancel(WORD, WORD id, |
| HWND, BOOL& handled) { // NOLINT |
| ExceptionBarrier eb; |
| VERIFY1(id == IDCANCEL); |
| |
| if (!is_close_enabled_) { |
| return 0; |
| } |
| |
| ++metric_worker_ui_esc_key_total; |
| MaybeCloseWindow(); |
| handled = true; |
| return 0; |
| } |
| |
| LRESULT ProgressWnd::OnUrlClicked(int, |
| LPNMHDR params, |
| BOOL& handled) { // NOLINT |
| CORE_LOG(L3, (_T("[ProgressWnd::OnUrlClicked]"))); |
| ASSERT1(params); |
| handled = true; |
| |
| if (IDC_GET_HELP_TEXT == params->idFrom) { |
| ++metric_worker_ui_get_help_click; |
| } |
| |
| NM_STATICEX* notification = reinterpret_cast<NM_STATICEX*>(params); |
| events_sink_->DoLaunchBrowser(notification->action); |
| |
| return 1; |
| } |
| |
| LRESULT ProgressWnd::OnInstallStopped(UINT msg, |
| WPARAM wparam, |
| LPARAM, |
| BOOL& handled) { // NOLINT |
| CORE_LOG(L3, (_T("[ProgressWnd::OnInstallStopped]"))); |
| UNREFERENCED_PARAMETER(msg); |
| |
| install_stopped_wnd_.reset(); |
| |
| ASSERT1(msg == WM_INSTALL_STOPPED); |
| ASSERT1(wparam == IDOK || wparam == IDCANCEL); |
| switch (wparam) { |
| case IDOK: |
| // TODO(omaha): Implement "Resume" here. |
| break; |
| case IDCANCEL: |
| // The user has decided to cancel. |
| metric_worker_ui_cancel_ms.AddSample(metrics_timer_->GetElapsedMs()); |
| ++metric_worker_ui_cancels; |
| DestroyWindow(); |
| if (events_sink_) { |
| events_sink_->DoClose(); |
| } |
| break; |
| default: |
| ASSERT1(false); |
| break; |
| } |
| |
| handled = true; |
| return 0; |
| } |
| |
| void ProgressWnd::OnCheckingForUpdate() { |
| CORE_LOG(L3, (_T("[ProgressWnd::OnCheckingForUpdate]"))); |
| ASSERT1(thread_id_ == ::GetCurrentThreadId()); |
| if (!IsWindow()) { |
| return; |
| } |
| |
| cur_state_ = STATE_CHECKING_FOR_UPDATE; |
| |
| CString s; |
| VERIFY1(s.LoadString(IDS_WAITING_TO_CONNECT)); |
| VERIFY1(::SetWindowText(GetDlgItem(IDC_INSTALLER_STATE_TEXT), s)); |
| |
| VERIFY1(SUCCEEDED(SetMarqueeMode(true))); |
| VERIFY1(SUCCEEDED(ChangeControlState())); |
| } |
| |
| void ProgressWnd::OnUpdateAvailable(const TCHAR* version_string) { |
| UNREFERENCED_PARAMETER(version_string); |
| CORE_LOG(L3, (_T("[ProgressWnd::OnUpdateAvailable][%s]"), version_string)); |
| ASSERT1(thread_id_ == ::GetCurrentThreadId()); |
| if (!IsWindow()) { |
| return; |
| } |
| } |
| |
| void ProgressWnd::OnWaitingToDownload() { |
| CORE_LOG(L3, (_T("[ProgressWnd::OnWaitingToDownload]"))); |
| ASSERT1(thread_id_ == ::GetCurrentThreadId()); |
| if (!IsWindow()) { |
| return; |
| } |
| |
| cur_state_ = STATE_WAITING_TO_DOWNLOAD; |
| |
| CString s; |
| s.FormatMessage(IDS_WAITING_TO_DOWNLOAD, product_name_); |
| VERIFY1(::SetWindowText(GetDlgItem(IDC_INSTALLER_STATE_TEXT), s)); |
| |
| VERIFY1(SUCCEEDED(SetMarqueeMode(true))); |
| VERIFY1(SUCCEEDED(ChangeControlState())); |
| } |
| |
| void ProgressWnd::OnDownloading(int time_remaining_ms, int pos) { |
| CORE_LOG(L5, (_T("[ProgressWnd::OnDownloading][pos=%d]"), pos)); |
| ASSERT1(thread_id_ == ::GetCurrentThreadId()); |
| if (!IsWindow()) { |
| return; |
| } |
| |
| ASSERT1(time_remaining_ms >=0); |
| ASSERT1(0 <= pos && pos <= 100); |
| |
| time_remaining_ms; // unreferenced formal parameter |
| |
| cur_state_ = STATE_DOWNLOADING; |
| |
| CString s; |
| s.FormatMessage(IDS_DOWNLOADING, product_name_); |
| VERIFY1(::SetWindowText(GetDlgItem(IDC_INSTALLER_STATE_TEXT), s)); |
| |
| VERIFY1(s.LoadString(IDS_PAUSE)); |
| VERIFY1(::SetWindowText(GetDlgItem(IDC_PAUSE_RESUME_TEXT), s)); |
| |
| // TODO(omaha): implement time left. |
| |
| // When the network is connecting keep the marquee moving, otherwise |
| // the user has no indication something is still going on. |
| // TODO(omaha): when resuming an incomplete download this will not work. |
| VERIFY1(SUCCEEDED(SetMarqueeMode(pos == 0))); |
| ::SendMessage(GetDlgItem(IDC_PROGRESS), PBM_SETPOS, pos, 0); |
| VERIFY1(SUCCEEDED(ChangeControlState())); |
| } |
| |
| void ProgressWnd::OnWaitingToInstall() { |
| CORE_LOG(L3, (_T("[ProgressWnd::OnWaitingToInstall]"))); |
| ASSERT1(thread_id_ == ::GetCurrentThreadId()); |
| if (!IsWindow()) { |
| return; |
| } |
| |
| cur_state_ = STATE_WAITING_TO_INSTALL; |
| |
| CString s; |
| s.FormatMessage(IDS_WAITING_TO_INSTALL, product_name_); |
| VERIFY1(::SetWindowText(GetDlgItem(IDC_INSTALLER_STATE_TEXT), s)); |
| |
| VERIFY1(SUCCEEDED(SetMarqueeMode(true))); |
| VERIFY1(SUCCEEDED(ChangeControlState())); |
| } |
| |
| void ProgressWnd::OnInstalling() { |
| CORE_LOG(L3, (_T("[ProgressWnd::OnInstalling]"))); |
| ASSERT1(thread_id_ == ::GetCurrentThreadId()); |
| if (!IsWindow()) { |
| return; |
| } |
| |
| cur_state_ = STATE_INSTALLING; |
| |
| // Close the 'Install Stop' window if it is on the screen because the user |
| // cannot cancel an install anyway. |
| CloseInstallStoppedWindow(); |
| |
| CString s; |
| s.FormatMessage(IDS_INSTALLING, product_name_); |
| VERIFY1(::SetWindowText(GetDlgItem(IDC_INSTALLER_STATE_TEXT), s)); |
| |
| VERIFY1(SUCCEEDED(EnableClose(false))); |
| VERIFY1(SUCCEEDED(SetMarqueeMode(true))); |
| VERIFY1(SUCCEEDED(ChangeControlState())); |
| } |
| |
| void ProgressWnd::OnPause() { |
| CORE_LOG(L3, (_T("[ProgressWnd::OnPause]"))); |
| ASSERT(false, (_T("These strings are not translated or in .rc files."))); |
| ASSERT1(thread_id_ == ::GetCurrentThreadId()); |
| if (!IsWindow()) { |
| return; |
| } |
| |
| cur_state_ = STATE_PAUSED; |
| |
| CString s; |
| s.FormatMessage(IDS_DOWNLOAD_PAUSED, product_name_); |
| VERIFY1(::SetWindowText(GetDlgItem(IDC_INSTALLER_STATE_TEXT), s)); |
| |
| VERIFY1(s.LoadString(IDS_RESUME)); |
| VERIFY1(::SetWindowText(GetDlgItem(IDC_PAUSE_RESUME_TEXT), s)); |
| |
| // TODO(omaha): implement time left. |
| |
| VERIFY1(SUCCEEDED(ChangeControlState())); |
| } |
| |
| void ProgressWnd::OnShow() { |
| CORE_LOG(L3, (_T("[ProgressWnd::OnShow]"))); |
| ASSERT1(thread_id_ == ::GetCurrentThreadId()); |
| if (!IsWindow() || IsWindowVisible()) { |
| return; |
| } |
| |
| CenterWindow(NULL); |
| SetVisible(true); |
| |
| if (!::SetForegroundWindow(*this)) { |
| CORE_LOG(LW, (_T("[::SetForegroundWindow failed %d]"), ::GetLastError())); |
| } |
| |
| UIDisplayedEventManager::SignalEvent(is_machine_); |
| } |
| |
| // The 'error_code' can contain an HRESULT or an installer error. |
| void ProgressWnd::OnComplete(CompletionCodes code, |
| const TCHAR* text, |
| DWORD error_code) { |
| CORE_LOG(L3, (_T("[ProgressWnd::OnComplete]") |
| _T("[code=%d][error_code=0x%08x]"), code, error_code)); |
| ASSERT1(thread_id_ == ::GetCurrentThreadId()); |
| |
| if (!IsWindow()) { |
| RequestExitProcess(); |
| return; |
| } |
| |
| // It is possible for the OnComplete callback to be called multiple times. |
| // Subclassing the control multiple times results in a crash, therefore |
| // unsubclass the control if the control has been created and subclassed |
| // before. |
| if (complete_text_ != NULL) { |
| complete_text_->UnsubclassWindow(true); |
| complete_text_.reset(NULL); |
| } |
| |
| // Close the 'Install Stop' window if it is on the screen. |
| CloseInstallStoppedWindow(); |
| |
| CString s; |
| switch (code) { |
| case COMPLETION_CODE_SUCCESS_CLOSE_UI: |
| cur_state_ = STATE_COMPLETE_SUCCESS; |
| VERIFY1(SUCCEEDED(CloseWindow())); |
| return; |
| |
| case COMPLETION_CODE_SUCCESS: |
| cur_state_ = STATE_COMPLETE_SUCCESS; |
| VERIFY1(s.LoadString(IDS_CLOSE)); |
| VERIFY1(::SetWindowText(GetDlgItem(IDC_CLOSE), s)); |
| complete_text_.reset(new StaticEx); |
| complete_text_->SubclassWindow(GetDlgItem(IDC_COMPLETE_TEXT)); |
| ASSERT1(text); |
| VERIFY1(::SetWindowText(GetDlgItem(IDC_COMPLETE_TEXT), text)); |
| break; |
| case COMPLETION_CODE_ERROR: |
| cur_state_ = STATE_COMPLETE_ERROR; |
| VERIFY1(s.LoadString(IDS_CLOSE)); |
| VERIFY1(::SetWindowText(GetDlgItem(IDC_CLOSE), s)); |
| complete_text_.reset(new StaticEx); |
| complete_text_->SubclassWindow(GetDlgItem(IDC_ERROR_TEXT)); |
| ASSERT1(text); |
| VERIFY1(::SetWindowText(GetDlgItem(IDC_ERROR_TEXT), text)); |
| VERIFY1(SUCCEEDED(ShowGetHelpLink(error_code))); |
| break; |
| case COMPLETION_CODE_RESTART_ALL_BROWSERS: |
| cur_state_ = STATE_COMPLETE_RESTART_ALL_BROWSERS; |
| VERIFY1(s.LoadString(IDS_RESTART_ALL_BROWSERS_NOW)); |
| VERIFY1(::SetWindowText(GetDlgItem(IDC_BUTTON1), s)); |
| VERIFY1(s.LoadString(IDS_RESTART_ALL_BROWSERS_LATER)); |
| VERIFY1(::SetWindowText(GetDlgItem(IDC_BUTTON2), s)); |
| s.FormatMessage(IDS_TEXT_RESTART_ALL_BROWSERS, product_name_); |
| VERIFY1(::SetWindowText(GetDlgItem(IDC_COMPLETE_TEXT), s)); |
| ++metric_worker_ui_restart_all_browsers_buttons_displayed; |
| break; |
| case COMPLETION_CODE_RESTART_BROWSER: |
| cur_state_ = STATE_COMPLETE_RESTART_BROWSER; |
| VERIFY1(s.LoadString(IDS_RESTART_BROWSER_NOW)); |
| VERIFY1(::SetWindowText(GetDlgItem(IDC_BUTTON1), s)); |
| VERIFY1(s.LoadString(IDS_RESTART_BROWSER_LATER)); |
| VERIFY1(::SetWindowText(GetDlgItem(IDC_BUTTON2), s)); |
| s.FormatMessage(IDS_TEXT_RESTART_BROWSER, product_name_); |
| VERIFY1(::SetWindowText(GetDlgItem(IDC_COMPLETE_TEXT), s)); |
| ++metric_worker_ui_restart_browser_buttons_displayed; |
| break; |
| case COMPLETION_CODE_REBOOT: |
| ASSERT(false, (_T("The button actions are not implemented."))); |
| cur_state_ = STATE_COMPLETE_REBOOT; |
| VERIFY1(s.LoadString(IDS_RESTART_NOW)); |
| VERIFY1(::SetWindowText(GetDlgItem(IDC_BUTTON1), s)); |
| VERIFY1(s.LoadString(IDS_RESTART_LATER)); |
| VERIFY1(::SetWindowText(GetDlgItem(IDC_BUTTON2), s)); |
| s.FormatMessage(IDS_TEXT_REBOOT, product_name_); |
| VERIFY1(::SetWindowText(GetDlgItem(IDC_COMPLETE_TEXT), s)); |
| ++metric_worker_ui_reboot_buttons_displayed; |
| break; |
| case COMPLETION_CODE_RESTART_ALL_BROWSERS_NOTICE_ONLY: |
| cur_state_ = STATE_COMPLETE_SUCCESS; |
| VERIFY1(s.LoadString(IDS_CLOSE)); |
| VERIFY1(::SetWindowText(GetDlgItem(IDC_CLOSE), s)); |
| s.FormatMessage(IDS_TEXT_RESTART_ALL_BROWSERS, product_name_); |
| VERIFY1(::SetWindowText(GetDlgItem(IDC_COMPLETE_TEXT), s)); |
| break; |
| case COMPLETION_CODE_REBOOT_NOTICE_ONLY: |
| cur_state_ = STATE_COMPLETE_SUCCESS; |
| VERIFY1(s.LoadString(IDS_CLOSE)); |
| VERIFY1(::SetWindowText(GetDlgItem(IDC_CLOSE), s)); |
| s.FormatMessage(IDS_TEXT_REBOOT, product_name_); |
| VERIFY1(::SetWindowText(GetDlgItem(IDC_COMPLETE_TEXT), s)); |
| break; |
| case COMPLETION_CODE_RESTART_BROWSER_NOTICE_ONLY: |
| cur_state_ = STATE_COMPLETE_SUCCESS; |
| VERIFY1(s.LoadString(IDS_CLOSE)); |
| VERIFY1(::SetWindowText(GetDlgItem(IDC_CLOSE), s)); |
| s.FormatMessage(IDS_TEXT_RESTART_BROWSER, product_name_); |
| VERIFY1(::SetWindowText(GetDlgItem(IDC_COMPLETE_TEXT), s)); |
| break; |
| case COMPLETION_CODE_RUN_COMMAND: |
| default: |
| ASSERT1(false); |
| break; |
| } |
| |
| VERIFY1(SUCCEEDED(EnableClose(true))); |
| VERIFY1(SUCCEEDED(ChangeControlState())); |
| } |
| |
| HRESULT ProgressWnd::ChangeControlState() { |
| for (size_t i = 0; i != arraysize(ProgressWnd::ctls_); ++i) { |
| const ControlState& ctl_state = ctls_[i]; |
| HWND hwnd = GetDlgItem(ctls_[i].id_); |
| ASSERT1(hwnd); |
| const ControlAttributes& attr = ctl_state.attr_[cur_state_]; |
| ::ShowWindow(hwnd, attr.is_visible_ ? SW_SHOW : SW_HIDE); |
| ::EnableWindow(hwnd, attr.is_enabled_ ? true : false); |
| if (attr.is_button_ && attr.is_default_) { |
| // We ask the dialog manager to give the default push button the focus, so |
| // for instance the <Enter> key works as expected. |
| GotoDlgCtrl(hwnd); |
| LONG style = ::GetWindowLong(hwnd, GWL_STYLE); |
| if (style) { |
| style |= BS_DEFPUSHBUTTON; |
| ::SetWindowLong(hwnd, GWL_STYLE, style); |
| } |
| } |
| } |
| return S_OK; |
| } |
| |
| HRESULT ProgressWnd::SetMarqueeMode(bool is_marquee) { |
| if (!SystemInfo::IsRunningOnXPOrLater()) { |
| // Marquee is not supported on OSes below XP. |
| return S_OK; |
| } |
| |
| HWND progress_bar = GetDlgItem(IDC_PROGRESS); |
| if (!progress_bar) { |
| return GOOPDATE_E_UI_INTERNAL_ERROR; |
| } |
| |
| LONG style = ::GetWindowLong(progress_bar, GWL_STYLE); |
| if (!style) { |
| return HRESULTFromLastError(); |
| } |
| |
| if (is_marquee) { |
| if (style & PBS_MARQUEE) { |
| return S_OK; |
| } |
| |
| style |= PBS_MARQUEE; |
| style = ::SetWindowLong(progress_bar, GWL_STYLE, style); |
| if (!style) { |
| return HRESULTFromLastError(); |
| } |
| |
| bool result = ::SendMessage(progress_bar, PBM_SETMARQUEE, |
| is_marquee, kMarqueeModeUpdatesMs) != 0; |
| return result ? S_OK : GOOPDATE_E_UI_INTERNAL_ERROR; |
| } else { |
| if (!(style & PBS_MARQUEE)) { |
| return S_OK; |
| } |
| |
| style &= ~PBS_MARQUEE; |
| style = ::SetWindowLong(progress_bar, GWL_STYLE, style); |
| if (!style) { |
| return HRESULTFromLastError(); |
| } |
| return S_OK; |
| } |
| } |
| |
| HRESULT ProgressWnd::EnableClose(bool enable) { |
| is_close_enabled_ = enable; |
| return EnableSystemCloseButton(is_close_enabled_); |
| } |
| |
| HRESULT ProgressWnd::EnableSystemCloseButton(bool enable) { |
| HMENU menu = ::GetSystemMenu(*this, false); |
| ASSERT1(menu); |
| uint32 flags = MF_BYCOMMAND; |
| flags |= enable ? MF_ENABLED : MF_GRAYED; |
| VERIFY1(::EnableMenuItem(menu, SC_CLOSE, flags) != -1); |
| return S_OK; |
| } |
| |
| // The system displays the system large icon in the ALT+TAB dialog box. |
| // We do not need any small icon in the window caption. However, setting |
| // ICON_BIG has the side effect of the window displaying a scaled down |
| // version of it in the window caption. We could not find any way to |
| // hide that icon, including setting the icon to NULL or handling WM_GETICON |
| // message. |
| HRESULT ProgressWnd::SetWindowIcon() { |
| const int cx = ::GetSystemMetrics(SM_CXICON); |
| const int cy = ::GetSystemMetrics(SM_CYICON); |
| HINSTANCE exe_instance = reinterpret_cast<HINSTANCE>(kExeLoadingAddress); |
| reset(hicon_, |
| reinterpret_cast<HICON>(::LoadImage(exe_instance, |
| MAKEINTRESOURCE(IDI_APP), |
| IMAGE_ICON, |
| cx, |
| cy, |
| LR_DEFAULTCOLOR))); |
| if (!hicon_) { |
| HRESULT hr = HRESULTFromLastError(); |
| CORE_LOG(LEVEL_ERROR, (_T("[LoadImage failed 0x%08x]"), hr)); |
| return hr; |
| } |
| VERIFY1(SendMessage(WM_SETICON, |
| ICON_BIG, |
| reinterpret_cast<LPARAM>(get(hicon_))) == NULL); |
| return S_OK; |
| } |
| |
| HRESULT ProgressWnd::ShowGetHelpLink(HRESULT error_code) { |
| // When running elevated and ProcessLauncherClass is not registered, the |
| // browser launch from the link will fail. Don't display a link that will not |
| // work. |
| // TODO(omaha): Determine if ProcessLauncherClass is registered. Maybe move |
| // this code to the Worker. |
| if (vista_util::IsVistaOrLater() && vista_util::IsUserAdmin()) { |
| return S_OK; |
| } |
| |
| // Do not display the link if the error already has a link. |
| if (GOOPDATE_E_OS_NOT_SUPPORTED == error_code) { |
| return S_OK; |
| } |
| |
| const TCHAR* const kHelpLinkSourceId = _T("gethelp"); |
| CString url; |
| HRESULT hr = goopdate_utils::BuildHttpGetString( |
| kUrlMoreInformation, |
| error_code, |
| 0, // extra code 1 |
| 0, // extra code 2 |
| GuidToString(product_guid_), |
| GetVersionString(), |
| is_machine_, |
| language_, |
| iid_, |
| brand_code_, |
| kHelpLinkSourceId, |
| &url); |
| if (FAILED(hr)) { |
| return hr; |
| } |
| |
| const TCHAR* const kLinkFormat = _T("<a=%s>%s</a>"); |
| CString display_text; |
| VERIFY1(display_text.LoadString(IDS_HELP_ME_FIX_THIS_TEXT)); |
| CString link_string; |
| link_string.Format(kLinkFormat, url, display_text); |
| |
| get_help_text_.reset(new StaticEx); |
| get_help_text_->SubclassWindow(GetDlgItem(IDC_GET_HELP_TEXT)); |
| VERIFY1(::SetWindowText(GetDlgItem(IDC_GET_HELP_TEXT), link_string)); |
| |
| ++metric_worker_ui_get_help_displayed; |
| return S_OK; |
| } |
| |
| bool ProgressWnd::CloseInstallStoppedWindow() { |
| if (install_stopped_wnd_.get() && install_stopped_wnd_->IsWindow()) { |
| VERIFY1(SUCCEEDED(install_stopped_wnd_->CloseWindow())); |
| install_stopped_wnd_.reset(); |
| return true; |
| } else { |
| return false; |
| } |
| } |
| |
| } // namespace omaha |
| |