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.

1309 lines
26 KiB

  1. /*++
  2. Copyright (c) 1994-2002 Microsoft Corporation
  3. Module Name :
  4. msg.cpp
  5. Abstract:
  6. Message Functions
  7. Author:
  8. Ronald Meijer (ronaldm)
  9. Sergei Antonov (sergeia)
  10. Project:
  11. Internet Services Manager
  12. Revision History:
  13. --*/
  14. #include "stdafx.h"
  15. #include <lmerr.h>
  16. #include <lmcons.h>
  17. #include <winsock2.h>
  18. #include "common.h"
  19. #include <pudebug.h>
  20. //
  21. // Needed for appsrv facility code.
  22. //
  23. //#include "webcluserr.h"
  24. #ifdef _DEBUG
  25. #undef THIS_FILE
  26. static char BASED_CODE THIS_FILE[] = __FILE__;
  27. #endif
  28. #define new DEBUG_NEW
  29. extern HINSTANCE hDLLInstance;
  30. #ifdef _MT
  31. //
  32. // Thread protected stuff
  33. //
  34. #define RaiseThreadProtection() \
  35. do {\
  36. EnterCriticalSection(&_csSect);\
  37. } while(0)
  38. #define LowerThreadProtection() \
  39. do {\
  40. LeaveCriticalSection(&_csSect);\
  41. } while (0)
  42. static CRITICAL_SECTION _csSect;
  43. #else
  44. #pragma message("Module is not thread-safe.")
  45. #define RaiseThreadProtection()
  46. #define LowerThreadProtection()
  47. #endif // _MT
  48. BOOL
  49. InitErrorFunctionality()
  50. /*++
  51. Routine Description:
  52. Initialize CError class, and allocate static objects
  53. Arguments:
  54. None:
  55. Return Value:
  56. TRUE for success, FALSE for failure
  57. --*/
  58. {
  59. #ifdef _MT
  60. INITIALIZE_CRITICAL_SECTION(&_csSect);
  61. #endif // _MT
  62. BOOL fOK = CError::AllocateStatics();
  63. return fOK;
  64. }
  65. void
  66. TerminateErrorFunctionality()
  67. /*++
  68. Routine Description:
  69. De-initialize CError class, freeing up static objects
  70. Arguments:
  71. None
  72. Return Value:
  73. None
  74. --*/
  75. {
  76. CError::DeAllocateStatics();
  77. #ifdef _MT
  78. DeleteCriticalSection(&_csSect);
  79. #endif // _MT
  80. }
  81. //
  82. // Static Initialization:
  83. //
  84. // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
  85. const TCHAR g_cszNull[] = _T("(Null)");
  86. const TCHAR CError::s_chEscape = _T('%'); // Error text escape
  87. const TCHAR CError::s_chEscText = _T('h'); // Escape code for text
  88. const TCHAR CError::s_chEscNumber = _T('H'); // Escape code for error code
  89. LPCTSTR CError::s_cszLMDLL = _T("netmsg.dll"); // LM Error File
  90. LPCTSTR CError::s_cszWSDLL = _T("iisui.dll"); // Winsock error file
  91. LPCTSTR CError::s_cszFacility[] =
  92. {
  93. /* FACILITY_NULL */ NULL,
  94. /* FACILITY_RPC */ NULL,
  95. /* FACILITY_DISPATCH */ NULL,
  96. /* FACILITY_STORAGE */ NULL,
  97. /* FACILITY_ITF */ NULL,
  98. /* FACILITY_DS */ NULL,
  99. /* 6 */ NULL,
  100. /* FACILITY_WIN32 */ NULL,
  101. /* FACILITY_WINDOWS */ NULL,
  102. /* FACILITY_SSPI */ NULL,
  103. /* FACILITY_CONTROL */ NULL,
  104. /* FACILITY_CERT */ NULL,
  105. /* FACILITY_INTERNET */ _T("metadata.dll"),
  106. /* FACILITY_MEDIASERVER */ NULL,
  107. /* FACILITY_MSMQ */ NULL,
  108. /* FACILITY_SETUPAPI */ NULL,
  109. /* FACILITY_SCARD */ NULL,
  110. /* 17 (MTX) */ _T("iisui.dll"),
  111. };
  112. HRESULT CError::s_cdwMinLMErr = NERR_BASE;
  113. HRESULT CError::s_cdwMaxLMErr = MAX_NERR;
  114. HRESULT CError::s_cdwMinWSErr = WSABASEERR;
  115. HRESULT CError::s_cdwMaxWSErr = WSABASEERR + 2000;
  116. DWORD CError::s_cdwFacilities = (sizeof(CError::s_cszFacility)\
  117. / sizeof(CError::s_cszFacility[0]));
  118. //
  119. // Allocated objects (static MFC objects in a DLL are a no-no)
  120. //
  121. CString * CError::s_pstrDefError;
  122. CString * CError::s_pstrDefSuccs;
  123. CMapDWORDtoCString * CError::s_pmapFacilities;
  124. BOOL CError::s_fAllocated = FALSE;
  125. /* protected */
  126. /* static */
  127. BOOL
  128. CError::AllocateStatics()
  129. /*++
  130. Routine Description:
  131. Allocate static objects
  132. Arguments:
  133. None
  134. Return Value:
  135. TRUE for successfull allocation, FALSE otherwise
  136. --*/
  137. {
  138. RaiseThreadProtection();
  139. if (!AreStaticsAllocated())
  140. {
  141. try
  142. {
  143. CError::s_pstrDefError = new CString;
  144. CError::s_pstrDefSuccs = new CString(_T("0x%08lx"));
  145. CError::s_pmapFacilities = new CMapDWORDtoCString;
  146. s_fAllocated = TRUE;
  147. LPTSTR lp = CError::s_pstrDefError->GetBuffer(255);
  148. if (!::LoadString(
  149. hDLLInstance,
  150. IDS_NO_MESSAGE,
  151. lp,
  152. 255
  153. ))
  154. {
  155. //
  156. // Just in case we didn't load this message from the resources
  157. //
  158. ASSERT_MSG("Unable to load resource message");
  159. lstrcpy(lp, _T("Error Code: 0x%08lx"));
  160. }
  161. CError::s_pstrDefError->ReleaseBuffer();
  162. }
  163. catch(CMemoryException * e)
  164. {
  165. TRACEEOLID("Initialization Failed");
  166. e->ReportError();
  167. e->Delete();
  168. }
  169. }
  170. LowerThreadProtection();
  171. return AreStaticsAllocated();
  172. }
  173. /* protected */
  174. /* static */
  175. void
  176. CError::DeAllocateStatics()
  177. /*++
  178. Routine Description:
  179. Clean up allocations
  180. Arguments:
  181. N/A
  182. Return Value:
  183. N/A
  184. --*/
  185. {
  186. RaiseThreadProtection();
  187. if (AreStaticsAllocated())
  188. {
  189. SAFE_DELETE(CError::s_pstrDefError);
  190. SAFE_DELETE(CError::s_pstrDefSuccs);
  191. SAFE_DELETE(CError::s_pmapFacilities);
  192. s_fAllocated = FALSE;
  193. }
  194. LowerThreadProtection();
  195. }
  196. /* static */
  197. HRESULT
  198. CError::CvtToInternalFormat(
  199. IN HRESULT hrCode
  200. )
  201. /*++
  202. Routine Description:
  203. Convert WIN32 or HRESULT code to internal (HRESULT) format.
  204. Arguments:
  205. DWORD dwCode Error code
  206. Return Value:
  207. HRESULT
  208. Notes:
  209. HRESULTS are left as is. Lanman and Winsock errors are converted
  210. to HRESULTS using private facility codes.
  211. --*/
  212. {
  213. if (IS_HRESULT(hrCode))
  214. {
  215. return hrCode;
  216. }
  217. if(hrCode >= s_cdwMinLMErr && hrCode <= s_cdwMaxLMErr)
  218. {
  219. return MAKE_HRESULT(SEVERITY_ERROR, FACILITY_LANMAN, (DWORD)hrCode);
  220. }
  221. if (hrCode >= s_cdwMinWSErr && hrCode <= s_cdwMaxWSErr)
  222. {
  223. return MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WINSOCK, (DWORD)hrCode);
  224. }
  225. return HResult(hrCode);
  226. }
  227. /* static */
  228. void
  229. CError::RegisterFacility(
  230. IN DWORD dwFacility,
  231. IN LPCSTR lpDLL OPTIONAL
  232. )
  233. /*++
  234. Routine Description:
  235. Register a DLL for a given facility code. Use NULL to unregister
  236. the DLL name.
  237. Arguments:
  238. DWORD dwFacility : Facility code
  239. LPCSTR lpDLL : DLL Name.
  240. Return Value:
  241. None
  242. --*/
  243. {
  244. RaiseThreadProtection();
  245. ASSERT(AreStaticsAllocated());
  246. if (lpDLL == NULL)
  247. {
  248. //
  249. // Remove the facility
  250. //
  251. s_pmapFacilities->RemoveKey(dwFacility);
  252. }
  253. else
  254. {
  255. CString str(lpDLL);
  256. //
  257. // Register facility
  258. //
  259. s_pmapFacilities->SetAt(dwFacility, str);
  260. }
  261. LowerThreadProtection();
  262. }
  263. /* static */
  264. LPCTSTR
  265. CError::FindFacility(
  266. IN DWORD dwFacility
  267. )
  268. /*++
  269. Routine Description:
  270. Determine if a DLL name has been registered for the given facility
  271. code.
  272. Arguments:
  273. DWORD dwFacility : Facility code
  274. Return Value:
  275. Returns the DLL name, or NULL.
  276. --*/
  277. {
  278. RaiseThreadProtection();
  279. ASSERT(AreStaticsAllocated());
  280. LPCTSTR pRes = NULL;
  281. CString strDLL;
  282. if (s_pmapFacilities->Lookup(dwFacility, strDLL))
  283. {
  284. pRes = strDLL;
  285. }
  286. LowerThreadProtection();
  287. return pRes;
  288. }
  289. CError::~CError()
  290. /*++
  291. Routine Description:
  292. Destructor
  293. Arguments:
  294. None
  295. Return Value:
  296. N/A
  297. --*/
  298. {
  299. }
  300. const CError &
  301. CError::Construct(
  302. IN HRESULT hr
  303. )
  304. /*++
  305. Routine Description:
  306. construct with new value.
  307. Arguments:
  308. HRESULT hr : New value, either an HRESULT or a WIN32
  309. error code.
  310. Return Value:
  311. Reference to current object
  312. --*/
  313. {
  314. ASSERT(AreStaticsAllocated());
  315. m_hrCode = CvtToInternalFormat(hr);
  316. return *this;
  317. }
  318. const CError &
  319. CError::Construct(
  320. IN const CError & err
  321. )
  322. /*++
  323. Routine Description:
  324. Assign new value.
  325. Arguments:
  326. CError & err : Error code
  327. Return Value:
  328. Reference to current object
  329. --*/
  330. {
  331. ASSERT(AreStaticsAllocated());
  332. m_hrCode = err.m_hrCode;
  333. return *this;
  334. }
  335. int
  336. CError::MessageBox(
  337. HWND hWnd,
  338. UINT nType,
  339. UINT nHelpContext
  340. ) const
  341. /*++
  342. Routine Description:
  343. Display error message in a message box
  344. Arguments:
  345. HRESULT hrCode : HRESULT error code
  346. UINT nType : See AfxMessageBox for documentation
  347. UINT nHelpContext : See AfxMessageBox for documentation
  348. Return Value:
  349. AfxMessageBox return code
  350. --*/
  351. {
  352. CString strMsg;
  353. nHelpContext;
  354. TextFromHRESULT(strMsg);
  355. //
  356. // Try to find the main window (hopefully an MFC app)
  357. //
  358. if (hWnd == NULL)
  359. {
  360. CWnd * pWnd = ::AfxGetMainWnd();
  361. if (pWnd)
  362. {
  363. hWnd = pWnd->m_hWnd;
  364. }
  365. nType |= MB_APPLMODAL;
  366. }
  367. return ::MessageBox(hWnd, strMsg, NULL, nType);
  368. }
  369. //
  370. // Extend CString just to get at FormatV publically
  371. //
  372. class CStringEx : public CString
  373. {
  374. public:
  375. void FormatV(LPCTSTR lpszFormat, va_list argList)
  376. {
  377. LPTSTR p = GetBuffer(1024);
  378. VERIFY(-1 != _vsntprintf(p, 1024, lpszFormat, argList));
  379. ReleaseBuffer(-1);
  380. }
  381. };
  382. int
  383. CError::MessageBoxFormat(
  384. HWND hWnd,
  385. UINT nFmt,
  386. UINT nType,
  387. UINT nHelpContext,
  388. ...
  389. ) const
  390. /*++
  391. Routine Description:
  392. Display formatted error message in messagebox. The format
  393. string (given as a resource ID) is a normal printf-style
  394. string, with the additional parameter of %h, which takes
  395. the text equivalent of the error message, or %H, which takes
  396. the error return code itself.
  397. Arguments:
  398. UINT nFmt : Resource format
  399. UINT nType : See AfxMessageBox for documentation
  400. UINT nHelpContext : See AfxMessageBox for documentation
  401. ... More as needed for sprintf
  402. Return Value:
  403. AfxMessageBox return code
  404. --*/
  405. {
  406. CString strFmt;
  407. CStringEx strMsg;
  408. strFmt.LoadString(nFmt);
  409. //
  410. // First expand the error
  411. //
  412. TextFromHRESULTExpand(strFmt);
  413. va_list marker;
  414. va_start(marker, nHelpContext);
  415. strMsg.FormatV(strFmt, marker);
  416. va_end(marker);
  417. if (hWnd == NULL)
  418. {
  419. CWnd * pWnd = ::AfxGetMainWnd();
  420. if (pWnd)
  421. {
  422. hWnd = pWnd->m_hWnd;
  423. }
  424. nType |= MB_APPLMODAL;
  425. }
  426. return ::MessageBox(hWnd, strMsg, NULL, nType);
  427. }
  428. BOOL
  429. CError::MessageBoxOnFailure(
  430. HWND hWnd,
  431. UINT nType,
  432. UINT nHelpContext
  433. ) const
  434. /*++
  435. Routine Description:
  436. Display message box if the current error is a failure
  437. condition, else do nothing
  438. Arguments:
  439. UINT nType : See AfxMessageBox for documentation
  440. UINT nHelpContext : See AfxMessageBox for documentation
  441. Return Value:
  442. TRUE if a messagebox was shown, FALSE otherwise
  443. --*/
  444. {
  445. if (Failed())
  446. {
  447. MessageBox(hWnd, nType, nHelpContext);
  448. return TRUE;
  449. }
  450. return FALSE;
  451. }
  452. BOOL
  453. CError::HasOverride(
  454. UINT * pnMessage OPTIONAL
  455. ) const
  456. /*++
  457. Routine Description:
  458. Check to see if a given HRESULT has an override
  459. Arguments:
  460. HRESULT hrCode : HRESULT to check for
  461. UINT * pnMessage : Optionally returns the override
  462. Return Value:
  463. TRUE if there is an override, FALSE if there is not.
  464. --*/
  465. {
  466. ASSERT(AreStaticsAllocated());
  467. UINT nID;
  468. HRESULT hrCode = CvtToInternalFormat(m_hrCode);
  469. BOOL fResult = mapOverrides.Lookup(hrCode, nID);
  470. if (fResult && pnMessage != NULL)
  471. {
  472. *pnMessage = nID;
  473. }
  474. return fResult;
  475. }
  476. UINT
  477. CError::AddOverride(
  478. HRESULT hrCode,
  479. UINT nMessage
  480. )
  481. /*++
  482. Routine Description:
  483. Add an override for a specific HRESULT.
  484. Arguments:
  485. HRESULT hrCode : HRESULT to override
  486. UINT nMessage : New message, or -1 to remove override
  487. Return Value:
  488. The previous override, or -1
  489. --*/
  490. {
  491. ASSERT(AreStaticsAllocated());
  492. UINT nPrev;
  493. hrCode = CvtToInternalFormat(hrCode);
  494. //
  495. // Fetch the current override
  496. //
  497. if (!mapOverrides.Lookup(hrCode, nPrev))
  498. {
  499. //
  500. // Didn't exist
  501. //
  502. nPrev = REMOVE_OVERRIDE;
  503. }
  504. if (nMessage == REMOVE_OVERRIDE)
  505. {
  506. //
  507. // Remove the override
  508. //
  509. mapOverrides.RemoveKey(hrCode);
  510. }
  511. else
  512. {
  513. //
  514. // Set new override
  515. //
  516. mapOverrides.SetAt(hrCode, nMessage);
  517. }
  518. return nPrev;
  519. }
  520. void
  521. CError::RemoveAllOverrides()
  522. /*++
  523. Routine Description:
  524. Remove all overrides
  525. Arguments:
  526. None
  527. Return Value:
  528. None
  529. --*/
  530. {
  531. ASSERT(AreStaticsAllocated());
  532. mapOverrides.RemoveAll();
  533. }
  534. HRESULT
  535. CError::TextFromHRESULT(
  536. LPTSTR szBuffer,
  537. DWORD cchBuffer
  538. ) const
  539. /*++
  540. Routine Description:
  541. Get text from the given HRESULT. Based on the range that the HRESULT
  542. falls in and the facility code, find the location of the message,
  543. and fetch it.
  544. Arguments:
  545. HRESULT hrCode HRESULT or (DWORD WIN32 error) whose message to get
  546. LPTSTR szBuffer Buffer to load message text into
  547. DWORD cchBuffer Size of buffer in characters.
  548. Return Value:
  549. HRESULT error code depending on whether the message was
  550. found. If the message was not found, some generic message
  551. is synthesized in the buffer if a buffer is provided.
  552. ERROR_FILE_NOT_FOUND No message found
  553. ERROR_INSUFFICIENT_BUFFER Buffer is a NULL pointer or too small
  554. --*/
  555. {
  556. HRESULT hrReturn = ERROR_SUCCESS;
  557. //
  558. // First check to see if this message is overridden
  559. //
  560. UINT nID;
  561. HRESULT hrCode = m_hrCode;
  562. if (HasOverride(&nID))
  563. {
  564. //
  565. // Message overridden. Load replacement message
  566. // instead.
  567. //
  568. BOOL fSuccess;
  569. //
  570. // Attempt to load from calling process first
  571. //
  572. // if (FALSE == (fSuccess = ::LoadString(
  573. // ::GetModuleHandle(NULL), nID, szBuffer, cchBuffer)))
  574. CString str;
  575. if (FALSE == (fSuccess = str.LoadString(nID)))
  576. {
  577. //
  578. // Try this dll
  579. //
  580. // fSuccess = ::LoadString(hDLLInstance, nID, szBuffer, cchBuffer);
  581. }
  582. if (fSuccess)
  583. {
  584. lstrcpyn(szBuffer, str, cchBuffer);
  585. return hrReturn;
  586. }
  587. //
  588. // Message didn't exist, skip the override, and
  589. // load as normal.
  590. //
  591. TRACEEOLID("Couldn't load " << nID);
  592. ASSERT_MSG("Attempted override failed");
  593. }
  594. LPCTSTR lpDll = NULL;
  595. HINSTANCE hDll = NULL;
  596. DWORD dwFacility = HRESULT_FACILITY(hrCode);
  597. // DWORD dwSeverity = HRESULT_SEVERITY(hrCode);
  598. DWORD dwCode = HRESULT_CODE(hrCode);
  599. BOOL fSuccess = Succeeded(hrCode);
  600. //
  601. // Strip off meaningless internal facility codes
  602. //
  603. if (dwFacility == FACILITY_LANMAN || dwFacility == FACILITY_WINSOCK)
  604. {
  605. dwFacility = FACILITY_NULL;
  606. hrCode = (HRESULT)dwCode;
  607. }
  608. DWORD dwFlags = FORMAT_MESSAGE_IGNORE_INSERTS |
  609. FORMAT_MESSAGE_MAX_WIDTH_MASK;
  610. //
  611. // Since we allow both HRESULTS and WIN32 codes to be
  612. // used here, we can't rely on the private FACILITY code
  613. // for lanman and winsock.
  614. //
  615. if(hrCode >= s_cdwMinLMErr && hrCode <= s_cdwMaxLMErr)
  616. {
  617. //
  618. // Lanman error
  619. //
  620. lpDll = s_cszLMDLL;
  621. }
  622. else if (hrCode >= s_cdwMinWSErr && hrCode <= s_cdwMaxWSErr)
  623. {
  624. //
  625. // Winsock error
  626. //
  627. lpDll = s_cszWSDLL;
  628. }
  629. else
  630. {
  631. //
  632. // Attempt to determine message location from facility code.
  633. // Check for registered facility first.
  634. //
  635. lpDll = FindFacility(dwFacility);
  636. if (lpDll == NULL)
  637. {
  638. if (dwFacility < s_cdwFacilities)
  639. {
  640. lpDll = s_cszFacility[dwFacility];
  641. }
  642. else
  643. {
  644. ASSERT_MSG("Bogus FACILITY code encountered.");
  645. lpDll = NULL;
  646. }
  647. }
  648. }
  649. do
  650. {
  651. if (szBuffer == NULL || cchBuffer <= 0)
  652. {
  653. hrReturn = HResult(ERROR_INSUFFICIENT_BUFFER);
  654. break;
  655. }
  656. if (lpDll)
  657. {
  658. //
  659. // Load message file
  660. //
  661. hDll = ::LoadLibraryEx(
  662. lpDll,
  663. NULL,
  664. LOAD_LIBRARY_AS_DATAFILE
  665. );
  666. if (hDll == NULL)
  667. {
  668. hrReturn = ::GetLastHRESULT();
  669. break;
  670. }
  671. dwFlags |= FORMAT_MESSAGE_FROM_HMODULE;
  672. }
  673. else
  674. {
  675. dwFlags |= FORMAT_MESSAGE_FROM_SYSTEM;
  676. }
  677. DWORD dwResult = 0L;
  678. DWORD dwID = hrCode;
  679. HINSTANCE hSource = hDll;
  680. while(!dwResult)
  681. {
  682. dwResult = ::FormatMessage(
  683. dwFlags,
  684. (LPVOID)hSource,
  685. dwID,
  686. 0,
  687. szBuffer,
  688. cchBuffer,
  689. NULL
  690. );
  691. if (dwResult > 0)
  692. {
  693. //
  694. // Successfully got a message
  695. //
  696. hrReturn = ERROR_SUCCESS;
  697. break;
  698. }
  699. hrReturn = ::GetLastHRESULT();
  700. if (dwID != dwCode && !fSuccess)
  701. {
  702. //
  703. // Try the SCODE portion of the error from win32
  704. // if this is an error message
  705. //
  706. dwID = dwCode;
  707. hSource = NULL;
  708. continue;
  709. }
  710. //
  711. // Failed to obtain a message
  712. //
  713. hrReturn = HResult(ERROR_FILE_NOT_FOUND);
  714. break;
  715. }
  716. }
  717. while(FALSE);
  718. if(hDll != NULL)
  719. {
  720. ::FreeLibrary(hDll);
  721. }
  722. if (Failed(hrReturn))
  723. {
  724. //
  725. // Unable to find the message, synthesize something with
  726. // the code in it if there's room (+8 for the number)
  727. //
  728. CString & strMsg = (fSuccess ? *s_pstrDefSuccs : *s_pstrDefError);
  729. if (cchBuffer > (DWORD)strMsg.GetLength() + 8)
  730. {
  731. TRACEEOLID("Substituting default message for " << (DWORD)m_hrCode);
  732. wsprintf(szBuffer, (LPCTSTR)strMsg, m_hrCode);
  733. }
  734. else
  735. {
  736. //
  737. // Not enough room for message code
  738. //
  739. ASSERT_MSG("Buffer too small for default message -- left blank");
  740. *szBuffer = _T('\0');
  741. }
  742. }
  743. return hrReturn;
  744. }
  745. HRESULT
  746. CError::TextFromHRESULT(
  747. CString & strBuffer
  748. ) const
  749. /*++
  750. Routine Description:
  751. Similar to the function above, but use a CString
  752. Arguments:
  753. HRESULT hrCode HRESULT or (DWORD WIN32 error) whose message to get
  754. CString & strBuffer Buffer to load message text into
  755. Return Value:
  756. HRESULT error code depending on whether the message was
  757. found. If the message was not found, some generic message
  758. is synthesized in the buffer if a buffer is provided.
  759. ERROR_FILE_NOT_FOUND No message found
  760. --*/
  761. {
  762. DWORD cchBuffer = 512;
  763. HRESULT hr = S_OK;
  764. for (;;)
  765. {
  766. LPTSTR szBuffer = strBuffer.GetBuffer(cchBuffer + 1);
  767. if (szBuffer == NULL)
  768. {
  769. return HResult(ERROR_NOT_ENOUGH_MEMORY);
  770. }
  771. hr = TextFromHRESULT(szBuffer, cchBuffer);
  772. if (Win32Error(hr) != ERROR_INSUFFICIENT_BUFFER)
  773. {
  774. //
  775. // Done!
  776. //
  777. break;
  778. }
  779. //
  780. // Insufficient buffer, enlarge and try again
  781. //
  782. cchBuffer *= 2;
  783. }
  784. strBuffer.ReleaseBuffer();
  785. return hr;
  786. }
  787. BOOL
  788. CError::ExpandEscapeCode(
  789. LPTSTR szBuffer,
  790. DWORD cchBuffer,
  791. LPTSTR & lp,
  792. CString & strReplacement,
  793. HRESULT & hr
  794. ) const
  795. /*++
  796. Routine Description:
  797. Expand escape code
  798. Arguments:
  799. LPTSTR szBuffer Buffer
  800. DWORD cchBuffer Size of buffer
  801. LPTSTR & lp Pointer to escape code
  802. CString & strReplacement Message to replace the escape code
  803. HRESULT & hr Returns HRESULT in case of failure
  804. Return Value:
  805. TRUE if the replacement was successful, FALSE otherwise.
  806. In the case of failure, hr will return an HRESULT.
  807. In the case of success, lp will be advanced past the
  808. replacement string.
  809. --*/
  810. {
  811. //
  812. // Make sure there's room (account for terminating NULL)
  813. // Free up 2 spaces for the escape code.
  814. //
  815. int cchFmt = lstrlen(szBuffer) - 2;
  816. int cchReplacement = strReplacement.GetLength();
  817. int cchRemainder = lstrlen(lp + 2);
  818. if ( lp != NULL && (lp + 2) != NULL
  819. && (DWORD)(cchReplacement + cchFmt) < cchBuffer
  820. )
  821. {
  822. //
  823. // Put it in
  824. //
  825. MoveMemory(
  826. lp + cchReplacement,
  827. lp + 2,
  828. (cchRemainder + 1) * sizeof(TCHAR)
  829. );
  830. CopyMemory(lp, strReplacement, cchReplacement * sizeof(TCHAR));
  831. lp += cchReplacement;
  832. return TRUE;
  833. }
  834. hr = HResult(ERROR_INSUFFICIENT_BUFFER);
  835. return FALSE;
  836. }
  837. LPCTSTR
  838. CError::TextFromHRESULTExpand(
  839. LPTSTR szBuffer,
  840. DWORD cchBuffer,
  841. HRESULT * phResult OPTIONAL
  842. ) const
  843. /*++
  844. Routine Description:
  845. Expand %h/%H strings in szBuffer to text from HRESULT,
  846. or error code respectively within the limits of szBuffer.
  847. Arguments:
  848. LPTSTR szBuffer Buffer to load message text into
  849. DWORD cchBuffer Buffer size in characters
  850. HRESULT * phResult Optional return code
  851. Return Value:
  852. Pointer to string.
  853. --*/
  854. {
  855. HRESULT hr = S_OK;
  856. if (szBuffer == NULL || cchBuffer <= 0)
  857. {
  858. hr = HResult(ERROR_INSUFFICIENT_BUFFER);
  859. }
  860. else
  861. {
  862. //
  863. // Look for the escape sequence
  864. //
  865. int cReplacements = 0;
  866. CString strMessage;
  867. LPTSTR lp = szBuffer;
  868. while (*lp)
  869. {
  870. if (*lp == s_chEscape)
  871. {
  872. switch(*(lp + 1))
  873. {
  874. case s_chEscText:
  875. //
  876. // Replace escape code with text message
  877. //
  878. hr = TextFromHRESULT(strMessage);
  879. if (ExpandEscapeCode(
  880. szBuffer,
  881. cchBuffer,
  882. lp,
  883. strMessage,
  884. hr
  885. ))
  886. {
  887. ++cReplacements;
  888. }
  889. break;
  890. case s_chEscNumber:
  891. //
  892. // Replace escape code with numeric error code
  893. //
  894. strMessage.Format(_T("0x%08x"), m_hrCode);
  895. if (ExpandEscapeCode(
  896. szBuffer,
  897. cchBuffer,
  898. lp,
  899. strMessage,
  900. hr
  901. ))
  902. {
  903. ++cReplacements;
  904. }
  905. break;
  906. default:
  907. //
  908. // Regular printf-style escape sequence.
  909. //
  910. break;
  911. }
  912. }
  913. ++lp;
  914. }
  915. if (!cReplacements)
  916. {
  917. //
  918. // Got to the end without finding any escape codes.
  919. //
  920. hr = HResult(ERROR_INVALID_PARAMETER);
  921. }
  922. }
  923. if (phResult)
  924. {
  925. *phResult = hr;
  926. }
  927. return szBuffer;
  928. }
  929. LPCTSTR
  930. CError::TextFromHRESULTExpand(
  931. CString & strBuffer
  932. ) const
  933. /*++
  934. Routine Description:
  935. Expand %h string in strBuffer to text from HRESULT
  936. Arguments:
  937. CString & strBuffer Buffer to load message text into
  938. Return Value:
  939. Pointer to string.
  940. --*/
  941. {
  942. DWORD cchBuffer = strBuffer.GetLength() + 1024;
  943. for (;;)
  944. {
  945. LPTSTR szBuffer = strBuffer.GetBuffer(cchBuffer + 1);
  946. if (szBuffer != NULL)
  947. {
  948. HRESULT hr;
  949. TextFromHRESULTExpand(szBuffer, cchBuffer, &hr);
  950. if (Win32Error(hr) != ERROR_INSUFFICIENT_BUFFER)
  951. {
  952. //
  953. // Done!
  954. //
  955. break;
  956. }
  957. //
  958. // Insufficient buffer, enlarge and try again
  959. //
  960. cchBuffer *= 2;
  961. }
  962. }
  963. strBuffer.ReleaseBuffer();
  964. return strBuffer;
  965. }