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.

647 lines
17 KiB

  1. //---------------------------------------------------------------------------
  2. //
  3. // Copyright (c) Microsoft Corporation 1991-1993
  4. //
  5. // File: stream.c
  6. //
  7. // This file contains some of the stream support code that is used by
  8. // the shell. It also contains the shells implementation of a memory
  9. // stream that is used by the cabinet to allow views to be serialized.
  10. //
  11. // History:
  12. // 08-20-93 KurtE Added header block and memory stream.
  13. //
  14. //---------------------------------------------------------------------------
  15. #include "priv.h"
  16. #include <new.h>
  17. #include "nullstm.h"
  18. // This code was stolen from shell32. This is the BETTER_STRONGER_FASTER
  19. // version (smaller and half the allocs), added after Win95 shipped.
  20. #include "stream.h"
  21. EXTERN_C HKEY SHRegDuplicateHKey(HKEY hkey);
  22. // The Win95/NT4/IE4 code did not enforce the grfMode. Turn this on to enforce:
  23. //#define ENFORCE_GRFMODE // Note: I haven't tested compat issues with this turned on yet... [mikesh]
  24. STDMETHODIMP CMemStream::QueryInterface(REFIID riid, void **ppvObj)
  25. {
  26. if (IsEqualIID(riid, IID_IStream) || IsEqualIID(riid, IID_IUnknown))
  27. {
  28. *ppvObj=this;
  29. this->cRef++;
  30. return S_OK;
  31. }
  32. *ppvObj = NULL;
  33. return E_NOINTERFACE;
  34. }
  35. STDMETHODIMP_(ULONG) CMemStream::AddRef()
  36. {
  37. this->cRef++;
  38. return this->cRef;
  39. }
  40. BOOL CMemStream::WriteToReg()
  41. {
  42. if (this->cbData)
  43. {
  44. return ERROR_SUCCESS == RegSetValueEx(this->hkey,
  45. this->szValue[0] ? this->szValue : NULL, 0, REG_BINARY,
  46. this->cbData ? this->pBuf : (LPBYTE)"", this->cbData);
  47. }
  48. else
  49. {
  50. DWORD dwRet = SHDeleteValue(this->hkey, NULL, this->szValue);
  51. // If the Stream is being stored in the default key, then
  52. // we should clean up the key. Otherwise, the caller
  53. // passed us the key, and they need it. It would be rude for us
  54. // to delete it. Fixes a Start Menu bug (NT#361333) where we would delete the
  55. // programs key where start menu stores it's stuff on a load, so we
  56. // never persist anything. - lamadio (6.25.99)
  57. if (this->szValue[0] == TEXT('\0'))
  58. {
  59. SHDeleteEmptyKey(this->hkey, NULL);
  60. }
  61. return ERROR_SUCCESS == dwRet;
  62. }
  63. }
  64. STDMETHODIMP_(ULONG) CMemStream::Release()
  65. {
  66. this->cRef--;
  67. if (this->cRef > 0)
  68. return this->cRef;
  69. // If this is backed up by the registry serialize the data
  70. if (this->hkey)
  71. {
  72. // Backed by the registry.
  73. // Write and cleanup.
  74. WriteToReg();
  75. RegCloseKey(this->hkey);
  76. }
  77. // Free the data buffer that is allocated to the stream
  78. if (this->pBuf)
  79. LocalFree(this->pBuf);
  80. LocalFree((HLOCAL)this);
  81. return 0;
  82. }
  83. STDMETHODIMP CMemStream::Read(void *pv, ULONG cb, ULONG *pcbRead)
  84. {
  85. #ifdef ENFORCE_GRFMODE
  86. if ((this->grfMode & (STGM_READ|STGM_WRITE|STGM_READWRITE)) == STGM_WRITE)
  87. {
  88. if (pcbRead != NULL)
  89. *pcbRead = 0;
  90. return STG_E_ACCESSDENIED;
  91. }
  92. #endif
  93. ASSERT(pv);
  94. // I guess a null read is ok.
  95. if (!cb)
  96. {
  97. if (pcbRead != NULL)
  98. *pcbRead = 0;
  99. return S_OK;
  100. }
  101. if (this->iSeek >= this->cbData)
  102. {
  103. if (pcbRead != NULL)
  104. *pcbRead = 0; // nothing read
  105. }
  106. else
  107. {
  108. if ((this->iSeek + cb) > this->cbData)
  109. cb = this->cbData - this->iSeek;
  110. // Now Copy the memory
  111. ASSERT(this->pBuf);
  112. CopyMemory(pv, this->pBuf + this->iSeek, cb);
  113. this->iSeek += (UINT)cb;
  114. if (pcbRead != NULL)
  115. *pcbRead = cb;
  116. }
  117. return S_OK;
  118. }
  119. LPBYTE CMemStream::GrowBuffer(ULONG cbNew)
  120. {
  121. if (this->pBuf == NULL)
  122. {
  123. this->pBuf = (LPBYTE)LocalAlloc(LPTR, cbNew);
  124. }
  125. else
  126. {
  127. LPBYTE pTemp = (LPBYTE)LocalReAlloc(this->pBuf, cbNew, LMEM_MOVEABLE | LMEM_ZEROINIT);
  128. if (pTemp)
  129. {
  130. this->pBuf = pTemp;
  131. }
  132. else
  133. {
  134. TraceMsg(TF_ERROR, "Stream buffer realloc failed");
  135. return NULL;
  136. }
  137. }
  138. if (this->pBuf)
  139. this->cbAlloc = cbNew;
  140. return this->pBuf;
  141. }
  142. #define SIZEINCR 0x1000
  143. STDMETHODIMP CMemStream::Write(void const *pv, ULONG cb, ULONG *pcbWritten)
  144. {
  145. #ifdef ENFORCE_GRFMODE
  146. if ((this->grfMode & (STGM_READ|STGM_WRITE|STGM_READWRITE)) == STGM_READ)
  147. {
  148. if (pcbWritten != NULL)
  149. *pcbWritten = 0;
  150. return STG_E_ACCESSDENIED;
  151. }
  152. #endif
  153. // I guess a null write is ok.
  154. if (!cb)
  155. {
  156. if (pcbWritten != NULL)
  157. *pcbWritten = 0;
  158. return S_OK;
  159. }
  160. // See if the data will fit into our current buffer
  161. if ((this->iSeek + cb) > this->cbAlloc)
  162. {
  163. // enlarge the buffer
  164. // Give it a little slop to avoid a lot of reallocs.
  165. if (GrowBuffer(this->iSeek + (UINT)cb + SIZEINCR) == NULL)
  166. return STG_E_INSUFFICIENTMEMORY;
  167. }
  168. ASSERT(this->pBuf);
  169. // See if we need to fill the area between the data size and
  170. // the seek position
  171. if (this->iSeek > this->cbData)
  172. {
  173. ZeroMemory(this->pBuf + this->cbData, this->iSeek - this->cbData);
  174. }
  175. CopyMemory(this->pBuf + this->iSeek, pv, cb);
  176. this->iSeek += (UINT)cb;
  177. if (this->iSeek > this->cbData)
  178. this->cbData = this->iSeek;
  179. if (pcbWritten != NULL)
  180. *pcbWritten = cb;
  181. return S_OK;
  182. }
  183. STDMETHODIMP CMemStream::Seek(LARGE_INTEGER dlibMove,
  184. DWORD dwOrigin, ULARGE_INTEGER *plibNewPosition)
  185. {
  186. LONG lNewSeek;
  187. // Note: curently not testing for error conditions for number wrap...
  188. switch (dwOrigin)
  189. {
  190. case STREAM_SEEK_SET:
  191. lNewSeek = (LONG)dlibMove.LowPart;
  192. break;
  193. case STREAM_SEEK_CUR:
  194. lNewSeek = (LONG)this->iSeek + (LONG)dlibMove.LowPart;
  195. break;
  196. case STREAM_SEEK_END:
  197. lNewSeek = (LONG)this->cbData + (LONG)dlibMove.LowPart;
  198. break;
  199. default:
  200. return STG_E_INVALIDPARAMETER;
  201. }
  202. if (lNewSeek < 0)
  203. return STG_E_INVALIDFUNCTION;
  204. this->iSeek = (UINT)lNewSeek;
  205. if (plibNewPosition != NULL)
  206. {
  207. plibNewPosition->LowPart = (DWORD)lNewSeek;
  208. plibNewPosition->HighPart = 0;
  209. }
  210. return S_OK;
  211. }
  212. STDMETHODIMP CMemStream::SetSize(ULARGE_INTEGER libNewSize)
  213. {
  214. #ifdef ENFORCE_GRFMODE
  215. if ((this->grfMode & (STGM_READ|STGM_WRITE|STGM_READWRITE)) == STGM_READ)
  216. {
  217. return STG_E_ACCESSDENIED;
  218. }
  219. #endif
  220. UINT cbNew = (UINT)libNewSize.LowPart;
  221. // See if the data will fit into our current buffer
  222. if (cbNew > this->cbData)
  223. {
  224. // See if we have to Enlarge the buffer.
  225. if (cbNew > this->cbAlloc)
  226. {
  227. // enlarge the buffer - Does not check wrap...
  228. // Give it a little slop to avoid a lot of reallocs.
  229. if (GrowBuffer(cbNew) == NULL)
  230. return STG_E_INSUFFICIENTMEMORY;
  231. }
  232. // Now fill some memory
  233. ZeroMemory(this->pBuf + this->cbData, cbNew - this->cbData);
  234. }
  235. // Save away the new size.
  236. this->cbData = cbNew;
  237. return S_OK;
  238. }
  239. STDMETHODIMP CMemStream::CopyTo(IStream *pstmTo,
  240. ULARGE_INTEGER cb, ULARGE_INTEGER *pcbRead, ULARGE_INTEGER *pcbWritten)
  241. {
  242. #ifdef ENFORCE_GRFMODE
  243. if ((this->grfMode & (STGM_READ|STGM_WRITE|STGM_READWRITE)) == STGM_WRITE)
  244. {
  245. if (pcbRead != NULL)
  246. ZeroMemory(pcbRead, sizeof(pcbRead));
  247. if (pcbWritten != NULL)
  248. ZeroMemory(pcbWritten, sizeof(pcbWritten));
  249. return STG_E_ACCESSDENIED;
  250. }
  251. #endif
  252. HRESULT hres = S_OK;
  253. UINT cbRead = this->cbData - this->iSeek;
  254. ULONG cbWritten = 0;
  255. if (cb.HighPart == 0 && cb.LowPart < cbRead)
  256. {
  257. cbRead = cb.LowPart;
  258. }
  259. if (cbRead > 0)
  260. {
  261. hres = pstmTo->Write(this->pBuf + this->iSeek, cbRead, &cbWritten);
  262. this->iSeek += cbRead;
  263. }
  264. if (pcbRead)
  265. {
  266. pcbRead->LowPart = cbRead;
  267. pcbRead->HighPart = 0;
  268. }
  269. if (pcbWritten)
  270. {
  271. pcbWritten->LowPart = cbWritten;
  272. pcbWritten->HighPart = 0;
  273. }
  274. return hres;
  275. }
  276. STDMETHODIMP CMemStream::Commit(DWORD grfCommitFlags)
  277. {
  278. return E_NOTIMPL;
  279. }
  280. STDMETHODIMP CMemStream::Revert()
  281. {
  282. return E_NOTIMPL;
  283. }
  284. STDMETHODIMP CMemStream::LockRegion(ULARGE_INTEGER libOffset,
  285. ULARGE_INTEGER cb, DWORD dwLockType)
  286. {
  287. return E_NOTIMPL;
  288. }
  289. STDMETHODIMP CMemStream::UnlockRegion(ULARGE_INTEGER libOffset,
  290. ULARGE_INTEGER cb, DWORD dwLockType)
  291. {
  292. return E_NOTIMPL;
  293. }
  294. // Trident calls this to determine the size of the structure.
  295. // No reason to not support this one.
  296. STDMETHODIMP CMemStream::Stat(STATSTG *pstatstg, DWORD grfStatFlag)
  297. {
  298. ZeroMemory(pstatstg, sizeof(*pstatstg));
  299. // we have no name
  300. pstatstg->type = STGTY_STREAM;
  301. pstatstg->cbSize.LowPart = this->cbData;
  302. // blow off modify, create, access times (we don't track anyway)
  303. pstatstg->grfMode = this->grfMode;
  304. // we're not transacting, so we have no lock modes
  305. // we're the null clsid already
  306. // we're not based on storage, so we have no state or storage bits
  307. return S_OK;
  308. }
  309. STDMETHODIMP CMemStream::Clone(IStream **ppstm)
  310. {
  311. *ppstm = NULL;
  312. return E_NOTIMPL;
  313. }
  314. CMemStream *
  315. CreateMemStreamEx(
  316. LPBYTE pInit,
  317. UINT cbInit,
  318. LPCTSTR pszValue) OPTIONAL
  319. {
  320. UINT l_cbAlloc = sizeof(CMemStream) + (pszValue ? lstrlen(pszValue) * sizeof(TCHAR) : 0);
  321. CMemStream *localthis = (CMemStream *)LocalAlloc(LPTR, l_cbAlloc);
  322. if (localthis)
  323. {
  324. new (localthis) CMemStream;
  325. localthis->cRef = 1;
  326. // See if there is some initial data we should map in here.
  327. if ((pInit != NULL) && (cbInit > 0))
  328. {
  329. if (localthis->GrowBuffer(cbInit) == NULL)
  330. {
  331. // Could not allocate buffer!
  332. LocalFree((HLOCAL)localthis);
  333. return NULL;
  334. }
  335. localthis->cbData = cbInit;
  336. CopyMemory(localthis->pBuf, pInit, cbInit);
  337. }
  338. if (pszValue)
  339. lstrcpy(localthis->szValue, pszValue);
  340. // We have no other value to set this to
  341. localthis->grfMode = STGM_READWRITE;
  342. return localthis;
  343. }
  344. return NULL;
  345. }
  346. STDAPI_(IStream *)
  347. SHCreateMemStream(
  348. LPBYTE pInit,
  349. UINT cbInit)
  350. {
  351. CMemStream *localthis = CreateMemStreamEx(pInit, cbInit, NULL);
  352. if (localthis)
  353. return localthis;
  354. return NULL;
  355. }
  356. //----------------------------------------------------------------------------
  357. // Open a stream to the reg file given an open key.
  358. // NB pszValue can be NULL.
  359. //
  360. // Win9x exported OpenRegStream which *always* returned a stream, even for read,
  361. // even when there was no data there. IE4 shell32 delegated to shlwapi's SHOpenRegStream
  362. // which needs to support this sub-optimal behavior. See NT5 bug 190878 (shell32 fault).
  363. //
  364. STDAPI_(IStream *)
  365. SHOpenRegStreamW(
  366. HKEY hkey,
  367. LPCWSTR pszSubkey,
  368. LPCWSTR pszValue, OPTIONAL
  369. DWORD grfMode)
  370. {
  371. IStream * pstm = SHOpenRegStream2W(hkey, pszSubkey, pszValue, grfMode);
  372. #ifndef UNIX
  373. if (!pstm)
  374. pstm = SHConstNullStream();
  375. #endif
  376. return pstm;
  377. }
  378. STDAPI_(IStream *)
  379. SHOpenRegStreamA(
  380. HKEY hkey,
  381. LPCSTR pszSubkey,
  382. LPCSTR pszValue, OPTIONAL
  383. DWORD grfMode)
  384. {
  385. IStream * pstm = SHOpenRegStream2A(hkey, pszSubkey, pszValue, grfMode);
  386. #ifndef UNIX
  387. if (!pstm)
  388. pstm = SHConstNullStream();
  389. #endif
  390. return pstm;
  391. }
  392. // We should add STGM_CREATE support to the shlwapi streams. When saving out
  393. // streams, we currently create the stream with STGM_WRITE (but not STGM_CREATE)
  394. // so shlwapi goes to all the wasted trouble of reading the old stream data into
  395. // memory, only to throw it away when we write over it.
  396. //
  397. // STGM_CREATE means "I don't care about the old values because I'm going to
  398. // overwrite them anyway." (It really should be named STGM_TRUNCATEONOPEN.)
  399. //
  400. STDAPI_(IStream *)
  401. SHOpenRegStream2(
  402. HKEY hkey,
  403. LPCTSTR pszSubkey,
  404. LPCTSTR pszValue, OPTIONAL
  405. DWORD grfMode)
  406. {
  407. CMemStream *localthis; // In bed with class...
  408. RIPMSG(IS_VALID_HANDLE(hkey, KEY), "SHOpenRegStream2: Caller passed invalid hkey");
  409. RIPMSG(!pszSubkey || IS_VALID_STRING_PTR(pszSubkey, -1), "SHOpenRegStream2: Caller passed invalid pszSubkey");
  410. RIPMSG(!pszValue || IS_VALID_STRING_PTR(pszValue, -1), "SHOpenRegStream2: Caller passed invalid pszValue");
  411. // Null keys are illegal.
  412. if (!hkey)
  413. {
  414. return NULL;
  415. }
  416. localthis = CreateMemStreamEx(NULL, 0, pszValue);
  417. if (!localthis)
  418. return NULL; // Failed to allocate space
  419. localthis->grfMode = grfMode;
  420. // Get the hkey we're going to deal with
  421. //
  422. // Did the caller pass us a subkey, and does it contain a string?
  423. if (pszSubkey && *pszSubkey)
  424. {
  425. // Yes; The try to bind to that key.
  426. // If this stream is one the user mentioned as wanting to write to
  427. // we need to save away the regkey and value.
  428. if ((grfMode & (STGM_READ | STGM_WRITE | STGM_READWRITE)) != STGM_READ)
  429. {
  430. // Store away the key.
  431. if (RegCreateKey(hkey, pszSubkey, &localthis->hkey) != ERROR_SUCCESS)
  432. {
  433. TraceMsg(TF_ERROR, "SHOpenRegStream: Unable to create key.");
  434. localthis->hkey = NULL; // be paranoid
  435. }
  436. }
  437. else if (RegOpenKey(hkey, pszSubkey, &localthis->hkey) != ERROR_SUCCESS)
  438. {
  439. localthis->hkey = NULL; // be paranoid
  440. }
  441. }
  442. else
  443. {
  444. localthis->hkey = SHRegDuplicateHKey(hkey);
  445. }
  446. // we don't have an hkey, bail
  447. if (NULL == localthis->hkey)
  448. {
  449. localthis->Release();
  450. return NULL;
  451. }
  452. // Now see if we need to initialize the stream.
  453. if ((grfMode & (STGM_READ | STGM_WRITE | STGM_READWRITE)) != STGM_WRITE)
  454. {
  455. DWORD dwType;
  456. DWORD cbData;
  457. if ((RegQueryValueEx(localthis->hkey, pszValue, NULL, &dwType, NULL, &cbData) == ERROR_SUCCESS) && cbData)
  458. {
  459. if (localthis->GrowBuffer(cbData) != NULL)
  460. {
  461. ASSERT(localthis->cbAlloc >= cbData);
  462. // Get the data.
  463. RegQueryValueEx(localthis->hkey, pszValue, NULL, &dwType, localthis->pBuf, &cbData);
  464. ASSERT(localthis->cbAlloc >= cbData);
  465. localthis->cbData = cbData;
  466. }
  467. else
  468. {
  469. TraceMsg(TF_ERROR, "OpenRegStream: Unable to initialize stream to registry.");
  470. localthis->Release();
  471. return NULL;
  472. }
  473. }
  474. }
  475. if (IsOS(OS_WHISTLERORGREATER))
  476. {
  477. // If the stream was opened read-only, then close the key so
  478. // CMemStream::Release won't try to write the "updates" back out to the
  479. // registry.
  480. if ((grfMode & (STGM_READ | STGM_WRITE | STGM_READWRITE)) == STGM_READ)
  481. {
  482. RegCloseKey(localthis->hkey);
  483. localthis->hkey = NULL;
  484. }
  485. }
  486. return localthis;
  487. }
  488. #ifdef UNICODE
  489. STDAPI_(IStream *)
  490. SHOpenRegStream2A(
  491. HKEY hkey,
  492. LPCSTR pszSubkey,
  493. LPCSTR pszValue, OPTIONAL
  494. DWORD grfMode)
  495. {
  496. IStream * pstm = NULL;
  497. RIPMSG(IS_VALID_HANDLE(hkey, KEY), "SHOpenRegStream2A: Caller passed invalid hkey");
  498. RIPMSG(!pszSubkey || IS_VALID_STRING_PTRA(pszSubkey, -1), "SHOpenRegStream2A: Caller passed invalid pszSubkey");
  499. RIPMSG(!pszValue || IS_VALID_STRING_PTRA(pszValue, -1), "SHOpenRegStream2A: Caller passed invalid pszValue");
  500. WCHAR wszSubkey[MAX_PATH];
  501. if (pszSubkey)
  502. {
  503. if (!MultiByteToWideChar(CP_ACP, 0, pszSubkey, -1, wszSubkey, SIZECHARS(wszSubkey)))
  504. return NULL;
  505. pszSubkey = (LPCSTR)wszSubkey;
  506. }
  507. WCHAR wszValue[MAX_PATH];
  508. if (pszValue)
  509. {
  510. if (!MultiByteToWideChar(CP_ACP, 0, pszValue, -1, wszValue, SIZECHARS(wszValue)))
  511. return NULL;
  512. pszValue = (LPCSTR)wszValue;
  513. }
  514. pstm = SHOpenRegStream2W(hkey, (LPCWSTR)pszSubkey, (LPCWSTR)pszValue, grfMode);
  515. return pstm;
  516. }
  517. #else
  518. STDAPI_(IStream *)
  519. SHOpenRegStream2W(
  520. HKEY hkey,
  521. LPCWSTR pszSubkey,
  522. LPCWSTR pszValue, OPTIONAL
  523. DWORD grfMode)
  524. {
  525. IStream * pstm = NULL;
  526. RIPMSG(IS_VALID_HANDLE(hkey, KEY), "SHOpenRegStream2W: Caller passed invalid hkey");
  527. RIPMSG(!pszSubkey || IS_VALID_STRING_PTRW(pszSubkey, -1), "SHOpenRegStream2W: Caller passed invalid pszSubkey");
  528. RIPMSG(!pszValue || IS_VALID_STRING_PTRW(pszValue, -1), "SHOpenRegStream2W: Caller passed invalid pszValue");
  529. CHAR szSubkey[MAX_PATH];
  530. if (pszSubkey)
  531. {
  532. if (!WideCharToMultiByte(CP_ACP, 0, pszSubkey, -1, szSubkey, SIZECHARS(szSubkey), NULL, NULL))
  533. return NULL;
  534. pszSubkey = (LPCWSTR)szSubkey;
  535. }
  536. CHAR szValue[MAX_PATH];
  537. if (pszValue)
  538. {
  539. if (!WideCharToMultiByte(CP_ACP, 0, pszValue, -1, szValue, SIZECHARS(szValue), NULL, NULL))
  540. return NULL;
  541. pszValue = (LPCWSTR)szValue;
  542. }
  543. pstm = SHOpenRegStream2A(hkey, (LPCSTR)pszSubkey, (LPCSTR)pszValue, grfMode);
  544. return pstm;
  545. }
  546. #endif // UNICODE