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.

779 lines
22 KiB

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