Skip to content

Commit

Permalink
Update readme for v1.1
Browse files Browse the repository at this point in the history
  • Loading branch information
MSDN-WhiteKnight committed Jul 9, 2022
1 parent 55bb692 commit d6c7f94
Show file tree
Hide file tree
Showing 5 changed files with 325 additions and 3 deletions.
2 changes: 1 addition & 1 deletion ErrLib/ReadMe.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ A library to assist in dealing with exceptions and errors in C/C++ Windows Appli

Author: MSDN.WhiteKnight (https://github.com/MSDN-WhiteKnight)
License: BSD 3-clause
Requirements: Windows Vista (or newer), Visual Studio 2010 (or newer)
Requirements: Windows Vista (or newer), Visual Studio 2012 (or newer)

** Features **

Expand Down
11 changes: 11 additions & 0 deletions ErrLib/changes.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# ErrLib changes

v1.1 (09.07.2022)

- Add support for custom logging target (ErrLib_SetLoggingCallback)
- Add C++ exception API (ErrLib_CPP.h)
- Add high-level stack trace API (ErrLib_Except_GetStackTraceData)
- Add NuGet package
- Add API docs using Doxygen
- Fix crash in ErrLib_PrintStack on x64
- Fix ErrLib_PrintStack crash on Win7 when reading PDB symbols built with /DEBUG:FASTLINK option
2 changes: 1 addition & 1 deletion pkg/ReadMe.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ A library to assist in dealing with exceptions and errors in C/C++ Windows Appli

Author: MSDN.WhiteKnight (https://github.com/MSDN-WhiteKnight)
License: BSD 3-clause
Requirements: Windows Vista or newer, Visual Studio 2010+ (VS 2015+ or NuGet 3.3+ for NuGet package)
Requirements: Windows Vista or newer, Visual Studio 2012+ (VS 2015+ or NuGet 3.3+ for NuGet package)

** Features **

Expand Down
133 changes: 132 additions & 1 deletion pkg/build/ErrLib.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#define ErrLib_H_INCLUDED
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>

#include <windows.h>
#include <strsafe.h>
Expand Down Expand Up @@ -97,6 +98,21 @@
#define ERRLIB_PARAM_VISUALCPPVERSION 100
#define ERRLIB_PARAM_ISDEBUGBUILD 101

/**
* Stack frame property: Symbol name
*/
#define ERRLIB_SYMBOL_NAME 1

/**
* Stack frame property: Module file path
*/
#define ERRLIB_SYMBOL_MODULE 2

/**
* Stack frame property: Symbol source file path
*/
#define ERRLIB_SYMBOL_SOURCE 3

// *** Typedefs ***

//Function pointer type used as unhandled exception callback
Expand All @@ -105,6 +121,32 @@ typedef LONG (WINAPI * ERRLIB_EXCEPTION_CALLBACK) ( struct _EXCEPTION_POINTERS
//Function pointer type used for custom logging targets
typedef void (WINAPI * ERRLIB_LOGGING_CALLBACK) (LPCWSTR, void*);

/**
* Represents a stack frame, an object that contains information about an individual call in stack trace
* @note Do not access the fields of this structure directly, they are considered private implementation details.
* @note Use ErrLib_ST_... functions instead to get stack frame properties.
*/
typedef struct structERRLIB_STACK_FRAME{
uint64_t addr;
uint64_t displacement;
WCHAR symbol[MAX_SYM_NAME];
WCHAR module[MAX_PATH];
WCHAR src_file[MAX_PATH];
DWORD src_line;
} ERRLIB_STACK_FRAME;

/**
* Represents a stack trace, a chain of function calls at the particular point of thread's execution
* @note Do not access the fields of this structure directly, they are considered private implementation details.
* @note Use ErrLib_ST_... functions instead to get stack trace properties.
*/
typedef struct structERRLIB_STACK_TRACE{
ERRLIB_STACK_FRAME *data;
int capacity;
int count;
BOOL isOnHeap;
} ERRLIB_STACK_TRACE;

// *** Custom exception codes for SEH ***

//Win32 Exception
Expand All @@ -131,6 +173,8 @@ extern "C" {
*/
ERRLIB_API void __stdcall ErrLib_ErrorMes(LPTSTR lpszFunction,DWORD dw,WCHAR* buf);

ERRLIB_API DWORD __stdcall ErrLib_GetWinapiErrorMessage(DWORD dwCode, BOOL localized, WCHAR* pOutput, int cch);

//Gets filename from full path
ERRLIB_API WCHAR* __stdcall ErrLib_FileNameFromPathW(WCHAR* path);

Expand Down Expand Up @@ -220,6 +264,83 @@ ERRLIB_API BOOL __stdcall ErrLib_RegisterEventSource();
*/
ERRLIB_API BOOL __stdcall ErrLib_UnregisterEventSource();

/**
* Gets the stack trace information for the specified context record
*
* @param ctx The pointer to a CONTEXT structure, containing valid context record on input
* @returns The structure that contains stack trace information
* @note You can obtain a context record via [RtlCaptureContext](https://docs.microsoft.com/en-us/windows/win32/api/winnt/nf-winnt-rtlcapturecontext) function
* or from the exception data.
* @note When you no longer need the stack trace information, free resources associated with it by calling ErrLib_FreeStackTrace.
*/
ERRLIB_API ERRLIB_STACK_TRACE __stdcall ErrLib_GetStackTrace(CONTEXT* ctx);

/**
* Gets the number of stack frames in the specified stack trace
*
* @param pStack The pointer to a ERRLIB_STACK_TRACE structure
* @returns The integer number of stack frames
*/
ERRLIB_API int __stdcall ErrLib_ST_GetFramesCount(const ERRLIB_STACK_TRACE* pStack);

/**
* Gets the frame with the specified number from a stack trace
*
* @param pStack The pointer to a ERRLIB_STACK_TRACE structure
* @param n The frame number to get
* @returns The pointer to a ERRLIB_STACK_FRAME structure that contains the stack frame data
* @note The returned structure is stored in a memory allocated as part of the stack trace data and is freed when
* you call ErrLib_FreeStackTrace.
*/
ERRLIB_API const ERRLIB_STACK_FRAME* __stdcall ErrLib_ST_GetFrame(const ERRLIB_STACK_TRACE* pStack, int n);

/**
* Gets the symbol address from the specified stack frame
*
* @param pFrame The pointer to a ERRLIB_STACK_FRAME structure
* @returns The 64-bit unsigned integer the represents the symbol address
*/
ERRLIB_API uint64_t __stdcall ErrLib_ST_GetAddress(const ERRLIB_STACK_FRAME* pFrame);

/**
* Gets the stack frame displacement from the symbol address (the difference between the instruction pointer value and
* the starting address of the function)
*
* @param pFrame The pointer to a ERRLIB_STACK_FRAME structure
* @returns The 64-bit unsigned integer the represents the stack frame displacement
*/
ERRLIB_API uint64_t __stdcall ErrLib_ST_GetDisplacement(const ERRLIB_STACK_FRAME* pFrame);

/**
* Gets the value of the string property associated with this stack frame
*
* @param pFrame The pointer to a ERRLIB_STACK_FRAME structure
* @param propId The integer value that identifies the property to get
* @returns A pointer to the null-terminated wide character string that contains the property value, or NULL
* if the property value is not set.
* @note Supported property ids are: ERRLIB_SYMBOL_NAME, ERRLIB_SYMBOL_MODULE and ERRLIB_SYMBOL_SOURCE.
* @note The returned string is stored in a memory allocated as part of the stack trace data and is freed when
* you call ErrLib_FreeStackTrace.
*/
ERRLIB_API const WCHAR* __stdcall ErrLib_ST_GetStringProperty(const ERRLIB_STACK_FRAME* pFrame, int propId);

/**
* Gets the source line number from the specified stack frame
*
* @param pFrame The pointer to a ERRLIB_STACK_FRAME structure
* @returns The unsigned integer the represents the source line number
* @note This function only returns a valid line number if the ERRLIB_SYMBOL_SOURCE property is set on the stack frame.
*/
ERRLIB_API DWORD __stdcall ErrLib_ST_GetSymLine(const ERRLIB_STACK_FRAME* pFrame);

/**
* Releases resources associated with the specified stack trace structure
*
* @param pStack The pointer to a ERRLIB_STACK_TRACE structure
* @note Do not pass the stack trace into any other functions after it has been freed by this function.
*/
ERRLIB_API void __stdcall ErrLib_FreeStackTrace(ERRLIB_STACK_TRACE* pStack);

/**
* Prints stack trace for the specified context record
*
Expand Down Expand Up @@ -289,12 +410,21 @@ ERRLIB_API DWORD __stdcall ErrLib_Except_GetCode();
ERRLIB_API LPWSTR __stdcall ErrLib_Except_GetMessage();

/**
* Gets the stack trace for the current exception in ERRLIB_CATCH/ERRLIB_CATCH_ALL block
* Gets the stack trace text for the current exception in ERRLIB_CATCH/ERRLIB_CATCH_ALL block
*
* @note When used outside of the exception handler block, the behaviour is undefined.
*/
ERRLIB_API LPWSTR __stdcall ErrLib_Except_GetStackTrace();

/**
* Gets the stack trace information for the current exception in ERRLIB_CATCH/ERRLIB_CATCH_ALL block
*
* @returns The structure that contains stack trace information
* @note When you no longer need the stack trace information, free resources associated with it by calling ErrLib_FreeStackTrace.
* @note When used outside of the exception handler block, the behaviour is undefined.
*/
ERRLIB_API ERRLIB_STACK_TRACE __stdcall ErrLib_Except_GetStackTraceData();

ERRLIB_API LONG __stdcall ErrLib_CatchCode( struct _EXCEPTION_POINTERS * ex, DWORD FilteredCode);
ERRLIB_API LONG __stdcall ErrLib_CatchAll( struct _EXCEPTION_POINTERS * ex);

Expand All @@ -303,6 +433,7 @@ ERRLIB_API LPVOID __stdcall ErrLib_ExArgs_GetPointer();

#ifdef __cplusplus
ERRLIB_API void __stdcall ErrLib_HResultToString(HRESULT hr,LPTSTR lpszFunction,WCHAR* buf);
ERRLIB_API void __stdcall ErrLib_GetHResultMessage(HRESULT hr,WCHAR* lpOutput, int cch);
}//extern "C"
#endif

Expand Down
180 changes: 180 additions & 0 deletions pkg/build/ErrLib_CPP.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
//Project: ErrLib
//Author: MSDN.WhiteKnight (https://github.com/MSDN-WhiteKnight)
#ifndef ERRLIB_CPP_H_INCLUDED
#define ERRLIB_CPP_H_INCLUDED
#include "ErrLib.h"
#include <string>

const DWORD ERRLIB_CPP_EXCEPTION = 0xC0400002;

#ifdef __cplusplus
namespace ErrLib{

/**
* Provides a base class for C++ exceptions that supports capturing a stack trace in the moment when exception
* is thrown and logging the exception information.
*/
class Exception : public std::exception{
private:
CONTEXT _context;
std::wstring _msg;
std::wstring _stack;
DWORD _code;
void* _data;

std::wstring PrintStackTraceImpl(){
WCHAR buf[ErrLib_StackLen]=L"";
ErrLib_PrintStack(&_context, buf, ErrLib_StackLen);
return std::wstring(buf);
}

protected:

void SetMsg(const std::wstring& msg){_msg=msg;}

void SetCode(DWORD code){_code=code;}

void SetData(void* data){_data=data;}

public:

/**
* Creates a new exception using the default empty error message
*/
Exception():_msg(L""),_code(ERRLIB_CPP_EXCEPTION),_data(nullptr){
RtlCaptureContext(&_context);
_stack = this->PrintStackTraceImpl();
}

/**
* Creates a new exception using the specified error message
*/
Exception(const std::wstring& message):_msg(message),_code(ERRLIB_CPP_EXCEPTION),_data(nullptr){
RtlCaptureContext(&_context);
_stack = this->PrintStackTraceImpl();
}

/**
* Creates a new exception using the specified error message, error code and additional data
*/
Exception(const std::wstring& message, DWORD code, void* data):_msg(message),_code(code),_data(data){
RtlCaptureContext(&_context);
_stack = this->PrintStackTraceImpl();
}

/**
* Gets the error message associated with this exception as a C++ wstring
*/
std::wstring GetMsg(){return _msg;}

/**
* Gets the error code associated with this exception
* @note When error code was not specified when creating exception, the default value DWORD ERRLIB_CPP_EXCEPTION (0xC0400002) is used.
*/
DWORD GetCode(){return _code;}

/**
* Gets the additional user-defined data associated with this exception
*/
void* GetData(){return _data;}

/**
* Gets the error message associated with this exception as a C wide-character string
* @param pOutput The pointer to the caller-allocated wide character array that will be filled with error message text on output.
* @param cch The maximum amount of characters that can be put into the array pointed by **pOutput** parameter.
*/
void GetMessageText(WCHAR* pOutput, int cch){
const WCHAR* pChars = _msg.c_str();
wcscpy_s(pOutput, cch, pChars);
}

/**
* Gets the processor context in the moment this exception was thrown
* @param pOutput The pointer to the caller-allocated buffer to store the CONTEXT structure. Must be at least sizeof(CONTEXT) bytes.
*/
void GetContext(CONTEXT* pOutput){
memcpy(pOutput, &_context, sizeof(CONTEXT));
}

/**
* Gets the stack trace in the moment this exception was thrown as a C wide-character string
* @param pOutput The pointer to the caller-allocated wide character array that will be filled with stack trace text on output.
* @param cch The maximum amount of characters that can be put into the array pointed by **pOutput** parameter.
*/
void PrintStackTrace(WCHAR* pOutput, int cch){
const WCHAR* p = this->_stack.c_str();
StringCchCopy(pOutput, cch, p);
}

/**
* Gets the stack trace in the moment this exception was thrown as a C++ wstring
*/
std::wstring PrintStackTrace(){
return _stack;
}

/**
* Outputs the exception information into configured log targets
* @param visible Pass `true` if you want to use ERRLIB_OUTPUT_STDERR/ERRLIB_OUTPUT_MBOX logging targets (if they are enabled by configuration flags), `false` otherwise
* This method outputs information into one or more logging targets, configured using ErrLib_SetParameter function.
* It only outputs information into the stderr stream and message box if the **visible** parameter is `true` (and if respective flags are enabled).
* By default, the enabled logging targets are log file and stderr stream.
* @note When outputting information in message box, stack trace is not included.
*/
void Log(bool visible){
BOOL bVisible;

if (visible) bVisible = TRUE;
else bVisible = FALSE;

ErrLib_LogExceptionInfo(_code, _msg.c_str(), this->PrintStackTrace().c_str(), bVisible);
}
};

class WinapiException : public Exception{
public:
WinapiException(DWORD code,const std::wstring& msg){
this->SetCode(code);
this->SetMsg(msg);
}

/**
* Creates a new WinapiException using the error code and message from the last WINAPI error
* @param localized The value indicating whether the exception message should use the current OS locale
* If the **localized** parameter is `true`, the message is in language of the current OS locale. Otherwise, it is always in English.
*/
static WinapiException FromLastError(bool localized){
DWORD code = GetLastError();
BOOL fLocalized;
WCHAR buf[ErrLib_MessageLen]=L"";

if(localized) fLocalized = TRUE;
else fLocalized = FALSE;

ErrLib_GetWinapiErrorMessage(code, fLocalized, buf, ErrLib_MessageLen);
return WinapiException(code, buf);
}
};

class ComException : public Exception{
public:
ComException(HRESULT hr,const std::wstring& msg){
this->SetCode((DWORD)hr);
this->SetMsg(msg);
}

/**
* Creates a new ComException using the error code and message from the specified HRESULT
* @param hr HRESULT value that would be used as error code
*/
static ComException FromHResult(HRESULT hr){
WCHAR buf[ErrLib_MessageLen]=L"";

ErrLib_GetHResultMessage(hr, buf, ErrLib_MessageLen);
return ComException(hr, buf);
}
};

}
#endif //__cplusplus
#endif

0 comments on commit d6c7f94

Please sign in to comment.