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.

764 lines
22 KiB

  1. // --------------------------------------------------------------------------------
  2. // InetStm.cpp
  3. // Copyright (c)1993-1995 Microsoft Corporation, All Rights Reserved
  4. // Steven J. Bailey
  5. // --------------------------------------------------------------------------------
  6. #include "pch.hxx"
  7. #include "inetstm.h"
  8. #include "stmlock.h"
  9. #include "shlwapi.h"
  10. #include "demand.h"
  11. // --------------------------------------------------------------------------------
  12. // INETSTMTRACING
  13. // --------------------------------------------------------------------------------
  14. //#define INETSTMTRACING 1
  15. #ifdef INETSTMTRACING
  16. #define INETSTMTRACE DebugTrace
  17. #else
  18. #define INETSTMTRACE
  19. #endif
  20. // --------------------------------------------------------------------------------
  21. // CInternetStream::CInternetStream
  22. // --------------------------------------------------------------------------------
  23. CInternetStream::CInternetStream(void)
  24. {
  25. m_cRef = 1;
  26. m_pStmLock = NULL;
  27. m_fFullyAvailable = TRUE;
  28. ULISet32(m_uliOffset, 0);
  29. ZeroMemory(&m_rLine, sizeof(INETSTREAMLINE));
  30. m_rLine.pb = m_rLine.rgbScratch;
  31. m_rLine.cbAlloc = sizeof(m_rLine.rgbScratch);
  32. }
  33. // --------------------------------------------------------------------------------
  34. // CInternetStream::~CInternetStream
  35. // --------------------------------------------------------------------------------
  36. CInternetStream::~CInternetStream(void)
  37. {
  38. // Do i need to free the line
  39. if (m_rLine.pb && m_rLine.pb != m_rLine.rgbScratch)
  40. g_pMalloc->Free(m_rLine.pb);
  41. // Reset the position of the stream to the real current offset
  42. if (m_pStmLock)
  43. {
  44. // Preserve the position of the stream
  45. SideAssert(SUCCEEDED(m_pStmLock->HrSetPosition(m_uliOffset)));
  46. // Release the LockBytes
  47. m_pStmLock->Release();
  48. }
  49. }
  50. // --------------------------------------------------------------------------------
  51. // CInternetStream::AddRef
  52. // --------------------------------------------------------------------------------
  53. STDMETHODIMP_(ULONG) CInternetStream::AddRef(void)
  54. {
  55. return ++m_cRef;
  56. }
  57. // --------------------------------------------------------------------------------
  58. // CInternetStream::Release
  59. // --------------------------------------------------------------------------------
  60. STDMETHODIMP_(ULONG) CInternetStream::Release(void)
  61. {
  62. if (0 != --m_cRef)
  63. return m_cRef;
  64. delete this;
  65. return 0;
  66. }
  67. // --------------------------------------------------------------------------------
  68. // CInternetStream::HrInitNew
  69. // --------------------------------------------------------------------------------
  70. HRESULT CInternetStream::HrInitNew(IStream *pStream)
  71. {
  72. // Locals
  73. HRESULT hr=S_OK;
  74. CStreamLockBytes *pStmLock=NULL;
  75. DWORD cbOffset;
  76. // Invalid Arg
  77. Assert(pStream);
  78. // Wrap pStream in a pStmLock
  79. CHECKALLOC(pStmLock = new CStreamLockBytes(pStream));
  80. // Get Current Stream Position
  81. CHECKHR(hr = HrGetStreamPos(pStream, &cbOffset));
  82. // Create text stream object
  83. InitNew(cbOffset, pStmLock);
  84. exit:
  85. // Cleanup
  86. SafeRelease(pStmLock);
  87. // Done
  88. return hr;
  89. }
  90. // --------------------------------------------------------------------------------
  91. // CInternetStream::InitNew
  92. // --------------------------------------------------------------------------------
  93. void CInternetStream::InitNew(DWORD dwOffset, CStreamLockBytes *pStmLock)
  94. {
  95. // Invalid Arg
  96. Assert(pStmLock);
  97. // Release Current
  98. SafeRelease(m_pStmLock);
  99. // Zero Current Buffer
  100. ZeroMemory(&m_rBuffer, sizeof(INETSTREAMBUFFER));
  101. // Reset m_rLine
  102. m_rLine.cb = 0;
  103. // Assume new StreamLockBytes
  104. m_pStmLock = pStmLock;
  105. m_pStmLock->AddRef();
  106. // Safe the Offset
  107. m_uliOffset.QuadPart = dwOffset;
  108. }
  109. // --------------------------------------------------------------------------------
  110. // CInternetStream::GetLockBytes
  111. // --------------------------------------------------------------------------------
  112. void CInternetStream::GetLockBytes(CStreamLockBytes **ppStmLock)
  113. {
  114. // Invalid Arg
  115. Assert(ppStmLock && m_pStmLock);
  116. // Return It
  117. (*ppStmLock) = m_pStmLock;
  118. (*ppStmLock)->AddRef();
  119. }
  120. // --------------------------------------------------------------------------------
  121. // CInternetStream::HrGetSize
  122. // --------------------------------------------------------------------------------
  123. HRESULT CInternetStream::HrGetSize(DWORD *pcbSize)
  124. {
  125. // Locals
  126. HRESULT hr=S_OK;
  127. STATSTG rStat;
  128. // Invalid Arg
  129. Assert(pcbSize && m_pStmLock);
  130. // Get the Stat
  131. CHECKHR(hr = m_pStmLock->Stat(&rStat, STATFLAG_NONAME));
  132. // Return Size
  133. *pcbSize = (DWORD)rStat.cbSize.QuadPart;
  134. exit:
  135. // Done
  136. return hr;
  137. }
  138. // --------------------------------------------------------------------------------
  139. // CInternetStream::Seek
  140. // --------------------------------------------------------------------------------
  141. void CInternetStream::Seek(DWORD dwOffset)
  142. {
  143. // Locals
  144. HRESULT hr=S_OK;
  145. BOOL fResetCache=FALSE;
  146. DWORD dw;
  147. // State Check
  148. Assert((m_rBuffer.cb == 0) || (m_uliOffset.QuadPart == m_rBuffer.uliOffset.QuadPart + m_rBuffer.i));
  149. // Already at the requested position
  150. if (dwOffset == m_uliOffset.QuadPart)
  151. goto exit;
  152. // Less than current position
  153. if (dwOffset < m_uliOffset.QuadPart)
  154. {
  155. // Compute Offset from current location
  156. dw = (DWORD)m_uliOffset.QuadPart - dwOffset;
  157. // Less than beginning
  158. if (dw > m_rBuffer.i)
  159. fResetCache = TRUE;
  160. else
  161. {
  162. Assert(dw <= m_rBuffer.i);
  163. m_rBuffer.i -= dw;
  164. }
  165. }
  166. // Else dwOffset > m_uliOffset.QuadPart
  167. else
  168. {
  169. // Compute Offset from current location
  170. dw = dwOffset - (DWORD)m_uliOffset.QuadPart;
  171. // Less than beginning
  172. if (m_rBuffer.i + dw > m_rBuffer.cb)
  173. fResetCache = TRUE;
  174. else
  175. {
  176. m_rBuffer.i += dw;
  177. Assert(m_rBuffer.i <= m_rBuffer.cb);
  178. }
  179. }
  180. // Reset the cache
  181. if (fResetCache)
  182. {
  183. // Empty current line and buffer
  184. *m_rLine.pb = *m_rBuffer.rgb = '\0';
  185. // No buffer
  186. m_rBuffer.uliOffset.QuadPart = m_rLine.cb = m_rBuffer.i = m_rBuffer.cb = 0;
  187. }
  188. // Save this position
  189. m_uliOffset.QuadPart = dwOffset;
  190. exit:
  191. // Done
  192. return;
  193. }
  194. // --------------------------------------------------------------------------------
  195. // CInternetStream::HrReadToEnd
  196. // --------------------------------------------------------------------------------
  197. HRESULT CInternetStream::HrReadToEnd(void)
  198. {
  199. // Locals
  200. HRESULT hr=S_OK;
  201. // While
  202. while(1)
  203. {
  204. // Validate
  205. Assert(m_rBuffer.i <= m_rBuffer.cb);
  206. // Increment Offset to end of current buffer
  207. m_uliOffset.QuadPart += (m_rBuffer.cb - m_rBuffer.i);
  208. // Set m_rBuffer.i to end of current buffer
  209. m_rBuffer.i = m_rBuffer.cb;
  210. // Get next buffer
  211. CHECKHR(hr = _HrGetNextBuffer());
  212. // No more data
  213. if (0 == m_rBuffer.cb)
  214. break;
  215. }
  216. exit:
  217. // Done
  218. return hr;
  219. }
  220. // --------------------------------------------------------------------------------
  221. // CInternetStream::_HrGetNextBuffer
  222. // --------------------------------------------------------------------------------
  223. HRESULT CInternetStream::_HrGetNextBuffer(void)
  224. {
  225. // Locals
  226. HRESULT hr=S_OK;
  227. ULONG cbRead;
  228. // Validate
  229. Assert(m_rBuffer.i <= m_rBuffer.cb);
  230. // Do we need to read a new buffer
  231. if (m_rBuffer.i == m_rBuffer.cb)
  232. {
  233. // Read a block from the stream, this could return E_PENDING
  234. CHECKHR(hr = m_pStmLock->ReadAt(m_uliOffset, m_rBuffer.rgb, sizeof(m_rBuffer.rgb), &cbRead));
  235. // Raid 43408: Work around Urlmon IStream::Read returning S_FALSE when it should return E_PENDING
  236. #ifdef DEBUG
  237. if (FALSE == m_fFullyAvailable && 0 == cbRead && S_FALSE == hr)
  238. {
  239. //AssertSz(FALSE, "Raid-43408 - Danpo Zhang is working on this bug, I hope.");
  240. //hr = E_PENDING;
  241. //goto exit;
  242. }
  243. #endif
  244. // Save cbRead
  245. m_rBuffer.cb = cbRead;
  246. // Save the offset of the start of this buffer
  247. m_rBuffer.uliOffset.QuadPart = m_uliOffset.QuadPart;
  248. // Reset buffer index
  249. m_rBuffer.i = 0;
  250. }
  251. else
  252. Assert(m_uliOffset.QuadPart == m_rBuffer.uliOffset.QuadPart + m_rBuffer.i);
  253. exit:
  254. // Done
  255. return hr;
  256. }
  257. // --------------------------------------------------------------------------------
  258. // CInternetStream::HrReadLine
  259. // --------------------------------------------------------------------------------
  260. HRESULT CInternetStream::HrReadLine(LPPROPSTRINGA pLine)
  261. {
  262. // Locals
  263. HRESULT hr=S_OK;
  264. UCHAR ch,
  265. chEndOfLine;
  266. ULONG cbRead,
  267. iStart;
  268. BOOL fEndOfLine=FALSE;
  269. // Init
  270. pLine->pszVal = NULL;
  271. pLine->cchVal = 0;
  272. // Reset the line
  273. if (m_rLine.fReset)
  274. {
  275. m_rLine.cb = 0;
  276. m_rLine.fReset = 0;
  277. }
  278. // Do the loop
  279. while(1)
  280. {
  281. // Get next buffer
  282. CHECKHR(hr = _HrGetNextBuffer());
  283. // Nothing Read ?
  284. if (m_rBuffer.cb == 0)
  285. break;
  286. // Seek to first '\n'
  287. iStart = m_rBuffer.i;
  288. Assert(chLF<32);
  289. Assert(chCR<32);
  290. Assert(0<32);
  291. // For large messages, the while-loop below ends up being a big
  292. // percentage of the execution time for IMimeMessage::Load. So,
  293. // we have carefully crafted our C++ code so that we get the
  294. // optimum code generation for this loop (at least, on Intel, with
  295. // VC 11.00.7071...)
  296. {
  297. register UCHAR *pCurr = m_rBuffer.rgb + m_rBuffer.i;
  298. register UCHAR *pEnd = m_rBuffer.rgb + m_rBuffer.cb;
  299. // We need to initialize this variable for two reasons: First,
  300. // if we don't initialize it, then for some reason VC decides that
  301. // it doesn't want to enregister it. And more importantly, if
  302. // we don't enter the while-loop at all, we need this variable
  303. // to be set this way...
  304. register UCHAR chPrev = m_rBuffer.chPrev;
  305. ch = m_rBuffer.chPrev;
  306. // While we have data
  307. while (pCurr < pEnd)
  308. {
  309. // Remember the previous character.
  310. chPrev = ch;
  311. // Get Character, and Increment
  312. ch = *pCurr;
  313. pCurr++;
  314. // The most common case - it's just a regular
  315. // character, and we haven't seen a carriage-return.
  316. // So jump back to the top of the loop and keep lookin'...
  317. if ((chCR != chPrev) && (ch >= 32))
  318. {
  319. continue;
  320. }
  321. // The next most common case - we are at end-of-line because
  322. // of a line-feed.
  323. if (chLF == ch)
  324. {
  325. chPrev = ch;
  326. chEndOfLine = ch;
  327. fEndOfLine = TRUE;
  328. break;
  329. }
  330. // This case really only happens when we are getting malformed
  331. // e-mail - there are embedded carriage-returns, which are *not*
  332. // followed by line-feeds. When we see those lonely CR's,
  333. // we make it look like we got a normal CR LF sequence.
  334. if (chCR == chPrev)
  335. {
  336. chPrev = chLF;
  337. pCurr--;
  338. chEndOfLine = chCR;
  339. fEndOfLine = TRUE;
  340. break;
  341. }
  342. // Fairly rare case - these are malformed messages because they
  343. // have NULL's embedded in them. We silently convert the
  344. // NULL's to dots.
  345. if ('\0' == ch)
  346. {
  347. ch = '.';
  348. *(pCurr-1) = ch;
  349. }
  350. }
  351. m_rBuffer.i = (ULONG) (pCurr - m_rBuffer.rgb);
  352. m_rBuffer.chPrev = chPrev;
  353. }
  354. // Number of bytes Read
  355. cbRead = (m_rBuffer.i - iStart);
  356. // Increment Position
  357. m_uliOffset.QuadPart += cbRead;
  358. // Do we need to realloc the line buffer ?
  359. if (m_rLine.cb + cbRead + 2 > m_rLine.cbAlloc)
  360. {
  361. // Fixup pszLine
  362. if (m_rLine.pb == m_rLine.rgbScratch)
  363. {
  364. // Null It
  365. m_rLine.pb = NULL;
  366. // Allocate it to m_rLine.cb
  367. CHECKHR(hr = HrAlloc((LPVOID *)&m_rLine.pb, m_rLine.cb + 1));
  368. // Copy static buffer
  369. CopyMemory(m_rLine.pb, m_rLine.rgbScratch, m_rLine.cb);
  370. }
  371. // Always Add a little extra to reduce the number of allocs
  372. m_rLine.cbAlloc = m_rLine.cb + cbRead + 256;
  373. // Realloc or alloc new
  374. CHECKHR(hr = HrRealloc((LPVOID *)&m_rLine.pb, m_rLine.cbAlloc));
  375. }
  376. // Copy the data
  377. CopyMemory(m_rLine.pb + m_rLine.cb, m_rBuffer.rgb + iStart, cbRead);
  378. // Update Counters and indexes
  379. m_rLine.cb += cbRead;
  380. // If End of line and last character was a '\r', append a '\n'
  381. if (TRUE == fEndOfLine)
  382. {
  383. // Better have something in the line
  384. Assert(m_rLine.cb);
  385. // If line ended with a '\r'
  386. if (chCR == chEndOfLine)
  387. {
  388. // Better have room for one more char
  389. Assert(m_rLine.cb + 1 < m_rLine.cbAlloc);
  390. // Append a '\n'
  391. m_rLine.pb[m_rLine.cb] = chLF;
  392. // Increment Length
  393. m_rLine.cb++;
  394. }
  395. // Otherwise...
  396. else
  397. {
  398. // Line better have ended with a \n
  399. Assert(chLF == chEndOfLine && chLF == m_rLine.pb[m_rLine.cb - 1]);
  400. // If Previous Character was not a \r
  401. if (m_rLine.cb < 2 || chCR != m_rLine.pb[m_rLine.cb - 2])
  402. {
  403. // Convert last char from \n to a \r
  404. m_rLine.pb[m_rLine.cb - 1] = chCR;
  405. // Better have room for one more char
  406. Assert(m_rLine.cb + 1 < m_rLine.cbAlloc);
  407. // Append a '\n'
  408. m_rLine.pb[m_rLine.cb] = chLF;
  409. // Increment Length
  410. m_rLine.cb++;
  411. }
  412. }
  413. // Done
  414. break;
  415. }
  416. }
  417. // A little check
  418. Assert(fEndOfLine ? m_rLine.cb >= 2 && chLF == m_rLine.pb[m_rLine.cb-1] && chCR == m_rLine.pb[m_rLine.cb-2] : TRUE);
  419. // Null terminator
  420. m_rLine.pb[m_rLine.cb] = '\0';
  421. // Set return values
  422. pLine->pszVal = (LPSTR)m_rLine.pb;
  423. pLine->cchVal = m_rLine.cb;
  424. // Tracking
  425. INETSTMTRACE("CInternetStream: %s", (LPSTR)m_rLine.pb);
  426. // No Line
  427. m_rLine.fReset = TRUE;
  428. exit:
  429. // Done
  430. return hr;
  431. }
  432. // --------------------------------------------------------------------------------
  433. // CInternetStream::HrReadHeaderLine
  434. // --------------------------------------------------------------------------------
  435. HRESULT CInternetStream::HrReadHeaderLine(LPPROPSTRINGA pLine, LONG *piColonPos)
  436. {
  437. // Locals
  438. HRESULT hr=S_OK;
  439. CHAR ch;
  440. ULONG cbRead=0,
  441. iStart,
  442. i;
  443. BOOL fEndOfLine=FALSE;
  444. DWORD cTrailingSpace=0;
  445. // Init
  446. *piColonPos = -1;
  447. pLine->pszVal = NULL;
  448. pLine->cchVal = 0;
  449. // Reset the line
  450. if (m_rLine.fReset)
  451. {
  452. m_rLine.cb = 0;
  453. m_rLine.fReset = 0;
  454. }
  455. // Do the loop
  456. while(1)
  457. {
  458. // Get next buffer
  459. CHECKHR(hr = _HrGetNextBuffer());
  460. // Nothing Read ?
  461. if (m_rBuffer.cb == 0)
  462. break;
  463. // Reset fSeenN
  464. fEndOfLine = FALSE;
  465. // Initialize
  466. iStart = m_rBuffer.i;
  467. // Seek to first '\n'
  468. while (m_rBuffer.i < m_rBuffer.cb)
  469. {
  470. // Get Character
  471. ch = *(m_rBuffer.rgb + m_rBuffer.i);
  472. // Convert Nulls to '.'
  473. if ('\0' == ch)
  474. {
  475. ch = '.';
  476. *(m_rBuffer.rgb + m_rBuffer.i) = ch;
  477. }
  478. // Goto next character
  479. m_rBuffer.i++;
  480. // New Line
  481. if (chLF == ch)
  482. {
  483. m_rBuffer.chPrev = ch;
  484. fEndOfLine = TRUE;
  485. break;
  486. }
  487. // Otherwise, if previous character was a '\r', then end of line
  488. else if (chCR == m_rBuffer.chPrev)
  489. {
  490. AssertSz(m_rBuffer.i > 0, "This is an un-handled boundary condition");
  491. if (m_rBuffer.i > 0)
  492. m_rBuffer.i--;
  493. m_rBuffer.chPrev = '\0';
  494. fEndOfLine = TRUE;
  495. break;
  496. }
  497. // Is Space
  498. if (' ' == ch || '\t' == ch)
  499. cTrailingSpace++;
  500. else
  501. cTrailingSpace = 0;
  502. // Save Previous Character
  503. m_rBuffer.chPrev = ch;
  504. }
  505. // Number of bytes Read
  506. cbRead = (m_rBuffer.i - iStart);
  507. // Increment Position
  508. m_uliOffset.QuadPart += cbRead;
  509. // Adjust cbRead to remove CRLF
  510. if (cbRead && chLF == m_rBuffer.rgb[iStart + cbRead - 1])
  511. cbRead--;
  512. if (cbRead && chCR == m_rBuffer.rgb[iStart + cbRead - 1])
  513. cbRead--;
  514. // Do we need to realloc the line buffer ?
  515. if (m_rLine.cb + cbRead + 3 > m_rLine.cbAlloc)
  516. {
  517. // Fixup pszLine
  518. if (m_rLine.pb == m_rLine.rgbScratch)
  519. {
  520. // Null It
  521. m_rLine.pb = NULL;
  522. // Allocate it to m_rLine.cb
  523. CHECKHR(hr = HrAlloc((LPVOID *)&m_rLine.pb, m_rLine.cb + 3));
  524. // Copy static buffer
  525. CopyMemory(m_rLine.pb, m_rLine.rgbScratch, m_rLine.cb);
  526. }
  527. // Always Add a little extra to reduce the number of allocs
  528. m_rLine.cbAlloc = m_rLine.cb + cbRead + 256;
  529. // Realloc or alloc new
  530. CHECKHR(hr = HrRealloc((LPVOID *)&m_rLine.pb, m_rLine.cbAlloc));
  531. }
  532. // Copy the data
  533. CopyMemory(m_rLine.pb + m_rLine.cb, m_rBuffer.rgb + iStart, cbRead);
  534. // Increment line byte count
  535. m_rLine.cb += cbRead;
  536. // If fSeenN, then check for continuation line (i.e. next character is ' ' or '\t'
  537. if (fEndOfLine)
  538. {
  539. // Get next buffer
  540. CHECKHR(hr = _HrGetNextBuffer());
  541. // Compare for continuation
  542. ch = m_rBuffer.rgb[m_rBuffer.i];
  543. // If line starts with a TAB or a space, this is a continuation line, keep reading
  544. if ((ch != ' ' && ch != '\t') || (0 == cbRead && 0 == m_rLine.cb))
  545. {
  546. // Done
  547. break;
  548. }
  549. // Otherwise, strip continuation...
  550. else
  551. {
  552. // Per RFC822, we should not step over a space
  553. if (ch == '\t')
  554. {
  555. m_rBuffer.i++;
  556. m_uliOffset.QuadPart++;
  557. }
  558. // No characters since last whitespace
  559. if (0 == cTrailingSpace)
  560. {
  561. // Locals
  562. DWORD cFrontSpace=0;
  563. // Look ahead in the buffer a little
  564. for (DWORD iLookAhead = m_rBuffer.i; iLookAhead < m_rBuffer.cb; iLookAhead++)
  565. {
  566. // Get Char
  567. ch = m_rBuffer.rgb[iLookAhead];
  568. // Break on non space
  569. if (' ' != ch && '\t' != ch)
  570. break;
  571. // Count Front Space
  572. cFrontSpace++;
  573. }
  574. // No Front Space ?
  575. if (0 == cFrontSpace)
  576. {
  577. // Lets do this only fro Received: and for Date: since dates that get split don't get parsed correctly?
  578. if ((m_rLine.cb >= 4 && 0 == StrCmpNI("Date", (LPCSTR)m_rLine.pb, 4)) || (m_rLine.cb >= 8 && 0 == StrCmpNI("Received", (LPCSTR)m_rLine.pb, 8)))
  579. {
  580. // Put a space in
  581. *(m_rLine.pb + m_rLine.cb) = ' ';
  582. // Increment line byte count
  583. m_rLine.cb += 1;
  584. }
  585. }
  586. }
  587. // Get next buffer
  588. CHECKHR(hr = _HrGetNextBuffer());
  589. // If Next character is a \r or \n, then stop, NETSCAPE bug
  590. ch = m_rBuffer.rgb[m_rBuffer.i];
  591. if (chCR == ch || chLF == ch)
  592. break;
  593. }
  594. // Reset
  595. cTrailingSpace = 0;
  596. }
  597. }
  598. // A little check
  599. #ifndef _WIN64
  600. Assert(chLF != m_rLine.pb[m_rLine.cb-1] && chCR != m_rLine.pb[m_rLine.cb-1]);
  601. #endif
  602. // Null terminator
  603. *(m_rLine.pb + m_rLine.cb) = '\0';
  604. // Lets locate the colon
  605. for (i=0; i<m_rLine.cb; i++)
  606. {
  607. // Colon ?
  608. if (':' == m_rLine.pb[i])
  609. {
  610. *piColonPos = i;
  611. break;
  612. }
  613. }
  614. // Set return values
  615. pLine->pszVal = (LPSTR)m_rLine.pb;
  616. pLine->cchVal = m_rLine.cb;
  617. // Tracking
  618. INETSTMTRACE("CInternetStream: %s\n", (LPSTR)m_rLine.pb);
  619. // Reset the Line
  620. m_rLine.fReset = TRUE;
  621. exit:
  622. // Done
  623. return hr;
  624. }