443 lines
11 KiB
C++
443 lines
11 KiB
C++
/*++
|
|
|
|
Copyright (c) 2000 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
result.h
|
|
|
|
Abstract:
|
|
|
|
|
|
|
|
Author:
|
|
|
|
Hakki T. Bostanci (hakkib) 06-Apr-2000
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#ifndef RESULT_H
|
|
#define RESULT_H
|
|
|
|
#ifndef ASSERT
|
|
#define ASSERT assert
|
|
#endif
|
|
|
|
#ifndef PCTSTR
|
|
#define PCTSTR LPCTSTR
|
|
#endif //PCTSTR
|
|
|
|
#if defined(_X86_)
|
|
#define CURRENT_MACHINE_TYPE IMAGE_FILE_MACHINE_I386
|
|
#elif defined(_MIPS_)
|
|
#define CURRENT_MACHINE_TYPE IMAGE_FILE_MACHINE_R4000
|
|
#elif defined(_ALPHA_)
|
|
#define CURRENT_MACHINE_TYPE IMAGE_FILE_MACHINE_ALPHA
|
|
#elif defined(_PPC_)
|
|
#define CURRENT_MACHINE_TYPE IMAGE_FILE_MACHINE_POWERPC
|
|
#elif defined(_AXP64_)
|
|
#define CURRENT_MACHINE_TYPE IMAGE_FILE_MACHINE_ALPHA64
|
|
#elif defined(_IA64_)
|
|
#define CURRENT_MACHINE_TYPE IMAGE_FILE_MACHINE_IA64
|
|
#ifndef CONTEXT_CONTROL
|
|
#pragma message("CONTEXT_CONTROL was not defined!!!")
|
|
#define CONTEXT_CONTROL CONTEXT86_CONTROL
|
|
#endif CONTEXT_CONTROL
|
|
#else
|
|
#undef CURRENT_MACHINE_TYPE
|
|
#endif
|
|
|
|
//
|
|
// The CHECK() macro can be used to evaluate the result of APIs that use
|
|
// LastError. These APIs typically return a non-zero value if there is
|
|
// no error and if there is an error, they return zero and SetLastError()
|
|
// with the extended error information.
|
|
//
|
|
|
|
#define CHECK(Expression) \
|
|
{ \
|
|
if ((Expression) == 0) { \
|
|
\
|
|
throw CError(GetLastError() STAMP(_T(#Expression))); \
|
|
} \
|
|
} \
|
|
|
|
//
|
|
// CHECK0 macro deals with the API functions that do not use the LastError
|
|
// value. Typically registry APIs fall into this category, they directly
|
|
// return the error code, or ERROR_SUCCESS if there is no error.
|
|
//
|
|
|
|
#define CHECK_REG(Expression) \
|
|
{ \
|
|
DWORD __dwResult = (DWORD) (Expression); \
|
|
\
|
|
if (__dwResult != ERROR_SUCCESS) { \
|
|
\
|
|
throw CError(__dwResult STAMP(_T(#Expression))); \
|
|
} \
|
|
} \
|
|
|
|
//
|
|
// CHECK0 macro deals with the API functions that do not use the LastError
|
|
// value. Typically registry APIs fall into this category, they directly
|
|
// return the error code, or ERROR_SUCCESS if there is no error.
|
|
//
|
|
|
|
#define CHECK_HR(Expression) \
|
|
{ \
|
|
HRESULT __hr = Expression; \
|
|
\
|
|
if (__hr != S_OK) \
|
|
{ \
|
|
throw CError(__hr STAMP(_T(#Expression))); \
|
|
} \
|
|
} \
|
|
|
|
//
|
|
// CHECK_LSA macro deals with the LSA API functions.
|
|
//
|
|
|
|
#define CHECK_LSA(Expression) \
|
|
{ \
|
|
DWORD __dwResult = (DWORD) (Expression); \
|
|
\
|
|
if (__dwResult != ERROR_SUCCESS) \
|
|
{ \
|
|
__dwResult = LsaNtStatusToWinError(__dwResult); \
|
|
\
|
|
throw CError(__dwResult STAMP(_T(#Expression))); \
|
|
} \
|
|
} \
|
|
|
|
//
|
|
// On DEBUG builds, we include the location STAMP on the error message popup,
|
|
// i.e. we display the expression that raised the error, the module name and
|
|
// the line number
|
|
//
|
|
|
|
#ifdef _CONSOLE
|
|
#define ENDL _T("\n")
|
|
#else //_CONSOLE
|
|
#define ENDL _T(", ")
|
|
#endif //_CONSOLE
|
|
|
|
#if defined(DEBUG) || defined(_DEBUG) || defined(DBG)
|
|
#define STAMP(pExpr) , pExpr, _T(__FILE__), __LINE__
|
|
#define STAMP_DECL , PCTSTR pExpr = _T(""), PCTSTR pFile = _T(""), INT nLine = 0
|
|
#define STAMP_INIT , m_pExpr(pExpr), m_pFile(pFile), m_nLine(nLine)
|
|
#define STAMP_ARGS , m_pExpr, m_pFile, (PCTSTR) m_nLine
|
|
#define STAMP_DEFINE PCTSTR m_pExpr; PCTSTR m_pFile; INT m_nLine; mutable CONTEXT m_Context;
|
|
#define STAMP_FORMAT _T("%s") ENDL _T("%s: %d") ENDL
|
|
#define STAMP_IOS << std::endl << rhs.m_pExpr << std::endl << rhs.m_pFile << _T(": ") << rhs.m_nLine
|
|
#define STAMP_LENGTH _tcslen(pExpr) + _tcslen(pFile) + 8
|
|
#else //DEBUG
|
|
#define STAMP(pExpr)
|
|
#define STAMP_DECL
|
|
#define STAMP_INIT
|
|
#define STAMP_ARGS
|
|
#define STAMP_DEFINE
|
|
#define STAMP_FORMAT
|
|
#define STAMP_IOS
|
|
#define STAMP_LENGTH 0
|
|
#endif //DEBUG
|
|
|
|
|
|
inline PTSTR _tcsdupl(LPCTSTR pStrSource)
|
|
{
|
|
if (!pStrSource) {
|
|
|
|
pStrSource = _T("");
|
|
}
|
|
|
|
PTSTR pStrDest = (PTSTR) ::LocalAlloc(
|
|
LMEM_FIXED,
|
|
(_tcslen(pStrSource) + 1) * sizeof(TCHAR)
|
|
);
|
|
|
|
if (pStrDest) {
|
|
|
|
_tcscpy(pStrDest, pStrSource);
|
|
}
|
|
|
|
return pStrDest;
|
|
}
|
|
|
|
inline PTSTR FormatMessageFromSystem(DWORD nNum)
|
|
{
|
|
PTSTR pText = 0;
|
|
|
|
DWORD dwResult = ::FormatMessage(
|
|
FORMAT_MESSAGE_ALLOCATE_BUFFER |
|
|
FORMAT_MESSAGE_FROM_SYSTEM |
|
|
FORMAT_MESSAGE_IGNORE_INSERTS |
|
|
FORMAT_MESSAGE_MAX_WIDTH_MASK,
|
|
0,
|
|
nNum,
|
|
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
|
(PTSTR) &pText,
|
|
0,
|
|
0
|
|
);
|
|
|
|
if (pText == 0) {
|
|
|
|
pText = _tcsdupl(_T("Unknown Error"));
|
|
}
|
|
|
|
return pText;
|
|
}
|
|
|
|
#ifdef _IOSTREAM_
|
|
|
|
// workaround for VC6 compiler bug (Q192539)
|
|
|
|
class CError;
|
|
std::ostream &operator <<(std::ostream &os, const CError &rhs);
|
|
|
|
#endif //_IOSTREAM_
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// CError
|
|
//
|
|
|
|
class CError
|
|
{
|
|
public:
|
|
CError(
|
|
PTSTR pText
|
|
STAMP_DECL
|
|
) :
|
|
m_nNum(0),
|
|
m_pText(pText),
|
|
m_bFree(false)
|
|
STAMP_INIT
|
|
{
|
|
#if (defined(DEBUG) || defined(_DEBUG) || defined(DBG)) && defined(CURRENT_MACHINE_TYPE)
|
|
m_Context.ContextFlags = CONTEXT_CONTROL;
|
|
GetThreadContext(GetCurrentThread(), &m_Context);
|
|
#endif
|
|
}
|
|
|
|
CError(
|
|
DWORD nNum
|
|
STAMP_DECL
|
|
) :
|
|
m_nNum(nNum),
|
|
m_pText(0),
|
|
m_bFree(false)
|
|
STAMP_INIT
|
|
{
|
|
#if (defined(DEBUG) || defined(_DEBUG) || defined(DBG)) && defined(CURRENT_MACHINE_TYPE)
|
|
m_Context.ContextFlags = CONTEXT_CONTROL;
|
|
GetThreadContext(GetCurrentThread(), &m_Context);
|
|
#endif
|
|
}
|
|
|
|
~CError()
|
|
{
|
|
if (m_bFree) {
|
|
|
|
LocalFree(m_pText);
|
|
}
|
|
}
|
|
|
|
DWORD
|
|
Num() const
|
|
{
|
|
return m_nNum;
|
|
}
|
|
|
|
PCTSTR
|
|
Text() const
|
|
{
|
|
if (!m_pText) {
|
|
|
|
m_pText = FormatMessageFromSystem(m_nNum);
|
|
m_bFree = true;
|
|
}
|
|
|
|
return m_pText;
|
|
}
|
|
|
|
static
|
|
void
|
|
AskDebugBreak()
|
|
{
|
|
if (MessageBox(
|
|
0,
|
|
_T("Do you want to break into the debugger?"),
|
|
0,
|
|
MB_ICONQUESTION | MB_YESNO
|
|
) == IDYES) {
|
|
|
|
DebugBreak();
|
|
}
|
|
}
|
|
|
|
#if (defined(DEBUG) || defined(_DEBUG) || defined(DBG)) && defined(CURRENT_MACHINE_TYPE) && defined(_IMAGEHLP_)
|
|
|
|
#define MAX_STACK_DEPTH 32
|
|
#define MAX_SYMBOL_LENGTH 256
|
|
|
|
template <int N>
|
|
struct CImagehlpSymbol : public IMAGEHLP_SYMBOL
|
|
{
|
|
CImagehlpSymbol()
|
|
{
|
|
ZeroMemory(this, sizeof(*this));
|
|
SizeOfStruct = sizeof(IMAGEHLP_SYMBOL);
|
|
MaxNameLength = N;
|
|
}
|
|
|
|
private:
|
|
CHAR NameData[N-1];
|
|
};
|
|
|
|
struct CImagehlpLine : public IMAGEHLP_LINE
|
|
{
|
|
CImagehlpLine()
|
|
{
|
|
ZeroMemory(this, sizeof(*this));
|
|
SizeOfStruct = sizeof(IMAGEHLP_LINE);
|
|
}
|
|
};
|
|
|
|
struct CImagehlpModule : public IMAGEHLP_MODULE
|
|
{
|
|
CImagehlpModule()
|
|
{
|
|
ZeroMemory(this, sizeof(*this));
|
|
SizeOfStruct = sizeof(IMAGEHLP_MODULE);
|
|
}
|
|
};
|
|
|
|
void DumpStack(FILE *fout = stdout) const
|
|
{
|
|
PCONTEXT pContext = &m_Context;
|
|
HANDLE hProcess = GetCurrentProcess();
|
|
HANDLE hThread = GetCurrentThread();
|
|
|
|
SymSetOptions(SYMOPT_UNDNAME | SYMOPT_DEFERRED_LOADS | SYMOPT_LOAD_LINES);
|
|
SymInitialize(hProcess, 0, TRUE);
|
|
|
|
STACKFRAME StackFrame = { 0 };
|
|
|
|
#if defined(_X86_)
|
|
StackFrame.AddrPC.Offset = pContext->Eip;
|
|
StackFrame.AddrFrame.Offset = pContext->Ebp;
|
|
StackFrame.AddrStack.Offset = pContext->Esp;
|
|
#elif defined(_MIPS_)
|
|
StackFrame.AddrPC.Offset = pContext->Fir;
|
|
StackFrame.AddrFrame.Offset = pContext->IntS6;
|
|
StackFrame.AddrStack.Offset = pContext->IntSp;
|
|
#elif defined(_ALPHA_)
|
|
StackFrame.AddrPC.Offset = pContext->Fir;
|
|
StackFrame.AddrFrame.Offset = pContext->IntFp;
|
|
StackFrame.AddrStack.Offset = pContext->IntSp;
|
|
#elif defined(_PPC_)
|
|
StackFrame.AddrPC.Offset = pContext->Iar;
|
|
StackFrame.AddrFrame.Offset = pContext->IntFp;
|
|
StackFrame.AddrStack.Offset = pContext->Gpr1;
|
|
#endif
|
|
|
|
StackFrame.AddrPC.Mode = AddrModeFlat;
|
|
StackFrame.AddrFrame.Mode = AddrModeFlat;
|
|
StackFrame.AddrStack.Mode = AddrModeFlat;
|
|
|
|
for (
|
|
int nStackWalkLevel = 0;
|
|
nStackWalkLevel < MAX_STACK_DEPTH &&
|
|
StackWalk(
|
|
CURRENT_MACHINE_TYPE,
|
|
hProcess,
|
|
hThread,
|
|
&StackFrame,
|
|
pContext,
|
|
0,
|
|
SymFunctionTableAccess,
|
|
SymGetModuleBase,
|
|
0
|
|
);
|
|
++nStackWalkLevel
|
|
) {
|
|
|
|
CImagehlpModule Module;
|
|
SymGetModuleInfo(hProcess, StackFrame.AddrPC.Offset, &Module);
|
|
|
|
DWORD dwDisplacement = 0;
|
|
CImagehlpSymbol<MAX_SYMBOL_LENGTH> Symbol;
|
|
SymGetSymFromAddr(hProcess, StackFrame.AddrPC.Offset, &dwDisplacement, &Symbol);
|
|
|
|
CHAR szUnDSymbol[MAX_SYMBOL_LENGTH] = "";
|
|
SymUnDName(&Symbol, szUnDSymbol, sizeof(szUnDSymbol));
|
|
|
|
DWORD dwLineDisplacement = 0;
|
|
CImagehlpLine Line;
|
|
SymGetLineFromAddr(hProcess, StackFrame.AddrPC.Offset, &dwLineDisplacement, &Line);
|
|
|
|
fprintf(
|
|
fout,
|
|
"%08x %08x %08x %08x %s!%s+0x%x (%s:%d)\n",
|
|
StackFrame.Params[0],
|
|
StackFrame.Params[1],
|
|
StackFrame.Params[2],
|
|
StackFrame.Params[3],
|
|
Module.ModuleName,
|
|
szUnDSymbol,
|
|
dwDisplacement,
|
|
Line.FileName,
|
|
Line.LineNumber
|
|
);
|
|
}
|
|
|
|
SymCleanup(hProcess);
|
|
}
|
|
|
|
#else
|
|
|
|
void
|
|
DumpStack(PVOID pVoid = 0) const
|
|
{
|
|
}
|
|
|
|
#endif
|
|
|
|
template <class F>
|
|
int
|
|
Print(F OutputFunction) const
|
|
{
|
|
OutputFunction(
|
|
_T("Error 0x%08x: %s") ENDL STAMP_FORMAT,
|
|
Num(),
|
|
Text()
|
|
STAMP_ARGS
|
|
);
|
|
|
|
return 1;
|
|
}
|
|
|
|
#ifdef _IOSTREAM_
|
|
|
|
friend std::ostream &operator <<(std::ostream &os, const CError &rhs)
|
|
{
|
|
return os <<
|
|
_T("Error ") << rhs.Num() << _T(": ") << rhs.Text()
|
|
STAMP_IOS << std::endl;
|
|
}
|
|
|
|
#endif //_IOSTREAM_
|
|
|
|
private:
|
|
DWORD m_nNum;
|
|
mutable PTSTR m_pText;
|
|
mutable bool m_bFree;
|
|
STAMP_DEFINE;
|
|
};
|
|
|
|
#endif //RESULT_H
|