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.

1651 lines
41 KiB

  1. /************************************************************************
  2. Copyright (c) Microsoft Corporation
  3. Module Name :
  4. bits_ie.cpp
  5. Abstract :
  6. Sample background downloader which uses BITS.
  7. Revision History :
  8. Notes:
  9. This program is a very simple background downloader which demonstrates
  10. the use of BITS. The program hooks into the IE context menu, to
  11. allow the user to schedule the download instead of using the default
  12. IE downloader.
  13. Concepts Covered:
  14. 1. Basic connection with manager and job submission.
  15. 2. Example presentation to user of job state.
  16. 3. Job control such as suspend/resume/cancel/complete.
  17. 4. Interface based callbacks for updating progress/state.
  18. 5. How to get the text message for a BITS error code.
  19. ***********************************************************************/
  20. #ifndef UNICODE
  21. #define UNICODE
  22. #endif
  23. #ifndef _UNICODE
  24. #define _UNICODE
  25. #endif
  26. #pragma warning( disable : 4786 )
  27. #include <windows.h>
  28. #include <stdio.h>
  29. #include <stdlib.h>
  30. #include <malloc.h>
  31. #include <math.h>
  32. #include <float.h>
  33. #include <commctrl.h>
  34. #include <commdlg.h>
  35. #include <shellapi.h>
  36. #include <shlwapi.h>
  37. #include <wininet.h>
  38. #include <shlobj.h>
  39. #include "resource.h"
  40. #include <bits.h>
  41. #include <comdef.h>
  42. // Maxstring size, bump up on problems
  43. #define MAX_STRING 0x800 // 2K
  44. GUID g_JobId;
  45. WCHAR g_szFileName[MAX_PATH];
  46. HWND g_hwndDlg = NULL;
  47. // These two global variables throttle updates.
  48. // The algorithm is to set a timer on the first update request,
  49. // and delay additional updates until after the timer expires.
  50. // The timer is set
  51. bool g_UpdateTimerSet = FALSE;
  52. // Received on update request while timer is active
  53. bool g_RefreshOnTimer = FALSE;
  54. IBackgroundCopyJob *g_pJob = NULL;
  55. IBackgroundCopyManager *g_pManager = NULL;
  56. HRESULT HandleUpdate( );
  57. const WCHAR * GetString( UINT id )
  58. {
  59. //
  60. // Retrieves the localized string for the resource id
  61. // caching the string when loaded.
  62. static const WCHAR* pStringArray[ IDS_MAX ];
  63. static WCHAR TempStringBuffer[ MAX_STRING ];
  64. const WCHAR * & pStringPointer = pStringArray[ id - 1 ];
  65. // Cache resource strings
  66. if ( pStringPointer )
  67. return pStringPointer;
  68. // Load string from resource
  69. int CharsLoaded =
  70. LoadStringW(
  71. (HINSTANCE)GetModuleHandle(NULL),
  72. id,
  73. TempStringBuffer,
  74. MAX_STRING );
  75. if ( !CharsLoaded )
  76. return L"";
  77. WCHAR *pNewString = new WCHAR[ CharsLoaded + 1];
  78. if ( !pNewString )
  79. return L"";
  80. wcscpy( pNewString, TempStringBuffer );
  81. return ( pStringPointer = pNewString );
  82. }
  83. void
  84. DeleteStartupLink(
  85. GUID JobID
  86. )
  87. {
  88. //
  89. // Delete the link in the Startup folder for the job
  90. //
  91. WCHAR szLinkFileName[MAX_PATH] = {0};
  92. WCHAR szGUIDString[MAX_PATH] = {0};
  93. BOOL bResult =
  94. SHGetSpecialFolderPath(
  95. NULL,
  96. szLinkFileName,
  97. CSIDL_STARTUP,
  98. FALSE );
  99. if ( !bResult )
  100. return;
  101. wcscat( szLinkFileName, L"\\" );
  102. wcscat( szLinkFileName, GetString( IDS_STARTUPLINK ) );
  103. wcscat( szLinkFileName, L" " );
  104. StringFromGUID2( JobID, szGUIDString, MAX_PATH );
  105. wcscat( szLinkFileName, szGUIDString );
  106. wcscat( szLinkFileName, L".lnk" );
  107. DeleteFile( szLinkFileName );
  108. }
  109. HRESULT
  110. CreateStartupLink(
  111. GUID JobID,
  112. WCHAR *pszFileName
  113. )
  114. {
  115. //
  116. // Create a link in the Startup folder for this job.
  117. //
  118. IShellLink* pShellLink = NULL;
  119. IPersistFile* pPersistFile = NULL;
  120. HRESULT hResult;
  121. LONG lCreateResult, lSetValueResult;
  122. WCHAR szLinkFileName[MAX_PATH] = {0};
  123. BOOL bResult =
  124. SHGetSpecialFolderPath(
  125. NULL,
  126. szLinkFileName,
  127. CSIDL_STARTUP,
  128. FALSE );
  129. if ( !bResult )
  130. return E_FAIL;
  131. WCHAR szLinkDescription[MAX_PATH] = {0};
  132. wcscpy( szLinkDescription, GetString( IDS_STARTUPLINK ) );
  133. wcscat( szLinkDescription, L" " );
  134. WCHAR szGUIDString[MAX_PATH] = {0};
  135. StringFromGUID2( JobID, szGUIDString, MAX_PATH );
  136. wcscat( szLinkDescription, szGUIDString );
  137. WCHAR szArguments[MAX_PATH] = {0};
  138. wcscpy( szArguments, L"/RESUMEJOB ");
  139. wcscat( szArguments, szGUIDString );
  140. wcscat( szArguments, L" " );
  141. wcscat( szArguments, pszFileName );
  142. wcscat( szLinkFileName, L"\\" );
  143. wcscat( szLinkFileName, szLinkDescription );
  144. wcscat( szLinkFileName, L".lnk" );
  145. hResult = CoCreateInstance( CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
  146. IID_IShellLink, (LPVOID*)&pShellLink);
  147. if ( FAILED( hResult ) ) goto EXIT;
  148. hResult = pShellLink->SetShowCmd( SW_SHOWMINIMIZED );
  149. if ( FAILED( hResult ) ) goto EXIT;
  150. hResult = pShellLink->QueryInterface( IID_IPersistFile, (LPVOID*)&pPersistFile );
  151. if ( FAILED( hResult ) ) goto EXIT;
  152. hResult = pShellLink->SetPath( L"%windir%\\system32\\bits_ie.exe" );
  153. if ( FAILED( hResult ) ) goto EXIT;
  154. hResult = pShellLink->SetArguments( szArguments );
  155. if ( FAILED( hResult ) ) goto EXIT;
  156. hResult = pShellLink->SetDescription( szLinkDescription );
  157. if ( FAILED( hResult ) ) goto EXIT;
  158. hResult = pPersistFile->Save( szLinkFileName, TRUE );
  159. EXIT:
  160. if (pPersistFile) {
  161. pPersistFile->Release();
  162. }
  163. if (pShellLink) {
  164. pShellLink->Release();
  165. }
  166. return hResult;
  167. }
  168. void SetWindowTime(
  169. HWND hwnd,
  170. FILETIME filetime
  171. )
  172. {
  173. // Set the window text to be the text representation
  174. // of the file time.
  175. // If an error occurs, set the window text to be error
  176. FILETIME localtime;
  177. FileTimeToLocalFileTime( &filetime, &localtime );
  178. SYSTEMTIME systemtime;
  179. FileTimeToSystemTime( &localtime, &systemtime );
  180. int RequiredDateSize =
  181. GetDateFormatW(
  182. LOCALE_USER_DEFAULT,
  183. 0,
  184. &systemtime,
  185. NULL,
  186. NULL,
  187. 0 );
  188. if ( !RequiredDateSize )
  189. {
  190. SetWindowText( hwnd, GetString( IDS_ERROR ) );
  191. return;
  192. }
  193. WCHAR *pszDateBuffer = (WCHAR*)alloca( sizeof(WCHAR) * (RequiredDateSize + 1) );
  194. int DateSize =
  195. GetDateFormatW(
  196. LOCALE_USER_DEFAULT,
  197. 0,
  198. &systemtime,
  199. NULL,
  200. pszDateBuffer,
  201. RequiredDateSize );
  202. if (!DateSize)
  203. {
  204. SetWindowText( hwnd, GetString( IDS_ERROR ) );
  205. return;
  206. }
  207. int RequiredTimeSize =
  208. GetTimeFormatW(
  209. LOCALE_USER_DEFAULT,
  210. 0,
  211. &systemtime,
  212. NULL,
  213. NULL,
  214. 0 );
  215. if (!RequiredTimeSize)
  216. {
  217. SetWindowText( hwnd, GetString( IDS_ERROR ) );
  218. return;
  219. }
  220. WCHAR *pszTimeBuffer = (WCHAR*)alloca( sizeof( WCHAR ) * ( RequiredTimeSize + 1 ) );
  221. int TimeSize =
  222. GetTimeFormatW(
  223. LOCALE_USER_DEFAULT,
  224. 0,
  225. &systemtime,
  226. NULL,
  227. pszTimeBuffer,
  228. RequiredTimeSize );
  229. if (!TimeSize)
  230. {
  231. SetWindowText( hwnd, GetString( IDS_ERROR ) );
  232. return;
  233. }
  234. // Add 2 for extra measure
  235. WCHAR *FullTime =
  236. (WCHAR*)alloca( sizeof( WCHAR ) *
  237. ( RequiredTimeSize + RequiredDateSize + 2 ) );
  238. swprintf( FullTime, L"%s %s", pszDateBuffer, pszTimeBuffer );
  239. SetWindowText( hwnd, FullTime );
  240. }
  241. UINT64
  242. GetSystemTimeAsUINT64()
  243. {
  244. //
  245. // Returns the system time as an UINT instead of a FILETIME.
  246. //
  247. FILETIME filetime;
  248. GetSystemTimeAsFileTime( &filetime );
  249. ULARGE_INTEGER large;
  250. memcpy( &large, &filetime, sizeof(FILETIME) );
  251. return large.QuadPart;
  252. }
  253. void SignalAlert(
  254. HWND hwndDlg,
  255. UINT Type
  256. )
  257. {
  258. //
  259. // Alert the user that an important event has occurred
  260. //
  261. FLASHWINFO FlashInfo;
  262. FlashInfo.cbSize = sizeof(FlashInfo);
  263. FlashInfo.hwnd = hwndDlg;
  264. FlashInfo.dwFlags = FLASHW_ALL | FLASHW_TIMERNOFG;
  265. FlashInfo.uCount = 0;
  266. FlashInfo.dwTimeout = 0;
  267. FlashWindowEx( &FlashInfo );
  268. MessageBeep( Type );
  269. }
  270. const WCHAR *
  271. MapStateToString(
  272. BG_JOB_STATE state
  273. )
  274. {
  275. //
  276. // Maps a BITS job state to a human readable string
  277. //
  278. switch( state )
  279. {
  280. case BG_JOB_STATE_QUEUED:
  281. return GetString( IDS_QUEUED );
  282. case BG_JOB_STATE_CONNECTING:
  283. return GetString( IDS_CONNECTING );
  284. case BG_JOB_STATE_TRANSFERRING:
  285. return GetString( IDS_TRANSFERRING );
  286. case BG_JOB_STATE_SUSPENDED:
  287. return GetString( IDS_SUSPENDED );
  288. case BG_JOB_STATE_ERROR:
  289. return GetString( IDS_FATALERROR );
  290. case BG_JOB_STATE_TRANSIENT_ERROR:
  291. return GetString( IDS_TRANSIENTERROR );
  292. case BG_JOB_STATE_TRANSFERRED:
  293. return GetString( IDS_TRANSFERRED );
  294. case BG_JOB_STATE_ACKNOWLEDGED:
  295. return GetString( IDS_ACKNOWLEDGED );
  296. case BG_JOB_STATE_CANCELLED:
  297. return GetString( IDS_CANCELLED );
  298. default:
  299. // NOTE: Always provide a default case
  300. // since new states may be added in future versions.
  301. return GetString( IDS_UNKNOWN );
  302. }
  303. }
  304. double
  305. ScaleDownloadRate(
  306. double Rate, // rate in seconds
  307. const WCHAR **pFormat )
  308. {
  309. //
  310. // Scales a download rate and selects the correct
  311. // format to pass to wprintf for printing.
  312. //
  313. double RateBounds[] =
  314. {
  315. 1073741824.0, // Gigabyte
  316. 1048576.0, // Megabyte
  317. 1024.0, // Kilobyte
  318. 0 // Byte
  319. };
  320. UINT RateFormat[] =
  321. {
  322. IDS_GIGAFORMAT,
  323. IDS_MEGAFORMAT,
  324. IDS_KILOFORMAT,
  325. IDS_BYTEFORMAT
  326. };
  327. for( unsigned int c = 0;; c++ )
  328. {
  329. if ( Rate >= RateBounds[c] )
  330. {
  331. *pFormat = GetString( RateFormat[c] );
  332. double scale = (RateBounds[c] >= 1.0) ? RateBounds[c] : 1.0;
  333. return Rate / scale;
  334. }
  335. }
  336. }
  337. UINT64
  338. ScaleDownloadEstimate(
  339. double Time, // time in seconds
  340. const WCHAR **pFormat )
  341. {
  342. //
  343. // Scales a download time estimate and selects the correct
  344. // format to pass to wprintf for printing.
  345. //
  346. double TimeBounds[] =
  347. {
  348. 60.0 * 60.0 * 24.0, // Days
  349. 60.0 * 60.0, // Hours
  350. 60.0, // Minutes
  351. 0.0 // Seconds
  352. };
  353. UINT TimeFormat[] =
  354. {
  355. IDS_DAYSFORMAT,
  356. IDS_HOURSFORMAT,
  357. IDS_MINUTESFORMAT,
  358. IDS_SECONDSFORMAT
  359. };
  360. for( unsigned int c = 0;; c++ )
  361. {
  362. if ( Time >= TimeBounds[c] )
  363. {
  364. *pFormat = GetString( TimeFormat[c] );
  365. double scale = (TimeBounds[c] >= 1.0) ? TimeBounds[c] : 1.0;
  366. return (UINT64)floor( ( Time / scale ) + 0.5);
  367. }
  368. }
  369. }
  370. void
  371. UpdateDialog(
  372. HWND hwndDlg
  373. )
  374. {
  375. //
  376. // Main update routine for the dialog box.
  377. // Retries the job state/properties from
  378. // BITS and updates the dialog box.
  379. //
  380. {
  381. // update the display name
  382. HWND hwndDisplayName = GetDlgItem( hwndDlg, IDC_DISPLAYNAME );
  383. WCHAR * pszDisplayName = NULL;
  384. if (FAILED( g_pJob->GetDisplayName( &pszDisplayName ) ) )
  385. return; // stop updating on an error
  386. SetWindowText( hwndDisplayName, pszDisplayName );
  387. ShowWindow( hwndDisplayName, SW_SHOW );
  388. CoTaskMemFree( pszDisplayName );
  389. }
  390. static BG_JOB_STATE prevstate = BG_JOB_STATE_SUSPENDED;
  391. BG_JOB_STATE state;
  392. if (FAILED(g_pJob->GetState( &state )))
  393. return; // stop updating on an error
  394. if ( BG_JOB_STATE_ACKNOWLEDGED == state ||
  395. BG_JOB_STATE_CANCELLED == state )
  396. {
  397. // someone else cancelled or completed the job on us,
  398. // just exist the exit.
  399. // May happen if job is canceled with bitsadmin
  400. DeleteStartupLink( g_JobId );
  401. ExitProcess( 0 );
  402. }
  403. BG_JOB_PROGRESS progress;
  404. if (FAILED(g_pJob->GetProgress( &progress )))
  405. return; // stop updating on an error
  406. {
  407. // update the title, progress bar, and progress description
  408. WCHAR szProgress[MAX_STRING];
  409. WCHAR szTitle[MAX_STRING];
  410. WPARAM newpos = 0;
  411. if ( progress.BytesTotal &&
  412. ( progress.BytesTotal != BG_SIZE_UNKNOWN ) )
  413. {
  414. swprintf( szProgress, GetString( IDS_LONGPROGRESS ), progress.BytesTransferred,
  415. progress.BytesTotal );
  416. double Percent = (double)(__int64)progress.BytesTransferred /
  417. (double)(__int64)progress.BytesTotal;
  418. Percent *= 100.0;
  419. swprintf( szTitle, L"%u%% %s", (unsigned int)Percent, g_szFileName );
  420. newpos = (WPARAM)Percent;
  421. }
  422. else
  423. {
  424. swprintf( szProgress, GetString( IDS_SHORTPROGRESS ), progress.BytesTransferred );
  425. wcscpy( szTitle, g_szFileName );
  426. newpos = 0;
  427. }
  428. SendDlgItemMessage( hwndDlg, IDC_PROGRESSBAR, PBM_SETPOS, newpos, 0 );
  429. SetWindowText( GetDlgItem( hwndDlg, IDC_PROGRESSINFO ), szProgress );
  430. ShowWindow( GetDlgItem( hwndDlg, IDC_PROGRESSINFO ), SW_SHOW );
  431. EnableWindow( GetDlgItem( hwndDlg, IDC_PROGRESSINFOTXT ), TRUE );
  432. SetWindowText( hwndDlg, szTitle );
  433. }
  434. {
  435. // update the status
  436. HWND hwndStatus = GetDlgItem( hwndDlg, IDC_STATUS );
  437. SetWindowText( hwndStatus, MapStateToString( state ) );
  438. ShowWindow( hwndStatus, SW_SHOW );
  439. // Only enable the finish button if the job is finished.
  440. EnableWindow( GetDlgItem( hwndDlg, IDC_FINISH ), ( state == BG_JOB_STATE_TRANSFERRED ) );
  441. // Only enable the suspend button if the job is not finished or transferred
  442. BOOL EnableSuspend =
  443. ( state != BG_JOB_STATE_SUSPENDED ) && ( state != BG_JOB_STATE_TRANSFERRED );
  444. EnableWindow( GetDlgItem( hwndDlg, IDC_SUSPEND ), EnableSuspend );
  445. // Only enable the resume button if the job is suspended
  446. BOOL EnableResume = ( BG_JOB_STATE_SUSPENDED == state );
  447. EnableWindow( GetDlgItem( hwndDlg, IDC_RESUME ), EnableResume );
  448. // Alert the user when something important happens
  449. // such as the job completes or a unrecoverable error occurs
  450. if ( BG_JOB_STATE_TRANSFERRED == state &&
  451. BG_JOB_STATE_TRANSFERRED != prevstate )
  452. SignalAlert( hwndDlg, MB_OK );
  453. else if ( BG_JOB_STATE_ERROR == state &&
  454. BG_JOB_STATE_ERROR != prevstate )
  455. SignalAlert( hwndDlg, MB_ICONEXCLAMATION );
  456. }
  457. {
  458. // update times
  459. BG_JOB_TIMES times;
  460. if (FAILED(g_pJob->GetTimes( &times )))
  461. return;
  462. HWND hwndCreationTime = GetDlgItem( hwndDlg, IDC_STARTTIME );
  463. SetWindowTime( hwndCreationTime, times.CreationTime );
  464. ShowWindow( hwndCreationTime, SW_SHOW );
  465. HWND hwndModificationTime = GetDlgItem( hwndDlg, IDC_MODIFICATIONTIME );
  466. SetWindowTime( hwndModificationTime, times.ModificationTime );
  467. ShowWindow( hwndModificationTime, SW_SHOW );
  468. HWND hwndCompletionTime = GetDlgItem( hwndDlg, IDC_COMPLETIONTIME );
  469. if ( !times.TransferCompletionTime.dwLowDateTime && !times.TransferCompletionTime.dwHighDateTime )
  470. {
  471. // BITS sets the CompletionTime to all zeros
  472. // if the job is incomplete
  473. ShowWindow( hwndCompletionTime, SW_HIDE );
  474. EnableWindow( GetDlgItem( hwndDlg, IDC_COMPLETIONTIMETXT ), FALSE );
  475. }
  476. else
  477. {
  478. SetWindowTime( hwndCompletionTime, times.TransferCompletionTime );
  479. ShowWindow( hwndCompletionTime, SW_SHOW );
  480. EnableWindow( GetDlgItem( hwndDlg, IDC_COMPLETIONTIMETXT ), TRUE );
  481. }
  482. }
  483. {
  484. // update the error message
  485. IBackgroundCopyError *pError;
  486. HRESULT Hr = g_pJob->GetError( &pError );
  487. if ( FAILED(Hr) )
  488. {
  489. ShowWindow( GetDlgItem( hwndDlg, IDC_ERRORMSG ), SW_HIDE );
  490. EnableWindow( GetDlgItem( hwndDlg, IDC_ERRORMSGTXT ), FALSE );
  491. }
  492. else
  493. {
  494. WCHAR* pszDescription = NULL;
  495. WCHAR* pszContext = NULL;
  496. SIZE_T SizeRequired = 0;
  497. // If these APIs fail, we should get back
  498. // a NULL string. So everything should be harmless.
  499. pError->GetErrorDescription(
  500. LANGIDFROMLCID( GetThreadLocale() ),
  501. &pszDescription );
  502. pError->GetErrorContextDescription(
  503. LANGIDFROMLCID( GetThreadLocale() ),
  504. &pszContext );
  505. if ( pszDescription )
  506. SizeRequired += wcslen( pszDescription );
  507. if ( pszContext )
  508. SizeRequired += wcslen( pszContext );
  509. WCHAR* pszFullText = (WCHAR*)_alloca((SizeRequired + 1) * sizeof(WCHAR));
  510. *pszFullText = L'\0';
  511. if ( pszDescription )
  512. wcscpy( pszFullText, pszDescription );
  513. if ( pszContext )
  514. wcscat( pszFullText, pszContext );
  515. CoTaskMemFree( pszDescription );
  516. CoTaskMemFree( pszContext );
  517. HWND hwndErrorText = GetDlgItem( hwndDlg, IDC_ERRORMSG );
  518. SetWindowText( hwndErrorText, pszFullText );
  519. ShowWindow( hwndErrorText, SW_SHOW );
  520. EnableWindow( GetDlgItem( hwndDlg, IDC_ERRORMSGTXT ), TRUE );
  521. }
  522. }
  523. if (!SendDlgItemMessage( hwndDlg, IDC_PRIORITY, CB_GETDROPPEDSTATE, 0, 0) )
  524. {
  525. // set the priority, but only do it if user isn't trying to
  526. // set the priority.
  527. BG_JOB_PRIORITY priority;
  528. g_pJob->GetPriority( &priority );
  529. SendDlgItemMessage( hwndDlg, IDC_PRIORITY, CB_SETCURSEL, (WPARAM)priority, 0 );
  530. }
  531. {
  532. //
  533. // This large block of text computes the average transfer rate
  534. // and estimated completion time. This code has much
  535. // room for improvement.
  536. //
  537. static BOOL HasRates = FALSE;
  538. static UINT64 LastMeasurementTime;
  539. static UINT64 LastMeasurementBytes;
  540. static double LastMeasurementRate;
  541. WCHAR szRateText[MAX_STRING];
  542. BOOL EnableRate = FALSE;
  543. if ( !( BG_JOB_STATE_QUEUED == state ) &&
  544. !( BG_JOB_STATE_CONNECTING == state ) &&
  545. !( BG_JOB_STATE_TRANSFERRING == state ) )
  546. {
  547. // If the job isn't running, then rate values won't
  548. // make any sense. Don't display them.
  549. HasRates = FALSE;
  550. }
  551. else
  552. {
  553. if ( !HasRates )
  554. {
  555. LastMeasurementTime = GetSystemTimeAsUINT64();
  556. LastMeasurementBytes = progress.BytesTransferred;
  557. LastMeasurementRate = 0;
  558. HasRates = TRUE;
  559. }
  560. else
  561. {
  562. UINT64 CurrentTime = GetSystemTimeAsUINT64();
  563. UINT64 NewTotalBytes = progress.BytesTransferred;
  564. UINT64 NewTimeDiff = CurrentTime - LastMeasurementTime;
  565. UINT64 NewBytesDiff = NewTotalBytes - LastMeasurementBytes;
  566. double NewInstantRate = (double)(__int64)NewBytesDiff /
  567. (double)(__int64)NewTimeDiff;
  568. double NewAvgRate = (0.3 * LastMeasurementRate) +
  569. (0.7 * NewInstantRate );
  570. if ( !_finite(NewInstantRate) || !_finite(NewAvgRate) )
  571. {
  572. NewInstantRate = 0;
  573. NewAvgRate = LastMeasurementRate;
  574. }
  575. LastMeasurementTime = CurrentTime;
  576. LastMeasurementBytes = NewTotalBytes;
  577. LastMeasurementRate = NewAvgRate;
  578. // convert from FILETIME units to seconds
  579. double NewDisplayRate = NewAvgRate * 10000000;
  580. const WCHAR *pRateFormat = NULL;
  581. double ScaledRate = ScaleDownloadRate( NewDisplayRate, &pRateFormat );
  582. swprintf( szRateText, pRateFormat, ScaledRate );
  583. EnableRate = TRUE;
  584. }
  585. }
  586. if (!EnableRate)
  587. {
  588. ShowWindow( GetDlgItem( hwndDlg, IDC_TRANSFERRATE ), SW_HIDE );
  589. EnableWindow( GetDlgItem( hwndDlg, IDC_TRANSFERRATETXT ), FALSE );
  590. }
  591. else
  592. {
  593. SetWindowText( GetDlgItem( hwndDlg, IDC_TRANSFERRATE ), szRateText );
  594. ShowWindow( GetDlgItem( hwndDlg, IDC_TRANSFERRATE ), SW_SHOW );
  595. EnableWindow( GetDlgItem( hwndDlg, IDC_TRANSFERRATETXT ), TRUE );
  596. }
  597. BOOL EnableEstimate = FALSE;
  598. WCHAR szEstimateText[MAX_STRING];
  599. if ( EnableRate )
  600. {
  601. if ( progress.BytesTotal != 0 &&
  602. progress.BytesTotal != BG_SIZE_UNKNOWN )
  603. {
  604. double TimeRemaining =
  605. ( (__int64)progress.BytesTotal - (__int64)LastMeasurementBytes ) / LastMeasurementRate;
  606. // convert from FILETIME units to seconds
  607. TimeRemaining = TimeRemaining / 10000000.0;
  608. static const double SecsPer30Days = 60.0 * 60.0 * 24.0 * 30.0;
  609. // Don't estimate if estimate is larger then 30 days.
  610. if ( TimeRemaining < SecsPer30Days )
  611. {
  612. const WCHAR *pFormat = NULL;
  613. UINT64 Time = ScaleDownloadEstimate( TimeRemaining, &pFormat );
  614. swprintf( szEstimateText, pFormat, Time );
  615. EnableEstimate = TRUE;
  616. }
  617. }
  618. }
  619. if (!EnableEstimate)
  620. {
  621. ShowWindow( GetDlgItem( hwndDlg, IDC_ESTIMATEDTIME ), SW_HIDE );
  622. EnableWindow( GetDlgItem( hwndDlg, IDC_ESTIMATEDTIMETXT ), FALSE );
  623. }
  624. else
  625. {
  626. SetWindowText( GetDlgItem( hwndDlg, IDC_ESTIMATEDTIME ), szEstimateText );
  627. ShowWindow( GetDlgItem( hwndDlg, IDC_ESTIMATEDTIME ), SW_SHOW );
  628. EnableWindow( GetDlgItem( hwndDlg, IDC_ESTIMATEDTIMETXT ), TRUE );
  629. }
  630. }
  631. prevstate = state;
  632. }
  633. void
  634. InitDialog(
  635. HWND hwndDlg
  636. )
  637. {
  638. //
  639. // Populate the priority list with priority descriptions
  640. //
  641. const WCHAR *Foreground = GetString( IDS_FOREGROUND );
  642. const WCHAR *High = GetString( IDS_HIGH );
  643. const WCHAR *Normal = GetString( IDS_NORMAL );
  644. const WCHAR *Low = GetString( IDS_LOW );
  645. SendDlgItemMessage( hwndDlg, IDC_PROGRESSBAR, PBM_SETRANGE, 0, MAKELPARAM(0, 100) );
  646. SendDlgItemMessage( hwndDlg, IDC_PRIORITY, CB_ADDSTRING, 0, (LPARAM)Foreground );
  647. SendDlgItemMessage( hwndDlg, IDC_PRIORITY, CB_ADDSTRING, 0, (LPARAM)High );
  648. SendDlgItemMessage( hwndDlg, IDC_PRIORITY, CB_ADDSTRING, 0, (LPARAM)Normal );
  649. SendDlgItemMessage( hwndDlg, IDC_PRIORITY, CB_ADDSTRING, 0, (LPARAM)Low );
  650. }
  651. void CheckHR( HWND hwnd, HRESULT Hr, bool bThrow )
  652. {
  653. //
  654. // Provides automatic error code checking and dialog
  655. // for generic system errors
  656. //
  657. if (SUCCEEDED(Hr))
  658. return;
  659. WCHAR * pszError = NULL;
  660. DWORD dwFormatError =
  661. FormatMessage(
  662. FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
  663. FORMAT_MESSAGE_IGNORE_INSERTS,
  664. NULL,
  665. (DWORD)Hr,
  666. LANGIDFROMLCID( GetThreadLocale() ),
  667. (WCHAR*)&pszError,
  668. 0,
  669. NULL );
  670. if ( !dwFormatError )
  671. {
  672. WCHAR ErrorMsg[ MAX_STRING ];
  673. swprintf( ErrorMsg, GetString( IDS_DISPLAYERRORCODE ), Hr );
  674. MessageBox( hwnd, ErrorMsg, GetString( IDS_ERRORBOXTITLE ),
  675. MB_OK | MB_ICONSTOP | MB_APPLMODAL );
  676. }
  677. else
  678. {
  679. MessageBox( hwnd, pszError, GetString( IDS_ERRORBOXTITLE ),
  680. MB_OK | MB_ICONSTOP | MB_APPLMODAL );
  681. LocalFree( pszError );
  682. }
  683. if ( bThrow )
  684. throw _com_error( Hr );
  685. }
  686. void BITSCheckHR( HWND hwnd, HRESULT Hr, bool bThrow )
  687. {
  688. //
  689. // Provides automatic error code checking and dialog
  690. // for BITS specific errors
  691. //
  692. if (SUCCEEDED(Hr))
  693. return;
  694. WCHAR * pszError = NULL;
  695. HRESULT hErrorHr =
  696. g_pManager->GetErrorDescription(
  697. Hr,
  698. LANGIDFROMLCID( GetThreadLocale() ),
  699. &pszError );
  700. if ( FAILED(hErrorHr) || !pszError )
  701. {
  702. WCHAR ErrorMsg[ MAX_STRING ];
  703. swprintf( ErrorMsg, GetString( IDS_DISPLAYERRORCODE ), Hr );
  704. MessageBox( hwnd, ErrorMsg, GetString( IDS_ERRORBOXTITLE ),
  705. MB_OK | MB_ICONSTOP | MB_APPLMODAL );
  706. }
  707. else
  708. {
  709. MessageBox( hwnd, pszError, GetString( IDS_ERRORBOXTITLE ),
  710. MB_OK | MB_ICONSTOP | MB_APPLMODAL );
  711. CoTaskMemFree( pszError );
  712. }
  713. if ( bThrow )
  714. throw _com_error( Hr );
  715. }
  716. void
  717. DoCancel(
  718. HWND hwndDlg,
  719. bool PromptUser
  720. )
  721. {
  722. //
  723. // Handle all the operations required to cancel the job.
  724. // This includes asking the user for confirmation.
  725. //
  726. if ( PromptUser )
  727. {
  728. int Result =
  729. MessageBox(
  730. hwndDlg,
  731. GetString( IDS_CANCELTEXT ),
  732. GetString( IDS_CANCELCAPTION ),
  733. MB_YESNO | MB_ICONWARNING | MB_DEFBUTTON2 | MB_APPLMODAL |
  734. MB_SETFOREGROUND | MB_TOPMOST );
  735. if ( IDYES != Result )
  736. return;
  737. }
  738. try
  739. {
  740. BITSCheckHR( hwndDlg, g_pJob->Cancel(), true );
  741. }
  742. catch( _com_error Error )
  743. {
  744. // If we can't cancel for some unknown reason,
  745. // don't exit
  746. return;
  747. }
  748. DeleteStartupLink( g_JobId );
  749. ExitProcess( 0 );
  750. }
  751. void
  752. DoFinish(
  753. HWND hwndDlg
  754. )
  755. {
  756. //
  757. // Handles all the necessary work to complete
  758. // the download.
  759. //
  760. try
  761. {
  762. BITSCheckHR( hwndDlg, g_pJob->Complete(), true );
  763. }
  764. catch( _com_error Error )
  765. {
  766. // If we can't finish/complete for some unknown reason,
  767. // don't exit
  768. return;
  769. }
  770. DeleteStartupLink( g_JobId );
  771. ExitProcess( 0 );
  772. }
  773. void
  774. DoClose(
  775. HWND hwndDlg
  776. )
  777. {
  778. //
  779. // Handles an attempt by the user to close the sample.
  780. //
  781. // Check to see if the download has finished,
  782. // if so don't let the user exit.
  783. BG_JOB_STATE state;
  784. HRESULT hResult = g_pJob->GetState( &state );
  785. if (FAILED( hResult ))
  786. {
  787. BITSCheckHR( hwndDlg, hResult, false );
  788. return;
  789. }
  790. if ( BG_JOB_STATE_ERROR == state ||
  791. BG_JOB_STATE_TRANSFERRED == state )
  792. {
  793. MessageBox(
  794. hwndDlg,
  795. GetString( IDS_ALREADYFINISHED ),
  796. GetString( IDS_ALREADYFINISHEDCAPTION ),
  797. MB_OK | MB_ICONERROR | MB_DEFBUTTON1 | MB_APPLMODAL |
  798. MB_SETFOREGROUND | MB_TOPMOST );
  799. return;
  800. }
  801. //
  802. // Inform the user that he selected close and ask
  803. // confirm the intention to exit. Explain that the job
  804. // will be canceled.
  805. int Result =
  806. MessageBox(
  807. hwndDlg,
  808. GetString( IDS_CLOSETEXT ),
  809. GetString( IDS_CLOSECAPTION ),
  810. MB_OKCANCEL | MB_ICONWARNING | MB_DEFBUTTON2 | MB_APPLMODAL |
  811. MB_SETFOREGROUND | MB_TOPMOST );
  812. if ( IDOK == Result )
  813. {
  814. // User confirmed the cancel, just do it.
  815. DoCancel( hwndDlg, false );
  816. return;
  817. }
  818. // The user didn't really want to exit, so ignore him
  819. else
  820. return;
  821. }
  822. void
  823. HandleTimerTick( HWND hwndDlg )
  824. {
  825. //
  826. // Handle the throttling timer event
  827. // and update the dialog if needed
  828. //
  829. if ( g_RefreshOnTimer )
  830. {
  831. // The timer fired, handle all updates at once.
  832. UpdateDialog( hwndDlg );
  833. g_RefreshOnTimer = FALSE;
  834. }
  835. else
  836. {
  837. // The timer expired with an additional modification
  838. // notification. Just kill the timer.
  839. KillTimer( hwndDlg, 0 );
  840. g_RefreshOnTimer = g_UpdateTimerSet = FALSE;
  841. }
  842. }
  843. HRESULT
  844. HandleUpdate()
  845. {
  846. //
  847. // Handle a update request, batching the update if needed
  848. //
  849. if ( !g_UpdateTimerSet )
  850. {
  851. // We aren't currently batching updates,
  852. // so do this one update but prevent
  853. // further updates until the timer expires.
  854. SetTimer( g_hwndDlg, 0, 1000, NULL );
  855. g_UpdateTimerSet = TRUE;
  856. UpdateDialog( g_hwndDlg );
  857. }
  858. else
  859. {
  860. // We've started batching and yet received
  861. // another update request. Delay this
  862. // update until the timer fires.
  863. g_RefreshOnTimer = TRUE;
  864. }
  865. return S_OK;
  866. }
  867. INT_PTR CALLBACK
  868. DialogProc(
  869. HWND hwndDlg, // handle to dialog box
  870. UINT uMsg, // message
  871. WPARAM wParam, // first message parameter
  872. LPARAM lParam // second message parameter
  873. )
  874. {
  875. //
  876. // Dialog proc for main dialog window
  877. //
  878. switch( uMsg )
  879. {
  880. case WM_INITDIALOG:
  881. g_hwndDlg = hwndDlg;
  882. InitDialog( hwndDlg );
  883. return TRUE;
  884. case WM_TIMER:
  885. HandleTimerTick( hwndDlg );
  886. return TRUE;
  887. case WM_CLOSE:
  888. DoClose( hwndDlg );
  889. return TRUE;
  890. case WM_COMMAND:
  891. switch( LOWORD( wParam ) )
  892. {
  893. case IDC_RESUME:
  894. BITSCheckHR( hwndDlg, g_pJob->Resume(), false );
  895. return TRUE;
  896. case IDC_SUSPEND:
  897. BITSCheckHR( hwndDlg, g_pJob->Suspend(), false );
  898. return TRUE;
  899. case IDC_CANCEL:
  900. DoCancel( hwndDlg, true );
  901. return TRUE;
  902. case IDC_FINISH:
  903. DoFinish( hwndDlg );
  904. return TRUE;
  905. case IDC_PRIORITY:
  906. switch( HIWORD( wParam ) )
  907. {
  908. case CBN_SELENDOK:
  909. // User clicked on priority,
  910. // update it.
  911. BITSCheckHR( hwndDlg,
  912. g_pJob->SetPriority( (BG_JOB_PRIORITY)
  913. SendDlgItemMessage( hwndDlg, IDC_PRIORITY, CB_GETCURSEL, 0, 0 ) ), false );
  914. return TRUE;
  915. case CBN_SELENDCANCEL:
  916. return TRUE;
  917. default:
  918. return FALSE;
  919. }
  920. default:
  921. return FALSE;
  922. }
  923. default:
  924. return FALSE;
  925. }
  926. }
  927. HRESULT
  928. HandleCOMCallback(
  929. IBackgroundCopyJob* pJob,
  930. bool CriticalEvent
  931. );
  932. class CBackgroundCopyCallback : public IBackgroundCopyCallback
  933. {
  934. //
  935. // Callback class. Used for change notifications.
  936. //
  937. public:
  938. virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppvObject)
  939. {
  940. if ( riid == _uuidof(IUnknown) )
  941. {
  942. *ppvObject = (IUnknown*)(IBackgroundCopyCallback*)this;
  943. return S_OK;
  944. }
  945. else if ( riid == _uuidof(IBackgroundCopyCallback) )
  946. {
  947. *ppvObject = (IBackgroundCopyCallback*)this;
  948. return S_OK;
  949. }
  950. else
  951. return E_NOINTERFACE;
  952. }
  953. virtual HRESULT STDMETHODCALLTYPE CreateInstance(
  954. IUnknown *pUnkOuter,
  955. REFIID riid,
  956. void **ppvObject )
  957. {
  958. if ( pUnkOuter )
  959. return CLASS_E_NOAGGREGATION;
  960. return QueryInterface( riid, ppvObject );
  961. }
  962. // We are cheating on COM here, but we
  963. // are forcing the lifetime of the callback object
  964. // to be the same of the lifetime of the exe.
  965. virtual ULONG STDMETHODCALLTYPE AddRef(void)
  966. {
  967. return 0;
  968. }
  969. virtual ULONG STDMETHODCALLTYPE Release(void)
  970. {
  971. return 0;
  972. }
  973. virtual HRESULT STDMETHODCALLTYPE JobTransferred(IBackgroundCopyJob *pJob)
  974. {
  975. return HandleCOMCallback( pJob, true );
  976. }
  977. virtual HRESULT STDMETHODCALLTYPE JobError(IBackgroundCopyJob *pJob, IBackgroundCopyError *pError)
  978. {
  979. return HandleCOMCallback( pJob, true );
  980. }
  981. virtual HRESULT STDMETHODCALLTYPE JobModification( IBackgroundCopyJob *pJob, DWORD dwReserved )
  982. {
  983. return HandleCOMCallback( pJob, true );
  984. }
  985. } g_Callback;
  986. HRESULT
  987. HandleCOMCallback(
  988. IBackgroundCopyJob* pJob,
  989. bool CriticalEvent
  990. )
  991. {
  992. // In addition to the work of HandleUpdate,
  993. // this function checks to see if we've
  994. // already initialized the manager. If not,
  995. // do it now.
  996. if ( !g_pManager )
  997. {
  998. try
  999. {
  1000. CheckHR( NULL,
  1001. CoCreateInstance( CLSID_BackgroundCopyManager,
  1002. NULL,
  1003. CLSCTX_LOCAL_SERVER,
  1004. IID_IBackgroundCopyManager,
  1005. (void**)&g_pManager ), true );
  1006. pJob->AddRef();
  1007. g_pJob = pJob;
  1008. BITSCheckHR( NULL, g_pJob->SetNotifyFlags( BG_NOTIFY_JOB_MODIFICATION ), true );
  1009. // As an optimization, set the notification interface to be the callback
  1010. // It shouldn't matter if this fails
  1011. g_pJob->SetNotifyInterface( (IBackgroundCopyCallback*)&g_Callback );
  1012. HRESULT Hr = HandleUpdate();
  1013. ShowWindow( g_hwndDlg, CriticalEvent ? SW_NORMAL : SW_MINIMIZE );
  1014. }
  1015. catch(_com_error error )
  1016. {
  1017. if ( g_pManager )
  1018. g_pManager->Release();
  1019. g_pManager = NULL;
  1020. if ( g_pJob )
  1021. g_pJob->Release();
  1022. g_pJob = NULL;
  1023. return error.Error();
  1024. }
  1025. }
  1026. return HandleUpdate();
  1027. }
  1028. HRESULT
  1029. CreateUI( int nShowCmd )
  1030. {
  1031. //
  1032. // Creates the dialog box for the sample.
  1033. //
  1034. g_hwndDlg =
  1035. CreateDialog(
  1036. (HINSTANCE)GetModuleHandle(NULL),
  1037. MAKEINTRESOURCE( IDD_DIALOG ),
  1038. GetDesktopWindow(),
  1039. DialogProc );
  1040. if ( !g_hwndDlg )
  1041. return HRESULT_FROM_WIN32(GetLastError());
  1042. ShowWindow( g_hwndDlg, nShowCmd );
  1043. return S_OK;
  1044. }
  1045. void CreateJob(
  1046. WCHAR* szJobURL
  1047. )
  1048. {
  1049. //
  1050. // Request a destination file name from the user
  1051. // and submit a new job.
  1052. //
  1053. try
  1054. {
  1055. // crack the URL and get the filename
  1056. WCHAR szURLFilePath[MAX_PATH] = {L'\0'};
  1057. URL_COMPONENTS UrlComponents;
  1058. memset( &UrlComponents, 0, sizeof(UrlComponents) );
  1059. UrlComponents.dwStructSize = sizeof(URL_COMPONENTS);
  1060. UrlComponents.lpszUrlPath = szURLFilePath;
  1061. UrlComponents.dwUrlPathLength =
  1062. sizeof(szURLFilePath)/sizeof(*szURLFilePath);
  1063. BOOL CrackResult =
  1064. InternetCrackUrl(
  1065. szJobURL,
  1066. 0,
  1067. 0,
  1068. &UrlComponents );
  1069. if (!CrackResult)
  1070. CheckHR( NULL, HRESULT_FROM_WIN32( GetLastError() ), false );
  1071. if ( UrlComponents.nScheme != INTERNET_SCHEME_HTTP &&
  1072. UrlComponents.nScheme != INTERNET_SCHEME_HTTPS
  1073. )
  1074. {
  1075. MessageBox(
  1076. NULL,
  1077. GetString( IDS_NOHTTPORHTTPS ),
  1078. GetString( IDS_ERRORBOXTITLE ),
  1079. MB_OK | MB_ICONERROR | MB_APPLMODAL |
  1080. MB_SETFOREGROUND | MB_TOPMOST );
  1081. throw _com_error( E_INVALIDARG );
  1082. }
  1083. const WCHAR *szURLFileName =
  1084. szURLFilePath + wcslen( szURLFilePath );
  1085. // parse out the filename part of the URL
  1086. while( szURLFileName != szURLFilePath )
  1087. {
  1088. if ( L'/' == *szURLFileName ||
  1089. L'\\' == *szURLFileName )
  1090. {
  1091. szURLFileName++;
  1092. break;
  1093. }
  1094. szURLFileName--;
  1095. }
  1096. // This is needed in case the first
  1097. // character is a slash.
  1098. if ( L'/' == *szURLFileName ||
  1099. L'\\' == *szURLFileName )
  1100. szURLFileName++;
  1101. // parse out the extension from the name
  1102. const WCHAR *szURLFileExtension =
  1103. szURLFileName + wcslen( szURLFileName );
  1104. while( szURLFileName != szURLFileExtension )
  1105. {
  1106. if ( L'.' == *szURLFileExtension )
  1107. break;
  1108. szURLFileExtension--;
  1109. }
  1110. // build the extension list
  1111. WCHAR *szExtensionList = NULL;
  1112. const WCHAR *szAllFiles = GetString( IDS_ALLFILES );
  1113. const size_t AllFilesSize = wcslen( szAllFiles ) + 1;
  1114. const WCHAR *szAllFilesPattern = L"*";
  1115. const size_t AllFilesPatternSize = sizeof(L"*")/sizeof(WCHAR);
  1116. WCHAR *p;
  1117. if ( szURLFileExtension == szURLFileName &&
  1118. *szURLFileExtension != L'.' )
  1119. {
  1120. size_t StringSize = sizeof(WCHAR) * ( AllFilesSize + AllFilesPatternSize + 2 );
  1121. szExtensionList = (WCHAR*)_alloca( StringSize );
  1122. p = szExtensionList;
  1123. }
  1124. else
  1125. {
  1126. size_t ExtensionSize = wcslen( szURLFileExtension ) + 1;
  1127. size_t StringSize =
  1128. sizeof(WCHAR) * ( ExtensionSize + ExtensionSize + 1 + AllFilesSize
  1129. + AllFilesPatternSize + 2 );
  1130. szExtensionList = (WCHAR*)_alloca( StringSize );
  1131. p = szExtensionList;
  1132. memcpy( p, szURLFileExtension, ExtensionSize * sizeof(WCHAR) );
  1133. p += ExtensionSize;
  1134. *p++ = L'*';
  1135. memcpy( p, szURLFileExtension, ExtensionSize * sizeof(WCHAR) );
  1136. p += ExtensionSize;
  1137. }
  1138. memcpy( p, szAllFiles, AllFilesSize * sizeof(WCHAR) );
  1139. p += AllFilesSize;
  1140. memcpy( p, szAllFilesPattern, AllFilesPatternSize * sizeof(WCHAR) );
  1141. p += AllFilesPatternSize;
  1142. memset( p, 0, sizeof(WCHAR) * 2 );
  1143. OPENFILENAME ofn;
  1144. memset( &ofn, 0, sizeof( ofn ) );
  1145. WCHAR szFileName[MAX_PATH];
  1146. WCHAR szFileTitle[MAX_PATH];
  1147. wcscpy(szFileName, szURLFileName);
  1148. wcscpy(szFileTitle, szURLFileName);
  1149. /* fill in non-variant fields of OPENFILENAME struct. */
  1150. ofn.lStructSize = sizeof(OPENFILENAME);
  1151. ofn.hwndOwner = g_hwndDlg;
  1152. ofn.lpstrFilter = szExtensionList;
  1153. ofn.lpstrCustomFilter = NULL;
  1154. ofn.nMaxCustFilter = 0;
  1155. ofn.nFilterIndex = 0;
  1156. ofn.lpstrFile = szFileName;
  1157. ofn.nMaxFile = MAX_PATH;
  1158. ofn.lpstrInitialDir = NULL;
  1159. ofn.lpstrFileTitle = szFileTitle;
  1160. ofn.nMaxFileTitle = MAX_PATH;
  1161. ofn.lpstrTitle = GetString( IDS_FILEDLGTITLE );
  1162. ofn.lpstrDefExt = NULL;
  1163. ofn.Flags = 0;
  1164. /* Use standard open dialog */
  1165. BOOL bResult = GetSaveFileName ((LPOPENFILENAME)&ofn);
  1166. if ( !bResult )
  1167. {
  1168. if ( !CommDlgExtendedError() )
  1169. {
  1170. // user canceled the box
  1171. ExitProcess( 0 );
  1172. }
  1173. else
  1174. CheckHR( NULL, HRESULT_FROM_WIN32( GetLastError() ), true );
  1175. }
  1176. wcscpy( g_szFileName, szFileTitle );
  1177. CheckHR( NULL,
  1178. CoCreateInstance( CLSID_BackgroundCopyManager,
  1179. NULL,
  1180. CLSCTX_LOCAL_SERVER,
  1181. IID_IBackgroundCopyManager,
  1182. (void**)&g_pManager ), true );
  1183. GUID guid;
  1184. BITSCheckHR( NULL,
  1185. g_pManager->CreateJob( szJobURL,
  1186. BG_JOB_TYPE_DOWNLOAD,
  1187. &guid,
  1188. &g_pJob ),
  1189. true );
  1190. memset( &g_JobId, 0, sizeof(GUID) );
  1191. BITSCheckHR( NULL, g_pJob->GetId( &g_JobId ), true );
  1192. BITSCheckHR( NULL, g_pJob->AddFile( szJobURL, szFileName ), true );
  1193. CheckHR( NULL, CreateStartupLink( g_JobId, g_szFileName ), true );
  1194. IBackgroundCopyCallback *pCallback = new CBackgroundCopyCallback();
  1195. BITSCheckHR( NULL, g_pJob->SetNotifyFlags( BG_NOTIFY_JOB_MODIFICATION ), true );
  1196. BITSCheckHR( NULL,
  1197. g_pJob->SetNotifyInterface( (IBackgroundCopyCallback*)&g_Callback ),
  1198. true );
  1199. BITSCheckHR( NULL, g_pJob->Resume(), true );
  1200. HandleUpdate();
  1201. }
  1202. catch(_com_error error )
  1203. {
  1204. if ( g_pJob )
  1205. {
  1206. g_pJob->Cancel();
  1207. DeleteStartupLink( g_JobId );
  1208. }
  1209. ExitProcess( error.Error() );
  1210. }
  1211. }
  1212. void ResumeJob(
  1213. WCHAR* szJobGUID,
  1214. WCHAR* szJobFileName
  1215. )
  1216. {
  1217. //
  1218. // Resume the display on an existing job
  1219. //
  1220. try
  1221. {
  1222. wcscpy( g_szFileName, szJobFileName );
  1223. CheckHR( NULL, IIDFromString( szJobGUID, &g_JobId ), true );
  1224. CheckHR( NULL,
  1225. CoCreateInstance( CLSID_BackgroundCopyManager,
  1226. NULL,
  1227. CLSCTX_LOCAL_SERVER,
  1228. IID_IBackgroundCopyManager,
  1229. (void**)&g_pManager ), true );
  1230. BITSCheckHR( NULL, g_pManager->GetJob( g_JobId, &g_pJob ), true );
  1231. BITSCheckHR( NULL,
  1232. g_pJob->SetNotifyInterface( (IBackgroundCopyCallback*)&g_Callback ),
  1233. true );
  1234. BITSCheckHR( NULL, g_pJob->SetNotifyFlags( BG_NOTIFY_JOB_MODIFICATION ), true );
  1235. ShowWindow( g_hwndDlg, SW_MINIMIZE );
  1236. HandleUpdate();
  1237. }
  1238. catch(_com_error error )
  1239. {
  1240. ExitProcess( error.Error() );
  1241. }
  1242. }
  1243. int WINAPI WinMain(
  1244. HINSTANCE hInstance, // handle to current instance
  1245. HINSTANCE hPrevInstance, // handle to previous instance
  1246. LPSTR lpCmdLine, // command line
  1247. int nCmdShow) // show state
  1248. {
  1249. //
  1250. // Expected syntax:
  1251. // bits_ie /CREATEJOB URL
  1252. // bits_ie /RESUMEJOB JobGUID DestinationFile
  1253. // /CREATEJOB - Called from the script which is run when
  1254. // "Background Download As" is selected.
  1255. // /RESUMEJOB - Called from the link in the startup directory
  1256. // to resume a job when it is restarted.
  1257. CoInitialize(NULL);
  1258. InitCommonControls();
  1259. if ( FAILED( CreateUI( nCmdShow ) ) )
  1260. return -1;
  1261. LPTSTR lpCommandLine = GetCommandLine();
  1262. int argc;
  1263. WCHAR **argv =
  1264. CommandLineToArgvW(
  1265. lpCommandLine,
  1266. &argc );
  1267. if ( argc < 2 )
  1268. return -1;
  1269. if ( argc == 3 &&
  1270. _wcsicmp( L"/CREATEJOB", argv[1] ) == 0 )
  1271. CreateJob( argv[2] );
  1272. else if ( argc == 4 &&
  1273. _wcsicmp( L"/RESUMEJOB", argv[1] ) == 0 )
  1274. ResumeJob( argv[2], argv[3] );
  1275. else
  1276. return -1;
  1277. MSG msg;
  1278. while( GetMessage( &msg, NULL, 0, 0 ) )
  1279. {
  1280. TranslateMessage( &msg );
  1281. DispatchMessage( &msg );
  1282. }
  1283. return 0;
  1284. }