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.

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