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.

754 lines
19 KiB

  1. //
  2. // Copyright (c) 2001 Microsoft Corporation
  3. //
  4. //
  5. #include "activexmime.h"
  6. #include <wininet.h>
  7. #define GOBACKHACK
  8. #ifdef GOBACKHACK
  9. #include <exdisp.h> // for IWebBrowser2
  10. #endif
  11. #include "macros.h"
  12. static const GUID CLSID_ActiveXMimePlayer =
  13. { 0xFDCDCCE0, 0xCBC4, 0x49FB, { 0x85, 0x8D, 0x92, 0x53, 0xA6, 0xB8, 0x16, 0x1F} };
  14. extern ULONG DllAddRef(void);
  15. extern ULONG DllRelease(void);
  16. // ----------------------------------------------------------------------------
  17. // ----------------------------------------------------------------------------
  18. CActiveXMimeClassFactory::CActiveXMimeClassFactory()
  19. {
  20. _cRef = 1;
  21. _hr = S_OK;
  22. }
  23. // ----------------------------------------------------------------------------
  24. HRESULT
  25. CActiveXMimeClassFactory::QueryInterface(REFIID iid, void **ppv)
  26. {
  27. _hr = S_OK;
  28. *ppv = NULL;
  29. if (iid == IID_IUnknown || iid == IID_IClassFactory)
  30. {
  31. *ppv = (IClassFactory *)this;
  32. }
  33. else
  34. {
  35. _hr = E_NOINTERFACE;
  36. goto exit;
  37. }
  38. ((IUnknown *)*ppv)->AddRef();
  39. exit:
  40. return _hr;
  41. }
  42. // ----------------------------------------------------------------------------
  43. ULONG
  44. CActiveXMimeClassFactory::AddRef()
  45. {
  46. return (ULONG) InterlockedIncrement(&_cRef);
  47. }
  48. ULONG
  49. CActiveXMimeClassFactory::Release()
  50. {
  51. LONG ulCount = InterlockedDecrement(&_cRef);
  52. if (ulCount <= 0)
  53. {
  54. delete this;
  55. }
  56. return (ULONG) ulCount;
  57. }
  58. HRESULT
  59. CActiveXMimeClassFactory::LockServer(BOOL lock)
  60. {
  61. if (lock)
  62. DllAddRef();
  63. else
  64. DllRelease();
  65. return (_hr = S_OK);
  66. }
  67. // ----------------------------------------------------------------------------
  68. HRESULT
  69. CActiveXMimeClassFactory::CreateInstance(IUnknown* pUnkOuter, REFIID iid, void** ppv)
  70. {
  71. _hr = S_OK;
  72. CActiveXMimePlayer *pMimePlayer = NULL;
  73. *ppv = NULL;
  74. IF_FALSE_EXIT(!pUnkOuter || iid == IID_IUnknown, CLASS_E_NOAGGREGATION);
  75. pMimePlayer = new CActiveXMimePlayer();
  76. IF_ALLOC_FAILED_EXIT(pMimePlayer);
  77. if (iid == IID_IUnknown)
  78. {
  79. *ppv = (IOleObject *)pMimePlayer;
  80. pMimePlayer->AddRef();
  81. }
  82. else
  83. {
  84. IF_FAILED_EXIT(pMimePlayer->QueryInterface(iid, ppv));
  85. }
  86. exit:
  87. SAFERELEASE(pMimePlayer);
  88. return _hr;
  89. }
  90. // ----------------------------------------------------------------------------
  91. // ----------------------------------------------------------------------------
  92. // CActiveXMimePlayer
  93. CActiveXMimePlayer::CActiveXMimePlayer()
  94. {
  95. _cRef = 1;
  96. _hr = S_OK;
  97. }
  98. CActiveXMimePlayer::~CActiveXMimePlayer()
  99. {
  100. }
  101. // ----------------------------------------------------------------------------
  102. HRESULT
  103. CActiveXMimePlayer::QueryInterface(REFIID iid, void** ppv)
  104. {
  105. _hr = S_OK;
  106. *ppv = NULL;
  107. if (iid == IID_IOleObject ||
  108. iid == IID_IUnknown)
  109. {
  110. *ppv = (IOleObject *)this;
  111. }
  112. else if (iid == IID_IObjectSafety)
  113. {
  114. *ppv = (IObjectSafety *)this;
  115. }
  116. else
  117. {
  118. _hr = E_NOINTERFACE;
  119. goto exit;
  120. }
  121. ((IUnknown *)*ppv)->AddRef();
  122. exit:
  123. return _hr;
  124. }
  125. // ----------------------------------------------------------------------------
  126. ULONG
  127. CActiveXMimePlayer::AddRef()
  128. {
  129. return (ULONG) InterlockedIncrement(&_cRef);
  130. }
  131. ULONG
  132. CActiveXMimePlayer::Release()
  133. {
  134. LONG ulCount = InterlockedDecrement(&_cRef);
  135. if (ulCount <= 0)
  136. {
  137. delete this;
  138. }
  139. return (ULONG) ulCount;
  140. }
  141. // ----------------------------------------------------------------------------
  142. //////////////////////////////////////////////
  143. // IOleObject interface
  144. HRESULT
  145. CActiveXMimePlayer::SetClientSite(IOleClientSite *pClientSite)
  146. {
  147. _hr = S_OK;
  148. if (pClientSite != NULL)
  149. {
  150. // Obtain URL from container moniker.
  151. IMoniker* pmk = NULL;
  152. if (SUCCEEDED(_hr = pClientSite->GetMoniker(
  153. OLEGETMONIKER_TEMPFORUSER,
  154. OLEWHICHMK_CONTAINER,
  155. &pmk)))
  156. {
  157. LPOLESTR pwzDisplayName = NULL;
  158. if (SUCCEEDED(_hr = pmk->GetDisplayName(NULL, NULL, &pwzDisplayName)))
  159. {
  160. _hr = _sURL.Assign(pwzDisplayName);
  161. CoTaskMemFree((LPVOID)pwzDisplayName);
  162. // _hr from _sURL.Assign() above
  163. if (SUCCEEDED(_hr) && FAILED(StartManifestHandler(_sURL)))
  164. MessageBox(NULL, L"Error downloading manifest or starting manifest handler. Cannot continue.", L"ClickOnce", MB_OK | MB_ICONEXCLAMATION);
  165. }
  166. }
  167. SAFERELEASE(pmk);
  168. if (FAILED(_hr))
  169. MessageBox(NULL, L"Error accessing manifest URL.", L"ClickOnce", MB_OK | MB_ICONEXCLAMATION);
  170. #ifdef GOBACKHACK
  171. // ask IE to go back instead of staying in the blank page
  172. IServiceProvider* pISP = NULL;
  173. if (SUCCEEDED(_hr = pClientSite->QueryInterface(IID_IServiceProvider, (void **)&pISP)))
  174. {
  175. IWebBrowser2* pIWebBrowser2 = NULL;
  176. if (SUCCEEDED(_hr = pISP->QueryService(IID_IWebBrowserApp, IID_IWebBrowser2, (void **)&pIWebBrowser2)))
  177. {
  178. // if (FAILED(
  179. _hr = pIWebBrowser2->GoBack();//))
  180. // NOTICE-2002/03/12-felixybc Go back can fail if new IE window with no viewed page, Start Run url, or Internet Shortcut
  181. // should close IE window instead?
  182. }
  183. SAFERELEASE(pIWebBrowser2);
  184. }
  185. SAFERELEASE(pISP);
  186. #endif
  187. }
  188. return S_OK;
  189. }
  190. HRESULT
  191. CActiveXMimePlayer::GetClientSite(IOleClientSite **ppClientSite)
  192. {
  193. _hr = S_OK;
  194. if (ppClientSite == NULL)
  195. _hr = E_INVALIDARG;
  196. else
  197. *ppClientSite = NULL; // return clientsite?
  198. return _hr;
  199. }
  200. HRESULT
  201. CActiveXMimePlayer::SetHostNames(LPCOLESTR szContainerApp,
  202. /* [in] */
  203. LPCOLESTR szContainerObj)
  204. /* [unique][in] */
  205. {
  206. // note: can check the name of container app here
  207. return (_hr = S_OK);
  208. }
  209. HRESULT
  210. CActiveXMimePlayer::Close(DWORD dwSaveOption)
  211. /* [in] */
  212. {
  213. return (_hr = S_OK);
  214. }
  215. HRESULT
  216. CActiveXMimePlayer::SetMoniker(DWORD dwWhichMoniker,
  217. /* [in] */
  218. IMoniker *pmk)
  219. /* [unique][in] */
  220. {
  221. return (_hr = E_NOTIMPL);
  222. }
  223. HRESULT
  224. CActiveXMimePlayer::GetMoniker(DWORD dwAssign,
  225. /* [in] */
  226. DWORD dwWhichMoniker,
  227. /* [in] */
  228. IMoniker **ppmk)
  229. /* [out] */
  230. {
  231. return (_hr = E_NOTIMPL);
  232. }
  233. HRESULT
  234. CActiveXMimePlayer::InitFromData(IDataObject *pDataObject,
  235. /* [unique][in] */
  236. BOOL fCreation,
  237. /* [in] */
  238. DWORD dwReserved)
  239. /* [in] */
  240. {
  241. return (_hr = E_NOTIMPL);
  242. }
  243. HRESULT
  244. CActiveXMimePlayer::GetClipboardData(DWORD dwReserved,
  245. /* [in] */
  246. IDataObject **ppDataObject)
  247. /* [out] */
  248. {
  249. return (_hr = E_NOTIMPL);
  250. }
  251. HRESULT
  252. CActiveXMimePlayer::DoVerb(LONG iVerb,
  253. /* [in] */
  254. LPMSG lpmsg,
  255. /* [unique][in] */
  256. IOleClientSite *pActiveSite,
  257. /* [unique][in] */
  258. LONG lindex,
  259. /* [in] */
  260. HWND hwndParent,
  261. /* [in] */
  262. LPCRECT lprcPosRect)
  263. /* [unique][in] */
  264. {
  265. return (_hr = OLEOBJ_E_NOVERBS); //E_NOTIMPL;
  266. }
  267. HRESULT
  268. CActiveXMimePlayer::EnumVerbs(IEnumOLEVERB **ppEnumOleVerb)
  269. /* [out] */
  270. {
  271. return (_hr = OLEOBJ_E_NOVERBS); //E_NOTIMPL;
  272. }
  273. HRESULT
  274. CActiveXMimePlayer::Update( void)
  275. {
  276. return (_hr = S_OK);
  277. }
  278. HRESULT
  279. CActiveXMimePlayer::IsUpToDate( void)
  280. {
  281. return (_hr = S_OK); //OLE_E_UNAVAILABLE;
  282. }
  283. HRESULT
  284. CActiveXMimePlayer::GetUserClassID(CLSID *pClsid)
  285. {
  286. if (pClsid != NULL)
  287. {
  288. *pClsid = CLSID_ActiveXMimePlayer;
  289. _hr = S_OK;
  290. }
  291. else
  292. _hr = E_INVALIDARG;
  293. return _hr;
  294. }
  295. HRESULT
  296. CActiveXMimePlayer::GetUserType(DWORD dwFormOfType,
  297. /* [in] */
  298. LPOLESTR *pszUserType)
  299. /* [out] */
  300. {
  301. return (_hr = OLE_S_USEREG); //E_NOTIMPL;
  302. }
  303. HRESULT
  304. CActiveXMimePlayer::SetExtent(DWORD dwDrawAspect,
  305. /* [in] */
  306. SIZEL *psizel)
  307. /* [in] */
  308. {
  309. return (_hr = E_NOTIMPL);
  310. }
  311. HRESULT
  312. CActiveXMimePlayer::GetExtent(DWORD dwDrawAspect,
  313. /* [in] */
  314. SIZEL *psizel)
  315. /* [out] */
  316. {
  317. return (_hr = E_NOTIMPL);
  318. }
  319. HRESULT
  320. CActiveXMimePlayer::Advise(IAdviseSink *pAdvSink,
  321. /* [unique][in] */
  322. DWORD *pdwConnection)
  323. /* [out] */
  324. {
  325. return (_hr = E_NOTIMPL);
  326. }
  327. HRESULT
  328. CActiveXMimePlayer::Unadvise(DWORD dwConnection)
  329. /* [in] */
  330. {
  331. return (_hr = E_NOTIMPL);
  332. }
  333. HRESULT
  334. CActiveXMimePlayer::EnumAdvise(IEnumSTATDATA **ppenumAdvise)
  335. /* [out] */
  336. {
  337. return (_hr = E_NOTIMPL);
  338. }
  339. HRESULT
  340. CActiveXMimePlayer::GetMiscStatus(DWORD dwAspect,
  341. DWORD *pdwStatus)
  342. {
  343. _hr = S_OK;
  344. // ignore dwAspect
  345. if (pdwStatus == NULL)
  346. _hr = E_INVALIDARG;
  347. else
  348. *pdwStatus = OLEMISC_SETCLIENTSITEFIRST | OLEMISC_NOUIACTIVATE; //OLEMISC_INVISIBLEATRUNTIME
  349. return _hr;
  350. }
  351. HRESULT
  352. CActiveXMimePlayer::SetColorScheme(LOGPALETTE *pLogpal)
  353. {
  354. return (_hr = E_NOTIMPL);
  355. }
  356. //////////////////////////////////////////////
  357. // IOjbectSafety interface
  358. HRESULT
  359. CActiveXMimePlayer::GetInterfaceSafetyOptions(REFIID riid,
  360. DWORD* pdwSupportedOptions,
  361. DWORD* pdwEnabledOptions)
  362. {
  363. _hr = S_OK;
  364. // regardless riid
  365. if (pdwSupportedOptions == NULL || pdwEnabledOptions == NULL)
  366. _hr = E_INVALIDARG;
  367. else
  368. {
  369. // do _not_ support safe for scripting - INTERFACESAFE_FOR_UNTRUSTED_CALLER
  370. *pdwSupportedOptions = INTERFACESAFE_FOR_UNTRUSTED_DATA;
  371. *pdwEnabledOptions = INTERFACESAFE_FOR_UNTRUSTED_DATA;
  372. }
  373. return _hr;
  374. }
  375. HRESULT
  376. CActiveXMimePlayer::SetInterfaceSafetyOptions(REFIID riid,
  377. DWORD dwOptionSetMask,
  378. DWORD dwEnabledOptions)
  379. {
  380. _hr = E_FAIL;
  381. if (riid == IID_IDispatch)
  382. {
  383. // do _not_ support IDispatch interface
  384. // The dwOptionSetMask parameter specifies an option that is not supported by the object.
  385. _hr = E_FAIL;
  386. }
  387. else //if (riid == IID_IPersist) // IID_IPersist*? what about IID_IUnknown?
  388. {
  389. // regardless riid
  390. // only acknowledge if only INTERFACESAFE_FOR_UNTRUSTED_DATA is passed in
  391. if (dwOptionSetMask == INTERFACESAFE_FOR_UNTRUSTED_DATA)
  392. _hr = S_OK; // The object is safe for loading.
  393. else
  394. _hr = E_FAIL;
  395. }
  396. return _hr;
  397. }
  398. // ----------------------------------------------------------------------------
  399. #define DEFAULT_PATH_LEN MAX_PATH
  400. #define TEMP_FILE_NAME_LEN sizeof("preuuuu.TMP") // from msdn
  401. HRESULT
  402. CActiveXMimePlayer::StartManifestHandler(CString& sURL)
  403. {
  404. HKEY hkey = NULL;
  405. DWORD dwcbData = 0;
  406. DWORD dwType = 0;
  407. LONG lReturn = 0;
  408. CString sOpenCommand;
  409. CString sCmdLine;
  410. LPWSTR pwzOpenCommand = NULL;
  411. CString sTempPath;
  412. STARTUPINFO si;
  413. PROCESS_INFORMATION pi;
  414. ZeroMemory(&si, sizeof(si));
  415. ZeroMemory(&pi, sizeof(pi));
  416. si.cb = sizeof(si);
  417. // check if sURL empty (including ending L'\0')
  418. // note: could do better check here, eg. check for http://
  419. IF_FALSE_EXIT(sURL._cc > 1, E_INVALIDARG);
  420. // assemble temp file path
  421. IF_FAILED_EXIT(sTempPath.ResizeBuffer(DEFAULT_PATH_LEN));
  422. {
  423. // ISSUE-2002/03/12-felixybc GetTempPath can overrun the buffer?
  424. DWORD dwLen = GetTempPath(DEFAULT_PATH_LEN, sTempPath._pwz);
  425. IF_WIN32_FALSE_EXIT(dwLen);
  426. if (dwLen >= DEFAULT_PATH_LEN)
  427. {
  428. // resize, add 1 for terminating null
  429. IF_FAILED_EXIT(sTempPath.ResizeBuffer(dwLen+1));
  430. DWORD dwLenNew = GetTempPath(dwLen+1, sTempPath._pwz);
  431. IF_WIN32_FALSE_EXIT(dwLenNew);
  432. IF_FALSE_EXIT(dwLenNew < dwLen+1, E_FAIL); // why is it still not enough?
  433. }
  434. }
  435. // why is the temp file created already?
  436. ASSERT(_sTempFile._pwz == NULL);
  437. {
  438. DWORD dwBufLen = lstrlen(sTempPath._pwz)+1;
  439. // note: can do a better check here
  440. IF_FALSE_EXIT(dwBufLen > 1, E_FAIL);
  441. // allocate buffer large enough for temp path and temp file name
  442. DWORD dwLenNew = (dwBufLen > DEFAULT_PATH_LEN)? dwBufLen : DEFAULT_PATH_LEN;
  443. dwLenNew += TEMP_FILE_NAME_LEN;
  444. // check for overflow
  445. IF_FALSE_EXIT(dwLenNew > dwBufLen, E_FAIL);
  446. IF_FAILED_EXIT(_sTempFile.ResizeBuffer(dwLenNew));
  447. *(_sTempFile._pwz) = L'\0';
  448. // note: temp file to be deleted by the child handler process created below
  449. IF_WIN32_FALSE_EXIT(GetTempFileName(sTempPath._pwz, L"FMA", 0, _sTempFile._pwz)); // fusion manifest file
  450. }
  451. IF_FAILED_EXIT(DownloadInternetFile(sURL, _sTempFile._pwz));
  452. // get the execution string (esp. path) from reg key
  453. // ISSUE-2002/03/21-adriaanc
  454. // This code should live in regclass.cpp and CRegImport/CRegEmit should accept
  455. // a root hive handle and fall back to HKCU if not provided.
  456. // HKEY_CLASSES_ROOT\manifestfile\shell\Open\command
  457. lReturn = RegOpenKeyEx(HKEY_CLASSES_ROOT, L"manifestfile\\shell\\Open\\command", 0,
  458. KEY_EXECUTE | KEY_QUERY_VALUE, &hkey);
  459. IF_WIN32_FAILED_EXIT(lReturn);
  460. lReturn = RegQueryValueEx(hkey, NULL, NULL, &dwType, NULL, &dwcbData);
  461. IF_WIN32_FAILED_EXIT(lReturn);
  462. IF_FALSE_EXIT(dwcbData, E_FAIL);
  463. // validate reg value type
  464. IF_FALSE_EXIT(dwType == REG_SZ || dwType == REG_EXPAND_SZ, E_UNEXPECTED);
  465. {
  466. // Allocate for call to RQEX, with one extra char in case
  467. // returned buffer is not null terminated.
  468. LPWSTR pwz = NULL;
  469. DWORD dwStrLen = dwcbData / sizeof(WCHAR);
  470. DWORD dwBufLen = dwStrLen+1;
  471. CStringAccessor<CString> acc;
  472. // Allocate and call RQVEX
  473. IF_ALLOC_FAILED_EXIT(pwzOpenCommand = new WCHAR[dwBufLen]);
  474. lReturn = RegQueryValueEx(hkey, NULL, NULL,
  475. &dwType, (LPBYTE) pwzOpenCommand, &dwcbData);
  476. IF_WIN32_FAILED_EXIT(lReturn);
  477. // Null terminate returned buffer.
  478. *(pwzOpenCommand + dwStrLen) = L'\0';
  479. // rundll32.exe fnsshell.dll,Start "%1" --> L"rundll32.exe fnsshell.dll,Start \"%s\" \"%s\""
  480. pwz = wcsrchr(pwzOpenCommand, L'%');
  481. IF_NULL_EXIT(pwz, E_FAIL);
  482. *pwz = L'\0';
  483. // Attach accessor, set buffer, detach with corrected length.
  484. IF_FAILED_EXIT(acc.Attach(sOpenCommand));
  485. *(&acc) = pwzOpenCommand;
  486. // Could avoid the strlen if we bothered to track the strlen.
  487. IF_FAILED_EXIT(acc.Detach());
  488. // If Detach succeeds, reset pointer so that it is freed once.
  489. pwzOpenCommand = NULL;
  490. }
  491. // NTRAID#NTBUG9-574001-2002/03/12-felixybc check format of the returned string value
  492. // assemble the command line
  493. IF_FAILED_EXIT(sCmdLine.Assign(sOpenCommand));
  494. IF_FAILED_EXIT(sCmdLine.Append(_sTempFile._pwz));
  495. IF_FAILED_EXIT(sCmdLine.Append(L"\" \""));
  496. IF_FAILED_EXIT(sCmdLine.Append(sURL));
  497. IF_FAILED_EXIT(sCmdLine.Append(L"\""));
  498. // start the process
  499. // NTRAID#NTBUG9-574001-2002/03/12-felixybc string value needs proper format
  500. // need quotes if executable path contains spaces, or instead pass lpApplicationName for better security
  501. // rundll32.exe should be in c:\windows\system32
  502. IF_WIN32_FALSE_EXIT(CreateProcess(NULL, sCmdLine._pwz, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi));
  503. exit:
  504. if(pi.hThread) CloseHandle(pi.hThread);
  505. if(pi.hProcess) CloseHandle(pi.hProcess);
  506. if (hkey)
  507. RegCloseKey(hkey);
  508. if (FAILED(_hr) && _sTempFile._pwz != NULL)
  509. {
  510. if (*(_sTempFile._pwz) != L'\0')
  511. {
  512. // ignore if error deleting the file
  513. DeleteFile(_sTempFile._pwz);
  514. }
  515. _sTempFile.FreeBuffer();
  516. }
  517. SAFEDELETEARRAY(pwzOpenCommand);
  518. return _hr;
  519. }
  520. // ----------------------------------------------------------------------------
  521. // download file from url into path
  522. HRESULT
  523. CActiveXMimePlayer::DownloadInternetFile(CString& sUrl,
  524. LPCWSTR pwzPath)
  525. {
  526. HINTERNET hInternet = NULL;
  527. HINTERNET hTransfer = NULL;
  528. HANDLE hFile = INVALID_HANDLE_VALUE;
  529. DWORD bytesRead = 0;
  530. DWORD bytesWritten = 0;
  531. BYTE *buffer = NULL;
  532. const DWORD dwBufferSize = 4096;
  533. BOOL bReadOk = FALSE;
  534. _hr = S_OK;
  535. // check offline mode
  536. /* DWORD dwState = 0;
  537. DWORD dwSize = sizeof(DWORD);
  538. if(InternetQueryOption(NULL, INTERNET_OPTION_CONNECTED_STATE, &dwState, &dwSize))
  539. {
  540. if(dwState & INTERNET_STATE_DISCONNECTED_BY_USER)
  541. // error!
  542. }*/
  543. // Step 1: Create the file
  544. // overwrites the file, if exists. file not shared
  545. hFile = CreateFile(pwzPath, GENERIC_WRITE, 0, NULL,
  546. CREATE_ALWAYS,
  547. FILE_ATTRIBUTE_NOT_CONTENT_INDEXED|FILE_ATTRIBUTE_TEMPORARY,
  548. NULL);
  549. IF_WIN32_FALSE_EXIT((hFile != INVALID_HANDLE_VALUE));
  550. // Step 2: Copy the files over the internet
  551. hInternet = InternetOpen(L"ManifestHandler", INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0);
  552. IF_WIN32_FALSE_EXIT((hInternet != NULL));
  553. // do not keep file in wininet cache
  554. hTransfer = InternetOpenUrl(hInternet, sUrl._pwz, NULL, 0, INTERNET_FLAG_NO_CACHE_WRITE, 0);
  555. IF_WIN32_FALSE_EXIT((hTransfer != NULL));
  556. // need to check if there's any error, eg. not found (404)...
  557. buffer = new BYTE[dwBufferSize];
  558. IF_ALLOC_FAILED_EXIT(buffer);
  559. // synchronous download
  560. while((bReadOk = InternetReadFile(hTransfer, buffer, dwBufferSize, &bytesRead)) && bytesRead != 0)
  561. {
  562. IF_WIN32_FALSE_EXIT(!( !WriteFile(hFile, buffer, bytesRead, &bytesWritten, NULL) ||
  563. bytesWritten != bytesRead ));
  564. }
  565. IF_WIN32_FALSE_EXIT(bReadOk);
  566. exit:
  567. SAFEDELETEARRAY(buffer);
  568. // ensure file/internet handles are closed
  569. if (hFile != INVALID_HANDLE_VALUE)
  570. CloseHandle(hFile);
  571. if (hTransfer != NULL)
  572. InternetCloseHandle(hTransfer);
  573. if (hInternet != NULL)
  574. InternetCloseHandle(hInternet);
  575. hInternet = NULL;
  576. hTransfer = NULL;
  577. hFile = INVALID_HANDLE_VALUE;
  578. // note: caller's responsibility to delete the file
  579. return _hr;
  580. }