Source code of Windows XP (NT5)
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.

1337 lines
34 KiB

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