#ifndef UNICODE #define UNICODE #endif #ifndef _UNICODE #define _UNICODE #endif #undef _WIN32_IE #define _WIN32_IE 0x0500 #pragma warning( disable : 4786 ) #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "resource.h" #include "dialog.h" #include extern HINSTANCE g_hInst; // Maxstring size, bump up on problems #define MAX_STRING 0x800 // 2K // BUGBUG - these two also have to be made per-instance // adriaanc GUID g_JobId; WCHAR g_szDefaultTitle[] = { L"ClickOnce Application" }; // Received on update request while timer is active LONG g_RefreshOnTimer = 0; bool g_IsMinimized = FALSE; #define TRAY_UID 0 // note: ensure no conflict with other messages #define MYWM_NOTIFYICON WM_USER+9 HRESULT CreateDialogObject(CDownloadDlg **ppDlg) { HRESULT hr = S_OK; MAKE_ERROR_MACROS_STATIC(hr); CDownloadDlg *pDlg = NULL; IF_ALLOC_FAILED_EXIT(pDlg = new CDownloadDlg); *ppDlg = pDlg; hr = (pDlg)->CreateUI(SW_SHOW); exit: return hr; } VOID CDownloadDlg::SetJobObject(IBackgroundCopyJob *pJob) { if(_pJob) { // update job data details ..... BG_JOB_PROGRESS progress; if(SUCCEEDED(_pJob->GetProgress( &progress ))) { if ( progress.BytesTotal != BG_SIZE_UNKNOWN ) { // BUGBUG: try to do an atomic add _ui64BytesFromPrevJobs += progress.BytesTransferred; } } _dwJobCount++; } SAFERELEASE(_pJob); _pJob = pJob; _pJob->AddRef(); } CDownloadDlg::CDownloadDlg() { _pJob = NULL; _hwndDlg = NULL; _ui64StartTime = GetSystemTimeAsUINT64(); _ui64BytesFromPrevJobs = 0; _dwJobCount = 0; _eState = DOWNLOADDLG_STATE_INIT; } CDownloadDlg::~CDownloadDlg() { SAFERELEASE(_pJob); } const WCHAR * CDownloadDlg::GetString( UINT id ) { // // Retrieves the localized string for the resource id // caching the string when loaded. static const WCHAR* pStringArray[ IDS_MAX ]; static WCHAR TempStringBuffer[ MAX_STRING ]; const WCHAR * & pStringPointer = pStringArray[ id - 1 ]; // Cache resource strings if ( pStringPointer ) return pStringPointer; // Load string from resource int CharsLoaded = LoadStringW( g_hInst, id, TempStringBuffer, MAX_STRING ); if ( !CharsLoaded ) return L""; WCHAR *pNewString = new WCHAR[ CharsLoaded + 1]; if ( !pNewString ) return L""; wcscpy( pNewString, TempStringBuffer ); return ( pStringPointer = pNewString ); } void CDownloadDlg::SetWindowTime( HWND hwnd, FILETIME filetime ) { // Set the window text to be the text representation // of the file time. // If an error occurs, set the window text to be error FILETIME localtime; FileTimeToLocalFileTime( &filetime, &localtime ); SYSTEMTIME systemtime; FileTimeToSystemTime( &localtime, &systemtime ); int RequiredDateSize = GetDateFormatW( LOCALE_USER_DEFAULT, 0, &systemtime, NULL, NULL, 0 ); if ( !RequiredDateSize ) { SetWindowText( hwnd, GetString( IDS_ERROR ) ); return; } WCHAR *pszDateBuffer = (WCHAR*)alloca( sizeof(WCHAR) * (RequiredDateSize + 1) ); int DateSize = GetDateFormatW( LOCALE_USER_DEFAULT, 0, &systemtime, NULL, pszDateBuffer, RequiredDateSize ); if (!DateSize) { SetWindowText( hwnd, GetString( IDS_ERROR ) ); return; } int RequiredTimeSize = GetTimeFormatW( LOCALE_USER_DEFAULT, 0, &systemtime, NULL, NULL, 0 ); if (!RequiredTimeSize) { SetWindowText( hwnd, GetString( IDS_ERROR ) ); return; } WCHAR *pszTimeBuffer = (WCHAR*)alloca( sizeof( WCHAR ) * ( RequiredTimeSize + 1 ) ); int TimeSize = GetTimeFormatW( LOCALE_USER_DEFAULT, 0, &systemtime, NULL, pszTimeBuffer, RequiredTimeSize ); if (!TimeSize) { SetWindowText( hwnd, GetString( IDS_ERROR ) ); return; } // Add 2 for extra measure WCHAR *FullTime = (WCHAR*)alloca( sizeof( WCHAR ) * ( RequiredTimeSize + RequiredDateSize + 2 ) ); wsprintf( FullTime, L"%s %s", pszDateBuffer, pszTimeBuffer ); SetWindowText( hwnd, FullTime ); } UINT64 CDownloadDlg::GetSystemTimeAsUINT64() { // // Returns the system time as an UINT instead of a FILETIME. // FILETIME filetime; GetSystemTimeAsFileTime( &filetime ); ULARGE_INTEGER large; memcpy( &large, &filetime, sizeof(FILETIME) ); return large.QuadPart; } void CDownloadDlg::SignalAlert( HWND hwndDlg, UINT Type ) { // // Alert the user that an important event has occurred // FLASHWINFO FlashInfo; FlashInfo.cbSize = sizeof(FlashInfo); FlashInfo.hwnd = hwndDlg; FlashInfo.dwFlags = FLASHW_ALL | FLASHW_TIMERNOFG; FlashInfo.uCount = 0; FlashInfo.dwTimeout = 0; FlashWindowEx( &FlashInfo ); MessageBeep( Type ); } const WCHAR * CDownloadDlg::MapStateToString( BG_JOB_STATE state ) { // // Maps a BITS job state to a human readable string // switch( state ) { case BG_JOB_STATE_QUEUED: return GetString( IDS_QUEUED ); case BG_JOB_STATE_CONNECTING: return GetString( IDS_CONNECTING ); case BG_JOB_STATE_TRANSFERRING: return GetString( IDS_TRANSFERRING ); case BG_JOB_STATE_SUSPENDED: return GetString( IDS_SUSPENDED ); case BG_JOB_STATE_ERROR: return GetString( IDS_FATALERROR ); case BG_JOB_STATE_TRANSIENT_ERROR: return GetString( IDS_TRANSIENTERROR ); case BG_JOB_STATE_TRANSFERRED: return GetString( IDS_TRANSFERRED ); case BG_JOB_STATE_ACKNOWLEDGED: return GetString( IDS_ACKNOWLEDGED ); case BG_JOB_STATE_CANCELLED: return GetString( IDS_CANCELLED ); default: // NOTE: Always provide a default case // since new states may be added in future versions. return GetString( IDS_UNKNOWN ); } } UINT64 CDownloadDlg::ScaleDownloadRate( double Rate, // rate in seconds const WCHAR **pFormat ) { // // Scales a download rate and selects the correct // format to pass to wprintf for printing. // double RateBounds[] = { 1073741824.0, // Gigabyte 1048576.0, // Megabyte 1024.0, // Kilobyte 0 // Byte }; UINT RateFormat[] = { IDS_GIGAFORMAT, IDS_MEGAFORMAT, IDS_KILOFORMAT, IDS_BYTEFORMAT }; for( unsigned int c = 0;; c++ ) { if ( Rate >= RateBounds[c] ) { *pFormat = GetString( RateFormat[c] ); double scale = (RateBounds[c] >= 1.0) ? RateBounds[c] : 1.0; return (UINT64)floor( ( Rate / scale ) + 0.5); } } } UINT64 CDownloadDlg::ScaleDownloadEstimate( double Time, // time in seconds const WCHAR **pFormat ) { // // Scales a download time estimate and selects the correct // format to pass to wprintf for printing. // double TimeBounds[] = { 60.0 * 60.0 * 24.0, // Days 60.0 * 60.0, // Hours 60.0, // Minutes 0.0 // Seconds }; UINT TimeFormat[] = { IDS_DAYSFORMAT, IDS_HOURSFORMAT, IDS_MINUTESFORMAT, IDS_SECONDSFORMAT }; for( unsigned int c = 0;; c++ ) { if ( Time >= TimeBounds[c] ) { *pFormat = GetString( TimeFormat[c] ); double scale = (TimeBounds[c] >= 1.0) ? TimeBounds[c] : 1.0; return (UINT64)floor( ( Time / scale ) + 0.5); } } } // DemoHack void CDownloadDlg::UpdateDialog( HWND hwndDlg, LPWSTR wzErrorMsg) { SetWindowText( GetDlgItem( hwndDlg, IDC_ERRORMSG ), wzErrorMsg ); ShowWindow( GetDlgItem( hwndDlg, IDC_ERRORMSG ), SW_SHOW ); } HRESULT CDownloadDlg::UpdateProgress( HWND hwndDlg ) { HRESULT hr = S_OK; MAKE_ERROR_MACROS_STATIC(hr); BG_JOB_PROGRESS progress; IBackgroundCopyError *pError = NULL; WCHAR szProgress[MAX_STRING]; WCHAR szTitle[MAX_STRING]; WPARAM newpos = 0; UINT64 ui64BytesTotal = _ui64BytesFromPrevJobs; UINT64 ui64BytesTransferred = _ui64BytesFromPrevJobs; double AvgRate = 0; static BG_JOB_STATE prevstate = BG_JOB_STATE_SUSPENDED; BG_JOB_STATE state; IF_FAILED_EXIT(_pJob->GetState( &state )); IF_FAILED_EXIT(_pJob->GetProgress( &progress )); // update the title, progress bar, and progress description if ( progress.BytesTotal != BG_SIZE_UNKNOWN ) { ui64BytesTotal += progress.BytesTotal; ui64BytesTransferred += progress.BytesTransferred; } if ( ui64BytesTotal ) { swprintf( szProgress, GetString( IDS_LONGPROGRESS ),ui64BytesTransferred, ui64BytesTotal ); double Percent = (double)ui64BytesTransferred *100 / (double)ui64BytesTotal; swprintf( szTitle, L"%u%% of %s Downloaded", (unsigned int)Percent, (_sTitle._cc != 0) ? _sTitle._pwz : g_szDefaultTitle ); newpos = (WPARAM)Percent; } else { swprintf( szProgress, GetString( IDS_SHORTPROGRESS ), ui64BytesTransferred ); wcscpy( szTitle, (_sTitle.CharCount() > 1) ? _sTitle._pwz : g_szDefaultTitle ); newpos = 0; } SendDlgItemMessage( hwndDlg, IDC_PROGRESSBAR, PBM_SETPOS, newpos, 0 ); SetWindowText( GetDlgItem( hwndDlg, IDC_PROGRESSINFO ), szProgress ); ShowWindow( GetDlgItem( hwndDlg, IDC_PROGRESSINFO ), SW_SHOW ); EnableWindow( GetDlgItem( hwndDlg, IDC_PROGRESSINFOTXT ), TRUE ); SetWindowText( hwndDlg, szTitle ); // Only enable the finish button if the job is finished. // ADRIAANC EnableWindow( GetDlgItem( hwndDlg, IDC_FINISH ), ( state == BG_JOB_STATE_TRANSFERRED ) ); EnableWindow( GetDlgItem( hwndDlg, IDC_FINISH ), ( state == BG_JOB_STATE_ACKNOWLEDGED ) ); // felixybc BUGBUG: CANCEL is not allowed when the job is done // - should hold off ACK-ing that job until user clicks FINISH so that it can still be canceled at 100%? EnableWindow( GetDlgItem( hwndDlg, IDC_CANCEL ), ( state != BG_JOB_STATE_ACKNOWLEDGED && state != BG_JOB_STATE_CANCELLED ) ); // Only enable the suspend button if the job is not finished or transferred BOOL EnableSuspend = ( state != BG_JOB_STATE_SUSPENDED ) && ( state != BG_JOB_STATE_TRANSFERRED ) && (state != BG_JOB_STATE_ACKNOWLEDGED); EnableWindow( GetDlgItem( hwndDlg, IDC_SUSPEND ), EnableSuspend ); // Only enable the resume button if the job is suspended BOOL EnableResume = ( BG_JOB_STATE_SUSPENDED == state ); EnableWindow( GetDlgItem( hwndDlg, IDC_RESUME ), EnableResume ); // Alert the user when something important happens // such as the job completes or a unrecoverable error occurs if ( (BG_JOB_STATE_ERROR == state) && (BG_JOB_STATE_ERROR != prevstate) ) SignalAlert( hwndDlg, MB_ICONEXCLAMATION ); // update the error message if ( FAILED(_pJob->GetError( &pError )) ) { ShowWindow( GetDlgItem( hwndDlg, IDC_ERRORMSG ), SW_HIDE ); EnableWindow( GetDlgItem( hwndDlg, IDC_ERRORMSGTXT ), FALSE ); } else { CString sErrMsg; IF_FAILED_EXIT(CAssemblyDownload::GetBITSErrorMsg(pError, sErrMsg)); HWND hwndErrorText = GetDlgItem( hwndDlg, IDC_ERRORMSG ); SetWindowText( hwndErrorText, sErrMsg._pwz ); ShowWindow( hwndErrorText, SW_SHOW ); EnableWindow( GetDlgItem( hwndDlg, IDC_ERRORMSGTXT ), TRUE ); } // // This large block of text computes the average transfer rate // and estimated completion time. This code has much // room for improvement. // BOOL HasRates = TRUE; BOOL EnableRate = FALSE; WCHAR szRateText[MAX_STRING]; if ( !( BG_JOB_STATE_QUEUED == state ) && !( BG_JOB_STATE_CONNECTING == state ) && !( BG_JOB_STATE_TRANSFERRING == state ) ) { // If the job isn't running, then rate values won't // make any sense. Don't display them. HasRates = FALSE; } if ( HasRates ) { UINT64 ui64CurrentTime = GetSystemTimeAsUINT64(); UINT64 ui64TimeDiff = ui64CurrentTime - _ui64StartTime; AvgRate = (double)(__int64)ui64BytesTransferred / (double)(__int64) ui64TimeDiff; // convert from FILETIME units to seconds double NewDisplayRate = AvgRate * 10000000; const WCHAR *pRateFormat = NULL; UINT64 Rate = ScaleDownloadRate( NewDisplayRate, &pRateFormat ); wsprintf( szRateText, pRateFormat, Rate ); EnableRate = TRUE; } if (!EnableRate) { ShowWindow( GetDlgItem( hwndDlg, IDC_TRANSFERRATE ), SW_HIDE ); EnableWindow( GetDlgItem( hwndDlg, IDC_TRANSFERRATETXT ), FALSE ); } else { SetWindowText( GetDlgItem( hwndDlg, IDC_TRANSFERRATE ), szRateText ); ShowWindow( GetDlgItem( hwndDlg, IDC_TRANSFERRATE ), SW_SHOW ); EnableWindow( GetDlgItem( hwndDlg, IDC_TRANSFERRATETXT ), TRUE ); } BOOL EnableEstimate = FALSE; WCHAR szEstimateText[MAX_STRING]; if ( EnableRate && ui64BytesTotal && AvgRate) { double TimeRemaining = ( ui64BytesTotal - ui64BytesTransferred ) / AvgRate; // convert from FILETIME units to seconds TimeRemaining = TimeRemaining / 10000000.0; static const double SecsPer30Days = 60.0 * 60.0 * 24.0 * 30.0; // Don't estimate if estimate is larger then 30 days. if ( TimeRemaining < SecsPer30Days ) { const WCHAR *pFormat = NULL; UINT64 Time = ScaleDownloadEstimate( TimeRemaining, &pFormat ); wsprintf( szEstimateText, pFormat, Time ); EnableEstimate = TRUE; } } if (!EnableEstimate) { ShowWindow( GetDlgItem( hwndDlg, IDC_ESTIMATEDTIME ), SW_HIDE ); EnableWindow( GetDlgItem( hwndDlg, IDC_ESTIMATEDTIMETXT ), FALSE ); } else { SetWindowText( GetDlgItem( hwndDlg, IDC_ESTIMATEDTIME ), szEstimateText ); ShowWindow( GetDlgItem( hwndDlg, IDC_ESTIMATEDTIME ), SW_SHOW ); EnableWindow( GetDlgItem( hwndDlg, IDC_ESTIMATEDTIMETXT ), TRUE ); } prevstate = state; exit : SAFERELEASE(pError); return hr; } void CDownloadDlg::UpdateDialog( HWND hwndDlg ) { UpdateProgress(hwndDlg); return; // // Main update routine for the dialog box. // Retries the job state/properties from // BITS and updates the dialog box. // // update the display name static BG_JOB_STATE prevstate = BG_JOB_STATE_SUSPENDED; BG_JOB_STATE state; if (FAILED(_pJob->GetState( &state ))) return; // stop updating on an error if ( BG_JOB_STATE_ACKNOWLEDGED == state || BG_JOB_STATE_CANCELLED == state ) { // someone else cancelled or completed the job on us, // just exist the exit. // May happen if job is canceled with bitsadmin // DeleteStartupLink( g_JobId ); // ExitProcess( 0 ); // BUGBUG: Should post a CANCEL message to assemblydownload } BG_JOB_PROGRESS progress; if (FAILED(_pJob->GetProgress( &progress ))) return; // stop updating on an error { // update the title, progress bar, and progress description WCHAR szProgress[MAX_STRING]; WCHAR szTitle[MAX_STRING]; WPARAM newpos = 0; if ( progress.BytesTotal && ( progress.BytesTotal != BG_SIZE_UNKNOWN ) ) { swprintf( szProgress, GetString( IDS_LONGPROGRESS ), progress.BytesTransferred, progress.BytesTotal ); double Percent = (double)(__int64)progress.BytesTransferred / (double)(__int64)progress.BytesTotal; Percent *= 100.0; swprintf( szTitle, L"%u%% of %s Downloaded", (unsigned int)Percent, (_sTitle._cc != 0) ? _sTitle._pwz : g_szDefaultTitle ); newpos = (WPARAM)Percent; } else { swprintf( szProgress, GetString( IDS_SHORTPROGRESS ), progress.BytesTransferred ); wcscpy( szTitle, (_sTitle._cc != 0) ? _sTitle._pwz : g_szDefaultTitle ); newpos = 0; } SendDlgItemMessage( hwndDlg, IDC_PROGRESSBAR, PBM_SETPOS, newpos, 0 ); SetWindowText( GetDlgItem( hwndDlg, IDC_PROGRESSINFO ), szProgress ); ShowWindow( GetDlgItem( hwndDlg, IDC_PROGRESSINFO ), SW_SHOW ); EnableWindow( GetDlgItem( hwndDlg, IDC_PROGRESSINFOTXT ), TRUE ); SetWindowText( hwndDlg, szTitle ); } { // Only enable the finish button if the job is finished. // ADRIAANC EnableWindow( GetDlgItem( hwndDlg, IDC_FINISH ), ( state == BG_JOB_STATE_TRANSFERRED ) ); EnableWindow( GetDlgItem( hwndDlg, IDC_FINISH ), ( state == BG_JOB_STATE_ACKNOWLEDGED ) ); // felixybc BUGBUG: CANCEL is not allowed when the job is done // - should hold off ACK-ing that job until user clicks FINISH so that it can still be canceled at 100%? EnableWindow( GetDlgItem( hwndDlg, IDC_CANCEL ), ( state != BG_JOB_STATE_ACKNOWLEDGED && state != BG_JOB_STATE_CANCELLED ) ); // Only enable the suspend button if the job is not finished or transferred BOOL EnableSuspend = ( state != BG_JOB_STATE_SUSPENDED ) && ( state != BG_JOB_STATE_TRANSFERRED ) && (state != BG_JOB_STATE_ACKNOWLEDGED); EnableWindow( GetDlgItem( hwndDlg, IDC_SUSPEND ), EnableSuspend ); // Only enable the resume button if the job is suspended BOOL EnableResume = ( BG_JOB_STATE_SUSPENDED == state ); EnableWindow( GetDlgItem( hwndDlg, IDC_RESUME ), EnableResume ); // Alert the user when something important happens // such as the job completes or a unrecoverable error occurs if ( BG_JOB_STATE_ERROR == state && BG_JOB_STATE_ERROR != prevstate ) SignalAlert( hwndDlg, MB_ICONEXCLAMATION ); } { // update the error message // BUGBUG - release the error interface. IBackgroundCopyError *pError; HRESULT Hr = _pJob->GetError( &pError ); if ( FAILED(Hr) ) { ShowWindow( GetDlgItem( hwndDlg, IDC_ERRORMSG ), SW_HIDE ); EnableWindow( GetDlgItem( hwndDlg, IDC_ERRORMSGTXT ), FALSE ); } else { WCHAR* pszDescription = NULL; WCHAR* pszContext = NULL; SIZE_T SizeRequired = 0; // If these APIs fail, we should get back // a NULL string. So everything should be harmless. pError->GetErrorDescription( LANGIDFROMLCID( GetThreadLocale() ), &pszDescription ); pError->GetErrorContextDescription( LANGIDFROMLCID( GetThreadLocale() ), &pszContext ); SAFERELEASE(pError); if ( pszDescription ) SizeRequired += wcslen( pszDescription ); if ( pszContext ) SizeRequired += wcslen( pszContext ); WCHAR* pszFullText = (WCHAR*)_alloca((SizeRequired + 1) * sizeof(WCHAR)); *pszFullText = L'\0'; if ( pszDescription ) wcscpy( pszFullText, pszDescription ); if ( pszContext ) wcscat( pszFullText, pszContext ); CoTaskMemFree( pszDescription ); CoTaskMemFree( pszContext ); HWND hwndErrorText = GetDlgItem( hwndDlg, IDC_ERRORMSG ); SetWindowText( hwndErrorText, pszFullText ); ShowWindow( hwndErrorText, SW_SHOW ); EnableWindow( GetDlgItem( hwndDlg, IDC_ERRORMSGTXT ), TRUE ); } } { // // This large block of text computes the average transfer rate // and estimated completion time. This code has much // room for improvement. // static BOOL HasRates = FALSE; static UINT64 LastMeasurementTime; static UINT64 LastMeasurementBytes; static double LastMeasurementRate; WCHAR szRateText[MAX_STRING]; BOOL EnableRate = FALSE; if ( !( BG_JOB_STATE_QUEUED == state ) && !( BG_JOB_STATE_CONNECTING == state ) && !( BG_JOB_STATE_TRANSFERRING == state ) ) { // If the job isn't running, then rate values won't // make any sense. Don't display them. HasRates = FALSE; } else { if ( !HasRates ) { LastMeasurementTime = GetSystemTimeAsUINT64(); LastMeasurementBytes = progress.BytesTransferred; LastMeasurementRate = 0; HasRates = TRUE; } else { UINT64 CurrentTime = GetSystemTimeAsUINT64(); UINT64 NewTotalBytes = progress.BytesTransferred; UINT64 NewTimeDiff = CurrentTime - LastMeasurementTime; UINT64 NewBytesDiff = NewTotalBytes - LastMeasurementBytes; double NewInstantRate = (double)(__int64)NewBytesDiff / (double)(__int64)NewTimeDiff; double NewAvgRate = (0.3 * LastMeasurementRate) + (0.7 * NewInstantRate ); if ( !_finite(NewInstantRate) || !_finite(NewAvgRate) ) { NewInstantRate = 0; NewAvgRate = LastMeasurementRate; } LastMeasurementTime = CurrentTime; LastMeasurementBytes = NewTotalBytes; LastMeasurementRate = NewAvgRate; // convert from FILETIME units to seconds double NewDisplayRate = NewAvgRate * 10000000; const WCHAR *pRateFormat = NULL; UINT64 Rate = ScaleDownloadRate( NewDisplayRate, &pRateFormat ); wsprintf( szRateText, pRateFormat, Rate ); EnableRate = TRUE; } } if (!EnableRate) { ShowWindow( GetDlgItem( hwndDlg, IDC_TRANSFERRATE ), SW_HIDE ); EnableWindow( GetDlgItem( hwndDlg, IDC_TRANSFERRATETXT ), FALSE ); } else { SetWindowText( GetDlgItem( hwndDlg, IDC_TRANSFERRATE ), szRateText ); ShowWindow( GetDlgItem( hwndDlg, IDC_TRANSFERRATE ), SW_SHOW ); EnableWindow( GetDlgItem( hwndDlg, IDC_TRANSFERRATETXT ), TRUE ); } BOOL EnableEstimate = FALSE; WCHAR szEstimateText[MAX_STRING]; if ( EnableRate ) { if ( progress.BytesTotal != 0 && progress.BytesTotal != BG_SIZE_UNKNOWN ) { double TimeRemaining = ( (__int64)progress.BytesTotal - (__int64)LastMeasurementBytes ) / LastMeasurementRate; // convert from FILETIME units to seconds TimeRemaining = TimeRemaining / 10000000.0; static const double SecsPer30Days = 60.0 * 60.0 * 24.0 * 30.0; // Don't estimate if estimate is larger then 30 days. if ( TimeRemaining < SecsPer30Days ) { const WCHAR *pFormat = NULL; UINT64 Time = ScaleDownloadEstimate( TimeRemaining, &pFormat ); wsprintf( szEstimateText, pFormat, Time ); EnableEstimate = TRUE; } } } if (!EnableEstimate) { ShowWindow( GetDlgItem( hwndDlg, IDC_ESTIMATEDTIME ), SW_HIDE ); EnableWindow( GetDlgItem( hwndDlg, IDC_ESTIMATEDTIMETXT ), FALSE ); } else { SetWindowText( GetDlgItem( hwndDlg, IDC_ESTIMATEDTIME ), szEstimateText ); ShowWindow( GetDlgItem( hwndDlg, IDC_ESTIMATEDTIME ), SW_SHOW ); EnableWindow( GetDlgItem( hwndDlg, IDC_ESTIMATEDTIMETXT ), TRUE ); } } prevstate = state; } void CDownloadDlg::InitDialog( HWND hwndDlg ) { // // Populate the priority list with priority descriptions // _hwndDlg = hwndDlg; SendDlgItemMessage( hwndDlg, IDC_PROGRESSBAR, PBM_SETRANGE, 0, MAKELPARAM(0, 100) ); } void CDownloadDlg::CheckHR( HWND hwnd, HRESULT Hr, bool bThrow ) { // // Provides automatic error code checking and dialog // for generic system errors // if (SUCCEEDED(Hr)) return; WCHAR * pszError = NULL; if(FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, (DWORD)Hr, LANGIDFROMLCID( GetThreadLocale() ), (WCHAR*)&pszError, 0, NULL )) { MessageBox( hwnd, pszError, GetString( IDS_ERRORBOXTITLE ), MB_OK | MB_ICONSTOP | MB_APPLMODAL ); LocalFree( pszError ); } if ( bThrow ) throw _com_error( Hr ); } void CDownloadDlg::BITSCheckHR( HWND hwnd, HRESULT Hr, bool bThrow ) { // // Provides automatic error code checking and dialog // for BITS specific errors // if (SUCCEEDED(Hr)) return; WCHAR * pszError = NULL; g_pBITSManager->GetErrorDescription( Hr, LANGIDFROMLCID( GetThreadLocale() ), &pszError ); MessageBox( hwnd, pszError, GetString( IDS_ERRORBOXTITLE ), MB_OK | MB_ICONSTOP | MB_APPLMODAL ); CoTaskMemFree( pszError ); if ( bThrow ) throw _com_error( Hr ); } void CDownloadDlg::DoCancel( HWND hwndDlg, bool PromptUser ) { // // Handle all the operations required to cancel the job. // This includes asking the user for confirmation. // /* if ( PromptUser ) { int Result = MessageBox( hwndDlg, GetString( IDS_CANCELTEXT ), GetString( IDS_CANCELCAPTION ), MB_YESNO | MB_ICONWARNING | MB_DEFBUTTON2 | MB_APPLMODAL | MB_SETFOREGROUND | MB_TOPMOST ); if ( IDYES != Result ) return; } */ // try { // BITSCheckHR( hwndDlg, _pJob->Cancel(), false );//felixybc true ); } // catch( _com_error Error ) { // If we can't cancel for some unknown reason, // don't exit // return; } // DeleteStartupLink( g_JobId ); //felixybc ExitProcess( 0 ); //KillTimer(hwndDlg, 0); PostMessage(hwndDlg, WM_CANCEL_DOWNLOAD, 0, 0); } void CDownloadDlg::DoFinish( HWND hwndDlg ) { // // Handles all the necessary work to complete // the download. // // try { // ADRIAANC // BITSCheckHR( hwndDlg, _pJob->Complete(), true ); } // catch( _com_error Error ) { // If we can't finish/complete for some unknown reason, // don't exit // return; } // DeleteStartupLink( g_JobId ); // ExitProcess( 0 ); // Commit the bits and notify done. //_pDownload->_pRootEmit->Commit(0); //KillTimer(hwndDlg, 0); PostMessage(hwndDlg, WM_FINISH_DOWNLOAD, 0, 0); return; } void CDownloadDlg::DoClose( HWND hwndDlg ) { // // Handles an attempt by the user to close the sample. // // Check to see if the download has finished, // if so don't let the user exit. BG_JOB_STATE state; HRESULT hResult = _pJob->GetState( &state ); if (FAILED( hResult )) { BITSCheckHR( hwndDlg, hResult, false ); return; } // BUGBUG: should also check for BG_JOB_STATE_ACKNOWLEDGED and don't call DoCancel then // _pJob->Cancel(); DoCancel( hwndDlg, false ); return; /* if ( BG_JOB_STATE_ERROR == state || BG_JOB_STATE_TRANSFERRED == state ) { MessageBox( hwndDlg, GetString( IDS_ALREADYFINISHED ), GetString( IDS_ALREADYFINISHEDCAPTION ), MB_OK | MB_ICONERROR | MB_DEFBUTTON1 | MB_APPLMODAL | MB_SETFOREGROUND | MB_TOPMOST ); return; } // // Inform the user that he selected close and ask // confirm the intention to exit. Explain that the job // will be canceled. int Result = MessageBox( hwndDlg, GetString( IDS_CLOSETEXT ), GetString( IDS_CLOSECAPTION ), MB_OKCANCEL | MB_ICONWARNING | MB_DEFBUTTON2 | MB_APPLMODAL | MB_SETFOREGROUND | MB_TOPMOST ); if ( IDOK == Result ) { // User confirmed the cancel, just do it. DoCancel( hwndDlg, false ); return; } // The user didn't really want to exit, so ignore him else return; */ } void CDownloadDlg::HandleTimerTick( HWND hwndDlg ) { // The timer fired. Update dialog. UpdateDialog( hwndDlg ); if (_eState == DOWNLOADDLG_STATE_ALL_DONE) { static bool bHasTip = FALSE; if (!g_IsMinimized) { // not minimized, continue to run app DoFinish(hwndDlg); } else { if (!bHasTip) { // minimized, pop up buttom tip NOTIFYICONDATA tnid = {0}; // ignore all error tnid.cbSize = sizeof(NOTIFYICONDATA); tnid.hWnd = hwndDlg; tnid.uID = TRAY_UID; tnid.uFlags = NIF_INFO; tnid.uTimeout = 20000; // in milliseconds tnid.dwInfoFlags = NIIF_INFO; lstrcpyn(tnid.szInfoTitle, L"ClickOnce application ready!", (sizeof(tnid.szInfoTitle)/sizeof(tnid.szInfoTitle[0]))); lstrcpyn(tnid.szInfo, L"Click this notification icon to start. You can also find this new application on your Start Menu, Programs listing.", (sizeof(tnid.szInfo)/sizeof(tnid.szInfo[0]))); Shell_NotifyIcon(NIM_MODIFY, &tnid); bHasTip = TRUE; } } } } HRESULT CDownloadDlg::HandleUpdate() { // Handle a update request, batching the update if needed DWORD dwRefresh = 0; dwRefresh = InterlockedIncrement(&g_RefreshOnTimer); if (dwRefresh == 1) { // First time in; fire off timer and update the dialog. UpdateDialog(_hwndDlg); SendMessage(_hwndDlg, WM_SETCALLBACKTIMER, 0, 0); } else { // We've already received the first callback. // Let the timer do any further work. InterlockedDecrement(&g_RefreshOnTimer); } return S_OK; } INT_PTR CALLBACK DialogProc( HWND hwndDlg, // handle to dialog box UINT uMsg, // message WPARAM wParam, // first message parameter LPARAM lParam // second message parameter ) { // // Dialog proc for main dialog window // static CDownloadDlg *pDlg = NULL; switch( uMsg ) { case WM_DESTROY: { Animate_Stop(GetDlgItem(hwndDlg, IDC_ANIMATE_DOWNLOAD)); Animate_Close(GetDlgItem(hwndDlg, IDC_ANIMATE_DOWNLOAD)); return FALSE; } case WM_INITDIALOG: pDlg = (CDownloadDlg*) lParam; pDlg->InitDialog(hwndDlg); ShowWindow(GetDlgItem(hwndDlg, IDC_ANIMATE_DOWNLOAD), SW_SHOW); Animate_Open(GetDlgItem(hwndDlg, IDC_ANIMATE_DOWNLOAD), MAKEINTRESOURCE(IDA_DOWNLOADING)); Animate_Play(GetDlgItem(hwndDlg, IDC_ANIMATE_DOWNLOAD), 0, -1, -1); return TRUE; case WM_SETCALLBACKTIMER: SetTimer(hwndDlg, 1, 500, NULL ); return TRUE; case WM_TIMER: pDlg->HandleTimerTick( hwndDlg ); return TRUE; case WM_CLOSE: pDlg->DoClose( hwndDlg ); return TRUE; case WM_COMMAND: switch( LOWORD( wParam ) ) { case IDC_RESUME: pDlg->BITSCheckHR( hwndDlg, pDlg->_pJob->Resume(), false ); return TRUE; case IDC_SUSPEND: pDlg->BITSCheckHR( hwndDlg, pDlg->_pJob->Suspend(), false ); return TRUE; case IDC_CANCEL: pDlg->DoCancel( hwndDlg, true ); return TRUE; case IDC_FINISH: pDlg->DoFinish( hwndDlg ); return TRUE; default: return FALSE; } case WM_SIZE: if (wParam == SIZE_MINIMIZED) { HICON hIcon = LoadIcon(g_hInst, MAKEINTRESOURCE(IDI_ICON)); if (hIcon != NULL) { NOTIFYICONDATA tnid = {0}; // ignore all error (user will not be able to restore dialog in some cases) tnid.cbSize = sizeof(NOTIFYICONDATA); tnid.hWnd = hwndDlg; tnid.uID = TRAY_UID; tnid.uFlags = NIF_MESSAGE | NIF_ICON | NIF_TIP; tnid.uCallbackMessage = MYWM_NOTIFYICON; tnid.hIcon = hIcon; lstrcpyn(tnid.szTip, L"Downloading ClickOnce application.", (sizeof(tnid.szTip)/sizeof(tnid.szTip[0]))); // set tip to file name Shell_NotifyIcon(NIM_ADD, &tnid); DestroyIcon(hIcon); // set shell32 v5 behavior tnid.uVersion = NOTIFYICON_VERSION; tnid.uFlags = 0; Shell_NotifyIcon(NIM_SETVERSION, &tnid); // hide window ShowWindow( hwndDlg, SW_HIDE ); g_IsMinimized = TRUE; return TRUE; } // else error loading icon - ignore } return FALSE; case MYWM_NOTIFYICON: if (g_IsMinimized && (lParam == WM_CONTEXTMENU || lParam == NIN_KEYSELECT || lParam == NIN_SELECT )) { // if the notification icon is clicked on NOTIFYICONDATA tnid = {0}; // show window ShowWindow( hwndDlg, SW_RESTORE ); g_IsMinimized = FALSE; // remove icon from tray tnid.cbSize = sizeof(NOTIFYICONDATA); tnid.hWnd = hwndDlg; tnid.uID = TRAY_UID; tnid.uFlags = 0; Shell_NotifyIcon(NIM_DELETE, &tnid); return TRUE; } return FALSE; default: return FALSE; } } HRESULT CDownloadDlg::CreateUI( int nShowCmd ) { DWORD dwError = 0; // // Creates the dialog box for the sample. // InitCommonControls(); _hwndDlg = CreateDialogParam( g_hInst, MAKEINTRESOURCE(IDD_DIALOG), NULL, DialogProc, (LPARAM) (this)); if (!_hwndDlg) { dwError = GetLastError(); return HRESULT_FROM_WIN32(dwError); } ShowWindow(_hwndDlg, nShowCmd); return S_OK; } void CDownloadDlg::ResumeJob( WCHAR* szJobGUID, WCHAR* szJobFileName ) { // // Resume the display on an existing job // // try { CheckHR( NULL, IIDFromString( szJobGUID, &g_JobId ), true ); CheckHR( NULL, CoCreateInstance( CLSID_BackgroundCopyManager, NULL, CLSCTX_LOCAL_SERVER, IID_IBackgroundCopyManager, (void**)&g_pBITSManager ), true ); BITSCheckHR( NULL, g_pBITSManager->GetJob( g_JobId, &_pJob ), true ); // BUGBUG - bits dialog class doesn't know about callbacks - adriaanc // BITSCheckHR( NULL, // _pJob->SetNotifyInterface( (IBackgroundCopyCallback*)&g_Callback ), // true ); BITSCheckHR( NULL, _pJob->SetNotifyFlags( BG_NOTIFY_JOB_MODIFICATION ), true ); ShowWindow(_hwndDlg, SW_MINIMIZE ); HandleUpdate(); } /* catch(_com_error error ) { ExitProcess( error.Error() ); } */ } void CDownloadDlg::SetJob(IBackgroundCopyJob *pJob) { SAFERELEASE(_pJob); _pJob = pJob; _pJob->AddRef(); } void CDownloadDlg::SetDlgState(DOWNLOADDLG_STATE eState) { _eState = eState; } HRESULT CDownloadDlg::SetDlgTitle(LPCWSTR pwzTitle) { return _sTitle.Assign((LPWSTR)pwzTitle); }