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.

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