Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1397 lines
37 KiB

#ifndef UNICODE
#define UNICODE
#endif
#ifndef _UNICODE
#define _UNICODE
#endif
#undef _WIN32_IE
#define _WIN32_IE 0x0500
#pragma warning( disable : 4786 )
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#include <math.h>
#include <float.h>
#include <commctrl.h>
#include <commdlg.h>
#include <shellapi.h>
#include <shlwapi.h>
#include <wininet.h>
#include <shlobj.h>
#include <bits.h>
#include <comdef.h>
#include <fusenetincludes.h>
#include "resource.h"
#include "dialog.h"
#include <assemblydownload.h>
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);
}