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.

783 lines
21 KiB

  1. #include "priv.h"
  2. #include "apithk.h"
  3. #ifdef _X86_
  4. #include <w95wraps.h>
  5. #endif
  6. class CFileStream : public IStream
  7. {
  8. public:
  9. // IUnknown
  10. STDMETHOD(QueryInterface) (THIS_ REFIID riid, void **ppvObj);
  11. STDMETHOD_(ULONG,AddRef) (THIS);
  12. STDMETHOD_(ULONG,Release) (THIS);
  13. // IStream
  14. STDMETHOD(Read) (THIS_ void *pv, ULONG cb, ULONG *pcbRead);
  15. STDMETHOD(Write) (THIS_ void const *pv, ULONG cb, ULONG *pcbWritten);
  16. STDMETHOD(Seek) (THIS_ LARGE_INTEGER dlibMove, DWORD dwOrigin, ULARGE_INTEGER *plibNewPosition);
  17. STDMETHOD(SetSize) (THIS_ ULARGE_INTEGER libNewSize);
  18. STDMETHOD(CopyTo) (THIS_ IStream *pstm, ULARGE_INTEGER cb, ULARGE_INTEGER *pcbRead, ULARGE_INTEGER *pcbWritten);
  19. STDMETHOD(Commit) (THIS_ DWORD grfCommitFlags);
  20. STDMETHOD(Revert) (THIS);
  21. STDMETHOD(LockRegion) (THIS_ ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType);
  22. STDMETHOD(UnlockRegion) (THIS_ ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType);
  23. STDMETHOD(Stat) (THIS_ STATSTG *pstatstg, DWORD grfStatFlag);
  24. STDMETHOD(Clone)(THIS_ IStream **ppstm);
  25. CFileStream(HANDLE hf, DWORD grfMode, LPCWSTR pszName);
  26. private:
  27. ~CFileStream();
  28. HRESULT InternalCommit(DWORD grfCommitFlags, BOOL fSendChange);
  29. LONG _cRef; // Reference count
  30. HANDLE _hFile; // the file.
  31. DWORD _grfMode; // The mode that we opened the file in.
  32. BOOL _fLastOpWrite; // The last operation was a write.
  33. ULONG _iBuffer; // Index in Buffer
  34. ULONG _cbBufLen; // length of buffer if reading
  35. BYTE _bBuffer[4096]; // buffer
  36. WCHAR _szName[MAX_PATH]; // file name in case someone calls Stat
  37. };
  38. CFileStream::CFileStream(HANDLE hf, DWORD grfMode, LPCWSTR pszName) : _cRef(1), _hFile(hf), _grfMode(grfMode)
  39. {
  40. ASSERT(_cbBufLen == 0);
  41. ASSERT(_iBuffer == 0);
  42. ASSERT(_fLastOpWrite == FALSE);
  43. StrCpyNW(_szName, pszName, ARRAYSIZE(_szName));
  44. }
  45. CFileStream::~CFileStream()
  46. {
  47. if (_fLastOpWrite)
  48. {
  49. InternalCommit(0, TRUE);
  50. }
  51. ASSERT(_hFile != INVALID_HANDLE_VALUE);
  52. CloseHandle(_hFile);
  53. }
  54. STDMETHODIMP CFileStream::QueryInterface(REFIID riid, void **ppv)
  55. {
  56. static const QITAB qit[] =
  57. {
  58. QITABENT(CFileStream, IStream),
  59. { 0 },
  60. };
  61. return QISearch(this, qit, riid, ppv);
  62. }
  63. STDMETHODIMP_(ULONG) CFileStream::AddRef()
  64. {
  65. return InterlockedIncrement(&_cRef);
  66. }
  67. STDMETHODIMP_(ULONG) CFileStream::Release()
  68. {
  69. if (InterlockedDecrement(&_cRef))
  70. return _cRef;
  71. delete this;
  72. return 0;
  73. }
  74. STDMETHODIMP CFileStream::Read(void *pv, ULONG cb, ULONG *pcbRead)
  75. {
  76. ULONG cbReadRequestSize = cb;
  77. ULONG cbT, cbRead;
  78. HRESULT hr = S_OK;
  79. // Have we write since our last read?
  80. if (_fLastOpWrite == TRUE)
  81. {
  82. hr = InternalCommit(0, FALSE);
  83. if (FAILED(hr))
  84. {
  85. if (pcbRead)
  86. *pcbRead = 0;
  87. return hr;
  88. }
  89. }
  90. _fLastOpWrite = FALSE;
  91. while (cb > 0)
  92. {
  93. // Assert if we are beyond the bufferlen and Not sizeof(_bBuffer) which
  94. // would imply a seek happened...
  95. ASSERT((_iBuffer <= _cbBufLen) || (_iBuffer == sizeof(_bBuffer)));
  96. if (_iBuffer < _cbBufLen)
  97. {
  98. cbT = _cbBufLen - _iBuffer;
  99. if (cbT > cb)
  100. cbT = cb;
  101. memcpy(pv, &_bBuffer[_iBuffer], cbT);
  102. _iBuffer += cbT;
  103. cb -= cbT;
  104. if (cb == 0)
  105. break;
  106. (BYTE *&)pv += cbT;
  107. }
  108. // Buffer's empty. Handle rest of large reads directly...
  109. //
  110. if (cb > sizeof(_bBuffer))
  111. {
  112. cbT = cb - cb % sizeof(_bBuffer);
  113. if (!ReadFile(_hFile, pv, cbT, &cbRead, NULL))
  114. {
  115. DebugMsg(DM_TRACE, TEXT("Stream read IO error %d"), GetLastError());
  116. hr = ResultFromLastError();
  117. break;
  118. }
  119. cb -= cbRead;
  120. (BYTE *&)pv += cbRead;
  121. if (cbT != cbRead)
  122. break; // end of file
  123. }
  124. if (cb == 0)
  125. break;
  126. // was the last read a partial read? if so we are done
  127. //
  128. if (_cbBufLen > 0 && _cbBufLen < sizeof(_bBuffer))
  129. {
  130. // DebugMsg(DM_TRACE, "Stream is empty");
  131. break;
  132. }
  133. // Read an entire buffer's worth. We may try to read past EOF,
  134. // so we must only check for != 0...
  135. //
  136. if (!ReadFile(_hFile, _bBuffer, sizeof(_bBuffer), &cbRead, NULL))
  137. {
  138. DebugMsg(DM_TRACE, TEXT("Stream read IO error 2 %d"), GetLastError());
  139. hr = ResultFromLastError();
  140. break;
  141. }
  142. if (cbRead == 0)
  143. break;
  144. _iBuffer = 0;
  145. _cbBufLen = cbRead;
  146. }
  147. if (pcbRead)
  148. *pcbRead = cbReadRequestSize - cb;
  149. if (cb != 0)
  150. {
  151. // DebugMsg(DM_TRACE, "CFileStream::Read() incomplete read");
  152. hr = S_FALSE; // still success! but not completely
  153. }
  154. return hr;
  155. }
  156. STDMETHODIMP CFileStream::Write(const void *pv, ULONG cb, ULONG *pcbWritten)
  157. {
  158. ULONG cbRequestedWrite = cb;
  159. ULONG cbT;
  160. HRESULT hr = S_OK;
  161. if (!((_grfMode & STGM_WRITE) || (_grfMode & STGM_READWRITE)))
  162. {
  163. // Can't write to a stream that we didn't open for write access
  164. return STG_E_ACCESSDENIED;
  165. }
  166. // Have we read since our last write?
  167. if (_fLastOpWrite == FALSE && _iBuffer < _cbBufLen)
  168. {
  169. // Need to reset the file pointer so that this write goes to the right spot
  170. SetFilePointer(_hFile, -(int)(_cbBufLen - _iBuffer), NULL, STREAM_SEEK_CUR);
  171. _iBuffer = 0;
  172. _cbBufLen = 0;
  173. }
  174. while (cb > 0)
  175. {
  176. if (_iBuffer < sizeof(_bBuffer))
  177. {
  178. cbT = min((ULONG)(sizeof(_bBuffer) - _iBuffer), cb);
  179. memcpy(&_bBuffer[_iBuffer], pv, cbT);
  180. _iBuffer += cbT;
  181. cb -= cbT;
  182. _fLastOpWrite = TRUE;
  183. if (cb == 0)
  184. break;
  185. (BYTE *&)pv += cbT;
  186. }
  187. hr = InternalCommit(0, FALSE);
  188. if (FAILED(hr))
  189. break;
  190. if (cb > sizeof(_bBuffer))
  191. {
  192. ULONG cbWrite;
  193. cbT = cb - cb % sizeof(_bBuffer);
  194. if (!WriteFile(_hFile, pv, cbT, &cbWrite, NULL))
  195. {
  196. DebugMsg(DM_TRACE, TEXT("Stream write IO error 2, %d"), GetLastError());
  197. hr = ResultFromLastError();
  198. break;
  199. }
  200. cb -= cbWrite;
  201. (BYTE *&)pv += cbWrite;
  202. if (cbWrite != cbT)
  203. break; // media full, we are done
  204. }
  205. }
  206. if (pcbWritten)
  207. *pcbWritten = cbRequestedWrite - cb;
  208. if ((cb != 0) && (hr == S_OK))
  209. {
  210. DebugMsg(DM_TRACE, TEXT("CFileStream::Write() incomplete"));
  211. hr = S_FALSE; // still success! but not completely
  212. }
  213. return hr;
  214. }
  215. STDMETHODIMP CFileStream::Seek(LARGE_INTEGER dlibMove, DWORD dwOrigin, ULARGE_INTEGER *plibNewPosition)
  216. {
  217. COMPILETIME_ASSERT(FILE_BEGIN == STREAM_SEEK_SET);
  218. COMPILETIME_ASSERT(FILE_CURRENT == STREAM_SEEK_CUR);
  219. COMPILETIME_ASSERT(FILE_END == STREAM_SEEK_END);
  220. HRESULT hr = S_OK;
  221. LARGE_INTEGER liOut;
  222. // Have we written since our last read?
  223. if (_fLastOpWrite == TRUE)
  224. {
  225. hr = InternalCommit(0, FALSE);
  226. if (FAILED(hr))
  227. {
  228. return hr;
  229. }
  230. }
  231. if (_iBuffer < _cbBufLen)
  232. {
  233. // Need to reset the file pointer to point to the right place
  234. SetFilePointer(_hFile, -(int)(_cbBufLen - _iBuffer), NULL, STREAM_SEEK_CUR);
  235. }
  236. // Invalidate the buffer because we may move the file pointer
  237. _iBuffer = 0;
  238. _cbBufLen = 0; // Say we have not read it yet.
  239. if (NT5_SetFilePointerEx(_hFile, dlibMove, &liOut, dwOrigin))
  240. {
  241. // Some callers pass NULL for the plibNewPosition parameter
  242. // in the IStream::Seek() call. \shell32\filetbl.c, _IconCacheSave()
  243. // is an example.
  244. if (plibNewPosition)
  245. {
  246. // SetFilePointerEx takes a LARGE_INTEGER, but Seek takes a ULARGE_INTEGER, Why the difference?
  247. plibNewPosition->QuadPart = liOut.QuadPart;
  248. }
  249. }
  250. else
  251. {
  252. hr = ResultFromLastError();
  253. }
  254. return hr;
  255. }
  256. STDMETHODIMP CFileStream::SetSize(ULARGE_INTEGER libNewSize)
  257. {
  258. if (IsOS(OS_WHISTLERORGREATER))
  259. {
  260. HRESULT hr = E_FAIL;
  261. // First save away the pointer's position
  262. LARGE_INTEGER pos, test;
  263. LARGE_INTEGER zero = {0};
  264. if (NT5_SetFilePointerEx(_hFile, zero, &pos, FILE_CURRENT))
  265. {
  266. if (libNewSize.HighPart != 0)
  267. {
  268. hr = STG_E_INVALIDFUNCTION;
  269. }
  270. else
  271. {
  272. // Now set the size
  273. LARGE_INTEGER largeint;
  274. largeint.HighPart = 0;
  275. largeint.LowPart = libNewSize.LowPart;
  276. if (NT5_SetFilePointerEx(_hFile, largeint, &test, FILE_BEGIN) &&
  277. SetEndOfFile(_hFile))
  278. {
  279. // Reset the file pointer position
  280. if (NT5_SetFilePointerEx(_hFile, pos, &test, FILE_BEGIN))
  281. {
  282. hr = S_OK;
  283. }
  284. }
  285. }
  286. }
  287. return hr;
  288. }
  289. else
  290. return E_NOTIMPL;
  291. }
  292. //
  293. // REVIEW: this could use the internal buffer in the stream to avoid
  294. // extra buffer copies.
  295. //
  296. STDMETHODIMP CFileStream::CopyTo(IStream *pstmTo, ULARGE_INTEGER cb,
  297. ULARGE_INTEGER *pcbRead, ULARGE_INTEGER *pcbWritten)
  298. {
  299. HRESULT hr = S_OK;
  300. if (pcbRead)
  301. pcbRead->QuadPart = 0;
  302. if (pcbWritten)
  303. pcbWritten->QuadPart = 0;
  304. //
  305. // I'd like to use a buffer size that takes about a second to copy
  306. // for the sake of cancel opportunities, but IStream doesn't give
  307. // me useful info like the stream speed.
  308. //
  309. const DWORD cbBuffer = 0x00010000;
  310. //
  311. // Alloc the buffer and begin the copy
  312. //
  313. BYTE * pBuf = (BYTE *) LocalAlloc(LPTR, cbBuffer);
  314. if (!pBuf)
  315. return E_OUTOFMEMORY;
  316. while (cb.QuadPart)
  317. {
  318. //
  319. // Cast is OK because we know sizeof(buf) fits in a ULONG
  320. //
  321. ULONG cbRead = (ULONG)min(cb.QuadPart, cbBuffer);
  322. hr = Read(pBuf, cbRead, &cbRead);
  323. if (pcbRead)
  324. pcbRead->QuadPart += cbRead;
  325. if (FAILED(hr) || (cbRead == 0))
  326. break;
  327. cb.QuadPart -= cbRead;
  328. hr = pstmTo->Write(pBuf, cbRead, &cbRead);
  329. if (pcbWritten)
  330. pcbWritten->QuadPart += cbRead;
  331. if (FAILED(hr) || (cbRead == 0))
  332. break;
  333. }
  334. LocalFree(pBuf);
  335. // ISSUE
  336. //
  337. // This was here when I got here, but from the SDK I don't see
  338. // why we'd accept S_FALSE as "complete success"
  339. if (S_FALSE == hr)
  340. hr = S_OK;
  341. return hr;
  342. }
  343. STDMETHODIMP CFileStream::Commit(DWORD grfCommitFlags)
  344. {
  345. return InternalCommit(grfCommitFlags, TRUE);
  346. }
  347. HRESULT CFileStream::InternalCommit(DWORD grfCommitFlags, BOOL fSendChange)
  348. {
  349. if (_fLastOpWrite)
  350. {
  351. if (_iBuffer > 0)
  352. {
  353. DWORD cbWrite;
  354. WriteFile(_hFile, _bBuffer, _iBuffer, &cbWrite, NULL);
  355. if (cbWrite != _iBuffer)
  356. {
  357. DebugMsg(DM_TRACE, TEXT("CFileStream::Commit() incomplete write %d"), GetLastError());
  358. return STG_E_MEDIUMFULL;
  359. }
  360. _iBuffer = 0;
  361. if (fSendChange)
  362. {
  363. SHChangeNotifyWrap(SHCNE_UPDATEITEM, SHCNF_PATHW, _szName, NULL);
  364. }
  365. }
  366. // Since we committed already, we don't need to commit again until the next write, so assume read
  367. _fLastOpWrite = FALSE;
  368. }
  369. return S_OK;
  370. }
  371. STDMETHODIMP CFileStream::Revert()
  372. {
  373. return E_NOTIMPL;
  374. }
  375. STDMETHODIMP CFileStream::LockRegion(ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType)
  376. {
  377. return E_NOTIMPL;
  378. }
  379. STDMETHODIMP CFileStream::UnlockRegion(ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType)
  380. {
  381. return E_NOTIMPL;
  382. }
  383. STDMETHODIMP CFileStream::Stat(STATSTG *pstatstg, DWORD grfStatFlag)
  384. {
  385. if ( !pstatstg )
  386. return STG_E_INVALIDPOINTER;
  387. ZeroMemory(pstatstg, sizeof(STATSTG)); // per COM conventions
  388. HRESULT hr = E_FAIL;
  389. BY_HANDLE_FILE_INFORMATION bhfi;
  390. if ( GetFileInformationByHandle(_hFile, &bhfi) )
  391. {
  392. if (grfStatFlag & STATFLAG_NONAME)
  393. hr = S_OK;
  394. else
  395. hr = SHStrDupW(PathFindFileNameW(_szName), &pstatstg->pwcsName);
  396. if (SUCCEEDED(hr))
  397. {
  398. pstatstg->type = STGTY_STREAM;
  399. pstatstg->cbSize.HighPart = bhfi.nFileSizeHigh;
  400. pstatstg->cbSize.LowPart = bhfi.nFileSizeLow;
  401. pstatstg->mtime = bhfi.ftLastWriteTime;
  402. pstatstg->ctime = bhfi.ftCreationTime;
  403. pstatstg->atime = bhfi.ftLastAccessTime;
  404. pstatstg->grfMode = _grfMode;
  405. pstatstg->reserved = bhfi.dwFileAttributes;
  406. }
  407. }
  408. return hr;
  409. }
  410. STDMETHODIMP CFileStream::Clone(IStream **ppstm)
  411. {
  412. return E_NOTIMPL;
  413. }
  414. // create an IStream from a Win32 file name.
  415. // in:
  416. // pszFile file name to open
  417. // grfMode STGM_ flags
  418. //
  419. // We export a W version of this function
  420. //
  421. STDAPI SHCreateStreamOnFileW(LPCWSTR pszFile, DWORD grfMode, IStream **ppstm)
  422. {
  423. *ppstm = NULL;
  424. // NOTE: these interpretations of the STGM bits are not done properly
  425. // but to maintain back compat we have to allow the invalid combinations
  426. // and not enforce the share bits right. use SHCreateStreamOnFileEx() to get
  427. // proper STGM bit support
  428. if (grfMode &
  429. ~(STGM_READ |
  430. STGM_WRITE |
  431. STGM_SHARE_DENY_NONE |
  432. STGM_SHARE_DENY_READ |
  433. STGM_SHARE_DENY_WRITE |
  434. STGM_SHARE_EXCLUSIVE |
  435. STGM_READWRITE |
  436. STGM_CREATE ))
  437. {
  438. DebugMsg(DM_ERROR, TEXT("CreateSreamOnFile: Invalid STGM_ mode"));
  439. return E_INVALIDARG;
  440. }
  441. HANDLE hFile;
  442. BOOL fCreated = FALSE;
  443. if ( grfMode & STGM_CREATE)
  444. {
  445. // Need to get the file attributes of the file first, so
  446. // that CREATE_ALWAYS will succeed for HIDDEN and SYSTEM
  447. // attributes.
  448. DWORD dwAttrib = GetFileAttributesW(pszFile);
  449. if ((DWORD)-1 == dwAttrib )
  450. {
  451. // something went wrong, so set attributes to something
  452. // normal before we try to create the file...
  453. dwAttrib = 0;
  454. fCreated = TRUE;
  455. }
  456. // STGM_CREATE
  457. hFile = CreateFileW(pszFile, GENERIC_READ | GENERIC_WRITE,
  458. FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, CREATE_ALWAYS,
  459. dwAttrib, NULL);
  460. }
  461. else
  462. {
  463. DWORD dwDesiredAccess, dwShareMode, dwShareBits;
  464. // not STGM_CREATE
  465. if ( grfMode & STGM_WRITE )
  466. {
  467. dwDesiredAccess = GENERIC_WRITE;
  468. }
  469. else
  470. {
  471. dwDesiredAccess = GENERIC_READ;
  472. }
  473. if ( grfMode & STGM_READWRITE )
  474. {
  475. dwDesiredAccess |= (GENERIC_READ | GENERIC_WRITE);
  476. }
  477. dwShareBits = grfMode & (STGM_SHARE_EXCLUSIVE |
  478. STGM_SHARE_DENY_WRITE |
  479. STGM_SHARE_DENY_READ |
  480. STGM_SHARE_DENY_NONE);
  481. switch( dwShareBits )
  482. {
  483. case STGM_SHARE_DENY_WRITE:
  484. dwShareMode = FILE_SHARE_READ;
  485. break;
  486. case STGM_SHARE_DENY_READ:
  487. dwShareMode = FILE_SHARE_WRITE;
  488. break;
  489. case STGM_SHARE_EXCLUSIVE:
  490. dwShareMode = 0;
  491. break;
  492. default:
  493. dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
  494. break;
  495. }
  496. hFile = CreateFileW(pszFile, dwDesiredAccess, dwShareMode, NULL, OPEN_EXISTING, 0, NULL);
  497. }
  498. HRESULT hr;
  499. if (INVALID_HANDLE_VALUE != hFile)
  500. {
  501. if ((grfMode & STGM_CREATE) && fCreated)
  502. {
  503. SHChangeNotifyWrap(SHCNE_CREATE, SHCNF_PATHW, pszFile, NULL);
  504. }
  505. *ppstm = (IStream *)new CFileStream(hFile, grfMode, pszFile);
  506. if (*ppstm)
  507. {
  508. hr = S_OK;
  509. }
  510. else
  511. {
  512. CloseHandle(hFile);
  513. hr = E_OUTOFMEMORY;
  514. }
  515. }
  516. else
  517. {
  518. DebugMsg(DM_TRACE, TEXT("CreateSreamOnFile: CreateFileW() failed %s"), pszFile);
  519. hr = ResultFromLastError();
  520. }
  521. return hr;
  522. }
  523. // We export an A version of this function
  524. STDAPI SHCreateStreamOnFileA(LPCSTR pszFile, DWORD grfMode, IStream **ppstm)
  525. {
  526. WCHAR szFile[MAX_PATH];
  527. SHAnsiToUnicode(pszFile, szFile, ARRAYSIZE(szFile));
  528. return SHCreateStreamOnFileW(szFile, grfMode, ppstm);
  529. }
  530. STDAPI ModeToCreateFileFlags(DWORD grfMode, BOOL fCreate, DWORD *pdwDesiredAccess, DWORD *pdwShareMode, DWORD *pdwCreationDisposition)
  531. {
  532. HRESULT hr = S_OK;
  533. *pdwDesiredAccess = *pdwShareMode = *pdwCreationDisposition = 0;
  534. switch (grfMode & (STGM_READ | STGM_WRITE | STGM_READWRITE))
  535. {
  536. case STGM_READ:
  537. *pdwDesiredAccess |= GENERIC_READ;
  538. break;
  539. case STGM_WRITE:
  540. *pdwDesiredAccess |= GENERIC_WRITE;
  541. break;
  542. case STGM_READWRITE:
  543. *pdwDesiredAccess |= GENERIC_READ | GENERIC_WRITE;
  544. break;
  545. default:
  546. hr = HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER );
  547. }
  548. if (SUCCEEDED(hr))
  549. {
  550. switch (grfMode & (STGM_SHARE_DENY_NONE | STGM_SHARE_DENY_READ | STGM_SHARE_DENY_WRITE | STGM_SHARE_EXCLUSIVE))
  551. {
  552. case STGM_SHARE_DENY_READ:
  553. *pdwShareMode = FILE_SHARE_WRITE | FILE_SHARE_DELETE;
  554. break;
  555. case STGM_SHARE_DENY_WRITE:
  556. *pdwShareMode = FILE_SHARE_READ;
  557. break;
  558. case STGM_SHARE_EXCLUSIVE:
  559. *pdwShareMode = 0;
  560. break;
  561. case STGM_SHARE_DENY_NONE:
  562. default:
  563. // assume STGM_SHARE_DENY_NONE as per documentation
  564. *pdwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;
  565. }
  566. if (SUCCEEDED(hr))
  567. {
  568. switch (grfMode & (STGM_CREATE | STGM_FAILIFTHERE))
  569. {
  570. case STGM_CREATE:
  571. *pdwCreationDisposition = CREATE_ALWAYS;
  572. break;
  573. case STGM_FAILIFTHERE: // this is a 0 flag
  574. *pdwCreationDisposition = fCreate ? CREATE_NEW : OPEN_EXISTING;
  575. break;
  576. default:
  577. hr = HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER );
  578. }
  579. }
  580. }
  581. return hr;
  582. }
  583. // similar to SHCreateStreamOnFile() but
  584. // 1) properly maps STGM bits into CreateFile() params
  585. // 2) takes dwAttributes for the STGM_CREATE case so you can create the file
  586. // with known attributes
  587. // NOTE: returns WIN32 errors from GetLastError through the HRESULT, NOT STG errors.
  588. STDAPI SHCreateStreamOnFileEx(LPCWSTR pszFile, DWORD grfMode, DWORD dwAttributes, BOOL fCreate, IStream * pstmTemplate, IStream **ppstm)
  589. {
  590. *ppstm = NULL;
  591. DWORD dwDesiredAccess, dwShareMode, dwCreationDisposition;
  592. HRESULT hr = ModeToCreateFileFlags(grfMode, fCreate, &dwDesiredAccess, &dwShareMode, &dwCreationDisposition);
  593. if (SUCCEEDED(hr))
  594. {
  595. HANDLE hFile = CreateFileW(pszFile, dwDesiredAccess, dwShareMode, NULL, dwCreationDisposition, dwAttributes, NULL);
  596. if (INVALID_HANDLE_VALUE == hFile)
  597. {
  598. DWORD dwErr = GetLastError();
  599. // for some reason CreateFile is dumb and doesn't perform to spec here (?)
  600. if ((dwErr == ERROR_ACCESS_DENIED) &&
  601. (dwCreationDisposition == CREATE_NEW) &&
  602. PathFileExistsW(pszFile))
  603. {
  604. dwErr = ERROR_ALREADY_EXISTS;
  605. }
  606. hr = HRESULT_FROM_WIN32(dwErr);
  607. }
  608. else
  609. {
  610. if ((CREATE_NEW == dwCreationDisposition) || (CREATE_ALWAYS == dwCreationDisposition))
  611. {
  612. SHChangeNotifyWrap(SHCNE_CREATE, SHCNF_PATHW, pszFile, NULL);
  613. }
  614. *ppstm = (IStream *)new CFileStream(hFile, grfMode, pszFile);
  615. if (*ppstm)
  616. {
  617. hr = S_OK;
  618. }
  619. else
  620. {
  621. CloseHandle(hFile);
  622. hr = E_OUTOFMEMORY;
  623. }
  624. }
  625. }
  626. return hr;
  627. }
  628. // maps win32 errors from SHCreateStreamOnFileEx into STG error codes, for
  629. // use in IStorage/IStream implementations.
  630. HRESULT MapWin32ErrorToSTG(HRESULT hrIn)
  631. {
  632. HRESULT hr = hrIn;
  633. if (FAILED(hr))
  634. {
  635. // munge some of the failure cases back into the STG error values
  636. // that are expected.
  637. switch (hr)
  638. {
  639. case HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND):
  640. case HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND):
  641. hr = STG_E_FILENOTFOUND;
  642. break;
  643. case HRESULT_FROM_WIN32(ERROR_FILE_EXISTS):
  644. case HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS):
  645. hr = STG_E_FILEALREADYEXISTS;
  646. break;
  647. case HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED):
  648. hr = STG_E_ACCESSDENIED;
  649. }
  650. }
  651. return hr;
  652. }