Click here to Skip to main content
15,892,927 members
Articles / Programming Languages / XML

XMLFoundation

Rate me:
Please Sign up or sign in to vote.
4.82/5 (12 votes)
2 Jul 20029 min read 75.2K   1.4K   34  
Obtaining data marked up in XML creates the need for Application Layer tools to easily and efficiently work with XML data.
// --------------------------------------------------------------------------
//					www.UnitedBusinessTechnologies.com
//			  Copyright (c) 1998 - 2002  All Rights Reserved.
//
// Source in this file is released to the public under the following license:
// --------------------------------------------------------------------------
// This toolkit may be used free of charge for any purpose including corporate
// and academic use.  For profit, and Non-Profit uses are permitted.
//
// This source code and any work derived from this source code must retain 
// this copyright at the top of each source file.
// 
// UBT welcomes any suggestions, improvements or new platform ports.
// email to: XMLFoundation@UnitedBusinessTechnologies.com
// --------------------------------------------------------------------------

#include "GException.h"
#include "GString.h"
#include "GProfile.h"
#include <stdlib.h> //for: atoi()
#include <stdarg.h> //for: va_start(), va_end 


GProfile &GetErrorProfile()
{
	static GString strErrorFile;
	GString *pstrErrorFileContents = 0;

	if (strErrorFile.IsEmpty())
	{
		const char *pzErrFile = GetProfile().GetString("System", "Errors", 0);
		if (pzErrFile && pzErrFile[0])
		{
			pstrErrorFileContents = new GString();
			if (!pstrErrorFileContents->FromFile(pzErrFile,0))
			{
				pzErrFile = 0;
			}
			strErrorFile = pzErrFile;
		}
		// if the error file could not be found, default to a minimal error file
		if (!pzErrFile)
		{
			(*pstrErrorFileContents) = "[Exception]\nSubSystem=0\n[Profile]\nSubSystem=1\n0=[%s.%s]Error Description File Not loaded.\n1=[%s]Error Description File Not loaded.\n";
			strErrorFile = "Static";
		}
	}
	static GProfile ErrorProfile(pstrErrorFileContents->StrVal(), pstrErrorFileContents->Length());
	
	return ErrorProfile;
}

#if defined _DEBUG && _WIN32

#include <eh.h>
#include <string>
#include <vector>
#pragma comment( lib, "imagehlp" )

///////////////////////////////////////////////////////////////////////////
/////////////////////// Win32 stack tracing ///////////////////////////////

#define gle (GetLastError())
#define lenof(a) (sizeof(a) / sizeof((a)[0]))
#define MAXNAMELEN 1024 // max name length for found symbols
#define IMGSYMLEN ( sizeof IMAGEHLP_SYMBOL )
#define TTBUFLEN 65536 // for a temp buffer

void _stack_se_translator(unsigned int e, _EXCEPTION_POINTERS* p)
{
	HANDLE hThread;

	DuplicateHandle( GetCurrentProcess(), GetCurrentThread(),
		GetCurrentProcess(), &hThread, 0, false, DUPLICATE_SAME_ACCESS );

	GenericCallStack stk(hThread, *(p->ContextRecord));
	
	CloseHandle( hThread );

	throw stk;
}


GenericCallStack::GenericCallStack(const GenericCallStack &src)
{
	_stk = src._stk;
}

struct ModuleEntry
{
	std::string imageName;
	std::string moduleName;
	DWORD baseAddress;
	DWORD size;
};
typedef std::vector< ModuleEntry > ModuleList;
typedef ModuleList::iterator ModuleListIter;

// miscellaneous toolhelp32 declarations; we cannot #include the header
// because not all systems may have it
#define MAX_MODULE_NAME32 255
#define TH32CS_SNAPMODULE   0x00000008
#pragma pack( push, 8 )
typedef struct tagMODULEENTRY32
{
    DWORD   dwSize;
    DWORD   th32ModuleID;       // This module
    DWORD   th32ProcessID;      // owning process
    DWORD   GlblcntUsage;       // Global usage count on the module
    DWORD   ProccntUsage;       // Module usage count in th32ProcessID's context
    BYTE  * modBaseAddr;        // Base address of module in th32ProcessID's context
    DWORD   modBaseSize;        // Size in bytes of module starting at modBaseAddr
    HMODULE hModule;            // The hModule of this module in th32ProcessID's context
    char    szModule[MAX_MODULE_NAME32 + 1];
    char    szExePath[MAX_PATH];
} MODULEENTRY32;
typedef MODULEENTRY32 *  PMODULEENTRY32;
typedef MODULEENTRY32 *  LPMODULEENTRY32;
#pragma pack( pop )

bool fillModuleListTH32( ModuleList& modules, DWORD pid )
{
	// CreateToolhelp32Snapshot()
	typedef HANDLE (__stdcall *tCT32S)( DWORD dwFlags, DWORD th32ProcessID );
	// Module32First()
	typedef BOOL (__stdcall *tM32F)( HANDLE hSnapshot, LPMODULEENTRY32 lpme );
	// Module32Next()
	typedef BOOL (__stdcall *tM32N)( HANDLE hSnapshot, LPMODULEENTRY32 lpme );

	// I think the DLL is called tlhelp32.dll on Win9X, so we try both
	const char *dllname[] = { "kernel32.dll", "tlhelp32.dll" };
	HINSTANCE hToolhelp;
	tCT32S pCT32S;
	tM32F pM32F;
	tM32N pM32N;

	HANDLE hSnap;
	MODULEENTRY32 me = { sizeof me };
	bool keepGoing;
	ModuleEntry e;
	int i;

	for ( i = 0; i < lenof( dllname ); ++ i )
	{
		hToolhelp = LoadLibrary( dllname[i] );
		if ( hToolhelp == 0 )
			continue;
		pCT32S = (tCT32S) GetProcAddress( hToolhelp, "CreateToolhelp32Snapshot" );
		pM32F = (tM32F) GetProcAddress( hToolhelp, "Module32First" );
		pM32N = (tM32N) GetProcAddress( hToolhelp, "Module32Next" );
		if ( pCT32S != 0 && pM32F != 0 && pM32N != 0 )
			break; // found the functions!
		FreeLibrary( hToolhelp );
		hToolhelp = 0;
	}

	if ( hToolhelp == 0 ) // nothing found?
		return false;

	hSnap = pCT32S( TH32CS_SNAPMODULE, pid );
	if ( hSnap == (HANDLE) -1 )
		return false;

	keepGoing = !!pM32F( hSnap, &me );
	while ( keepGoing )
	{
		// here, we have a filled-in MODULEENTRY32
		e.imageName = me.szExePath;
		e.moduleName = me.szModule;
		e.baseAddress = (DWORD) me.modBaseAddr;
		e.size = me.modBaseSize;
		modules.push_back( e );
		keepGoing = !!pM32N( hSnap, &me );
	}

	CloseHandle( hSnap );

	FreeLibrary( hToolhelp );

	return modules.size() != 0;
}

// miscellaneous psapi declarations; we cannot #include the header
// because not all systems may have it
typedef struct _MODULEINFO {
    LPVOID lpBaseOfDll;
    DWORD SizeOfImage;
    LPVOID EntryPoint;
} MODULEINFO, *LPMODULEINFO;

bool fillModuleListPSAPI( ModuleList& modules, DWORD pid, HANDLE hProcess )
{
	// EnumProcessModules()
	typedef BOOL (__stdcall *tEPM)( HANDLE hProcess, HMODULE *lphModule, DWORD cb, LPDWORD lpcbNeeded );
	// GetModuleFileNameEx()
	typedef DWORD (__stdcall *tGMFNE)( HANDLE hProcess, HMODULE hModule, LPSTR lpFilename, DWORD nSize );
	// GetModuleBaseName() -- redundant, as GMFNE() has the same prototype, but who cares?
	typedef DWORD (__stdcall *tGMBN)( HANDLE hProcess, HMODULE hModule, LPSTR lpFilename, DWORD nSize );
	// GetModuleInformation()
	typedef BOOL (__stdcall *tGMI)( HANDLE hProcess, HMODULE hModule, LPMODULEINFO pmi, DWORD nSize );

	HINSTANCE hPsapi;
	tEPM pEPM;
	tGMFNE pGMFNE;
	tGMBN pGMBN;
	tGMI pGMI;

	int i;
	ModuleEntry e;
	DWORD cbNeeded;
	MODULEINFO mi;
	HMODULE *hMods = 0;
	char *tt = 0;

	hPsapi = LoadLibrary( "psapi.dll" );
	if ( hPsapi == 0 )
		return false;

	modules.clear();

	pEPM = (tEPM) GetProcAddress( hPsapi, "EnumProcessModules" );
	pGMFNE = (tGMFNE) GetProcAddress( hPsapi, "GetModuleFileNameExA" );
	pGMBN = (tGMFNE) GetProcAddress( hPsapi, "GetModuleBaseNameA" );
	pGMI = (tGMI) GetProcAddress( hPsapi, "GetModuleInformation" );
	if ( pEPM == 0 || pGMFNE == 0 || pGMBN == 0 || pGMI == 0 )
	{
		// yuck. Some API is missing.
		FreeLibrary( hPsapi );
		return false;
	}

	hMods = new HMODULE[TTBUFLEN / sizeof HMODULE];
	tt = new char[TTBUFLEN];
	// not that this is a sample. Which means I can get away with
	// not checking for errors, but you cannot. :)

	if ( ! pEPM( hProcess, hMods, TTBUFLEN, &cbNeeded ) )
	{
		goto cleanup;
	}

	if ( cbNeeded > TTBUFLEN )
	{
		goto cleanup;
	}

	for ( i = 0; i < cbNeeded / sizeof hMods[0]; ++ i )
	{
		// for each module, get:
		// base address, size
		pGMI( hProcess, hMods[i], &mi, sizeof mi );
		e.baseAddress = (DWORD) mi.lpBaseOfDll;
		e.size = mi.SizeOfImage;
		// image file name
		tt[0] = '\0';
		pGMFNE( hProcess, hMods[i], tt, TTBUFLEN );
		e.imageName = tt;
		// module name
		tt[0] = '\0';
		pGMBN( hProcess, hMods[i], tt, TTBUFLEN );
		e.moduleName = tt;

		modules.push_back( e );
	}

cleanup:
	if ( hPsapi )
		FreeLibrary( hPsapi );
	delete [] tt;
	delete [] hMods;

	return modules.size() != 0;
}

bool fillModuleList( ModuleList& modules, DWORD pid, HANDLE hProcess )
{
	// try toolhelp32 first
	if ( fillModuleListTH32( modules, pid ) )
		return true;
	// nope? try psapi, then
	return fillModuleListPSAPI( modules, pid, hProcess );
}

void enumAndLoadModuleSymbols( HANDLE hProcess, DWORD pid )
{
	ModuleList modules;
	ModuleListIter it;
	char *img, *mod;

	// fill in module list
	fillModuleList( modules, pid, hProcess );

	for ( it = modules.begin(); it != modules.end(); ++ it )
	{
		// unfortunately, SymLoadModule() wants writeable strings
		img = new char[(*it).imageName.size() + 1];
		strcpy( img, (*it).imageName.c_str() );
		mod = new char[(*it).moduleName.size() + 1];
		strcpy( mod, (*it).moduleName.c_str() );

		SymLoadModule( hProcess, 0, img, mod, (*it).baseAddress, (*it).size );

		delete [] img;
		delete [] mod;
	}
}

GenericCallStack::GenericCallStack(HANDLE hThread, CONTEXT& c)
{
	DWORD imageType = IMAGE_FILE_MACHINE_I386;
	HANDLE hProcess = GetCurrentProcess();

	int frameNum; // counts walked frames
	DWORD offsetFromSymbol; // tells us how far from the symbol we were
	DWORD symOptions; // symbol handler settings
	IMAGEHLP_SYMBOL *pSym = (IMAGEHLP_SYMBOL *) malloc( IMGSYMLEN + MAXNAMELEN );
	char undFullName[MAXNAMELEN]; // undecorated name with all shenanigans

	IMAGEHLP_MODULE Module;
	IMAGEHLP_LINE Line;
	std::string symSearchPath;
	char *tt = 0, *p;

	STACKFRAME s; // in/out stackframe
	memset( &s, '\0', sizeof s );

	tt = new char[TTBUFLEN];

	// build symbol search path from:
	symSearchPath = "";
	// current directory
	if ( GetCurrentDirectory( TTBUFLEN, tt ) )
		symSearchPath += tt + std::string( ";" );
	// dir with executable
	if ( GetModuleFileName( 0, tt, TTBUFLEN ) )
	{
		for ( p = tt + strlen( tt ) - 1; p >= tt; -- p )
		{
			// locate the rightmost path separator
			if ( *p == '\\' || *p == '/' || *p == ':' )
				break;
		}
		// if we found one, p is pointing at it; if not, tt only contains
		// an exe name (no path), and p points before its first byte
		if ( p != tt ) // path sep found?
		{
			if ( *p == ':' ) // we leave colons in place
				++ p;
			*p = '\0'; // eliminate the exe name and last path sep
			symSearchPath += tt + std::string( ";" );
		}
	}
	// environment variable _NT_SYMBOL_PATH
	if ( GetEnvironmentVariable( "_NT_SYMBOL_PATH", tt, TTBUFLEN ) )
		symSearchPath += tt + std::string( ";" );
	// environment variable _NT_ALTERNATE_SYMBOL_PATH
	if ( GetEnvironmentVariable( "_NT_ALTERNATE_SYMBOL_PATH", tt, TTBUFLEN ) )
		symSearchPath += tt + std::string( ";" );
	// environment variable SYSTEMROOT
	if ( GetEnvironmentVariable( "SYSTEMROOT", tt, TTBUFLEN ) )
		symSearchPath += tt + std::string( ";" );

	if ( symSearchPath.size() > 0 ) // if we added anything, we have a trailing semicolon
		symSearchPath = symSearchPath.substr( 0, symSearchPath.size() - 1 );

	// why oh why does SymInitialize() want a writeable string?
	strncpy( tt, symSearchPath.c_str(), TTBUFLEN );
	tt[TTBUFLEN - 1] = '\0'; // if strncpy() overruns, it doesn't add the null terminator

	// init symbol handler stuff (SymInitialize())
	if ( ! SymInitialize( hProcess, tt, false ) )
	{
		goto tagCleanUp;
	}

	symOptions = SymGetOptions();
	symOptions |= SYMOPT_LOAD_LINES;
	symOptions &= ~SYMOPT_UNDNAME;
	SymSetOptions( symOptions ); // SymSetOptions()

	enumAndLoadModuleSymbols( hProcess, GetCurrentProcessId() );

	// init STACKFRAME for first call
	// Notes: will have to be #ifdef-ed for Alphas; MIPSes are dead anyway,
	// and good riddance.
	s.AddrPC.Offset = c.Eip;
	s.AddrPC.Mode = AddrModeFlat;
	s.AddrFrame.Offset = c.Ebp;
	s.AddrFrame.Mode = AddrModeFlat;

	memset( pSym, '\0', IMGSYMLEN + MAXNAMELEN );
	pSym->SizeOfStruct = IMGSYMLEN;
	pSym->MaxNameLength = MAXNAMELEN;

	memset( &Line, '\0', sizeof Line );
	Line.SizeOfStruct = sizeof Line;

	memset( &Module, '\0', sizeof Module );
	Module.SizeOfStruct = sizeof Module;

	offsetFromSymbol = 0;

	for ( frameNum = 0; ;)
	{
		// get next stack frame (StackWalk(), SymFunctionTableAccess(), SymGetModuleBase())
		// if this returns ERROR_INVALID_ADDRESS (487) or ERROR_NOACCESS (998), you can
		// assume that either you are done, or that the stack is so hosed that the next
		// deeper frame could not be found.
		if ( ! StackWalk( imageType, hProcess, hThread, &s, &c, NULL,
			SymFunctionTableAccess, SymGetModuleBase, NULL ) )
			break;

		if ( s.AddrPC.Offset != 0 )
		{ // we seem to have a valid PC
			// show procedure info (SymGetSymFromAddr())
			if ( ! SymGetSymFromAddr( hProcess, s.AddrPC.Offset, &offsetFromSymbol, pSym ) )
			{
				break;
			}
			else
			{
				// UnDecorateSymbolName()
				UnDecorateSymbolName( pSym->Name, undFullName, MAXNAMELEN, UNDNAME_COMPLETE );
				if (!frameNum && strstr(undFullName, "Exception"))
					continue;
				_stk += undFullName;
			}
		} // we seem to have a valid PC

		// no return address means no deeper stackframe
		if ( s.AddrReturn.Offset == 0 )
		{
			// avoid misunderstandings in the printf() following the loop
			SetLastError( 0 );
			break;
		}
		++frameNum;
	}

	// de-init symbol handler etc. (SymCleanup())
	SymCleanup( hProcess );
	free( pSym );
tagCleanUp:;	
	delete [] tt;
}

GenericCallStack::~GenericCallStack()
{
}

const GStringList *GenericCallStack::GetStack()
{
	return &_stk;
}

const char *GenericCallStack::GetStackAsString() 
{
	_strStk.Empty();
	GStringIterator it(&_stk);
	
	while (it())
	{
		if (!_strStk.IsEmpty())
			_strStk += "\n";
		_strStk += it++;
	}

	return (const char *)_strStk;
}

#endif

GenericException::GenericException(int nError, int nSubSystem, const char *pzText, GStringList *pStack)
{
	_error = nError;
	_subSystem = nSubSystem;
	_cause = 0;
	this->Format("%s",pzText);

	if (pStack)
	{
		GStringIterator it(pStack);
		while (it())
			_stk.AddLast(it++);
	}
}

void GenericException::AddErrorDetail(int nError, const char *pzErrorText)
{
	GString strTemp;
	strTemp.Format("[Error %ld] %s\n",nError,pzErrorText);
	_ErrorDetail.AddLast(strTemp);
}


void GenericException::AddErrorDetail(const char* szSystem, int error, ...)
{
	int nSubSystem = 0;
	int nError = error;
	try
	{
		GProfile &ErrorProfile = GetErrorProfile();
		nSubSystem = atoi(ErrorProfile.GetString(szSystem, "SubSystem"));

		GString strKey;
		strKey.Format("%ld", error);
		strKey = ErrorProfile.GetString(szSystem, (const char *)strKey);

		GString strTemp;
		va_list argList;
		va_start(argList, error);
		strTemp.FormatV((const char *)strKey, argList);
		va_end(argList);

		GString strAddedToUserStack;
		strAddedToUserStack.Format("[Error %ld:%ld] %s\n", nSubSystem, nError, (const char *)strTemp);
		_ErrorDetail.AddLast(strAddedToUserStack);

	}
	catch (GenericException &e)
	{
		_subSystem = e._subSystem;
		_error = e._error;

		Format("%s", (const char *)e);
		return;
	}
}


GenericException::GenericException(const char* szSystem, int error, ...)
{
	_subSystem = 0;
	_error = error;

	try
	{
		GProfile &ErrorProfile = GetErrorProfile();
		_subSystem = atoi(ErrorProfile.GetString(szSystem, "SubSystem"));
		GString strKey;
		strKey.Format("%ld", error);
		strKey = ErrorProfile.GetString(szSystem, (const char *)strKey);

		va_list argList;
		va_start(argList, error);
		FormatV((const char *)strKey, argList);
		va_end(argList);
	}
	catch (GenericException &e)
	{
		_subSystem = e._subSystem;
		_error = e._error;

		Format("%s", (const char *)e);
		return;
	}

#if defined _DEBUG && _WIN32 // record the callstack.
//	_se_translator_function	f;
//	f = _set_se_translator(_stack_se_translator);
//	try
//	{
//		int div = 0;
//		int crash = 1/div;
//	}
//	catch (GenericCallStack &gcs)
//	{
//		_stk = *gcs.GetStack();
//	}
//	_set_se_translator(f);
#endif
}

GenericException::GenericException(const GenericException &cpy)
{
	_subSystem = cpy._subSystem;
	_error = cpy._error;
	_ErrorDetail = cpy._ErrorDetail;
	Format("%s", (const char *)cpy);
}

GenericException::GenericException(int error, const char *str)
{
	_subSystem = 0;
	_error = error;
	Format("%s", str);
}

GenericException::GenericException()
{
	_subSystem = 0;
}

GenericException::~GenericException()
{
}

const char *GenericException::GetDescription() 
{
	// Get the user stack into strUserStack
	GString strUserStack;
	GStringIterator it(&_ErrorDetail);
	while (it())
	{
		strUserStack += it++;
		if (it())
			strUserStack += "\n";
	}

	_ret.Format("[Error %ld:%ld] %s\n%s", _subSystem, _error, (const char *)*this, (const char *)strUserStack);
	return (const char *)_ret; 
}


const char *GenericException::ToXML(const char *pzExceptionParent/* = "TransactResultSet"*/) 
{
	_ret.Empty();

	// Get the user stack into strUserStack
	GString strUserStack;
	GStringIterator it(&_ErrorDetail);
	while (it())
	{
		strUserStack += "\t\t<UserStack>";
		strUserStack.AppendEscapeXMLReserved(it++);
		strUserStack += "</UserStack>";
		if (it())
			strUserStack += "\n";
	}


	GString strDescription = (const char *)*this;
	strDescription.EscapeXMLReserved();
	

	_ret.Format("<%s>\n\t<Exception>\n\t\t<Description>%s</Description>\n\t\t"
				"<ErrorNumber>%ld</ErrorNumber>\n\t\t<SubSystem>%d</SubSystem>\n%s\t</Exception></%s>",
				pzExceptionParent,
				(const char *)strDescription,
				_error,
				_subSystem,
				(const char *)strUserStack,
				pzExceptionParent);


	return (const char *)_ret; 
}

long GenericException::GetCause()
{ 
	_cause = _subSystem;
	_cause <<= 4;

	_cause |= _error;

	return _cause;
}

const GStringList *GenericException::GetStack()
{
	return &_stk;
}

const char *GenericException::GetStackAsString()
{
	_ret.Empty();

	if (_stk.isEmpty())
	{
		_ret = "Call stack unavailable.";
	}
	else
	{
		GStringIterator it(&_stk);
		
		while (it())
		{
			if (!_ret.IsEmpty())
				_ret += "\n";
			_ret += it++;
		}
	}

	return (const char *)_ret;
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Founder United Business Technologies
United States United States
http://about.me/brian.aberle
https://www.linkedin.com/in/brianaberle
http://SyrianRue.org/Brian

Comments and Discussions