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.

1271 lines
24 KiB

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