#ifndef UNICODE #define UNICODE #endif #ifndef _UNICODE #define _UNICODE #endif #ifdef SIMPLEUI #undef _WIN32_IE #define _WIN32_IE 0x0500 #endif #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; #ifndef SIMPLEUI WCHAR g_szFileName[MAX_PATH]; #else WCHAR g_szFileName[] = { L"ClickOnce Application" }; #endif // Received on update request while timer is active LONG g_RefreshOnTimer = 0; #ifdef SIMPLEUI bool g_IsMinimized = FALSE; #define TRAY_UID 0 // note: ensure no conflict with other messages #define MYWM_NOTIFYICON WM_USER+9 #endif IBackgroundCopyManager *g_pManager = NULL; HRESULT CreateDialogObject(CDownloadDlg **ppDlg, IBackgroundCopyJob *pJob) { CDownloadDlg *pDlg = new CDownloadDlg; pDlg->SetJobObject(pJob); *ppDlg = pDlg; return (pDlg)->CreateUI(SW_SHOW); } VOID CDownloadDlg::SetJobObject(IBackgroundCopyJob *pJob) { SAFERELEASE(_pJob); _pJob = pJob; _pJob->AddRef(); } CDownloadDlg::CDownloadDlg() { _pJob = NULL; _hwndDlg = NULL; _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( (HINSTANCE)GetModuleHandle(L"fusenet.dll"), 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); } } } void CDownloadDlg::UpdateDialog( HWND hwndDlg ) { // // Main update routine for the dialog box. // Retries the job state/properties from // BITS and updates the dialog box. // { // update the display name #ifndef SIMPLEUI HWND hwndDisplayName = GetDlgItem( hwndDlg, IDC_DISPLAYNAME ); WCHAR * pszDisplayName = NULL; if (FAILED( _pJob->GetDisplayName( &pszDisplayName ) ) ) return; // stop updating on an error SetWindowText( hwndDisplayName, pszDisplayName ); ShowWindow( hwndDisplayName, SW_SHOW ); CoTaskMemFree( pszDisplayName ); #endif } 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, g_szFileName ); newpos = (WPARAM)Percent; } else { swprintf( szProgress, GetString( IDS_SHORTPROGRESS ), progress.BytesTransferred ); wcscpy( szTitle, g_szFileName ); 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 ); } { #ifndef SIMPLEUI // update the status HWND hwndStatus = GetDlgItem( hwndDlg, IDC_STATUS ); SetWindowText( hwndStatus, MapStateToString( state ) ); ShowWindow( hwndStatus, SW_SHOW ); #endif // 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 #ifndef SIMPLEUI if ( BG_JOB_STATE_TRANSFERRED == state && BG_JOB_STATE_TRANSFERRED != prevstate ) { SignalAlert( hwndDlg, MB_OK ); // DestroyWindow(hwndStatus); } else #endif if ( BG_JOB_STATE_ERROR == state && BG_JOB_STATE_ERROR != prevstate ) SignalAlert( hwndDlg, MB_ICONEXCLAMATION ); } #ifndef SIMPLEUI { // update times BG_JOB_TIMES times; if (FAILED(_pJob->GetTimes( × ))) return; HWND hwndCreationTime = GetDlgItem( hwndDlg, IDC_STARTTIME ); SetWindowTime( hwndCreationTime, times.CreationTime ); ShowWindow( hwndCreationTime, SW_SHOW ); HWND hwndModificationTime = GetDlgItem( hwndDlg, IDC_MODIFICATIONTIME ); SetWindowTime( hwndModificationTime, times.ModificationTime ); ShowWindow( hwndModificationTime, SW_SHOW ); HWND hwndCompletionTime = GetDlgItem( hwndDlg, IDC_COMPLETIONTIME ); if ( !times.TransferCompletionTime.dwLowDateTime && !times.TransferCompletionTime.dwHighDateTime ) { // BITS sets the CompletionTime to all zeros // if the job is incomplete ShowWindow( hwndCompletionTime, SW_HIDE ); EnableWindow( GetDlgItem( hwndDlg, IDC_COMPLETIONTIMETXT ), FALSE ); } else { SetWindowTime( hwndCompletionTime, times.TransferCompletionTime ); ShowWindow( hwndCompletionTime, SW_SHOW ); EnableWindow( GetDlgItem( hwndDlg, IDC_COMPLETIONTIMETXT ), TRUE ); } } #endif { // update the error message 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 ); 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 ); } } #ifndef SIMPLEUI if (!SendDlgItemMessage( hwndDlg, IDC_PRIORITY, CB_GETDROPPEDSTATE, 0, 0) ) { // set the priority, but only do it if user isn't trying to // set the priority. BG_JOB_PRIORITY priority; _pJob->GetPriority( &priority ); SendDlgItemMessage( hwndDlg, IDC_PRIORITY, CB_SETCURSEL, (WPARAM)priority, 0 ); } #endif { // // 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; #ifndef SIMPLEUI const WCHAR *Foreground = GetString( IDS_FOREGROUND ); const WCHAR *High = GetString( IDS_HIGH ); const WCHAR *Normal = GetString( IDS_NORMAL ); const WCHAR *Low = GetString( IDS_LOW ); #endif SendDlgItemMessage( hwndDlg, IDC_PROGRESSBAR, PBM_SETRANGE, 0, MAKELPARAM(0, 100) ); #ifndef SIMPLEUI SendDlgItemMessage( hwndDlg, IDC_PRIORITY, CB_ADDSTRING, 0, (LPARAM)Foreground ); SendDlgItemMessage( hwndDlg, IDC_PRIORITY, CB_ADDSTRING, 0, (LPARAM)High ); SendDlgItemMessage( hwndDlg, IDC_PRIORITY, CB_ADDSTRING, 0, (LPARAM)Normal ); SendDlgItemMessage( hwndDlg, IDC_PRIORITY, CB_ADDSTRING, 0, (LPARAM)Low ); #endif } 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; 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_pManager->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 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 ); #ifdef SIMPLEUI 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; } } } #endif } 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); SetTimer( _hwndDlg, 0, 500, NULL ); } 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: { #ifdef SIMPLEUI Animate_Stop(GetDlgItem(hwndDlg, IDC_ANIMATE_DOWNLOAD)); Animate_Close(GetDlgItem(hwndDlg, IDC_ANIMATE_DOWNLOAD)); #endif return FALSE; } case WM_INITDIALOG: pDlg = (CDownloadDlg*) lParam; pDlg->InitDialog(hwndDlg); #ifdef SIMPLEUI 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); #endif 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; #ifndef SIMPLEUI case IDC_PRIORITY: switch( HIWORD( wParam ) ) { case CBN_SELENDOK: // User clicked on priority, // update it. pDlg->BITSCheckHR( hwndDlg, pDlg->_pJob->SetPriority( (BG_JOB_PRIORITY) SendDlgItemMessage( hwndDlg, IDC_PRIORITY, CB_GETCURSEL, 0, 0 ) ), false ); return TRUE; case CBN_SELENDCANCEL: return TRUE; default: return FALSE; } #endif default: return FALSE; } #ifdef SIMPLEUI 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; #endif default: return FALSE; } } HRESULT CDownloadDlg::HandleCOMCallback( IBackgroundCopyJob* pJob, bool CriticalEvent ) { // In addition to the work of HandleUpdate, // this function checks to see if we've // already initialized the manager. If not, // do it now. g_pManager = CAssemblyDownload::g_pManager; /* if ( !g_pManager ) { // try { CheckHR( NULL, CoCreateInstance( CLSID_BackgroundCopyManager, NULL, CLSCTX_LOCAL_SERVER, IID_IBackgroundCopyManager, (void**)&g_pManager ), true ); //pJob->AddRef(); //_pJob = pJob; BITSCheckHR( NULL, _pJob->SetNotifyFlags( BG_NOTIFY_JOB_MODIFICATION ), true ); // As an optimization, set the notification interface to be the callback // It shouldn't matter if this fails // BUGBUG - what the heck does that mean? adriaanc // _pJob->SetNotifyInterface( (IBackgroundCopyCallback*)&g_Callback ); HRESULT Hr = HandleUpdate(); ShowWindow(_hwndDlg, CriticalEvent ? SW_NORMAL : SW_MINIMIZE); } HRESULT Hr = HandleUpdate(); ShowWindow(_hwndDlg, CriticalEvent ? SW_NORMAL : SW_MINIMIZE); catch(_com_error error ) { if ( g_pManager ) g_pManager->Release(); g_pManager = NULL; if ( _pJob ) _pJob->Release(); _pJob = NULL; return error.Error(); } } */ return HandleUpdate(); } HRESULT CDownloadDlg::CreateUI( int nShowCmd ) { HINSTANCE hinstance = GetModuleHandle(L"fusenet.dll"); 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 { #ifndef SIMPLEUI wcscpy( g_szFileName, szJobFileName ); #endif CheckHR( NULL, IIDFromString( szJobGUID, &g_JobId ), true ); CheckHR( NULL, CoCreateInstance( CLSID_BackgroundCopyManager, NULL, CLSCTX_LOCAL_SERVER, IID_IBackgroundCopyManager, (void**)&g_pManager ), true ); BITSCheckHR( NULL, g_pManager->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; }