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

// SSRLog.cpp : Implementation of CSSRLog
#include "stdafx.h"
#include "SSRTE.h"
#include "SSRLog.h"
#include <Userenv.h>
#include "global.h"
#include "SSRMembership.h"
extern CComModule _Module;
/////////////////////////////////////////////////////////////////////////////
// CSsrLog
static LPCWSTR s_pszDefLogFileName = L"Log.txt";
static LPCWSTR s_pwszSource = L"Source=";
static LPCWSTR s_pwszDetail = L"Detail=";
static LPCWSTR s_pwszErrorCode = L"ErrorCode=";
static LPCWSTR s_pwszErrorTextNotFound = L"Error text can't be found";
static LPCWSTR s_pwszNotSpecified = L"Not specified";
static LPCWSTR s_pwszSep = L" ";
static LPCWSTR s_pwszCRLF = L"\r\n";
static const DWORD s_dwSourceLen = wcslen(s_pwszSource);
static const DWORD s_dwSepLen = wcslen(s_pwszSep);
static const DWORD s_dwErrorLen = wcslen(s_pwszErrorCode);
static const DWORD s_dwDetailLen = wcslen(s_pwszDetail);
static const DWORD s_dwCRLFLen = wcslen(s_pwszCRLF);
/*
Routine Description:
Name:
CSsrLog::CSsrLog
Functionality:
constructor
Virtual:
no.
Arguments:
none.
Return Value:
none.
Notes:
*/
CSsrLog::CSsrLog()
: m_bstrLogFile(s_pszDefLogFileName)
{
}
/*
Routine Description:
Name:
CSsrLog::~CSsrLog
Functionality:
destructor
Virtual:
yes.
Arguments:
none.
Return Value:
none.
Notes:
*/
CSsrLog::~CSsrLog()
{
}
/*
Routine Description:
Name:
CSsrLog::LogResult
Functionality:
Log error code information. This function will do a message format function
and then log both the error code and the formatted msg.
Virtual:
yes.
Arguments:
bstrSrc - source of error. This is just indicative of the information source.
dwErrorCode - The error code itself.
dwCodeType - The type of error code. We use WMI extensively,
its error code lookup is slightly different from others.
Return Value:
?.
Notes:
The error code may not be an error. It can be a success code.
*/
STDMETHODIMP
CSsrLog::LogResult (
BSTR bstrSrc,
LONG lErrorCode,
LONG lCodeType
)
{
HRESULT hr = S_OK;
if (m_bstrLogFilePath.Length() == 0)
{
hr = CreateLogFilePath();
if (FAILED(hr))
{
return hr;
}
}
//
// default to not able to find the error text
//
LPCWSTR pwszLog = s_pwszErrorTextNotFound;
//
// will hold the hex decimal of the error code in case the
// error code can't be translated into a string
//
CComBSTR bstrErrorText;
hr = GetErrorText(lErrorCode, lCodeType, &bstrErrorText);
if (SUCCEEDED(hr))
{
//
// if we can get error test, then this is the log we want
//
pwszLog = bstrErrorText;
}
//
// now write to the file
//
LPCWSTR pwszSrcString = s_pwszNotSpecified;
if (bstrSrc != NULL && *bstrSrc != L'\0')
{
pwszSrcString = bstrSrc;
}
//
// one error log is like this: Source=XXXX*****ErrorCode=XXXX*****Detail=XXXX
// where XXXX represent any text and the ***** represents the separater.
//
int iLen = s_dwSourceLen +
wcslen(pwszSrcString) +
s_dwSepLen +
s_dwErrorLen +
10 + // hex decimal has 10 chars for DWORD
s_dwSepLen +
s_dwDetailLen +
wcslen(pwszLog);
LPWSTR pwszLogString = new WCHAR[iLen + 1];
//
// format the log string and do the logging
//
if (pwszLogString != NULL)
{
_snwprintf(pwszLogString,
iLen + 1,
L"%s%s%s%s0x%X%s%s%s",
s_pwszSource,
pwszSrcString,
s_pwszSep,
s_pwszErrorCode,
lErrorCode,
s_pwszSep,
s_pwszDetail,
pwszLog
);
hr = PrivateLogString(pwszLogString);
delete [] pwszLogString;
pwszLogString = NULL;
}
return hr;
}
/*
Routine Description:
Name:
CSsrLog::GetErrorText
Functionality:
Lookup the error text using the error code
Virtual:
no.
Arguments:
lErrorCode - The error code itself.
lCodeType - The type of error code. We use WMI extensively,
its error code lookup is slightly different from others.
pbstrErrorText - The error text corresponding to this error code
Return Value:
?.
Notes:
The error code may not be an error. It can be a success code.
*/
HRESULT
CSsrLog::GetErrorText (
IN LONG lErrorCode,
IN LONG lCodeType,
OUT BSTR * pbstrErrorText
)
{
if (pbstrErrorText == NULL)
{
return E_INVALIDARG;
}
*pbstrErrorText = NULL;
LPVOID pMsgBuf = NULL;
HRESULT hr = S_OK;
if (lCodeType == SSR_LOG_ERROR_TYPE_Wbem)
{
hr = GetWbemErrorText(lErrorCode, pbstrErrorText);
}
else
{
DWORD flag = FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS;
DWORD dwRet = ::FormatMessage(
flag,
NULL,
lErrorCode,
0, // Default language
(LPWSTR) &pMsgBuf,
0,
NULL
);
//
// trying our own errors if this failes to give us anything
//
if (dwRet == 0)
{
flag = FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_HMODULE |
FORMAT_MESSAGE_IGNORE_INSERTS;
dwRet = ::FormatMessage(
flag,
_Module.m_hInst,
lErrorCode,
0, // Default language
(LPWSTR) &pMsgBuf,
0,
NULL
);
}
if (dwRet != 0)
{
*pbstrErrorText = ::SysAllocString((LPCWSTR)pMsgBuf);
if (*pbstrErrorText == NULL)
{
hr = E_OUTOFMEMORY;
}
}
else
{
hr = HRESULT_FROM_WIN32(GetLastError());
}
}
if (pMsgBuf != NULL)
{
::LocalFree( pMsgBuf );
}
if (FAILED(hr) && E_OUTOFMEMORY != hr)
{
if (*pbstrErrorText != NULL)
{
::SysFreeString(*pbstrErrorText);
*pbstrErrorText = NULL;
}
//
// fall back to just give the error code
//
WCHAR wszErrorCode[g_dwHexDwordLen];
_snwprintf(wszErrorCode, g_dwHexDwordLen, L"0x%X", lErrorCode);
*pbstrErrorText = ::SysAllocString(wszErrorCode);
hr = (*pbstrErrorText != NULL) ? S_OK : E_OUTOFMEMORY;
}
return hr;
}
/*
Routine Description:
Name:
CSsrLog::PrivateLogString
Functionality:
Just log the string to the log file. We don't attempt to do any formatting.
Virtual:
no.
Arguments:
pwszLogRecord - The string to be logged into the log file
Return Value:
Success: S_OK.
Failure: various error codes.
Notes:
*/
HRESULT
CSsrLog::PrivateLogString (
IN LPCWSTR pwszLogRecord
)
{
HRESULT hr = S_OK;
if (m_bstrLogFilePath.Length() == 0)
{
hr = CreateLogFilePath();
if (FAILED(hr))
{
return hr;
}
}
//
// $consider:shawnwu,
// Right now, we write to the log file each time the function is called.
// That might cause the problem too much file access and becomes a performance
// issue. We might want to consider optimizing this.
//
DWORD dwWait = ::WaitForSingleObject(g_fblog.m_hLogMutex, INFINITE);
//
// $undone:shawnwu, some error happened, should we continue to log?
//
if (dwWait != WAIT_OBJECT_0 && dwWait != WAIT_ABANDONED)
{
return E_SSR_LOG_FILE_MUTEX_WAIT;
}
HANDLE hFile = ::CreateFile(m_bstrLogFilePath,
GENERIC_WRITE,
0, // not shared
NULL,
OPEN_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
NULL
);
if (hFile != INVALID_HANDLE_VALUE)
{
//
// append the text to the end of the file
//
::SetFilePointer (hFile, 0, NULL, FILE_END);
DWORD dwBytesWritten = 0;
if ( 0 == ::WriteFile (hFile,
(LPCVOID)pwszLogRecord,
wcslen(pwszLogRecord) * sizeof(WCHAR),
&dwBytesWritten,
NULL) )
{
hr = HRESULT_FROM_WIN32(GetLastError());
}
//
// put a line break
//
if ( 0 == ::WriteFile (hFile,
(LPCVOID)s_pwszCRLF,
s_dwCRLFLen * sizeof(WCHAR),
&dwBytesWritten,
NULL) )
{
hr = HRESULT_FROM_WIN32(GetLastError());
}
::CloseHandle(hFile);
}
else
{
hr = HRESULT_FROM_WIN32(GetLastError());
}
::ReleaseMutex(g_fblog.m_hLogMutex);
return hr;
}
/*
Routine Description:
Name:
CSsrLog::get_LogFilePath
Functionality:
Will return the full path of the log file this object uses
Virtual:
yes.
Arguments:
pbstrLogFilePath - receives the current log file's full path.
Return Value:
Success: S_OK;
Failure: E_OUTOFMEMORY
Notes:
*/
STDMETHODIMP
CSsrLog::get_LogFilePath (
OUT BSTR * pbstrLogFilePath /*[out, retval]*/
)
{
HRESULT hr = S_OK;
if (pbstrLogFilePath == NULL)
{
return E_INVALIDARG;
}
if (m_bstrLogFilePath.Length() == 0)
{
hr = CreateLogFilePath();
}
if (SUCCEEDED(hr))
{
*pbstrLogFilePath = ::SysAllocString(m_bstrLogFilePath);
}
return (*pbstrLogFilePath != NULL) ? S_OK : E_OUTOFMEMORY;
}
/*
Routine Description:
Name:
CSsrLog::put_LogFile
Functionality:
Will set the log file.
Virtual:
yes.
Arguments:
bstrLogFile - the file name (plus extension) the caller wants this
object to use.
Return Value:
?.
Notes:
The bstrLogFile must be just a file name without any directory path
*/
STDMETHODIMP
CSsrLog::put_LogFile (
IN BSTR bstrLogFile
)
{
HRESULT hr = S_OK;
//
// you can't give me a invalid log file name
// It also must just be an name
//
if (bstrLogFile == NULL ||
*bstrLogFile == L'\0' ||
::wcsstr(bstrLogFile, L"\\") != NULL)
{
hr = E_INVALIDARG;
}
else
{
m_bstrLogFile = bstrLogFile;
hr = CreateLogFilePath();
}
return hr;
}
/*
Routine Description:
Name:
CSsrLog::CreateLogFilePath
Functionality:
Create the log file's path.
Virtual:
no.
Arguments:
None
Return Value:
Success: S_OK;
Failure: E_OUTOFMEMORY
Notes:
The bstrLogFile must be just a file name without any directory path
*/
HRESULT
CSsrLog::CreateLogFilePath ( )
{
if (wcslen(g_wszSsrRoot) + 1 + wcslen(g_pwszLogs) + 1 + wcslen(m_bstrLogFile) > MAX_PATH)
{
return HRESULT_FROM_WIN32(ERROR_FILENAME_EXCED_RANGE);
}
WCHAR wcLogFilePath[MAX_PATH + 1];
_snwprintf(wcLogFilePath,
MAX_PATH + 1,
L"%s%c%s%c%s",
g_wszSsrRoot,
L'\\',
g_pwszLogs,
L'\\',
m_bstrLogFile
);
m_bstrLogFilePath.Empty(); // so that in case of out-of-memory, it will be NULL
m_bstrLogFilePath = wcLogFilePath;
return m_bstrLogFilePath != NULL ? S_OK : E_OUTOFMEMORY;
}
/*
Routine Description:
Name:
CSsrLog::GetWbemErrorText
Functionality:
Private helper to lookup WMI error text based on the error code
Virtual:
yes.
Arguments:
hrCode - HRESULT code.
pbstrErrText - The out paramter that receives the text of the error code
Return Value:
Success:
S_OK if everything is OK.
S_FALSE if we can't locate the error text. in that case, we fall back
to give text, which is just the text representation of the
error code
Failure:
various error codes.
Notes:
The error code may not be an error. It can be a success code.
*/
HRESULT
CSsrLog::GetWbemErrorText (
HRESULT hrCode,
BSTR * pbstrErrText
)
{
if (pbstrErrText == NULL)
{
return E_INVALIDARG;
}
*pbstrErrText = NULL;
HRESULT hr = S_OK;
if (m_srpStatusCodeText == NULL)
{
hr = ::CoCreateInstance(CLSID_WbemStatusCodeText,
0,
CLSCTX_INPROC_SERVER,
IID_IWbemStatusCodeText,
(LPVOID*)&m_srpStatusCodeText
);
}
if (m_srpStatusCodeText)
{
//
// IWbemStatusCodeText is to translate the HRESULT to text
//
hr = m_srpStatusCodeText->GetErrorCodeText(hrCode, 0, 0, pbstrErrText);
}
if (FAILED(hr) || *pbstrErrText == NULL)
{
//
// we fall back to just formatting the error code.
// Hex of DWORD has 10 WCHARs
//
WCHAR wszCode[g_dwHexDwordLen];
_snwprintf(wszCode, g_dwHexDwordLen, L"0x%X", hrCode);
*pbstrErrText = ::SysAllocString(wszCode);
if (*pbstrErrText != NULL)
{
hr = S_FALSE;
}
else
{
hr = E_OUTOFMEMORY;
}
}
else
{
hr = S_OK;
}
return hr;
}
//--------------------------------------------------------------
// implementation of CFBLogMgr
/*
Routine Description:
Name:
CFBLogMgr::CFBLogMgr
Functionality:
constructor
Virtual:
no.
Arguments:
none.
Return Value:
none.
Notes:
*/
CFBLogMgr::CFBLogMgr()
: m_hLogMutex(NULL),
m_dwRemainSteps(0),
m_bVerbose(false)
{
HRESULT hr = CComObject<CSsrLog>::CreateInstance(&m_pLog);
if (SUCCEEDED(hr))
{
m_pLog->AddRef();
m_hLogMutex = ::CreateMutex(NULL, FALSE, L"ISsrLogMutex");
}
//
// see if we are logging verbose or not
//
HKEY hRootKey = NULL;
LONG lStatus = ::RegOpenKeyEx(
HKEY_LOCAL_MACHINE,
g_pwszSSRRegRoot,
0,
KEY_READ,
&hRootKey
);
if (ERROR_SUCCESS == lStatus)
{
DWORD dwSize = sizeof(DWORD);
DWORD dwVerbose = 0;
DWORD dwType;
lStatus = ::RegQueryValueEx(hRootKey,
L"LogVerbose",
NULL,
&dwType,
(LPBYTE)&dwVerbose,
&dwSize
);
if (ERROR_SUCCESS == lStatus)
{
m_bVerbose = (dwVerbose == 0) ? false : true;
}
::RegCloseKey(hRootKey);
}
}
/*
Routine Description:
Name:
CFBLogMgr::~CFBLogMgr
Functionality:
destructor
Virtual:
no.
Arguments:
none.
Return Value:
none.
Notes:
*/
CFBLogMgr::~CFBLogMgr()
{
if (m_pLog)
{
m_pLog->Release();
if (m_hLogMutex)
{
::CloseHandle(m_hLogMutex);
}
}
}
/*
Routine Description:
Name:
CFBLogMgr::SetFeedbackSink
Functionality:
Caches the feedback sink interface. This allows us to send
feedback. If the in parameter is not a valid interface, then
we won't send feedback.
Virtual:
no.
Arguments:
varFeedbackSink - The variant that holds an ISsrFeedbackSink COM
interface pointer.
Return Value:
Success: S_OK
Failure: E_INVALIDARG;
Notes:
*/
HRESULT
CFBLogMgr::SetFeedbackSink (
IN VARIANT varFeedbackSink
)
{
DWORD dwWait = ::WaitForSingleObject(m_hLogMutex, INFINITE);
if (dwWait != WAIT_OBJECT_0 && dwWait != WAIT_ABANDONED)
{
return HRESULT_FROM_WIN32(GetLastError());
}
m_srpFeedback.Release();
HRESULT hr = S_FALSE;
if (varFeedbackSink.vt == VT_UNKNOWN)
{
hr = varFeedbackSink.punkVal->QueryInterface(IID_ISsrFeedbackSink,
(LPVOID*)&m_srpFeedback);
}
else if (varFeedbackSink.vt == VT_DISPATCH)
{
hr = varFeedbackSink.pdispVal->QueryInterface(IID_ISsrFeedbackSink,
(LPVOID*)&m_srpFeedback);
}
if (hr == S_FALSE)
{
hr = E_INVALIDARG;
}
::ReleaseMutex(m_hLogMutex);
return hr;
}
/*
Routine Description:
Name:
CFBLogMgr::LogFeedback
Functionality:
Will log/feedback ULONG informaiton for SSR Engine's custom behavior.
Virtual:
no.
Arguments:
lSsrFbLogMsg - This parameter contains two parts: everything under SSR_FB_ALL_MASK
is the ssr feedback message. Other bits are for logging perposes.
dwErrorCode - The error code.
ulDetail - The detail of the information in integer format.
uCauseResID - The cause's resource ID. This helps us to localize. If this
value is 0, then it means no valid resource ID.
Return Value:
none.
Notes:
*/
void
CFBLogMgr::LogFeedback (
IN LONG lSsrFbLogMsg,
IN DWORD dwErrorCode,
IN LPCWSTR pwszObjDetail,
IN ULONG uCauseResID
)
{
bool bNeedFB = NeedFeedback(lSsrFbLogMsg);
bool bNeedLog = NeedLog(lSsrFbLogMsg);
if (!bNeedFB && !bNeedLog)
{
return;
}
HRESULT hr = S_OK;
LONG lSsrFbMsg = lSsrFbLogMsg & SSR_FB_ALL_MASK;
//
// hex for DWORD is 10 wchar long
//
LPWSTR pwszCode = new WCHAR[s_dwErrorLen + g_dwHexDwordLen];
if (pwszCode != NULL)
{
CComBSTR bstrLogStr;
hr = GetLogString(uCauseResID, dwErrorCode, pwszObjDetail, lSsrFbLogMsg, &bstrLogStr);
if (SUCCEEDED(hr) && bNeedFB)
{
//
// need to feedback
//
VARIANT var;
var.vt = VT_UI4;
var.ulVal = dwErrorCode;
m_srpFeedback->OnNotify(lSsrFbMsg, var, bstrLogStr);
}
if (SUCCEEDED(hr) && bNeedLog)
{
//
// need to log
//
m_pLog->LogString(bstrLogStr);
}
delete [] pwszCode;
}
}
/*
Routine Description:
Name:
CFBLogMgr::LogFeedback
Functionality:
Will log/feedback string informaiton for SSR Engine's custom behavior.
Virtual:
no.
Arguments:
lSsrFbLogMsg - This parameter contains two parts: everything under SSR_FB_ALL_MASK
is the ssr feedback message. Other bits are for logging perposes.
pwszError - The error test.
pwszObjDetail - Some extra informaiton about the target "object"
uCauseResID - The cause's resource ID. This helps us to localize. If this
value is 0, then it means no valid resource ID.
Return Value:
none.
Notes:
*/
void
CFBLogMgr::LogFeedback (
IN LONG lSsrFbLogMsg,
IN LPCWSTR pwszError,
IN LPCWSTR pwszObjDetail,
IN ULONG uCauseResID
)
{
//
// See if we need to send feedback notification or logging
//
bool bNeedFB = NeedFeedback(lSsrFbLogMsg);
bool bNeedLog = NeedLog(lSsrFbLogMsg);
LONG lSsrFbMsg = lSsrFbLogMsg & SSR_FB_ALL_MASK;
if (!bNeedFB && !bNeedLog)
{
return;
}
HRESULT hr = S_OK;
CComBSTR bstrLogStr;
hr = GetLogString(uCauseResID, pwszError, pwszObjDetail, lSsrFbLogMsg, &bstrLogStr);
if (SUCCEEDED(hr) && bNeedFB)
{
//
// need to feedback
//
CComVariant var(pwszError);
m_srpFeedback->OnNotify(lSsrFbMsg, var, bstrLogStr);
}
if (SUCCEEDED(hr) && bNeedLog)
{
//
// need to log
//
m_pLog->LogString(bstrLogStr);
}
}
/*
Routine Description:
Name:
CFBLogMgr::LogError
Functionality:
Will log the error.
Virtual:
no.
Arguments:
dwErrorCode - The error code
pwszMember - The member's name. Can be NULL.
pwszExtraInfo - Extra inforamtion. Can be NULL.
Return Value:
none.
Notes:
*/
void
CFBLogMgr::LogError (
IN DWORD dwErrorCode,
IN LPCWSTR pwszMember,
IN LPCWSTR pwszExtraInfo
)
{
if (m_pLog != NULL)
{
CComBSTR bstrErrorText;
HRESULT hr = m_pLog->GetErrorText(dwErrorCode,
SSR_LOG_ERROR_TYPE_COM,
&bstrErrorText
);
if (SUCCEEDED(hr))
{
//
// if we have a member, then put a separator and then
// append the member's name
//
if (pwszMember != NULL && *pwszMember != L'\0')
{
bstrErrorText += s_pwszSep;
bstrErrorText += pwszMember;
}
//
// if we have extra info, then put a separator and then
// append the extra info
//
if (pwszExtraInfo != NULL && *pwszExtraInfo != L'\0')
{
bstrErrorText += s_pwszSep;
bstrErrorText += pwszExtraInfo;
}
m_pLog->PrivateLogString(bstrErrorText);
}
}
}
/*
Routine Description:
Name:
CFBLogMgr::LogString
Functionality:
Will log the error.
Virtual:
no.
Arguments:
dwResID - The resource ID
pwszDetail - if not NULL, we will insert this string into
the string from the resource.
Return Value:
none.
Notes:
Caller must guarantee that the resource string contains
formatting info if pwszDetail is not NULL.
*/
void
CFBLogMgr::LogString (
IN DWORD dwResID,
IN LPCWSTR pwszDetail
)
{
if (m_pLog != NULL)
{
CComBSTR strText;
//
// load the string
//
if (strText.LoadString(dwResID))
{
LPWSTR pwszLogText = NULL;
bool bReleaseLogText = false;
//
// need to reformat the text if pwszDetail is not NULL
//
if (pwszDetail != NULL)
{
DWORD dwDetailLen = (pwszDetail == NULL) ? 0 : wcslen(pwszDetail);
dwDetailLen += strText.Length() + 1;
pwszLogText = new WCHAR[dwDetailLen];
if (pwszLogText != NULL)
{
_snwprintf(pwszLogText, dwDetailLen, strText, pwszDetail);
bReleaseLogText = true;
}
}
else
{
pwszLogText = strText.m_str;
}
if (pwszLogText != NULL)
{
m_pLog->PrivateLogString(pwszLogText);
}
if (bReleaseLogText)
{
delete [] pwszLogText;
}
}
}
}
/*
Routine Description:
Name:
CFBLogMgr::GetLogObject
Functionality:
Return the ISsrLog Object wrapped up by this class in VARIANT.
Virtual:
no.
Arguments:
pvarVal - Receives the ISsrLog object
Return Value:
none.
Notes:
*/
HRESULT CFBLogMgr::GetLogObject (
OUT VARIANT * pvarVal
)
{
HRESULT hr = S_FALSE;
::VariantInit(pvarVal);
if (m_pLog)
{
CComPtr<ISsrLog> srpObj;
hr = m_pLog->QueryInterface(IID_ISsrLog, (LPVOID*)&srpObj);
if (S_OK == hr)
{
pvarVal->vt = VT_DISPATCH;
pvarVal->pdispVal = srpObj.Detach();
}
}
return hr;
}
/*
Routine Description:
Name:
CFBLogMgr::GetLogString
Functionality:
Return the ISsrLog Object wrapped up by this class in VARIANT.
Virtual:
no.
Arguments:
uCauseResID - The resource ID for the cause this log/feedback information
pwszText - Whatever text the caller want to pass.
pwszObjDetail - The target object or info detail
lSsrMsg - The msg will eventually affect how detail our logging information
will be. Currently, it is not used.
pbstrLogStr - Receives the formatted single piece of text.
Return Value:
Success: S_OK.
Failure: various error codes
Notes:
*/
HRESULT
CFBLogMgr::GetLogString (
IN ULONG uCauseResID,
IN LPCWSTR pwszText,
IN LPCWSTR pwszObjDetail,
IN LONG lSsrMsg,
OUT BSTR * pbstrLogStr
)const
{
UNREFERENCED_PARAMETER(lSsrMsg);
if (pbstrLogStr == NULL)
{
return E_INVALIDARG;
}
//
// if verbose logging, then we will prefix the log text with
// the heading.
//
*pbstrLogStr = NULL;
CComBSTR bstrLogText = (m_bVerbose) ? m_bstrVerboseHeading : L"";
if (bstrLogText.m_str == NULL)
{
return E_OUTOFMEMORY;
}
if (uCauseResID != g_dwResNothing)
{
CComBSTR bstrRes;
if (bstrRes.LoadString(uCauseResID))
{
bstrRes += s_pwszSep;
bstrLogText += bstrRes;
}
}
if (pwszText != NULL)
{
bstrLogText += s_pwszSep;
bstrLogText += pwszText;
}
if (pwszObjDetail != NULL)
{
bstrLogText += s_pwszSep;
bstrLogText += pwszObjDetail;
}
*pbstrLogStr = bstrLogText.Detach();
return (*pbstrLogStr == NULL) ? E_OUTOFMEMORY : S_OK;
}
/*
Routine Description:
Name:
CFBLogMgr::GetLogString
Functionality:
Return the ISsrLog Object wrapped up by this class in VARIANT.
Virtual:
no.
Arguments:
uCauseResID - The resource ID for the cause this log/feedback information
lSsrFbMsg - The feedback msg will eventually be used to determine how
detail (verbose) the information will be logged. Currently,
it is not used.
pbstrDescription- Receives the description text.
Return Value:
Success: S_OK.
Failure: various error codes
Notes:
*/
HRESULT
CFBLogMgr::GetLogString (
IN ULONG uCauseResID,
IN DWORD dwErrorCode,
IN LPCWSTR pwszObjDetail,
IN LONG lSsrFbMsg,
OUT BSTR * pbstrLogStr
)const
{
UNREFERENCED_PARAMETER( lSsrFbMsg );
if (pbstrLogStr == NULL)
{
return E_INVALIDARG;
}
*pbstrLogStr = NULL;
CComBSTR bstrLogText = (m_bVerbose) ? m_bstrVerboseHeading : L"";
if (bstrLogText.m_str == NULL)
{
return E_OUTOFMEMORY;
}
if (uCauseResID != 0)
{
CComBSTR bstrRes;
if (bstrRes.LoadString(uCauseResID))
{
bstrRes += s_pwszSep;
bstrLogText += bstrRes;
}
}
HRESULT hr = S_OK;
//
// get the detail based on the error code
//
if (m_pLog != NULL)
{
CComBSTR bstrDetail;
hr = m_pLog->GetErrorText(dwErrorCode,
SSR_LOG_ERROR_TYPE_COM,
&bstrDetail
);
if (SUCCEEDED(hr))
{
bstrLogText += bstrDetail;
if (pwszObjDetail != NULL)
{
bstrLogText += s_pwszSep;
bstrLogText += pwszObjDetail;
}
}
}
*pbstrLogStr = bstrLogText.Detach();
return (*pbstrLogStr == NULL) ? E_OUTOFMEMORY : hr;
}
/*
Routine Description:
Name:
CFBLogMgr::SetTotalSteps
Functionality:
Inform the sink (if any) that the entire action will take these many
steps to complte. Later on, we will use Steps function to inform the sink
that that many steps have just been completed. This forms the notication
for progress feedback
Virtual:
no.
Arguments:
dwTotal - The total number of steps for the entire process to complete
Return Value:
none.
Notes:
*/
void
CFBLogMgr::SetTotalSteps (
IN DWORD dwTotal
)
{
DWORD dwWait = ::WaitForSingleObject(m_hLogMutex, INFINITE);
if (dwWait != WAIT_OBJECT_0 && dwWait != WAIT_ABANDONED)
{
return;
}
m_dwRemainSteps = dwTotal;
if (m_srpFeedback != NULL)
{
VARIANT var;
var.vt = VT_UI4;
var.ulVal = dwTotal;
static CComBSTR bstrTotalSteps;
if (bstrTotalSteps.m_str == NULL)
{
bstrTotalSteps.LoadString(IDS_TOTAL_STEPS);
}
if (bstrTotalSteps.m_str != NULL)
{
m_srpFeedback->OnNotify(SSR_FB_TOTAL_STEPS, var, bstrTotalSteps);
}
}
::ReleaseMutex(m_hLogMutex);
}
/*
Routine Description:
Name:
CFBLogMgr::Steps
Functionality:
Inform the sink (if any) that these many steps have just been completed.
This count is not the total number of steps that have been completed to
this point. It is the steps since last notification that have been done.
Virtual:
no.
Arguments:
dwSteps - The completed steps done since last notification
Return Value:
none.
Notes:
*/
void
CFBLogMgr::Steps (
IN DWORD dwSteps
)
{
DWORD dwWait = ::WaitForSingleObject(m_hLogMutex, INFINITE);
if (dwWait != WAIT_OBJECT_0 && dwWait != WAIT_ABANDONED)
{
return;
}
//
// we will never progress more than the remaining steps
//
DWORD dwStepsToNotify = dwSteps;
if (m_dwRemainSteps < dwSteps)
{
dwStepsToNotify = m_dwRemainSteps;
}
m_dwRemainSteps -= dwStepsToNotify;
if (m_srpFeedback != NULL)
{
VARIANT var;
var.vt = VT_UI4;
var.ulVal = dwStepsToNotify;
CComBSTR bstrNoDetail;
m_srpFeedback->OnNotify(SSR_FB_STEPS_JUST_DONE, var, bstrNoDetail);
}
::ReleaseMutex(m_hLogMutex);
}
/*
Routine Description:
Name:
CFBLogMgr::TerminateFeedback
Functionality:
We will let go the feedback sink object once our action is
completed. This is also a place to progress any remaining steps.
Virtual:
no.
Arguments:
None.
Return Value:
none.
Notes:
Many errors will cause a function to prematurely
return and cause the total steps not going down to zero,
this is a good place to do the final steps count balance.
*/
void
CFBLogMgr::TerminateFeedback()
{
DWORD dwWait = ::WaitForSingleObject(m_hLogMutex, INFINITE);
if (dwWait != WAIT_OBJECT_0 && dwWait != WAIT_ABANDONED)
{
return;
}
Steps(m_dwRemainSteps);
m_srpFeedback.Release();
::ReleaseMutex(m_hLogMutex);
}
/*
Routine Description:
Name:
CFBLogMgr::SetMemberAction
Functionality:
This function sets the member and action information
that can be used for verbose logging. We will as a result
create a log heading which will be added to the log when
LogFeedback functions are called.
Virtual:
no.
Arguments:
pwszMember - The member's name
pwszAction - the action
Return Value:
none.
Notes:
*/
void
CFBLogMgr::SetMemberAction (
IN LPCWSTR pwszMember,
IN LPCWSTR pwszAction
)
{
//
// wait for the mutex.
//
DWORD dwWait = ::WaitForSingleObject(m_hLogMutex, INFINITE);
if (dwWait != WAIT_OBJECT_0 && dwWait != WAIT_ABANDONED)
{
return;
}
m_bstrVerboseHeading.Empty();
//
// for better formatting, we will reserve 20 characters for
// the name of member and action. For that need, we will prepare
// an array containing only space characters.
//
const DWORD PART_LENGTH = 20;
WCHAR wszHeading[ 2 * PART_LENGTH + 1];
::memset(wszHeading, 1, 2 * PART_LENGTH * sizeof(WCHAR));
wszHeading[2 * PART_LENGTH] = L'\0';
_wcsset(wszHeading, L' ');
//
// if the given member and action's total length is more
// than our pre-determined buffer, then we are going to
// let the heading grow
//
DWORD dwMemLen = wcslen(pwszMember);
DWORD dwActLen = wcslen(pwszAction);
if (dwMemLen + dwActLen > 2 * PART_LENGTH)
{
//
// just let the heading grow to whatever length it needs
//
m_bstrVerboseHeading = pwszMember;
m_bstrVerboseHeading += s_pwszSep;
m_bstrVerboseHeading += pwszAction;
m_bstrVerboseHeading += s_pwszSep;
}
else
{
//
// copy the member
//
LPWSTR pwszHead = wszHeading;
for (ULONG i = 0; i < dwMemLen; i++)
{
*pwszHead = pwszMember[i];
pwszHead++;
}
if (i < PART_LENGTH)
{
pwszHead = wszHeading + PART_LENGTH;
}
//
// copy the action
//
for (i = 0; i < dwActLen; i++)
{
*pwszHead = pwszAction[i];
pwszHead++;
}
m_bstrVerboseHeading.m_str = ::SysAllocString(wszHeading);
}
::ReleaseMutex(m_hLogMutex);
}