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

  1. #ifndef UNICODE
  2. #define UNICODE
  3. #endif
  4. #ifndef _UNICODE
  5. #define _UNICODE
  6. #endif
  7. #undef _WIN32_IE
  8. #define _WIN32_IE 0x0500
  9. #pragma warning( disable : 4786 )
  10. #include <windows.h>
  11. #include <stdio.h>
  12. #include <stdlib.h>
  13. #include <malloc.h>
  14. #include <math.h>
  15. #include <float.h>
  16. #include <commctrl.h>
  17. #include <commdlg.h>
  18. #include <shellapi.h>
  19. #include <shlwapi.h>
  20. #include <wininet.h>
  21. #include <shlobj.h>
  22. #include <bits.h>
  23. #include <comdef.h>
  24. #include <fusenetincludes.h>
  25. #include "resource.h"
  26. #include "dialog.h"
  27. #include <assemblydownload.h>
  28. extern HINSTANCE g_hInst;
  29. // Maxstring size, bump up on problems
  30. #define MAX_STRING 0x800 // 2K
  31. // BUGBUG - these two also have to be made per-instance
  32. // adriaanc
  33. GUID g_JobId;
  34. WCHAR g_szDefaultTitle[] = { L"ClickOnce Application" };
  35. // Received on update request while timer is active
  36. LONG g_RefreshOnTimer = 0;
  37. bool g_IsMinimized = FALSE;
  38. #define TRAY_UID 0
  39. // note: ensure no conflict with other messages
  40. #define MYWM_NOTIFYICON WM_USER+9
  41. HRESULT CreateDialogObject(CDownloadDlg **ppDlg)
  42. {
  43. HRESULT hr = S_OK;
  44. MAKE_ERROR_MACROS_STATIC(hr);
  45. CDownloadDlg *pDlg = NULL;
  46. IF_ALLOC_FAILED_EXIT(pDlg = new CDownloadDlg);
  47. *ppDlg = pDlg;
  48. hr = (pDlg)->CreateUI(SW_SHOW);
  49. exit:
  50. return hr;
  51. }
  52. VOID CDownloadDlg::SetJobObject(IBackgroundCopyJob *pJob)
  53. {
  54. if(_pJob)
  55. {
  56. // update job data details .....
  57. BG_JOB_PROGRESS progress;
  58. if(SUCCEEDED(_pJob->GetProgress( &progress )))
  59. {
  60. if ( progress.BytesTotal != BG_SIZE_UNKNOWN )
  61. {
  62. // BUGBUG: try to do an atomic add
  63. _ui64BytesFromPrevJobs += progress.BytesTransferred;
  64. }
  65. }
  66. _dwJobCount++;
  67. }
  68. SAFERELEASE(_pJob);
  69. _pJob = pJob;
  70. _pJob->AddRef();
  71. }
  72. CDownloadDlg::CDownloadDlg()
  73. {
  74. _pJob = NULL;
  75. _hwndDlg = NULL;
  76. _ui64StartTime = GetSystemTimeAsUINT64();
  77. _ui64BytesFromPrevJobs = 0;
  78. _dwJobCount = 0;
  79. _eState = DOWNLOADDLG_STATE_INIT;
  80. }
  81. CDownloadDlg::~CDownloadDlg()
  82. {
  83. SAFERELEASE(_pJob);
  84. }
  85. const WCHAR * CDownloadDlg::GetString( UINT id )
  86. {
  87. //
  88. // Retrieves the localized string for the resource id
  89. // caching the string when loaded.
  90. static const WCHAR* pStringArray[ IDS_MAX ];
  91. static WCHAR TempStringBuffer[ MAX_STRING ];
  92. const WCHAR * & pStringPointer = pStringArray[ id - 1 ];
  93. // Cache resource strings
  94. if ( pStringPointer )
  95. return pStringPointer;
  96. // Load string from resource
  97. int CharsLoaded =
  98. LoadStringW(
  99. g_hInst,
  100. id,
  101. TempStringBuffer,
  102. MAX_STRING );
  103. if ( !CharsLoaded )
  104. return L"";
  105. WCHAR *pNewString = new WCHAR[ CharsLoaded + 1];
  106. if ( !pNewString )
  107. return L"";
  108. wcscpy( pNewString, TempStringBuffer );
  109. return ( pStringPointer = pNewString );
  110. }
  111. void CDownloadDlg::SetWindowTime(
  112. HWND hwnd,
  113. FILETIME filetime
  114. )
  115. {
  116. // Set the window text to be the text representation
  117. // of the file time.
  118. // If an error occurs, set the window text to be error
  119. FILETIME localtime;
  120. FileTimeToLocalFileTime( &filetime, &localtime );
  121. SYSTEMTIME systemtime;
  122. FileTimeToSystemTime( &localtime, &systemtime );
  123. int RequiredDateSize =
  124. GetDateFormatW(
  125. LOCALE_USER_DEFAULT,
  126. 0,
  127. &systemtime,
  128. NULL,
  129. NULL,
  130. 0 );
  131. if ( !RequiredDateSize )
  132. {
  133. SetWindowText( hwnd, GetString( IDS_ERROR ) );
  134. return;
  135. }
  136. WCHAR *pszDateBuffer = (WCHAR*)alloca( sizeof(WCHAR) * (RequiredDateSize + 1) );
  137. int DateSize =
  138. GetDateFormatW(
  139. LOCALE_USER_DEFAULT,
  140. 0,
  141. &systemtime,
  142. NULL,
  143. pszDateBuffer,
  144. RequiredDateSize );
  145. if (!DateSize)
  146. {
  147. SetWindowText( hwnd, GetString( IDS_ERROR ) );
  148. return;
  149. }
  150. int RequiredTimeSize =
  151. GetTimeFormatW(
  152. LOCALE_USER_DEFAULT,
  153. 0,
  154. &systemtime,
  155. NULL,
  156. NULL,
  157. 0 );
  158. if (!RequiredTimeSize)
  159. {
  160. SetWindowText( hwnd, GetString( IDS_ERROR ) );
  161. return;
  162. }
  163. WCHAR *pszTimeBuffer = (WCHAR*)alloca( sizeof( WCHAR ) * ( RequiredTimeSize + 1 ) );
  164. int TimeSize =
  165. GetTimeFormatW(
  166. LOCALE_USER_DEFAULT,
  167. 0,
  168. &systemtime,
  169. NULL,
  170. pszTimeBuffer,
  171. RequiredTimeSize );
  172. if (!TimeSize)
  173. {
  174. SetWindowText( hwnd, GetString( IDS_ERROR ) );
  175. return;
  176. }
  177. // Add 2 for extra measure
  178. WCHAR *FullTime =
  179. (WCHAR*)alloca( sizeof( WCHAR ) *
  180. ( RequiredTimeSize + RequiredDateSize + 2 ) );
  181. wsprintf( FullTime, L"%s %s", pszDateBuffer, pszTimeBuffer );
  182. SetWindowText( hwnd, FullTime );
  183. }
  184. UINT64 CDownloadDlg::GetSystemTimeAsUINT64()
  185. {
  186. //
  187. // Returns the system time as an UINT instead of a FILETIME.
  188. //
  189. FILETIME filetime;
  190. GetSystemTimeAsFileTime( &filetime );
  191. ULARGE_INTEGER large;
  192. memcpy( &large, &filetime, sizeof(FILETIME) );
  193. return large.QuadPart;
  194. }
  195. void CDownloadDlg::SignalAlert(
  196. HWND hwndDlg,
  197. UINT Type
  198. )
  199. {
  200. //
  201. // Alert the user that an important event has occurred
  202. //
  203. FLASHWINFO FlashInfo;
  204. FlashInfo.cbSize = sizeof(FlashInfo);
  205. FlashInfo.hwnd = hwndDlg;
  206. FlashInfo.dwFlags = FLASHW_ALL | FLASHW_TIMERNOFG;
  207. FlashInfo.uCount = 0;
  208. FlashInfo.dwTimeout = 0;
  209. FlashWindowEx( &FlashInfo );
  210. MessageBeep( Type );
  211. }
  212. const WCHAR *
  213. CDownloadDlg::MapStateToString(
  214. BG_JOB_STATE state
  215. )
  216. {
  217. //
  218. // Maps a BITS job state to a human readable string
  219. //
  220. switch( state )
  221. {
  222. case BG_JOB_STATE_QUEUED:
  223. return GetString( IDS_QUEUED );
  224. case BG_JOB_STATE_CONNECTING:
  225. return GetString( IDS_CONNECTING );
  226. case BG_JOB_STATE_TRANSFERRING:
  227. return GetString( IDS_TRANSFERRING );
  228. case BG_JOB_STATE_SUSPENDED:
  229. return GetString( IDS_SUSPENDED );
  230. case BG_JOB_STATE_ERROR:
  231. return GetString( IDS_FATALERROR );
  232. case BG_JOB_STATE_TRANSIENT_ERROR:
  233. return GetString( IDS_TRANSIENTERROR );
  234. case BG_JOB_STATE_TRANSFERRED:
  235. return GetString( IDS_TRANSFERRED );
  236. case BG_JOB_STATE_ACKNOWLEDGED:
  237. return GetString( IDS_ACKNOWLEDGED );
  238. case BG_JOB_STATE_CANCELLED:
  239. return GetString( IDS_CANCELLED );
  240. default:
  241. // NOTE: Always provide a default case
  242. // since new states may be added in future versions.
  243. return GetString( IDS_UNKNOWN );
  244. }
  245. }
  246. UINT64
  247. CDownloadDlg::ScaleDownloadRate(
  248. double Rate, // rate in seconds
  249. const WCHAR **pFormat )
  250. {
  251. //
  252. // Scales a download rate and selects the correct
  253. // format to pass to wprintf for printing.
  254. //
  255. double RateBounds[] =
  256. {
  257. 1073741824.0, // Gigabyte
  258. 1048576.0, // Megabyte
  259. 1024.0, // Kilobyte
  260. 0 // Byte
  261. };
  262. UINT RateFormat[] =
  263. {
  264. IDS_GIGAFORMAT,
  265. IDS_MEGAFORMAT,
  266. IDS_KILOFORMAT,
  267. IDS_BYTEFORMAT
  268. };
  269. for( unsigned int c = 0;; c++ )
  270. {
  271. if ( Rate >= RateBounds[c] )
  272. {
  273. *pFormat = GetString( RateFormat[c] );
  274. double scale = (RateBounds[c] >= 1.0) ? RateBounds[c] : 1.0;
  275. return (UINT64)floor( ( Rate / scale ) + 0.5);
  276. }
  277. }
  278. }
  279. UINT64
  280. CDownloadDlg::ScaleDownloadEstimate(
  281. double Time, // time in seconds
  282. const WCHAR **pFormat )
  283. {
  284. //
  285. // Scales a download time estimate and selects the correct
  286. // format to pass to wprintf for printing.
  287. //
  288. double TimeBounds[] =
  289. {
  290. 60.0 * 60.0 * 24.0, // Days
  291. 60.0 * 60.0, // Hours
  292. 60.0, // Minutes
  293. 0.0 // Seconds
  294. };
  295. UINT TimeFormat[] =
  296. {
  297. IDS_DAYSFORMAT,
  298. IDS_HOURSFORMAT,
  299. IDS_MINUTESFORMAT,
  300. IDS_SECONDSFORMAT
  301. };
  302. for( unsigned int c = 0;; c++ )
  303. {
  304. if ( Time >= TimeBounds[c] )
  305. {
  306. *pFormat = GetString( TimeFormat[c] );
  307. double scale = (TimeBounds[c] >= 1.0) ? TimeBounds[c] : 1.0;
  308. return (UINT64)floor( ( Time / scale ) + 0.5);
  309. }
  310. }
  311. }
  312. // DemoHack
  313. void
  314. CDownloadDlg::UpdateDialog(
  315. HWND hwndDlg, LPWSTR wzErrorMsg)
  316. {
  317. SetWindowText( GetDlgItem( hwndDlg, IDC_ERRORMSG ), wzErrorMsg );
  318. ShowWindow( GetDlgItem( hwndDlg, IDC_ERRORMSG ), SW_SHOW );
  319. }
  320. HRESULT CDownloadDlg::UpdateProgress( HWND hwndDlg )
  321. {
  322. HRESULT hr = S_OK;
  323. MAKE_ERROR_MACROS_STATIC(hr);
  324. BG_JOB_PROGRESS progress;
  325. IBackgroundCopyError *pError = NULL;
  326. WCHAR szProgress[MAX_STRING];
  327. WCHAR szTitle[MAX_STRING];
  328. WPARAM newpos = 0;
  329. UINT64 ui64BytesTotal = _ui64BytesFromPrevJobs;
  330. UINT64 ui64BytesTransferred = _ui64BytesFromPrevJobs;
  331. double AvgRate = 0;
  332. static BG_JOB_STATE prevstate = BG_JOB_STATE_SUSPENDED;
  333. BG_JOB_STATE state;
  334. IF_FAILED_EXIT(_pJob->GetState( &state ));
  335. IF_FAILED_EXIT(_pJob->GetProgress( &progress ));
  336. // update the title, progress bar, and progress description
  337. if ( progress.BytesTotal != BG_SIZE_UNKNOWN )
  338. {
  339. ui64BytesTotal += progress.BytesTotal;
  340. ui64BytesTransferred += progress.BytesTransferred;
  341. }
  342. if ( ui64BytesTotal )
  343. {
  344. swprintf( szProgress, GetString( IDS_LONGPROGRESS ),ui64BytesTransferred,
  345. ui64BytesTotal );
  346. double Percent = (double)ui64BytesTransferred *100 /
  347. (double)ui64BytesTotal;
  348. swprintf( szTitle, L"%u%% of %s Downloaded", (unsigned int)Percent, (_sTitle._cc != 0) ? _sTitle._pwz : g_szDefaultTitle );
  349. newpos = (WPARAM)Percent;
  350. }
  351. else
  352. {
  353. swprintf( szProgress, GetString( IDS_SHORTPROGRESS ), ui64BytesTransferred );
  354. wcscpy( szTitle, (_sTitle.CharCount() > 1) ? _sTitle._pwz : g_szDefaultTitle );
  355. newpos = 0;
  356. }
  357. SendDlgItemMessage( hwndDlg, IDC_PROGRESSBAR, PBM_SETPOS, newpos, 0 );
  358. SetWindowText( GetDlgItem( hwndDlg, IDC_PROGRESSINFO ), szProgress );
  359. ShowWindow( GetDlgItem( hwndDlg, IDC_PROGRESSINFO ), SW_SHOW );
  360. EnableWindow( GetDlgItem( hwndDlg, IDC_PROGRESSINFOTXT ), TRUE );
  361. SetWindowText( hwndDlg, szTitle );
  362. // Only enable the finish button if the job is finished.
  363. // ADRIAANC EnableWindow( GetDlgItem( hwndDlg, IDC_FINISH ), ( state == BG_JOB_STATE_TRANSFERRED ) );
  364. EnableWindow( GetDlgItem( hwndDlg, IDC_FINISH ), ( state == BG_JOB_STATE_ACKNOWLEDGED ) );
  365. // felixybc BUGBUG: CANCEL is not allowed when the job is done
  366. // - should hold off ACK-ing that job until user clicks FINISH so that it can still be canceled at 100%?
  367. EnableWindow( GetDlgItem( hwndDlg, IDC_CANCEL ), ( state != BG_JOB_STATE_ACKNOWLEDGED && state != BG_JOB_STATE_CANCELLED ) );
  368. // Only enable the suspend button if the job is not finished or transferred
  369. BOOL EnableSuspend =
  370. ( state != BG_JOB_STATE_SUSPENDED ) && ( state != BG_JOB_STATE_TRANSFERRED ) && (state != BG_JOB_STATE_ACKNOWLEDGED);
  371. EnableWindow( GetDlgItem( hwndDlg, IDC_SUSPEND ), EnableSuspend );
  372. // Only enable the resume button if the job is suspended
  373. BOOL EnableResume = ( BG_JOB_STATE_SUSPENDED == state );
  374. EnableWindow( GetDlgItem( hwndDlg, IDC_RESUME ), EnableResume );
  375. // Alert the user when something important happens
  376. // such as the job completes or a unrecoverable error occurs
  377. if ( (BG_JOB_STATE_ERROR == state) && (BG_JOB_STATE_ERROR != prevstate) )
  378. SignalAlert( hwndDlg, MB_ICONEXCLAMATION );
  379. // update the error message
  380. if ( FAILED(_pJob->GetError( &pError )) )
  381. {
  382. ShowWindow( GetDlgItem( hwndDlg, IDC_ERRORMSG ), SW_HIDE );
  383. EnableWindow( GetDlgItem( hwndDlg, IDC_ERRORMSGTXT ), FALSE );
  384. }
  385. else
  386. {
  387. CString sErrMsg;
  388. IF_FAILED_EXIT(CAssemblyDownload::GetBITSErrorMsg(pError, sErrMsg));
  389. HWND hwndErrorText = GetDlgItem( hwndDlg, IDC_ERRORMSG );
  390. SetWindowText( hwndErrorText, sErrMsg._pwz );
  391. ShowWindow( hwndErrorText, SW_SHOW );
  392. EnableWindow( GetDlgItem( hwndDlg, IDC_ERRORMSGTXT ), TRUE );
  393. }
  394. //
  395. // This large block of text computes the average transfer rate
  396. // and estimated completion time. This code has much
  397. // room for improvement.
  398. //
  399. BOOL HasRates = TRUE;
  400. BOOL EnableRate = FALSE;
  401. WCHAR szRateText[MAX_STRING];
  402. if ( !( BG_JOB_STATE_QUEUED == state ) &&
  403. !( BG_JOB_STATE_CONNECTING == state ) &&
  404. !( BG_JOB_STATE_TRANSFERRING == state ) )
  405. {
  406. // If the job isn't running, then rate values won't
  407. // make any sense. Don't display them.
  408. HasRates = FALSE;
  409. }
  410. if ( HasRates )
  411. {
  412. UINT64 ui64CurrentTime = GetSystemTimeAsUINT64();
  413. UINT64 ui64TimeDiff = ui64CurrentTime - _ui64StartTime;
  414. AvgRate = (double)(__int64)ui64BytesTransferred /
  415. (double)(__int64) ui64TimeDiff;
  416. // convert from FILETIME units to seconds
  417. double NewDisplayRate = AvgRate * 10000000;
  418. const WCHAR *pRateFormat = NULL;
  419. UINT64 Rate = ScaleDownloadRate( NewDisplayRate, &pRateFormat );
  420. wsprintf( szRateText, pRateFormat, Rate );
  421. EnableRate = TRUE;
  422. }
  423. if (!EnableRate)
  424. {
  425. ShowWindow( GetDlgItem( hwndDlg, IDC_TRANSFERRATE ), SW_HIDE );
  426. EnableWindow( GetDlgItem( hwndDlg, IDC_TRANSFERRATETXT ), FALSE );
  427. }
  428. else
  429. {
  430. SetWindowText( GetDlgItem( hwndDlg, IDC_TRANSFERRATE ), szRateText );
  431. ShowWindow( GetDlgItem( hwndDlg, IDC_TRANSFERRATE ), SW_SHOW );
  432. EnableWindow( GetDlgItem( hwndDlg, IDC_TRANSFERRATETXT ), TRUE );
  433. }
  434. BOOL EnableEstimate = FALSE;
  435. WCHAR szEstimateText[MAX_STRING];
  436. if ( EnableRate && ui64BytesTotal && AvgRate)
  437. {
  438. double TimeRemaining = ( ui64BytesTotal - ui64BytesTransferred ) / AvgRate;
  439. // convert from FILETIME units to seconds
  440. TimeRemaining = TimeRemaining / 10000000.0;
  441. static const double SecsPer30Days = 60.0 * 60.0 * 24.0 * 30.0;
  442. // Don't estimate if estimate is larger then 30 days.
  443. if ( TimeRemaining < SecsPer30Days )
  444. {
  445. const WCHAR *pFormat = NULL;
  446. UINT64 Time = ScaleDownloadEstimate( TimeRemaining, &pFormat );
  447. wsprintf( szEstimateText, pFormat, Time );
  448. EnableEstimate = TRUE;
  449. }
  450. }
  451. if (!EnableEstimate)
  452. {
  453. ShowWindow( GetDlgItem( hwndDlg, IDC_ESTIMATEDTIME ), SW_HIDE );
  454. EnableWindow( GetDlgItem( hwndDlg, IDC_ESTIMATEDTIMETXT ), FALSE );
  455. }
  456. else
  457. {
  458. SetWindowText( GetDlgItem( hwndDlg, IDC_ESTIMATEDTIME ), szEstimateText );
  459. ShowWindow( GetDlgItem( hwndDlg, IDC_ESTIMATEDTIME ), SW_SHOW );
  460. EnableWindow( GetDlgItem( hwndDlg, IDC_ESTIMATEDTIMETXT ), TRUE );
  461. }
  462. prevstate = state;
  463. exit :
  464. SAFERELEASE(pError);
  465. return hr;
  466. }
  467. void
  468. CDownloadDlg::UpdateDialog(
  469. HWND hwndDlg
  470. )
  471. {
  472. UpdateProgress(hwndDlg);
  473. return;
  474. //
  475. // Main update routine for the dialog box.
  476. // Retries the job state/properties from
  477. // BITS and updates the dialog box.
  478. //
  479. // update the display name
  480. static BG_JOB_STATE prevstate = BG_JOB_STATE_SUSPENDED;
  481. BG_JOB_STATE state;
  482. if (FAILED(_pJob->GetState( &state )))
  483. return; // stop updating on an error
  484. if ( BG_JOB_STATE_ACKNOWLEDGED == state ||
  485. BG_JOB_STATE_CANCELLED == state )
  486. {
  487. // someone else cancelled or completed the job on us,
  488. // just exist the exit.
  489. // May happen if job is canceled with bitsadmin
  490. // DeleteStartupLink( g_JobId );
  491. // ExitProcess( 0 );
  492. // BUGBUG: Should post a CANCEL message to assemblydownload
  493. }
  494. BG_JOB_PROGRESS progress;
  495. if (FAILED(_pJob->GetProgress( &progress )))
  496. return; // stop updating on an error
  497. {
  498. // update the title, progress bar, and progress description
  499. WCHAR szProgress[MAX_STRING];
  500. WCHAR szTitle[MAX_STRING];
  501. WPARAM newpos = 0;
  502. if ( progress.BytesTotal &&
  503. ( progress.BytesTotal != BG_SIZE_UNKNOWN ) )
  504. {
  505. swprintf( szProgress, GetString( IDS_LONGPROGRESS ), progress.BytesTransferred,
  506. progress.BytesTotal );
  507. double Percent = (double)(__int64)progress.BytesTransferred /
  508. (double)(__int64)progress.BytesTotal;
  509. Percent *= 100.0;
  510. swprintf( szTitle, L"%u%% of %s Downloaded", (unsigned int)Percent, (_sTitle._cc != 0) ? _sTitle._pwz : g_szDefaultTitle );
  511. newpos = (WPARAM)Percent;
  512. }
  513. else
  514. {
  515. swprintf( szProgress, GetString( IDS_SHORTPROGRESS ), progress.BytesTransferred );
  516. wcscpy( szTitle, (_sTitle._cc != 0) ? _sTitle._pwz : g_szDefaultTitle );
  517. newpos = 0;
  518. }
  519. SendDlgItemMessage( hwndDlg, IDC_PROGRESSBAR, PBM_SETPOS, newpos, 0 );
  520. SetWindowText( GetDlgItem( hwndDlg, IDC_PROGRESSINFO ), szProgress );
  521. ShowWindow( GetDlgItem( hwndDlg, IDC_PROGRESSINFO ), SW_SHOW );
  522. EnableWindow( GetDlgItem( hwndDlg, IDC_PROGRESSINFOTXT ), TRUE );
  523. SetWindowText( hwndDlg, szTitle );
  524. }
  525. {
  526. // Only enable the finish button if the job is finished.
  527. // ADRIAANC EnableWindow( GetDlgItem( hwndDlg, IDC_FINISH ), ( state == BG_JOB_STATE_TRANSFERRED ) );
  528. EnableWindow( GetDlgItem( hwndDlg, IDC_FINISH ), ( state == BG_JOB_STATE_ACKNOWLEDGED ) );
  529. // felixybc BUGBUG: CANCEL is not allowed when the job is done
  530. // - should hold off ACK-ing that job until user clicks FINISH so that it can still be canceled at 100%?
  531. EnableWindow( GetDlgItem( hwndDlg, IDC_CANCEL ), ( state != BG_JOB_STATE_ACKNOWLEDGED && state != BG_JOB_STATE_CANCELLED ) );
  532. // Only enable the suspend button if the job is not finished or transferred
  533. BOOL EnableSuspend =
  534. ( state != BG_JOB_STATE_SUSPENDED ) && ( state != BG_JOB_STATE_TRANSFERRED ) && (state != BG_JOB_STATE_ACKNOWLEDGED);
  535. EnableWindow( GetDlgItem( hwndDlg, IDC_SUSPEND ), EnableSuspend );
  536. // Only enable the resume button if the job is suspended
  537. BOOL EnableResume = ( BG_JOB_STATE_SUSPENDED == state );
  538. EnableWindow( GetDlgItem( hwndDlg, IDC_RESUME ), EnableResume );
  539. // Alert the user when something important happens
  540. // such as the job completes or a unrecoverable error occurs
  541. if ( BG_JOB_STATE_ERROR == state &&
  542. BG_JOB_STATE_ERROR != prevstate )
  543. SignalAlert( hwndDlg, MB_ICONEXCLAMATION );
  544. }
  545. {
  546. // update the error message
  547. // BUGBUG - release the error interface.
  548. IBackgroundCopyError *pError;
  549. HRESULT Hr = _pJob->GetError( &pError );
  550. if ( FAILED(Hr) )
  551. {
  552. ShowWindow( GetDlgItem( hwndDlg, IDC_ERRORMSG ), SW_HIDE );
  553. EnableWindow( GetDlgItem( hwndDlg, IDC_ERRORMSGTXT ), FALSE );
  554. }
  555. else
  556. {
  557. WCHAR* pszDescription = NULL;
  558. WCHAR* pszContext = NULL;
  559. SIZE_T SizeRequired = 0;
  560. // If these APIs fail, we should get back
  561. // a NULL string. So everything should be harmless.
  562. pError->GetErrorDescription(
  563. LANGIDFROMLCID( GetThreadLocale() ),
  564. &pszDescription );
  565. pError->GetErrorContextDescription(
  566. LANGIDFROMLCID( GetThreadLocale() ),
  567. &pszContext );
  568. SAFERELEASE(pError);
  569. if ( pszDescription )
  570. SizeRequired += wcslen( pszDescription );
  571. if ( pszContext )
  572. SizeRequired += wcslen( pszContext );
  573. WCHAR* pszFullText = (WCHAR*)_alloca((SizeRequired + 1) * sizeof(WCHAR));
  574. *pszFullText = L'\0';
  575. if ( pszDescription )
  576. wcscpy( pszFullText, pszDescription );
  577. if ( pszContext )
  578. wcscat( pszFullText, pszContext );
  579. CoTaskMemFree( pszDescription );
  580. CoTaskMemFree( pszContext );
  581. HWND hwndErrorText = GetDlgItem( hwndDlg, IDC_ERRORMSG );
  582. SetWindowText( hwndErrorText, pszFullText );
  583. ShowWindow( hwndErrorText, SW_SHOW );
  584. EnableWindow( GetDlgItem( hwndDlg, IDC_ERRORMSGTXT ), TRUE );
  585. }
  586. }
  587. {
  588. //
  589. // This large block of text computes the average transfer rate
  590. // and estimated completion time. This code has much
  591. // room for improvement.
  592. //
  593. static BOOL HasRates = FALSE;
  594. static UINT64 LastMeasurementTime;
  595. static UINT64 LastMeasurementBytes;
  596. static double LastMeasurementRate;
  597. WCHAR szRateText[MAX_STRING];
  598. BOOL EnableRate = FALSE;
  599. if ( !( BG_JOB_STATE_QUEUED == state ) &&
  600. !( BG_JOB_STATE_CONNECTING == state ) &&
  601. !( BG_JOB_STATE_TRANSFERRING == state ) )
  602. {
  603. // If the job isn't running, then rate values won't
  604. // make any sense. Don't display them.
  605. HasRates = FALSE;
  606. }
  607. else
  608. {
  609. if ( !HasRates )
  610. {
  611. LastMeasurementTime = GetSystemTimeAsUINT64();
  612. LastMeasurementBytes = progress.BytesTransferred;
  613. LastMeasurementRate = 0;
  614. HasRates = TRUE;
  615. }
  616. else
  617. {
  618. UINT64 CurrentTime = GetSystemTimeAsUINT64();
  619. UINT64 NewTotalBytes = progress.BytesTransferred;
  620. UINT64 NewTimeDiff = CurrentTime - LastMeasurementTime;
  621. UINT64 NewBytesDiff = NewTotalBytes - LastMeasurementBytes;
  622. double NewInstantRate = (double)(__int64)NewBytesDiff /
  623. (double)(__int64)NewTimeDiff;
  624. double NewAvgRate = (0.3 * LastMeasurementRate) +
  625. (0.7 * NewInstantRate );
  626. if ( !_finite(NewInstantRate) || !_finite(NewAvgRate) )
  627. {
  628. NewInstantRate = 0;
  629. NewAvgRate = LastMeasurementRate;
  630. }
  631. LastMeasurementTime = CurrentTime;
  632. LastMeasurementBytes = NewTotalBytes;
  633. LastMeasurementRate = NewAvgRate;
  634. // convert from FILETIME units to seconds
  635. double NewDisplayRate = NewAvgRate * 10000000;
  636. const WCHAR *pRateFormat = NULL;
  637. UINT64 Rate = ScaleDownloadRate( NewDisplayRate, &pRateFormat );
  638. wsprintf( szRateText, pRateFormat, Rate );
  639. EnableRate = TRUE;
  640. }
  641. }
  642. if (!EnableRate)
  643. {
  644. ShowWindow( GetDlgItem( hwndDlg, IDC_TRANSFERRATE ), SW_HIDE );
  645. EnableWindow( GetDlgItem( hwndDlg, IDC_TRANSFERRATETXT ), FALSE );
  646. }
  647. else
  648. {
  649. SetWindowText( GetDlgItem( hwndDlg, IDC_TRANSFERRATE ), szRateText );
  650. ShowWindow( GetDlgItem( hwndDlg, IDC_TRANSFERRATE ), SW_SHOW );
  651. EnableWindow( GetDlgItem( hwndDlg, IDC_TRANSFERRATETXT ), TRUE );
  652. }
  653. BOOL EnableEstimate = FALSE;
  654. WCHAR szEstimateText[MAX_STRING];
  655. if ( EnableRate )
  656. {
  657. if ( progress.BytesTotal != 0 &&
  658. progress.BytesTotal != BG_SIZE_UNKNOWN )
  659. {
  660. double TimeRemaining =
  661. ( (__int64)progress.BytesTotal - (__int64)LastMeasurementBytes ) / LastMeasurementRate;
  662. // convert from FILETIME units to seconds
  663. TimeRemaining = TimeRemaining / 10000000.0;
  664. static const double SecsPer30Days = 60.0 * 60.0 * 24.0 * 30.0;
  665. // Don't estimate if estimate is larger then 30 days.
  666. if ( TimeRemaining < SecsPer30Days )
  667. {
  668. const WCHAR *pFormat = NULL;
  669. UINT64 Time = ScaleDownloadEstimate( TimeRemaining, &pFormat );
  670. wsprintf( szEstimateText, pFormat, Time );
  671. EnableEstimate = TRUE;
  672. }
  673. }
  674. }
  675. if (!EnableEstimate)
  676. {
  677. ShowWindow( GetDlgItem( hwndDlg, IDC_ESTIMATEDTIME ), SW_HIDE );
  678. EnableWindow( GetDlgItem( hwndDlg, IDC_ESTIMATEDTIMETXT ), FALSE );
  679. }
  680. else
  681. {
  682. SetWindowText( GetDlgItem( hwndDlg, IDC_ESTIMATEDTIME ), szEstimateText );
  683. ShowWindow( GetDlgItem( hwndDlg, IDC_ESTIMATEDTIME ), SW_SHOW );
  684. EnableWindow( GetDlgItem( hwndDlg, IDC_ESTIMATEDTIMETXT ), TRUE );
  685. }
  686. }
  687. prevstate = state;
  688. }
  689. void
  690. CDownloadDlg::InitDialog(
  691. HWND hwndDlg
  692. )
  693. {
  694. //
  695. // Populate the priority list with priority descriptions
  696. //
  697. _hwndDlg = hwndDlg;
  698. SendDlgItemMessage( hwndDlg, IDC_PROGRESSBAR, PBM_SETRANGE, 0, MAKELPARAM(0, 100) );
  699. }
  700. void CDownloadDlg::CheckHR( HWND hwnd, HRESULT Hr, bool bThrow )
  701. {
  702. //
  703. // Provides automatic error code checking and dialog
  704. // for generic system errors
  705. //
  706. if (SUCCEEDED(Hr))
  707. return;
  708. WCHAR * pszError = NULL;
  709. if(FormatMessage(
  710. FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
  711. NULL,
  712. (DWORD)Hr,
  713. LANGIDFROMLCID( GetThreadLocale() ),
  714. (WCHAR*)&pszError,
  715. 0,
  716. NULL ))
  717. {
  718. MessageBox( hwnd, pszError, GetString( IDS_ERRORBOXTITLE ),
  719. MB_OK | MB_ICONSTOP | MB_APPLMODAL );
  720. LocalFree( pszError );
  721. }
  722. if ( bThrow )
  723. throw _com_error( Hr );
  724. }
  725. void CDownloadDlg::BITSCheckHR( HWND hwnd, HRESULT Hr, bool bThrow )
  726. {
  727. //
  728. // Provides automatic error code checking and dialog
  729. // for BITS specific errors
  730. //
  731. if (SUCCEEDED(Hr))
  732. return;
  733. WCHAR * pszError = NULL;
  734. g_pBITSManager->GetErrorDescription(
  735. Hr,
  736. LANGIDFROMLCID( GetThreadLocale() ),
  737. &pszError );
  738. MessageBox( hwnd, pszError, GetString( IDS_ERRORBOXTITLE ),
  739. MB_OK | MB_ICONSTOP | MB_APPLMODAL );
  740. CoTaskMemFree( pszError );
  741. if ( bThrow )
  742. throw _com_error( Hr );
  743. }
  744. void
  745. CDownloadDlg::DoCancel(
  746. HWND hwndDlg,
  747. bool PromptUser
  748. )
  749. {
  750. //
  751. // Handle all the operations required to cancel the job.
  752. // This includes asking the user for confirmation.
  753. //
  754. /*
  755. if ( PromptUser )
  756. {
  757. int Result =
  758. MessageBox(
  759. hwndDlg,
  760. GetString( IDS_CANCELTEXT ),
  761. GetString( IDS_CANCELCAPTION ),
  762. MB_YESNO | MB_ICONWARNING | MB_DEFBUTTON2 | MB_APPLMODAL |
  763. MB_SETFOREGROUND | MB_TOPMOST );
  764. if ( IDYES != Result )
  765. return;
  766. }
  767. */
  768. // try
  769. {
  770. // BITSCheckHR( hwndDlg, _pJob->Cancel(), false );//felixybc true );
  771. }
  772. // catch( _com_error Error )
  773. {
  774. // If we can't cancel for some unknown reason,
  775. // don't exit
  776. // return;
  777. }
  778. // DeleteStartupLink( g_JobId );
  779. //felixybc ExitProcess( 0 );
  780. //KillTimer(hwndDlg, 0);
  781. PostMessage(hwndDlg, WM_CANCEL_DOWNLOAD, 0, 0);
  782. }
  783. void
  784. CDownloadDlg::DoFinish(
  785. HWND hwndDlg
  786. )
  787. {
  788. //
  789. // Handles all the necessary work to complete
  790. // the download.
  791. //
  792. // try
  793. {
  794. // ADRIAANC
  795. // BITSCheckHR( hwndDlg, _pJob->Complete(), true );
  796. }
  797. // catch( _com_error Error )
  798. {
  799. // If we can't finish/complete for some unknown reason,
  800. // don't exit
  801. // return;
  802. }
  803. // DeleteStartupLink( g_JobId );
  804. // ExitProcess( 0 );
  805. // Commit the bits and notify done.
  806. //_pDownload->_pRootEmit->Commit(0);
  807. //KillTimer(hwndDlg, 0);
  808. PostMessage(hwndDlg, WM_FINISH_DOWNLOAD, 0, 0);
  809. return;
  810. }
  811. void
  812. CDownloadDlg::DoClose(
  813. HWND hwndDlg
  814. )
  815. {
  816. //
  817. // Handles an attempt by the user to close the sample.
  818. //
  819. // Check to see if the download has finished,
  820. // if so don't let the user exit.
  821. BG_JOB_STATE state;
  822. HRESULT hResult = _pJob->GetState( &state );
  823. if (FAILED( hResult ))
  824. {
  825. BITSCheckHR( hwndDlg, hResult, false );
  826. return;
  827. }
  828. // BUGBUG: should also check for BG_JOB_STATE_ACKNOWLEDGED and don't call DoCancel then
  829. // _pJob->Cancel();
  830. DoCancel( hwndDlg, false );
  831. return;
  832. /*
  833. if ( BG_JOB_STATE_ERROR == state ||
  834. BG_JOB_STATE_TRANSFERRED == state )
  835. {
  836. MessageBox(
  837. hwndDlg,
  838. GetString( IDS_ALREADYFINISHED ),
  839. GetString( IDS_ALREADYFINISHEDCAPTION ),
  840. MB_OK | MB_ICONERROR | MB_DEFBUTTON1 | MB_APPLMODAL |
  841. MB_SETFOREGROUND | MB_TOPMOST );
  842. return;
  843. }
  844. //
  845. // Inform the user that he selected close and ask
  846. // confirm the intention to exit. Explain that the job
  847. // will be canceled.
  848. int Result =
  849. MessageBox(
  850. hwndDlg,
  851. GetString( IDS_CLOSETEXT ),
  852. GetString( IDS_CLOSECAPTION ),
  853. MB_OKCANCEL | MB_ICONWARNING | MB_DEFBUTTON2 | MB_APPLMODAL |
  854. MB_SETFOREGROUND | MB_TOPMOST );
  855. if ( IDOK == Result )
  856. {
  857. // User confirmed the cancel, just do it.
  858. DoCancel( hwndDlg, false );
  859. return;
  860. }
  861. // The user didn't really want to exit, so ignore him
  862. else
  863. return;
  864. */
  865. }
  866. void
  867. CDownloadDlg::HandleTimerTick( HWND hwndDlg )
  868. {
  869. // The timer fired. Update dialog.
  870. UpdateDialog( hwndDlg );
  871. if (_eState == DOWNLOADDLG_STATE_ALL_DONE)
  872. {
  873. static bool bHasTip = FALSE;
  874. if (!g_IsMinimized)
  875. {
  876. // not minimized, continue to run app
  877. DoFinish(hwndDlg);
  878. }
  879. else
  880. {
  881. if (!bHasTip)
  882. {
  883. // minimized, pop up buttom tip
  884. NOTIFYICONDATA tnid = {0};
  885. // ignore all error
  886. tnid.cbSize = sizeof(NOTIFYICONDATA);
  887. tnid.hWnd = hwndDlg;
  888. tnid.uID = TRAY_UID;
  889. tnid.uFlags = NIF_INFO;
  890. tnid.uTimeout = 20000; // in milliseconds
  891. tnid.dwInfoFlags = NIIF_INFO;
  892. lstrcpyn(tnid.szInfoTitle, L"ClickOnce application ready!", (sizeof(tnid.szInfoTitle)/sizeof(tnid.szInfoTitle[0])));
  893. 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])));
  894. Shell_NotifyIcon(NIM_MODIFY, &tnid);
  895. bHasTip = TRUE;
  896. }
  897. }
  898. }
  899. }
  900. HRESULT
  901. CDownloadDlg::HandleUpdate()
  902. {
  903. // Handle a update request, batching the update if needed
  904. DWORD dwRefresh = 0;
  905. dwRefresh = InterlockedIncrement(&g_RefreshOnTimer);
  906. if (dwRefresh == 1)
  907. {
  908. // First time in; fire off timer and update the dialog.
  909. UpdateDialog(_hwndDlg);
  910. SendMessage(_hwndDlg, WM_SETCALLBACKTIMER, 0, 0);
  911. }
  912. else
  913. {
  914. // We've already received the first callback.
  915. // Let the timer do any further work.
  916. InterlockedDecrement(&g_RefreshOnTimer);
  917. }
  918. return S_OK;
  919. }
  920. INT_PTR CALLBACK DialogProc(
  921. HWND hwndDlg, // handle to dialog box
  922. UINT uMsg, // message
  923. WPARAM wParam, // first message parameter
  924. LPARAM lParam // second message parameter
  925. )
  926. {
  927. //
  928. // Dialog proc for main dialog window
  929. //
  930. static CDownloadDlg *pDlg = NULL;
  931. switch( uMsg )
  932. {
  933. case WM_DESTROY:
  934. {
  935. Animate_Stop(GetDlgItem(hwndDlg, IDC_ANIMATE_DOWNLOAD));
  936. Animate_Close(GetDlgItem(hwndDlg, IDC_ANIMATE_DOWNLOAD));
  937. return FALSE;
  938. }
  939. case WM_INITDIALOG:
  940. pDlg = (CDownloadDlg*) lParam;
  941. pDlg->InitDialog(hwndDlg);
  942. ShowWindow(GetDlgItem(hwndDlg, IDC_ANIMATE_DOWNLOAD), SW_SHOW);
  943. Animate_Open(GetDlgItem(hwndDlg, IDC_ANIMATE_DOWNLOAD), MAKEINTRESOURCE(IDA_DOWNLOADING));
  944. Animate_Play(GetDlgItem(hwndDlg, IDC_ANIMATE_DOWNLOAD), 0, -1, -1);
  945. return TRUE;
  946. case WM_SETCALLBACKTIMER:
  947. SetTimer(hwndDlg, 1, 500, NULL );
  948. return TRUE;
  949. case WM_TIMER:
  950. pDlg->HandleTimerTick( hwndDlg );
  951. return TRUE;
  952. case WM_CLOSE:
  953. pDlg->DoClose( hwndDlg );
  954. return TRUE;
  955. case WM_COMMAND:
  956. switch( LOWORD( wParam ) )
  957. {
  958. case IDC_RESUME:
  959. pDlg->BITSCheckHR( hwndDlg, pDlg->_pJob->Resume(), false );
  960. return TRUE;
  961. case IDC_SUSPEND:
  962. pDlg->BITSCheckHR( hwndDlg, pDlg->_pJob->Suspend(), false );
  963. return TRUE;
  964. case IDC_CANCEL:
  965. pDlg->DoCancel( hwndDlg, true );
  966. return TRUE;
  967. case IDC_FINISH:
  968. pDlg->DoFinish( hwndDlg );
  969. return TRUE;
  970. default:
  971. return FALSE;
  972. }
  973. case WM_SIZE:
  974. if (wParam == SIZE_MINIMIZED)
  975. {
  976. HICON hIcon = LoadIcon(g_hInst, MAKEINTRESOURCE(IDI_ICON));
  977. if (hIcon != NULL)
  978. {
  979. NOTIFYICONDATA tnid = {0};
  980. // ignore all error (user will not be able to restore dialog in some cases)
  981. tnid.cbSize = sizeof(NOTIFYICONDATA);
  982. tnid.hWnd = hwndDlg;
  983. tnid.uID = TRAY_UID;
  984. tnid.uFlags = NIF_MESSAGE | NIF_ICON | NIF_TIP;
  985. tnid.uCallbackMessage = MYWM_NOTIFYICON;
  986. tnid.hIcon = hIcon;
  987. lstrcpyn(tnid.szTip, L"Downloading ClickOnce application.", (sizeof(tnid.szTip)/sizeof(tnid.szTip[0]))); // set tip to file name
  988. Shell_NotifyIcon(NIM_ADD, &tnid);
  989. DestroyIcon(hIcon);
  990. // set shell32 v5 behavior
  991. tnid.uVersion = NOTIFYICON_VERSION;
  992. tnid.uFlags = 0;
  993. Shell_NotifyIcon(NIM_SETVERSION, &tnid);
  994. // hide window
  995. ShowWindow( hwndDlg, SW_HIDE );
  996. g_IsMinimized = TRUE;
  997. return TRUE;
  998. }
  999. // else error loading icon - ignore
  1000. }
  1001. return FALSE;
  1002. case MYWM_NOTIFYICON:
  1003. if (g_IsMinimized && (lParam == WM_CONTEXTMENU || lParam == NIN_KEYSELECT || lParam == NIN_SELECT ))
  1004. {
  1005. // if the notification icon is clicked on
  1006. NOTIFYICONDATA tnid = {0};
  1007. // show window
  1008. ShowWindow( hwndDlg, SW_RESTORE );
  1009. g_IsMinimized = FALSE;
  1010. // remove icon from tray
  1011. tnid.cbSize = sizeof(NOTIFYICONDATA);
  1012. tnid.hWnd = hwndDlg;
  1013. tnid.uID = TRAY_UID;
  1014. tnid.uFlags = 0;
  1015. Shell_NotifyIcon(NIM_DELETE, &tnid);
  1016. return TRUE;
  1017. }
  1018. return FALSE;
  1019. default:
  1020. return FALSE;
  1021. }
  1022. }
  1023. HRESULT
  1024. CDownloadDlg::CreateUI( int nShowCmd )
  1025. {
  1026. DWORD dwError = 0;
  1027. //
  1028. // Creates the dialog box for the sample.
  1029. //
  1030. InitCommonControls();
  1031. _hwndDlg =
  1032. CreateDialogParam(
  1033. g_hInst,
  1034. MAKEINTRESOURCE(IDD_DIALOG),
  1035. NULL,
  1036. DialogProc,
  1037. (LPARAM) (this));
  1038. if (!_hwndDlg)
  1039. {
  1040. dwError = GetLastError();
  1041. return HRESULT_FROM_WIN32(dwError);
  1042. }
  1043. ShowWindow(_hwndDlg, nShowCmd);
  1044. return S_OK;
  1045. }
  1046. void CDownloadDlg::ResumeJob(
  1047. WCHAR* szJobGUID,
  1048. WCHAR* szJobFileName
  1049. )
  1050. {
  1051. //
  1052. // Resume the display on an existing job
  1053. //
  1054. // try
  1055. {
  1056. CheckHR( NULL, IIDFromString( szJobGUID, &g_JobId ), true );
  1057. CheckHR( NULL,
  1058. CoCreateInstance( CLSID_BackgroundCopyManager,
  1059. NULL,
  1060. CLSCTX_LOCAL_SERVER,
  1061. IID_IBackgroundCopyManager,
  1062. (void**)&g_pBITSManager ), true );
  1063. BITSCheckHR( NULL, g_pBITSManager->GetJob( g_JobId, &_pJob ), true );
  1064. // BUGBUG - bits dialog class doesn't know about callbacks - adriaanc
  1065. // BITSCheckHR( NULL,
  1066. // _pJob->SetNotifyInterface( (IBackgroundCopyCallback*)&g_Callback ),
  1067. // true );
  1068. BITSCheckHR( NULL, _pJob->SetNotifyFlags( BG_NOTIFY_JOB_MODIFICATION ), true );
  1069. ShowWindow(_hwndDlg, SW_MINIMIZE );
  1070. HandleUpdate();
  1071. }
  1072. /*
  1073. catch(_com_error error )
  1074. {
  1075. ExitProcess( error.Error() );
  1076. }
  1077. */
  1078. }
  1079. void CDownloadDlg::SetJob(IBackgroundCopyJob *pJob)
  1080. {
  1081. SAFERELEASE(_pJob);
  1082. _pJob = pJob;
  1083. _pJob->AddRef();
  1084. }
  1085. void CDownloadDlg::SetDlgState(DOWNLOADDLG_STATE eState)
  1086. {
  1087. _eState = eState;
  1088. }
  1089. HRESULT CDownloadDlg::SetDlgTitle(LPCWSTR pwzTitle)
  1090. {
  1091. return _sTitle.Assign((LPWSTR)pwzTitle);
  1092. }