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.

1882 lines
36 KiB

  1. // SSRLog.cpp : Implementation of CSSRLog
  2. #include "stdafx.h"
  3. #include "SSRTE.h"
  4. #include "SSRLog.h"
  5. #include <Userenv.h>
  6. #include "global.h"
  7. #include "SSRMembership.h"
  8. extern CComModule _Module;
  9. /////////////////////////////////////////////////////////////////////////////
  10. // CSsrLog
  11. static LPCWSTR s_pszDefLogFileName = L"Log.txt";
  12. static LPCWSTR s_pwszSource = L"Source=";
  13. static LPCWSTR s_pwszDetail = L"Detail=";
  14. static LPCWSTR s_pwszErrorCode = L"ErrorCode=";
  15. static LPCWSTR s_pwszErrorTextNotFound = L"Error text can't be found";
  16. static LPCWSTR s_pwszNotSpecified = L"Not specified";
  17. static LPCWSTR s_pwszSep = L" ";
  18. static LPCWSTR s_pwszCRLF = L"\r\n";
  19. static const DWORD s_dwSourceLen = wcslen(s_pwszSource);
  20. static const DWORD s_dwSepLen = wcslen(s_pwszSep);
  21. static const DWORD s_dwErrorLen = wcslen(s_pwszErrorCode);
  22. static const DWORD s_dwDetailLen = wcslen(s_pwszDetail);
  23. static const DWORD s_dwCRLFLen = wcslen(s_pwszCRLF);
  24. /*
  25. Routine Description:
  26. Name:
  27. CSsrLog::CSsrLog
  28. Functionality:
  29. constructor
  30. Virtual:
  31. no.
  32. Arguments:
  33. none.
  34. Return Value:
  35. none.
  36. Notes:
  37. */
  38. CSsrLog::CSsrLog()
  39. : m_bstrLogFile(s_pszDefLogFileName)
  40. {
  41. }
  42. /*
  43. Routine Description:
  44. Name:
  45. CSsrLog::~CSsrLog
  46. Functionality:
  47. destructor
  48. Virtual:
  49. yes.
  50. Arguments:
  51. none.
  52. Return Value:
  53. none.
  54. Notes:
  55. */
  56. CSsrLog::~CSsrLog()
  57. {
  58. }
  59. /*
  60. Routine Description:
  61. Name:
  62. CSsrLog::LogResult
  63. Functionality:
  64. Log error code information. This function will do a message format function
  65. and then log both the error code and the formatted msg.
  66. Virtual:
  67. yes.
  68. Arguments:
  69. bstrSrc - source of error. This is just indicative of the information source.
  70. dwErrorCode - The error code itself.
  71. dwCodeType - The type of error code. We use WMI extensively,
  72. its error code lookup is slightly different from others.
  73. Return Value:
  74. ?.
  75. Notes:
  76. The error code may not be an error. It can be a success code.
  77. */
  78. STDMETHODIMP
  79. CSsrLog::LogResult (
  80. BSTR bstrSrc,
  81. LONG lErrorCode,
  82. LONG lCodeType
  83. )
  84. {
  85. HRESULT hr = S_OK;
  86. if (m_bstrLogFilePath.Length() == 0)
  87. {
  88. hr = CreateLogFilePath();
  89. if (FAILED(hr))
  90. {
  91. return hr;
  92. }
  93. }
  94. //
  95. // default to not able to find the error text
  96. //
  97. LPCWSTR pwszLog = s_pwszErrorTextNotFound;
  98. //
  99. // will hold the hex decimal of the error code in case the
  100. // error code can't be translated into a string
  101. //
  102. CComBSTR bstrErrorText;
  103. hr = GetErrorText(lErrorCode, lCodeType, &bstrErrorText);
  104. if (SUCCEEDED(hr))
  105. {
  106. //
  107. // if we can get error test, then this is the log we want
  108. //
  109. pwszLog = bstrErrorText;
  110. }
  111. //
  112. // now write to the file
  113. //
  114. LPCWSTR pwszSrcString = s_pwszNotSpecified;
  115. if (bstrSrc != NULL && *bstrSrc != L'\0')
  116. {
  117. pwszSrcString = bstrSrc;
  118. }
  119. //
  120. // one error log is like this: Source=XXXX*****ErrorCode=XXXX*****Detail=XXXX
  121. // where XXXX represent any text and the ***** represents the separater.
  122. //
  123. int iLen = s_dwSourceLen +
  124. wcslen(pwszSrcString) +
  125. s_dwSepLen +
  126. s_dwErrorLen +
  127. 10 + // hex decimal has 10 chars for DWORD
  128. s_dwSepLen +
  129. s_dwDetailLen +
  130. wcslen(pwszLog);
  131. LPWSTR pwszLogString = new WCHAR[iLen + 1];
  132. //
  133. // format the log string and do the logging
  134. //
  135. if (pwszLogString != NULL)
  136. {
  137. _snwprintf(pwszLogString,
  138. iLen + 1,
  139. L"%s%s%s%s0x%X%s%s%s",
  140. s_pwszSource,
  141. pwszSrcString,
  142. s_pwszSep,
  143. s_pwszErrorCode,
  144. lErrorCode,
  145. s_pwszSep,
  146. s_pwszDetail,
  147. pwszLog
  148. );
  149. hr = PrivateLogString(pwszLogString);
  150. delete [] pwszLogString;
  151. pwszLogString = NULL;
  152. }
  153. return hr;
  154. }
  155. /*
  156. Routine Description:
  157. Name:
  158. CSsrLog::GetErrorText
  159. Functionality:
  160. Lookup the error text using the error code
  161. Virtual:
  162. no.
  163. Arguments:
  164. lErrorCode - The error code itself.
  165. lCodeType - The type of error code. We use WMI extensively,
  166. its error code lookup is slightly different from others.
  167. pbstrErrorText - The error text corresponding to this error code
  168. Return Value:
  169. ?.
  170. Notes:
  171. The error code may not be an error. It can be a success code.
  172. */
  173. HRESULT
  174. CSsrLog::GetErrorText (
  175. IN LONG lErrorCode,
  176. IN LONG lCodeType,
  177. OUT BSTR * pbstrErrorText
  178. )
  179. {
  180. if (pbstrErrorText == NULL)
  181. {
  182. return E_INVALIDARG;
  183. }
  184. *pbstrErrorText = NULL;
  185. LPVOID pMsgBuf = NULL;
  186. HRESULT hr = S_OK;
  187. if (lCodeType == SSR_LOG_ERROR_TYPE_Wbem)
  188. {
  189. hr = GetWbemErrorText(lErrorCode, pbstrErrorText);
  190. }
  191. else
  192. {
  193. DWORD flag = FORMAT_MESSAGE_ALLOCATE_BUFFER |
  194. FORMAT_MESSAGE_FROM_SYSTEM |
  195. FORMAT_MESSAGE_IGNORE_INSERTS;
  196. DWORD dwRet = ::FormatMessage(
  197. flag,
  198. NULL,
  199. lErrorCode,
  200. 0, // Default language
  201. (LPWSTR) &pMsgBuf,
  202. 0,
  203. NULL
  204. );
  205. //
  206. // trying our own errors if this failes to give us anything
  207. //
  208. if (dwRet == 0)
  209. {
  210. flag = FORMAT_MESSAGE_ALLOCATE_BUFFER |
  211. FORMAT_MESSAGE_FROM_HMODULE |
  212. FORMAT_MESSAGE_IGNORE_INSERTS;
  213. dwRet = ::FormatMessage(
  214. flag,
  215. _Module.m_hInst,
  216. lErrorCode,
  217. 0, // Default language
  218. (LPWSTR) &pMsgBuf,
  219. 0,
  220. NULL
  221. );
  222. }
  223. if (dwRet != 0)
  224. {
  225. *pbstrErrorText = ::SysAllocString((LPCWSTR)pMsgBuf);
  226. if (*pbstrErrorText == NULL)
  227. {
  228. hr = E_OUTOFMEMORY;
  229. }
  230. }
  231. else
  232. {
  233. hr = HRESULT_FROM_WIN32(GetLastError());
  234. }
  235. }
  236. if (pMsgBuf != NULL)
  237. {
  238. ::LocalFree( pMsgBuf );
  239. }
  240. if (FAILED(hr) && E_OUTOFMEMORY != hr)
  241. {
  242. if (*pbstrErrorText != NULL)
  243. {
  244. ::SysFreeString(*pbstrErrorText);
  245. *pbstrErrorText = NULL;
  246. }
  247. //
  248. // fall back to just give the error code
  249. //
  250. WCHAR wszErrorCode[g_dwHexDwordLen];
  251. _snwprintf(wszErrorCode, g_dwHexDwordLen, L"0x%X", lErrorCode);
  252. *pbstrErrorText = ::SysAllocString(wszErrorCode);
  253. hr = (*pbstrErrorText != NULL) ? S_OK : E_OUTOFMEMORY;
  254. }
  255. return hr;
  256. }
  257. /*
  258. Routine Description:
  259. Name:
  260. CSsrLog::PrivateLogString
  261. Functionality:
  262. Just log the string to the log file. We don't attempt to do any formatting.
  263. Virtual:
  264. no.
  265. Arguments:
  266. pwszLogRecord - The string to be logged into the log file
  267. Return Value:
  268. Success: S_OK.
  269. Failure: various error codes.
  270. Notes:
  271. */
  272. HRESULT
  273. CSsrLog::PrivateLogString (
  274. IN LPCWSTR pwszLogRecord
  275. )
  276. {
  277. HRESULT hr = S_OK;
  278. if (m_bstrLogFilePath.Length() == 0)
  279. {
  280. hr = CreateLogFilePath();
  281. if (FAILED(hr))
  282. {
  283. return hr;
  284. }
  285. }
  286. //
  287. // $consider:shawnwu,
  288. // Right now, we write to the log file each time the function is called.
  289. // That might cause the problem too much file access and becomes a performance
  290. // issue. We might want to consider optimizing this.
  291. //
  292. DWORD dwWait = ::WaitForSingleObject(g_fblog.m_hLogMutex, INFINITE);
  293. //
  294. // $undone:shawnwu, some error happened, should we continue to log?
  295. //
  296. if (dwWait != WAIT_OBJECT_0 && dwWait != WAIT_ABANDONED)
  297. {
  298. return E_SSR_LOG_FILE_MUTEX_WAIT;
  299. }
  300. HANDLE hFile = ::CreateFile(m_bstrLogFilePath,
  301. GENERIC_WRITE,
  302. 0, // not shared
  303. NULL,
  304. OPEN_ALWAYS,
  305. FILE_ATTRIBUTE_NORMAL,
  306. NULL
  307. );
  308. if (hFile != INVALID_HANDLE_VALUE)
  309. {
  310. //
  311. // append the text to the end of the file
  312. //
  313. ::SetFilePointer (hFile, 0, NULL, FILE_END);
  314. DWORD dwBytesWritten = 0;
  315. if ( 0 == ::WriteFile (hFile,
  316. (LPCVOID)pwszLogRecord,
  317. wcslen(pwszLogRecord) * sizeof(WCHAR),
  318. &dwBytesWritten,
  319. NULL) )
  320. {
  321. hr = HRESULT_FROM_WIN32(GetLastError());
  322. }
  323. //
  324. // put a line break
  325. //
  326. if ( 0 == ::WriteFile (hFile,
  327. (LPCVOID)s_pwszCRLF,
  328. s_dwCRLFLen * sizeof(WCHAR),
  329. &dwBytesWritten,
  330. NULL) )
  331. {
  332. hr = HRESULT_FROM_WIN32(GetLastError());
  333. }
  334. ::CloseHandle(hFile);
  335. }
  336. else
  337. {
  338. hr = HRESULT_FROM_WIN32(GetLastError());
  339. }
  340. ::ReleaseMutex(g_fblog.m_hLogMutex);
  341. return hr;
  342. }
  343. /*
  344. Routine Description:
  345. Name:
  346. CSsrLog::get_LogFilePath
  347. Functionality:
  348. Will return the full path of the log file this object uses
  349. Virtual:
  350. yes.
  351. Arguments:
  352. pbstrLogFilePath - receives the current log file's full path.
  353. Return Value:
  354. Success: S_OK;
  355. Failure: E_OUTOFMEMORY
  356. Notes:
  357. */
  358. STDMETHODIMP
  359. CSsrLog::get_LogFilePath (
  360. OUT BSTR * pbstrLogFilePath /*[out, retval]*/
  361. )
  362. {
  363. HRESULT hr = S_OK;
  364. if (pbstrLogFilePath == NULL)
  365. {
  366. return E_INVALIDARG;
  367. }
  368. if (m_bstrLogFilePath.Length() == 0)
  369. {
  370. hr = CreateLogFilePath();
  371. }
  372. if (SUCCEEDED(hr))
  373. {
  374. *pbstrLogFilePath = ::SysAllocString(m_bstrLogFilePath);
  375. }
  376. return (*pbstrLogFilePath != NULL) ? S_OK : E_OUTOFMEMORY;
  377. }
  378. /*
  379. Routine Description:
  380. Name:
  381. CSsrLog::put_LogFile
  382. Functionality:
  383. Will set the log file.
  384. Virtual:
  385. yes.
  386. Arguments:
  387. bstrLogFile - the file name (plus extension) the caller wants this
  388. object to use.
  389. Return Value:
  390. ?.
  391. Notes:
  392. The bstrLogFile must be just a file name without any directory path
  393. */
  394. STDMETHODIMP
  395. CSsrLog::put_LogFile (
  396. IN BSTR bstrLogFile
  397. )
  398. {
  399. HRESULT hr = S_OK;
  400. //
  401. // you can't give me a invalid log file name
  402. // It also must just be an name
  403. //
  404. if (bstrLogFile == NULL ||
  405. *bstrLogFile == L'\0' ||
  406. ::wcsstr(bstrLogFile, L"\\") != NULL)
  407. {
  408. hr = E_INVALIDARG;
  409. }
  410. else
  411. {
  412. m_bstrLogFile = bstrLogFile;
  413. hr = CreateLogFilePath();
  414. }
  415. return hr;
  416. }
  417. /*
  418. Routine Description:
  419. Name:
  420. CSsrLog::CreateLogFilePath
  421. Functionality:
  422. Create the log file's path.
  423. Virtual:
  424. no.
  425. Arguments:
  426. None
  427. Return Value:
  428. Success: S_OK;
  429. Failure: E_OUTOFMEMORY
  430. Notes:
  431. The bstrLogFile must be just a file name without any directory path
  432. */
  433. HRESULT
  434. CSsrLog::CreateLogFilePath ( )
  435. {
  436. if (wcslen(g_wszSsrRoot) + 1 + wcslen(g_pwszLogs) + 1 + wcslen(m_bstrLogFile) > MAX_PATH)
  437. {
  438. return HRESULT_FROM_WIN32(ERROR_FILENAME_EXCED_RANGE);
  439. }
  440. WCHAR wcLogFilePath[MAX_PATH + 1];
  441. _snwprintf(wcLogFilePath,
  442. MAX_PATH + 1,
  443. L"%s%c%s%c%s",
  444. g_wszSsrRoot,
  445. L'\\',
  446. g_pwszLogs,
  447. L'\\',
  448. m_bstrLogFile
  449. );
  450. m_bstrLogFilePath.Empty(); // so that in case of out-of-memory, it will be NULL
  451. m_bstrLogFilePath = wcLogFilePath;
  452. return m_bstrLogFilePath != NULL ? S_OK : E_OUTOFMEMORY;
  453. }
  454. /*
  455. Routine Description:
  456. Name:
  457. CSsrLog::GetWbemErrorText
  458. Functionality:
  459. Private helper to lookup WMI error text based on the error code
  460. Virtual:
  461. yes.
  462. Arguments:
  463. hrCode - HRESULT code.
  464. pbstrErrText - The out paramter that receives the text of the error code
  465. Return Value:
  466. Success:
  467. S_OK if everything is OK.
  468. S_FALSE if we can't locate the error text. in that case, we fall back
  469. to give text, which is just the text representation of the
  470. error code
  471. Failure:
  472. various error codes.
  473. Notes:
  474. The error code may not be an error. It can be a success code.
  475. */
  476. HRESULT
  477. CSsrLog::GetWbemErrorText (
  478. HRESULT hrCode,
  479. BSTR * pbstrErrText
  480. )
  481. {
  482. if (pbstrErrText == NULL)
  483. {
  484. return E_INVALIDARG;
  485. }
  486. *pbstrErrText = NULL;
  487. HRESULT hr = S_OK;
  488. if (m_srpStatusCodeText == NULL)
  489. {
  490. hr = ::CoCreateInstance(CLSID_WbemStatusCodeText,
  491. 0,
  492. CLSCTX_INPROC_SERVER,
  493. IID_IWbemStatusCodeText,
  494. (LPVOID*)&m_srpStatusCodeText
  495. );
  496. }
  497. if (m_srpStatusCodeText)
  498. {
  499. //
  500. // IWbemStatusCodeText is to translate the HRESULT to text
  501. //
  502. hr = m_srpStatusCodeText->GetErrorCodeText(hrCode, 0, 0, pbstrErrText);
  503. }
  504. if (FAILED(hr) || *pbstrErrText == NULL)
  505. {
  506. //
  507. // we fall back to just formatting the error code.
  508. // Hex of DWORD has 10 WCHARs
  509. //
  510. WCHAR wszCode[g_dwHexDwordLen];
  511. _snwprintf(wszCode, g_dwHexDwordLen, L"0x%X", hrCode);
  512. *pbstrErrText = ::SysAllocString(wszCode);
  513. if (*pbstrErrText != NULL)
  514. {
  515. hr = S_FALSE;
  516. }
  517. else
  518. {
  519. hr = E_OUTOFMEMORY;
  520. }
  521. }
  522. else
  523. {
  524. hr = S_OK;
  525. }
  526. return hr;
  527. }
  528. //--------------------------------------------------------------
  529. // implementation of CFBLogMgr
  530. /*
  531. Routine Description:
  532. Name:
  533. CFBLogMgr::CFBLogMgr
  534. Functionality:
  535. constructor
  536. Virtual:
  537. no.
  538. Arguments:
  539. none.
  540. Return Value:
  541. none.
  542. Notes:
  543. */
  544. CFBLogMgr::CFBLogMgr()
  545. : m_hLogMutex(NULL),
  546. m_dwRemainSteps(0),
  547. m_bVerbose(false)
  548. {
  549. HRESULT hr = CComObject<CSsrLog>::CreateInstance(&m_pLog);
  550. if (SUCCEEDED(hr))
  551. {
  552. m_pLog->AddRef();
  553. m_hLogMutex = ::CreateMutex(NULL, FALSE, L"ISsrLogMutex");
  554. }
  555. //
  556. // see if we are logging verbose or not
  557. //
  558. HKEY hRootKey = NULL;
  559. LONG lStatus = ::RegOpenKeyEx(
  560. HKEY_LOCAL_MACHINE,
  561. g_pwszSSRRegRoot,
  562. 0,
  563. KEY_READ,
  564. &hRootKey
  565. );
  566. if (ERROR_SUCCESS == lStatus)
  567. {
  568. DWORD dwSize = sizeof(DWORD);
  569. DWORD dwVerbose = 0;
  570. DWORD dwType;
  571. lStatus = ::RegQueryValueEx(hRootKey,
  572. L"LogVerbose",
  573. NULL,
  574. &dwType,
  575. (LPBYTE)&dwVerbose,
  576. &dwSize
  577. );
  578. if (ERROR_SUCCESS == lStatus)
  579. {
  580. m_bVerbose = (dwVerbose == 0) ? false : true;
  581. }
  582. ::RegCloseKey(hRootKey);
  583. }
  584. }
  585. /*
  586. Routine Description:
  587. Name:
  588. CFBLogMgr::~CFBLogMgr
  589. Functionality:
  590. destructor
  591. Virtual:
  592. no.
  593. Arguments:
  594. none.
  595. Return Value:
  596. none.
  597. Notes:
  598. */
  599. CFBLogMgr::~CFBLogMgr()
  600. {
  601. if (m_pLog)
  602. {
  603. m_pLog->Release();
  604. if (m_hLogMutex)
  605. {
  606. ::CloseHandle(m_hLogMutex);
  607. }
  608. }
  609. }
  610. /*
  611. Routine Description:
  612. Name:
  613. CFBLogMgr::SetFeedbackSink
  614. Functionality:
  615. Caches the feedback sink interface. This allows us to send
  616. feedback. If the in parameter is not a valid interface, then
  617. we won't send feedback.
  618. Virtual:
  619. no.
  620. Arguments:
  621. varFeedbackSink - The variant that holds an ISsrFeedbackSink COM
  622. interface pointer.
  623. Return Value:
  624. Success: S_OK
  625. Failure: E_INVALIDARG;
  626. Notes:
  627. */
  628. HRESULT
  629. CFBLogMgr::SetFeedbackSink (
  630. IN VARIANT varFeedbackSink
  631. )
  632. {
  633. DWORD dwWait = ::WaitForSingleObject(m_hLogMutex, INFINITE);
  634. if (dwWait != WAIT_OBJECT_0 && dwWait != WAIT_ABANDONED)
  635. {
  636. return HRESULT_FROM_WIN32(GetLastError());
  637. }
  638. m_srpFeedback.Release();
  639. HRESULT hr = S_FALSE;
  640. if (varFeedbackSink.vt == VT_UNKNOWN)
  641. {
  642. hr = varFeedbackSink.punkVal->QueryInterface(IID_ISsrFeedbackSink,
  643. (LPVOID*)&m_srpFeedback);
  644. }
  645. else if (varFeedbackSink.vt == VT_DISPATCH)
  646. {
  647. hr = varFeedbackSink.pdispVal->QueryInterface(IID_ISsrFeedbackSink,
  648. (LPVOID*)&m_srpFeedback);
  649. }
  650. if (hr == S_FALSE)
  651. {
  652. hr = E_INVALIDARG;
  653. }
  654. ::ReleaseMutex(m_hLogMutex);
  655. return hr;
  656. }
  657. /*
  658. Routine Description:
  659. Name:
  660. CFBLogMgr::LogFeedback
  661. Functionality:
  662. Will log/feedback ULONG informaiton for SSR Engine's custom behavior.
  663. Virtual:
  664. no.
  665. Arguments:
  666. lSsrFbLogMsg - This parameter contains two parts: everything under SSR_FB_ALL_MASK
  667. is the ssr feedback message. Other bits are for logging perposes.
  668. dwErrorCode - The error code.
  669. ulDetail - The detail of the information in integer format.
  670. uCauseResID - The cause's resource ID. This helps us to localize. If this
  671. value is 0, then it means no valid resource ID.
  672. Return Value:
  673. none.
  674. Notes:
  675. */
  676. void
  677. CFBLogMgr::LogFeedback (
  678. IN LONG lSsrFbLogMsg,
  679. IN DWORD dwErrorCode,
  680. IN LPCWSTR pwszObjDetail,
  681. IN ULONG uCauseResID
  682. )
  683. {
  684. bool bNeedFB = NeedFeedback(lSsrFbLogMsg);
  685. bool bNeedLog = NeedLog(lSsrFbLogMsg);
  686. if (!bNeedFB && !bNeedLog)
  687. {
  688. return;
  689. }
  690. HRESULT hr = S_OK;
  691. LONG lSsrFbMsg = lSsrFbLogMsg & SSR_FB_ALL_MASK;
  692. //
  693. // hex for DWORD is 10 wchar long
  694. //
  695. LPWSTR pwszCode = new WCHAR[s_dwErrorLen + g_dwHexDwordLen];
  696. if (pwszCode != NULL)
  697. {
  698. CComBSTR bstrLogStr;
  699. hr = GetLogString(uCauseResID, dwErrorCode, pwszObjDetail, lSsrFbLogMsg, &bstrLogStr);
  700. if (SUCCEEDED(hr) && bNeedFB)
  701. {
  702. //
  703. // need to feedback
  704. //
  705. VARIANT var;
  706. var.vt = VT_UI4;
  707. var.ulVal = dwErrorCode;
  708. m_srpFeedback->OnNotify(lSsrFbMsg, var, bstrLogStr);
  709. }
  710. if (SUCCEEDED(hr) && bNeedLog)
  711. {
  712. //
  713. // need to log
  714. //
  715. m_pLog->LogString(bstrLogStr);
  716. }
  717. delete [] pwszCode;
  718. }
  719. }
  720. /*
  721. Routine Description:
  722. Name:
  723. CFBLogMgr::LogFeedback
  724. Functionality:
  725. Will log/feedback string informaiton for SSR Engine's custom behavior.
  726. Virtual:
  727. no.
  728. Arguments:
  729. lSsrFbLogMsg - This parameter contains two parts: everything under SSR_FB_ALL_MASK
  730. is the ssr feedback message. Other bits are for logging perposes.
  731. pwszError - The error test.
  732. pwszObjDetail - Some extra informaiton about the target "object"
  733. uCauseResID - The cause's resource ID. This helps us to localize. If this
  734. value is 0, then it means no valid resource ID.
  735. Return Value:
  736. none.
  737. Notes:
  738. */
  739. void
  740. CFBLogMgr::LogFeedback (
  741. IN LONG lSsrFbLogMsg,
  742. IN LPCWSTR pwszError,
  743. IN LPCWSTR pwszObjDetail,
  744. IN ULONG uCauseResID
  745. )
  746. {
  747. //
  748. // See if we need to send feedback notification or logging
  749. //
  750. bool bNeedFB = NeedFeedback(lSsrFbLogMsg);
  751. bool bNeedLog = NeedLog(lSsrFbLogMsg);
  752. LONG lSsrFbMsg = lSsrFbLogMsg & SSR_FB_ALL_MASK;
  753. if (!bNeedFB && !bNeedLog)
  754. {
  755. return;
  756. }
  757. HRESULT hr = S_OK;
  758. CComBSTR bstrLogStr;
  759. hr = GetLogString(uCauseResID, pwszError, pwszObjDetail, lSsrFbLogMsg, &bstrLogStr);
  760. if (SUCCEEDED(hr) && bNeedFB)
  761. {
  762. //
  763. // need to feedback
  764. //
  765. CComVariant var(pwszError);
  766. m_srpFeedback->OnNotify(lSsrFbMsg, var, bstrLogStr);
  767. }
  768. if (SUCCEEDED(hr) && bNeedLog)
  769. {
  770. //
  771. // need to log
  772. //
  773. m_pLog->LogString(bstrLogStr);
  774. }
  775. }
  776. /*
  777. Routine Description:
  778. Name:
  779. CFBLogMgr::LogError
  780. Functionality:
  781. Will log the error.
  782. Virtual:
  783. no.
  784. Arguments:
  785. dwErrorCode - The error code
  786. pwszMember - The member's name. Can be NULL.
  787. pwszExtraInfo - Extra inforamtion. Can be NULL.
  788. Return Value:
  789. none.
  790. Notes:
  791. */
  792. void
  793. CFBLogMgr::LogError (
  794. IN DWORD dwErrorCode,
  795. IN LPCWSTR pwszMember,
  796. IN LPCWSTR pwszExtraInfo
  797. )
  798. {
  799. if (m_pLog != NULL)
  800. {
  801. CComBSTR bstrErrorText;
  802. HRESULT hr = m_pLog->GetErrorText(dwErrorCode,
  803. SSR_LOG_ERROR_TYPE_COM,
  804. &bstrErrorText
  805. );
  806. if (SUCCEEDED(hr))
  807. {
  808. //
  809. // if we have a member, then put a separator and then
  810. // append the member's name
  811. //
  812. if (pwszMember != NULL && *pwszMember != L'\0')
  813. {
  814. bstrErrorText += s_pwszSep;
  815. bstrErrorText += pwszMember;
  816. }
  817. //
  818. // if we have extra info, then put a separator and then
  819. // append the extra info
  820. //
  821. if (pwszExtraInfo != NULL && *pwszExtraInfo != L'\0')
  822. {
  823. bstrErrorText += s_pwszSep;
  824. bstrErrorText += pwszExtraInfo;
  825. }
  826. m_pLog->PrivateLogString(bstrErrorText);
  827. }
  828. }
  829. }
  830. /*
  831. Routine Description:
  832. Name:
  833. CFBLogMgr::LogString
  834. Functionality:
  835. Will log the error.
  836. Virtual:
  837. no.
  838. Arguments:
  839. dwResID - The resource ID
  840. pwszDetail - if not NULL, we will insert this string into
  841. the string from the resource.
  842. Return Value:
  843. none.
  844. Notes:
  845. Caller must guarantee that the resource string contains
  846. formatting info if pwszDetail is not NULL.
  847. */
  848. void
  849. CFBLogMgr::LogString (
  850. IN DWORD dwResID,
  851. IN LPCWSTR pwszDetail
  852. )
  853. {
  854. if (m_pLog != NULL)
  855. {
  856. CComBSTR strText;
  857. //
  858. // load the string
  859. //
  860. if (strText.LoadString(dwResID))
  861. {
  862. LPWSTR pwszLogText = NULL;
  863. bool bReleaseLogText = false;
  864. //
  865. // need to reformat the text if pwszDetail is not NULL
  866. //
  867. if (pwszDetail != NULL)
  868. {
  869. DWORD dwDetailLen = (pwszDetail == NULL) ? 0 : wcslen(pwszDetail);
  870. dwDetailLen += strText.Length() + 1;
  871. pwszLogText = new WCHAR[dwDetailLen];
  872. if (pwszLogText != NULL)
  873. {
  874. _snwprintf(pwszLogText, dwDetailLen, strText, pwszDetail);
  875. bReleaseLogText = true;
  876. }
  877. }
  878. else
  879. {
  880. pwszLogText = strText.m_str;
  881. }
  882. if (pwszLogText != NULL)
  883. {
  884. m_pLog->PrivateLogString(pwszLogText);
  885. }
  886. if (bReleaseLogText)
  887. {
  888. delete [] pwszLogText;
  889. }
  890. }
  891. }
  892. }
  893. /*
  894. Routine Description:
  895. Name:
  896. CFBLogMgr::GetLogObject
  897. Functionality:
  898. Return the ISsrLog Object wrapped up by this class in VARIANT.
  899. Virtual:
  900. no.
  901. Arguments:
  902. pvarVal - Receives the ISsrLog object
  903. Return Value:
  904. none.
  905. Notes:
  906. */
  907. HRESULT CFBLogMgr::GetLogObject (
  908. OUT VARIANT * pvarVal
  909. )
  910. {
  911. HRESULT hr = S_FALSE;
  912. ::VariantInit(pvarVal);
  913. if (m_pLog)
  914. {
  915. CComPtr<ISsrLog> srpObj;
  916. hr = m_pLog->QueryInterface(IID_ISsrLog, (LPVOID*)&srpObj);
  917. if (S_OK == hr)
  918. {
  919. pvarVal->vt = VT_DISPATCH;
  920. pvarVal->pdispVal = srpObj.Detach();
  921. }
  922. }
  923. return hr;
  924. }
  925. /*
  926. Routine Description:
  927. Name:
  928. CFBLogMgr::GetLogString
  929. Functionality:
  930. Return the ISsrLog Object wrapped up by this class in VARIANT.
  931. Virtual:
  932. no.
  933. Arguments:
  934. uCauseResID - The resource ID for the cause this log/feedback information
  935. pwszText - Whatever text the caller want to pass.
  936. pwszObjDetail - The target object or info detail
  937. lSsrMsg - The msg will eventually affect how detail our logging information
  938. will be. Currently, it is not used.
  939. pbstrLogStr - Receives the formatted single piece of text.
  940. Return Value:
  941. Success: S_OK.
  942. Failure: various error codes
  943. Notes:
  944. */
  945. HRESULT
  946. CFBLogMgr::GetLogString (
  947. IN ULONG uCauseResID,
  948. IN LPCWSTR pwszText,
  949. IN LPCWSTR pwszObjDetail,
  950. IN LONG lSsrMsg,
  951. OUT BSTR * pbstrLogStr
  952. )const
  953. {
  954. UNREFERENCED_PARAMETER(lSsrMsg);
  955. if (pbstrLogStr == NULL)
  956. {
  957. return E_INVALIDARG;
  958. }
  959. //
  960. // if verbose logging, then we will prefix the log text with
  961. // the heading.
  962. //
  963. *pbstrLogStr = NULL;
  964. CComBSTR bstrLogText = (m_bVerbose) ? m_bstrVerboseHeading : L"";
  965. if (bstrLogText.m_str == NULL)
  966. {
  967. return E_OUTOFMEMORY;
  968. }
  969. if (uCauseResID != g_dwResNothing)
  970. {
  971. CComBSTR bstrRes;
  972. if (bstrRes.LoadString(uCauseResID))
  973. {
  974. bstrRes += s_pwszSep;
  975. bstrLogText += bstrRes;
  976. }
  977. }
  978. if (pwszText != NULL)
  979. {
  980. bstrLogText += s_pwszSep;
  981. bstrLogText += pwszText;
  982. }
  983. if (pwszObjDetail != NULL)
  984. {
  985. bstrLogText += s_pwszSep;
  986. bstrLogText += pwszObjDetail;
  987. }
  988. *pbstrLogStr = bstrLogText.Detach();
  989. return (*pbstrLogStr == NULL) ? E_OUTOFMEMORY : S_OK;
  990. }
  991. /*
  992. Routine Description:
  993. Name:
  994. CFBLogMgr::GetLogString
  995. Functionality:
  996. Return the ISsrLog Object wrapped up by this class in VARIANT.
  997. Virtual:
  998. no.
  999. Arguments:
  1000. uCauseResID - The resource ID for the cause this log/feedback information
  1001. lSsrFbMsg - The feedback msg will eventually be used to determine how
  1002. detail (verbose) the information will be logged. Currently,
  1003. it is not used.
  1004. pbstrDescription- Receives the description text.
  1005. Return Value:
  1006. Success: S_OK.
  1007. Failure: various error codes
  1008. Notes:
  1009. */
  1010. HRESULT
  1011. CFBLogMgr::GetLogString (
  1012. IN ULONG uCauseResID,
  1013. IN DWORD dwErrorCode,
  1014. IN LPCWSTR pwszObjDetail,
  1015. IN LONG lSsrFbMsg,
  1016. OUT BSTR * pbstrLogStr
  1017. )const
  1018. {
  1019. UNREFERENCED_PARAMETER( lSsrFbMsg );
  1020. if (pbstrLogStr == NULL)
  1021. {
  1022. return E_INVALIDARG;
  1023. }
  1024. *pbstrLogStr = NULL;
  1025. CComBSTR bstrLogText = (m_bVerbose) ? m_bstrVerboseHeading : L"";
  1026. if (bstrLogText.m_str == NULL)
  1027. {
  1028. return E_OUTOFMEMORY;
  1029. }
  1030. if (uCauseResID != 0)
  1031. {
  1032. CComBSTR bstrRes;
  1033. if (bstrRes.LoadString(uCauseResID))
  1034. {
  1035. bstrRes += s_pwszSep;
  1036. bstrLogText += bstrRes;
  1037. }
  1038. }
  1039. HRESULT hr = S_OK;
  1040. //
  1041. // get the detail based on the error code
  1042. //
  1043. if (m_pLog != NULL)
  1044. {
  1045. CComBSTR bstrDetail;
  1046. hr = m_pLog->GetErrorText(dwErrorCode,
  1047. SSR_LOG_ERROR_TYPE_COM,
  1048. &bstrDetail
  1049. );
  1050. if (SUCCEEDED(hr))
  1051. {
  1052. bstrLogText += bstrDetail;
  1053. if (pwszObjDetail != NULL)
  1054. {
  1055. bstrLogText += s_pwszSep;
  1056. bstrLogText += pwszObjDetail;
  1057. }
  1058. }
  1059. }
  1060. *pbstrLogStr = bstrLogText.Detach();
  1061. return (*pbstrLogStr == NULL) ? E_OUTOFMEMORY : hr;
  1062. }
  1063. /*
  1064. Routine Description:
  1065. Name:
  1066. CFBLogMgr::SetTotalSteps
  1067. Functionality:
  1068. Inform the sink (if any) that the entire action will take these many
  1069. steps to complte. Later on, we will use Steps function to inform the sink
  1070. that that many steps have just been completed. This forms the notication
  1071. for progress feedback
  1072. Virtual:
  1073. no.
  1074. Arguments:
  1075. dwTotal - The total number of steps for the entire process to complete
  1076. Return Value:
  1077. none.
  1078. Notes:
  1079. */
  1080. void
  1081. CFBLogMgr::SetTotalSteps (
  1082. IN DWORD dwTotal
  1083. )
  1084. {
  1085. DWORD dwWait = ::WaitForSingleObject(m_hLogMutex, INFINITE);
  1086. if (dwWait != WAIT_OBJECT_0 && dwWait != WAIT_ABANDONED)
  1087. {
  1088. return;
  1089. }
  1090. m_dwRemainSteps = dwTotal;
  1091. if (m_srpFeedback != NULL)
  1092. {
  1093. VARIANT var;
  1094. var.vt = VT_UI4;
  1095. var.ulVal = dwTotal;
  1096. static CComBSTR bstrTotalSteps;
  1097. if (bstrTotalSteps.m_str == NULL)
  1098. {
  1099. bstrTotalSteps.LoadString(IDS_TOTAL_STEPS);
  1100. }
  1101. if (bstrTotalSteps.m_str != NULL)
  1102. {
  1103. m_srpFeedback->OnNotify(SSR_FB_TOTAL_STEPS, var, bstrTotalSteps);
  1104. }
  1105. }
  1106. ::ReleaseMutex(m_hLogMutex);
  1107. }
  1108. /*
  1109. Routine Description:
  1110. Name:
  1111. CFBLogMgr::Steps
  1112. Functionality:
  1113. Inform the sink (if any) that these many steps have just been completed.
  1114. This count is not the total number of steps that have been completed to
  1115. this point. It is the steps since last notification that have been done.
  1116. Virtual:
  1117. no.
  1118. Arguments:
  1119. dwSteps - The completed steps done since last notification
  1120. Return Value:
  1121. none.
  1122. Notes:
  1123. */
  1124. void
  1125. CFBLogMgr::Steps (
  1126. IN DWORD dwSteps
  1127. )
  1128. {
  1129. DWORD dwWait = ::WaitForSingleObject(m_hLogMutex, INFINITE);
  1130. if (dwWait != WAIT_OBJECT_0 && dwWait != WAIT_ABANDONED)
  1131. {
  1132. return;
  1133. }
  1134. //
  1135. // we will never progress more than the remaining steps
  1136. //
  1137. DWORD dwStepsToNotify = dwSteps;
  1138. if (m_dwRemainSteps < dwSteps)
  1139. {
  1140. dwStepsToNotify = m_dwRemainSteps;
  1141. }
  1142. m_dwRemainSteps -= dwStepsToNotify;
  1143. if (m_srpFeedback != NULL)
  1144. {
  1145. VARIANT var;
  1146. var.vt = VT_UI4;
  1147. var.ulVal = dwStepsToNotify;
  1148. CComBSTR bstrNoDetail;
  1149. m_srpFeedback->OnNotify(SSR_FB_STEPS_JUST_DONE, var, bstrNoDetail);
  1150. }
  1151. ::ReleaseMutex(m_hLogMutex);
  1152. }
  1153. /*
  1154. Routine Description:
  1155. Name:
  1156. CFBLogMgr::TerminateFeedback
  1157. Functionality:
  1158. We will let go the feedback sink object once our action is
  1159. completed. This is also a place to progress any remaining steps.
  1160. Virtual:
  1161. no.
  1162. Arguments:
  1163. None.
  1164. Return Value:
  1165. none.
  1166. Notes:
  1167. Many errors will cause a function to prematurely
  1168. return and cause the total steps not going down to zero,
  1169. this is a good place to do the final steps count balance.
  1170. */
  1171. void
  1172. CFBLogMgr::TerminateFeedback()
  1173. {
  1174. DWORD dwWait = ::WaitForSingleObject(m_hLogMutex, INFINITE);
  1175. if (dwWait != WAIT_OBJECT_0 && dwWait != WAIT_ABANDONED)
  1176. {
  1177. return;
  1178. }
  1179. Steps(m_dwRemainSteps);
  1180. m_srpFeedback.Release();
  1181. ::ReleaseMutex(m_hLogMutex);
  1182. }
  1183. /*
  1184. Routine Description:
  1185. Name:
  1186. CFBLogMgr::SetMemberAction
  1187. Functionality:
  1188. This function sets the member and action information
  1189. that can be used for verbose logging. We will as a result
  1190. create a log heading which will be added to the log when
  1191. LogFeedback functions are called.
  1192. Virtual:
  1193. no.
  1194. Arguments:
  1195. pwszMember - The member's name
  1196. pwszAction - the action
  1197. Return Value:
  1198. none.
  1199. Notes:
  1200. */
  1201. void
  1202. CFBLogMgr::SetMemberAction (
  1203. IN LPCWSTR pwszMember,
  1204. IN LPCWSTR pwszAction
  1205. )
  1206. {
  1207. //
  1208. // wait for the mutex.
  1209. //
  1210. DWORD dwWait = ::WaitForSingleObject(m_hLogMutex, INFINITE);
  1211. if (dwWait != WAIT_OBJECT_0 && dwWait != WAIT_ABANDONED)
  1212. {
  1213. return;
  1214. }
  1215. m_bstrVerboseHeading.Empty();
  1216. //
  1217. // for better formatting, we will reserve 20 characters for
  1218. // the name of member and action. For that need, we will prepare
  1219. // an array containing only space characters.
  1220. //
  1221. const DWORD PART_LENGTH = 20;
  1222. WCHAR wszHeading[ 2 * PART_LENGTH + 1];
  1223. ::memset(wszHeading, 1, 2 * PART_LENGTH * sizeof(WCHAR));
  1224. wszHeading[2 * PART_LENGTH] = L'\0';
  1225. _wcsset(wszHeading, L' ');
  1226. //
  1227. // if the given member and action's total length is more
  1228. // than our pre-determined buffer, then we are going to
  1229. // let the heading grow
  1230. //
  1231. DWORD dwMemLen = wcslen(pwszMember);
  1232. DWORD dwActLen = wcslen(pwszAction);
  1233. if (dwMemLen + dwActLen > 2 * PART_LENGTH)
  1234. {
  1235. //
  1236. // just let the heading grow to whatever length it needs
  1237. //
  1238. m_bstrVerboseHeading = pwszMember;
  1239. m_bstrVerboseHeading += s_pwszSep;
  1240. m_bstrVerboseHeading += pwszAction;
  1241. m_bstrVerboseHeading += s_pwszSep;
  1242. }
  1243. else
  1244. {
  1245. //
  1246. // copy the member
  1247. //
  1248. LPWSTR pwszHead = wszHeading;
  1249. for (ULONG i = 0; i < dwMemLen; i++)
  1250. {
  1251. *pwszHead = pwszMember[i];
  1252. pwszHead++;
  1253. }
  1254. if (i < PART_LENGTH)
  1255. {
  1256. pwszHead = wszHeading + PART_LENGTH;
  1257. }
  1258. //
  1259. // copy the action
  1260. //
  1261. for (i = 0; i < dwActLen; i++)
  1262. {
  1263. *pwszHead = pwszAction[i];
  1264. pwszHead++;
  1265. }
  1266. m_bstrVerboseHeading.m_str = ::SysAllocString(wszHeading);
  1267. }
  1268. ::ReleaseMutex(m_hLogMutex);
  1269. }