/*<<<***************************************************************************
The MIT License (MIT)

Copyright(C) 2013 - 2015 Marc Mirti Mancinelli, Christophe Lardau and Philippe Pamart (xdcube.org)

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
////////////////////////////////////////////////////////////////////////////////
#Title: xbException
#Project: xdcBase
////////////////////////////////////////////////////////////////////////////////
#Author: Marc MIRTI MANCINELLI
#History:
<11/02/2014: creation.
12/02/2014: this notice inserted.
***************************************************************************>>>*/
#include "stdafx.h"

#include "xbException.h"
#include "xbConfig.h"
#include "xbFileTools.h"
#include "xbExpr.h"
#include "xbVersion.h"
#include "xbMsg.h"

#pragma warning(push)
#pragma warning(disable: 4311)

xbEimpl(xbEadv,xbEbase);
xbEimpl(xbEuser,xbEadv);
xbEimpl(xbEapp,xbEadv);
xbEimpl(xbEappCfg,xbEapp);
xbEimpl(xbEarg,xbEadv);
xbEimpl(xbElib,xbEadv);
xbEimpl(xbElock,xbEadv);
xbEimpl(xbEadvCtx,xbEadv);
xbEimpl(xbEstack,xbEadv);
xbEimpl(xbEseh,xbEadv);
/*
#include "xbLock.h"
#include "xbDnItf.h"
*/
xbExceptionEngine		g_xbExceptionEngine("xdcBase Exception Engine");

void xbEadv::msgFmt(const char *pszMsg,va_list ap)
{
	//if \1 present, first part is message, 2nd is context
	LPCSTR ptr = ::xbStrchr(pszMsg,'\1');
	if (ptr == NULL)
	{
		m_msg = ::xbFormatV(pszMsg,ap);
		return;
	}
	int pos = (int)((xbNptr(ptr) - xbNptr(pszMsg)) / sizeof(*pszMsg));
	m_msg = ::xbFormatV(xbString(pszMsg,pos),ap);
	m_ctx = ::xbFormatV(pszMsg+pos+1,ap);
}

void xbEadv::traceExcept(xbEctorMode ctorMode)
{
	try
	{
		const char *pszEtype = GetFullEname();
		if (!pszEtype)
			return;

		xbString msg(m_msg);
		if (m_ctx.IsValid())
			msg += " / ctx="+m_ctx;
		TraceExcept(pszEtype, GetHR(), m_msg,ctorMode);
	}
	catch(xbEbase *pE)
	{
		pE->Delete();
	}
	catch(...)
	{
	}
}
/*int xbExceptionEngine::findCtxCb(const CtxSt &ctx,const void *thId)
{
	if (ctx.threadId < DWORD(thId)) return -1;
	if (ctx.threadId > DWORD(thId)) return 1;
	return 0;
}

xbExceptionEngine::CtxSt *xbExceptionEngine::findCtxSt(DWORD thId, int *pNo)
{
	return m_arrCtx.SortedFind((const void*)thId,findCtxCb,pNo);
}
*/
sIxbExceptionCtx	*xbExceptionEngine::GetContext()
{
	if (!isNowStarted()) return NULL;
	CheckReady();
	xbThreadContext *pThCtx = xbThreadContext::Current();
	if (!pThCtx || !pThCtx->m_pExceptionCtx)
	{
		xbSectLock lck(&m_sect);
		return m_pDfltCtx;
	}
	return pThCtx->m_pExceptionCtx;

/*	try
	{
		if (!isNowStarted()) return NULL;
		xbLock lckStarted;
		if (!IsNowStarted(&lckStarted)) return NULL;

		DWORD thId = ::xbThreadId();
		xbSectLock lck(&m_sect);
		CtxSt *pCtx = findCtxSt(thId);
		if (!pCtx) return m_pDfltCtx;
		return pCtx->pEctx;
	}
	catch(xbEbase *pE)
	{
		pE->Delete();
	}
	catch(...)
	{
	}
	return NULL;*/
}

sIxbExceptionCtx	*xbExceptionEngine::GetDefaultContext()
{
	if (!isNowStarted()) return NULL;
//	xbLock lckStarted;
//	if (!IsNowStarted(&lckStarted)) return NULL;
	CheckReady();
	xbSectLock lck(&m_sect);
	return m_pDfltCtx;
}

sIxbExceptionCtx	*xbExceptionEngine::SetContext(sIxbExceptionCtx *pCtx)
{
	xbThreadContext *pThCtx = xbThreadContext::Current();
	if (!pThCtx)
		xbThrow xbElock("Thread context not found !");
	sIxbExceptionCtx *pOld = pThCtx->m_pExceptionCtx;
	pThCtx->m_pExceptionCtx = pCtx;
	return pOld;
/*	xbLock lckStarted(this);
	DWORD thId = ::xbThreadId();
	xbSectLock lck(&m_sect);
	int no;
	CtxSt *pCtxSt = findCtxSt(thId,&no);
	if (!pCtxSt) 
	{
		if (!pCtx) return NULL;
		CtxSt newCtx;
		newCtx.pEctx = pCtx;
		newCtx.threadId = thId;
		if (no <= 0) m_arrCtx.Insert(1,newCtx);
		else m_arrCtx.Insert(no,newCtx);

		return NULL;
	}
	sIxbExceptionCtx *pOld = pCtxSt->pEctx;
	xbAssert(pOld != NULL);
	if (pCtx)
		pCtxSt->pEctx = pCtx;
	else
		m_arrCtx.Delete(no);
	return pOld;*/
}

sIxbExceptionCtx	*xbExceptionEngine::SetDefaultContext(sIxbExceptionCtx *pCtx)
{
	CheckReady();
	xbSectLock lck(&m_sect);
	sIxbExceptionCtx *pOld = m_pDfltCtx;
	m_pDfltCtx = pCtx;
	return pOld;
}

bool xbExceptionEngine::cfgGetKey(const xbString &szEtype,const xbString &szvalname,xbCfgKey &keyRet)
{
	xbCfgKey	keyApp(xbCfgAppli,"Exception");
	if (keyApp.Exists())
	{
		xbCfgKey	eKey(keyApp,szEtype);
		if (eKey.ValueExists(szvalname))
		{
			keyRet = eKey;
			return true;
		}
	}
	xbCfgKey	keyCom(xbCfgCommon,"Exception");
	if (keyCom.Exists())
	{
		xbCfgKey	eKey(keyCom,szEtype);
		if (eKey.ValueExists(szvalname))
		{
			keyRet = eKey;
			return true;
		}
	}
	if (keyApp.Exists())
	{
		if (keyApp.ValueExists(szvalname))
		{
			keyRet = keyApp;
			return true;
		}
	}
	if (keyCom.Exists())
	{
		if (keyCom.ValueExists(szvalname))
		{
			keyRet = keyCom;
			return true;
		}
	}
	return false;
}

bool xbExceptionEngine::cfgGetKey(const xbString &szvalname,xbCfgKey &keyRet)
{
	xbCfgKey	keyApp(xbCfgAppli,"Exception");
	xbCfgKey	keyCom(xbCfgCommon,"Exception");
	if (keyApp.Exists())
	{
		if (keyApp.ValueExists(szvalname))
		{
			keyRet = keyApp;
			return true;
		}
	}
	if (keyCom.Exists())
	{
		if (keyCom.ValueExists(szvalname))
		{
			keyRet = keyCom;
			return true;
		}
	}
	return false;
}

bool				xbExceptionEngine::IsToDebug(LPCSTR pszEtype)
{
	return IsToDebug(pszEtype,false);
}

bool				xbExceptionEngine::IsToDebug(LPCSTR pszEtype,bool bDflt)
{
	try
	{
		if (!isNowStarted()) 
			return false;
		if (m_bDebugAll) 
			return true;
		if (g_xbCfgEngine.isInConfigLoad())
			return false;
		
		xbLock lckStarted;
		if (!IsNowStarted(&lckStarted)) 
			return true;

		xbCfgKey key;
		if (!cfgGetKey(pszEtype,"debug",key))
			return bDflt;

		return key.Value["debug"].AsBool;
	}
	catch(xbEbase *pE)
	{
		xbRelOutput("xbExceptionEngine::IsToDebug(): error -> debug all: "+xbGetMsg(NULL,pE));
		pE->Delete();
		m_bDebugAll = true;
	}
	catch(...)
	{
		m_bDebugAll = true;
		xbRelOutput("xbExceptionEngine::IsToDebug(): error -> debug all: "+xbGetMsg(NULL,NULL));
	}
	return true;
}

bool				xbExceptionEngine::IsToLog(LPCSTR pszEtype)
{
	return IsToLog(pszEtype,false);
}

bool				xbExceptionEngine::IsToLog(LPCSTR pszEtype,bool bDflt)
{
	try
	{
		if (!isNowStarted()) 
			return false;
		if (m_bDebugAll) 
			return true;
		if (g_xbCfgEngine.isInConfigLoad())
			return true;

		xbLock lckStarted;
		if (!IsNowStarted(&lckStarted)) 
			return true;

		xbCfgKey key;
		if (!cfgGetKey(pszEtype,"log",key))
			return bDflt;

		return key.Value["log"].AsBool;
	}
	catch(xbEbase *pE)
	{
		xbRelOutput("xbExceptionEngine::IsToLog(): error -> trace all: "+xbGetMsg(NULL,pE));
		pE->Delete();
		m_bDebugAll = true;
	}
	catch(...)
	{
		m_bDebugAll = true;
		xbRelOutput("xbExceptionEngine::IsToLog(): error -> trace all: "+xbGetMsg(NULL,NULL));
	}
	return true;
}

bool				xbExceptionEngine::IsToStop(LPCSTR pszEtype)
{
	return IsToStop(pszEtype,false);
}

bool				xbExceptionEngine::IsToStop(LPCSTR pszEtype,bool bDflt)
{
#if defined(_DEBUG) || defined(_RELDBG)
	try
	{
		if (!isNowStarted()) 
			return false;
		if (m_bDebugAll) 
			return false;
		if (g_xbCfgEngine.isInConfigLoad())
			return false;

		xbLock lckStarted;
		if (!IsNowStarted(&lckStarted)) 
			return false;


		xbCfgKey key;
		if (!cfgGetKey(pszEtype,"stop",key))
			return bDflt;

		return key.Value["stop"].AsBool;
	}
	catch(xbEbase *pE)
	{
		xbRelOutput("xbExceptionEngine::IsToStop(): error -> trace all: "+xbGetMsg(NULL,pE));
		pE->Delete();
		m_bDebugAll = true;
	}
	catch(...)
	{
		xbRelOutput("xbExceptionEngine::IsToStop(): error -> trace all: "+xbGetMsg(NULL,NULL));
		m_bDebugAll = true;
	}
#endif
	return false;
}

bool				xbExceptionEngine::cfgGetBool(const xbString &szEtype,const xbString &szvalname,bool bDflt,bool bDfltErr)
{
	try
	{
		if (!isNowStarted()) 
			return bDfltErr;

		xbLock lckStarted;
		if (!IsNowStarted(&lckStarted)) 
			return bDfltErr;

		xbCfgKey key;
		if (!cfgGetKey(szEtype,szvalname,key))
			return bDflt;
		return key.Value[szvalname].AsBool;
	}
	catch(xbEbase *pE)
	{
		pE->Delete();
	}
	return bDfltErr;
}

bool				xbExceptionEngine::cfgGetBool(const xbString &szvalname,bool bDflt,bool bDfltErr)
{
	try
	{
		if (!isNowStarted()) 
			return bDfltErr;

		xbLock lckStarted;
		if (!IsNowStarted(&lckStarted)) 
			return bDfltErr;

		xbCfgKey key;
		if (!cfgGetKey(szvalname,key))
			return bDflt;
		
		return key.Value[szvalname].AsBool;
	}
	catch(xbEbase *pE)
	{
		pE->Delete();
	}
	return bDfltErr;
}

ulong				xbExceptionEngine::cfgGetUlong(const xbString &szEtype,const xbString &szvalname,ulong bDflt,ulong bDfltErr)
{
	try
	{
		if (!isNowStarted()) 
			return bDfltErr;

		xbLock lckStarted;
		if (!IsNowStarted(&lckStarted)) 
			return bDfltErr;

		xbCfgKey key;
		if (!cfgGetKey(szEtype,szvalname,key))
			return bDflt;

		return key.Value[szvalname].AsUlong;
	}
	catch(xbEbase *pE)
	{
		pE->Delete();
	}
	return bDfltErr;
}

ulong				xbExceptionEngine::cfgGetUlong(const xbString &szvalname,ulong bDflt,ulong bDfltErr)
{
	try
	{
		if (!isNowStarted()) 
			return bDfltErr;

		xbLock lckStarted;
		if (!IsNowStarted(&lckStarted)) 
			return bDfltErr;

		xbCfgKey key;
		if (!cfgGetKey(szvalname,key))
			return bDflt;
		
		return key.Value[szvalname].AsUlong;
	}
	catch(xbEbase *pE)
	{
		pE->Delete();
	}
	return bDfltErr;
}

xbString	xbGetMsgFull(LPCSTR pFmt,const xbEbase *pE,bool bAddEname,bool bAddHR)
{
	LPCSTR _pFmt = (!pFmt || xbStricmp(pFmt,"text")!=0)?"raw":pFmt;
	xbString ret(xbGetMsg(_pFmt,pE));
	{
		xbString ctx(xbGetCtx(_pFmt,pE,bAddEname,bAddHR));
		if (ctx.IsValid())
		{
			if (pFmt && xbStricmp(pFmt,"text")!=0)
			{
				ret += "${=chtl}<br><B>Context:</B><br>${=text}";
			}
			else
				ret += "\nContext:\n";
			ret += ctx;
		}
	}

	if (_pFmt != pFmt && pFmt && *pFmt && xbStricmp(pFmt,"raw") != 0)
		return xbSubstFmt(pFmt,ret,"text");
	return ret;
}

xbString	xbGetMsg(LPCSTR pFmt,const xbEbase *pE)
{
	if (!pE)
		return "Unknown type exception !";

	if (!pFmt)
		pFmt = "text";

	const xbEbase *pEb = (const xbEbase*)pE;

	xbSubstForException substInfo(pEb);
	return xbSubstVar(&substInfo,pFmt,pEb->GetMsg(),NULL,NULL,"text");
}

xbString	xbGetCtx(LPCSTR pFmt,const xbEbase *pE,bool bAddEname,bool bAddHR)
{
	xbString ret;
	xbSubstForException substInfo(NULL);
	if (pE)
	{
		const xbEadv *pEadv;
		if (xbCastIn<xbEadv>(pE,&pEadv))
			ret = pEadv->GetCtxString();

		substInfo.m_pE = pE;

		if (bAddHR)
		{
			xbString str;
			eProtectBegin();
			HRESULT hr = pE->GetRealHR();
			if (hr != E_FAIL && hr != 0)
			{
				const char *pHRmsg = pE->GetHRmsg();
				if (!pHRmsg)
					str = xbFormat("${if cppdebug}[Error code: %08lX ???]${endif}",hr);
				else 
					str = xbFormat("${if cppdebug}[Error code: %08lX - ",hr)+ pHRmsg +"]${endif}";
			}
			eProtectEndTrace();
			if (str.IsValid())
			{
				if (pFmt && xbStricmp(pFmt,"text")!=0)
				{
					str = "${=chtl}<I>"+str+"</I>";
					if (ret.IsValid())
						str +="${=text}";
				}
				if (ret.IsValid())
					ret = str+"\n"+ret;
				else
					ret = str;
			}
		}
	}
	if (bAddEname)
	{
		xbString str("[Type: "+xbGetEname(pE)+"]");
		if (pFmt && xbStricmp(pFmt,"text")!=0)
		{
			str = "${=chtl}<I>"+str+"</I>";
			if (ret.IsValid())
				str +="${=text}";
		}
		if (ret.IsValid())
			ret = str+"\n"+ret;
		else
			ret = str;
	}
	if (ret.IsEmpty())
		return ret;

	if (!pFmt)
		pFmt = "text";
	return xbSubstVar(&substInfo,pFmt,ret,NULL,NULL,"text");
}

LPCSTR		xbGetFullEname(const xbEbase *pE)
{
	if (!pE)
		return "xbEunknown";

	return pE->GetFullEname();
}

xbString	xbGetEname(const xbEbase *pE)
{
	if (!pE)
		return "unknown";
	return _xbGetEname(pE);
}

LPCSTR	_xbGetEname(const xbEbase *pE)
{
	LPCSTR pEname = xbGetFullEname(pE);
	while(*pEname >= 'a' && *pEname <= 'z')
		pEname++;

	if (*pEname == 0 || *pEname != 'E')
		return xbGetFullEname(pE);

	return pEname;
}

xbString	xbGetHRmsg(const xbEbase *pE)
{
	xbString str;
	if (pE)
	{
		eProtectBegin();
		const char *pHRmsg = pE->GetHRmsg();
		if (pHRmsg)
			str = pHRmsg;
		eProtectEndTrace();
	}
	return str;
}

HRESULT		xbGetHR(const xbEbase *pE)
{
	if (pE)
		return ((const xbEbase*)pE)->GetHR();
	return E_POINTER;
}

xbMBretType xbShowException(xbEbase *pE,bool bDel,LPCSTR pFile,int nFileLine,LPCSTR pBeforeMsg,LPCSTR pAfterMsg,xbMBbtnType mboxBtns,bool bResumeStack)
{
	if (bDel || bResumeStack)
		pE->exceptionProcess();

	xbMBretType showRet = xbMBret_Undef;
	try
	{
		xbHWND hWnd;
		sIxbExceptionCtx *pEctx = g_xbExceptionEngine.GetContext();

		if (pEctx)
		{
			xbMBretType ret = xbMBret_Undef;
			if (pEctx->ExceptionShow(pE,bDel,pFile,nFileLine,pBeforeMsg,pAfterMsg,mboxBtns,&ret))
				return ret;

			hWnd = pEctx->GetDlgParentHwnd();
		}

		xbString		msg(xbGetMsg("text",pE));
		xbString		mboxTitle;
		xbMBiconType	mboxIcon = xbMBicon_Stop;
		bool		bWriteToLog = true;
		if (pE)
		{
			mboxTitle =	pE->GetMBoxTitle();
			mboxIcon =	pE->GetMBoxIcon();
			bWriteToLog = pE->IsToWriteOnLogFile();
		}
		else 
		{
			mboxTitle = "System error";
		}
		xbString appTitle;
		{
			try
			{
				if (!pEctx || !pEctx->GetAppTitle(&appTitle))
				{
					appTitle = xbGetProductName();
				}
			}
			catch(xbEbase *e)
			{
				e->Delete();
			}
			catch(...)
			{
			}
		}

		bool bCanShowMBox = pEctx != NULL && pEctx->CanShowDlg();
		if (bWriteToLog || !bCanShowMBox)
		{
			if (!bWriteToLog
				&& !bCanShowMBox
				&& xbIsHRabort(::xbGetHR(pE)))
				return showRet;

			if (!bCanShowMBox)
				::xbBeep(mboxIcon);

			xbEadv *pEadv;
			xbString ctx;
			if (pE && xbCastIn<xbEadv>(pE,&pEadv))
				ctx = ::xbGetCtx("text",pE,true);

			xbString shdr("<<EXCEPTION ! mbtrace---------------------------------------------------------------------------------------------------<<\n");
			if (pFile && *pFile)
			{
				shdr += xbString(pFile) + '(' + xbString(nFileLine) + "): ";
			}
			if (!bCanShowMBox)
				shdr += "(Can't show MBox) ";
			shdr += mboxTitle;
			if (appTitle.IsValid()) 
				shdr += " / " + appTitle;
			shdr += ": ";
			shdr += msg;
			if (!ctx.IsEmpty()) shdr += "\n -> Context:\n"+ctx;
			shdr += "\n>>mbtrace--------------------------------------------------------------------------------------------------------------->>";
			xbRelOutput(shdr,bWriteToLog);
		}

		if (bCanShowMBox)
		{
			xbExceptionDlgLock	edlgLock(pEctx);

		#ifdef _DEBUG
			mboxTitle += " ! [debug]";
		#else
			mboxTitle += " !";
		#endif

			if (pBeforeMsg) msg = xbString(pBeforeMsg) + "\n\n" + msg;
			if (pAfterMsg) msg += "\n\n" + xbString(pAfterMsg);

		#if defined(_DEBUG) || defined(_RELDBG)
			if (xbIsDebuggerPresent() && (mboxBtns == xbMBbtn_Ok || mboxBtns == xbMBbtn_Default))
			{
				xbMBretType showRet = ::xbMBox(hWnd,msg+"\n\n(Debugger present: CANCEL -> Stop in debugger)",
					mboxTitle,mboxIcon,xbMBbtn_OkCancel);
				if (showRet == xbMBret_Cancel) 
					::xbDebugBreak();
			}
			else
			{
		#endif
				showRet = ::xbMBox(hWnd,msg,mboxTitle,mboxIcon,mboxBtns);
		#if defined(_DEBUG) || defined(_RELDBG)
			}
		#endif
		}
	}
	catch(xbEbase *pSubE)
	{
		pSubE->Delete();
		OutputDebugString("xdcBase xbEbase on xbShowException !!\n");
		showRet = xbMBret_Undef;
	}
	catch(...)
	{
		OutputDebugString("xdcBase System Exception on xbShowException !!\n");
		showRet = xbMBret_Undef;
	}
	if (bDel && pE) 
		pE->Delete();
	return showRet;
}

void xbTraceException(xbEbase *pE,bool bDel,LPCSTR pFile,int nFileLine,bool bResumeStack)
{
	if (bDel || bResumeStack)
		pE->exceptionProcess();
	try
	{
		sIxbExceptionCtx *pEctx = g_xbExceptionEngine.GetContext();
		if (pEctx)
		{
			if (pEctx->ExceptionTrace(pE,bDel,pFile,nFileLine))
				return;
		}

		xbString mboxTitle;
		xbString ctx;
		if (pE)
		{
			if (!pE->IsToWriteOnLogFile()) 
			{
				if (bDel && pE) 
					pE->Delete();
				return;
			}

			mboxTitle =	pE->GetMBoxTitle();
			ctx = ::xbGetCtx("text",pE,true);
		}
		else 
		{
			mboxTitle = "System error";
		}
		xbString appTitle;
		{
			try
			{
				if (!pEctx || !pEctx->GetAppTitle(&appTitle))
					appTitle = xbGetProductName();
			}
			catch(xbEbase *e)
			{
				e->Delete();
			}
			catch(...)
			{
			}
		}
		xbString msg(xbGetMsg("text",pE));
		xbString shdr("<<EXCEPTION !trace------------------------------------------------------------------------------------------------------<<\n");
		if (pFile && *pFile)
		{
			shdr += xbString(pFile) + '(' + xbString(nFileLine) + "): ";
		}
		if (appTitle.IsValid()) 
			shdr += appTitle + " / ";
		shdr += mboxTitle;
		shdr += ": ";
		shdr += msg;
		if (!ctx.IsEmpty()) 
			shdr += "\n -> Context:\n"+ctx;
		shdr += "\n>>trace----------------------------------------------------------------------------------------------------------------->>";
		xbRelOutput(shdr,true);
	}
	catch(xbEbase *e)
	{
		e->Delete();
		OutputDebugString("xdcBase xbEbase on xbTraceException !!\n");
	}
	catch(...)
	{
		OutputDebugString("xdcBase System Exception on xbTraceException !!\n");
	}
	if (bDel && pE) 
		pE->Delete();
}

xbMBretType xbMBox(xbHWND hWnd,const xbString &sText,const xbString &sTitle,xbMBiconType icon,xbMBbtnType btn)
{
	sIxbExceptionCtx *pEctx = g_xbExceptionEngine.GetContext();
	if (!pEctx || pEctx->IsDlgDispLocked())
	{
		::xbBeep(xbMBicon_Stop);
		xbRelOutput("xdcBase / MBox display forbidden: "+sTitle+" / "+sText);
		return xbMBret_Undef;
	}
	{
		xbMBretType ret;
		if (pEctx->ShowMBox(hWnd,sText,sTitle,icon,btn,&ret))
			return ret;
	}

	xbDlgUseLock	dlgUseLck(pEctx);
	xbDlgDispLock	dlgDispLck(pEctx);
	xbString title;
	{
		try
		{
			if (!pEctx->GetAppTitle(&title))
				title = xbGetProductName();
		}
		catch(xbEbase *e)
		{
			e->Delete();
		}
		catch(...)
		{
		}
		if (!sTitle.IsEmpty()) 
		{
			if (title.IsEmpty()) 
				title = sTitle;
			else 
				title = sTitle + " / " + title;
		}
	}

	if (btn == xbMBbtn_Default) 
		btn = xbMBbtn_Ok;
	
	xbSaveMsgInfo msginfo;
	MSG msg;
	BOOL bQuit = ::PeekMessage(&msg, NULL, WM_QUIT, WM_QUIT, PM_REMOVE);
	int ret = ::MessageBox(hWnd,sText,(title.IsEmpty()?(LPCSTR)NULL:(LPCSTR)title),((ulong)btn)|((ulong)icon));
	if (bQuit)
		PostQuitMessage(int(msg.wParam));
	return (xbMBretType)ret;
}

xbAppBase	*xbAppBase::s_pAppli = NULL;
xbAppBase::xbAppBase(LPCSTR pszAppId,LPCSTR pszAppLabel)
	:m_ctx(pszAppId,NULL)
{
	xbRdbgAssert(s_pAppli == NULL);
	m_appId = pszAppId;
	if (pszAppLabel) 
		m_appLabel = pszAppLabel;
	m_cLockExceptionDlg = m_cLockDlgUse = m_cLockDlgDisp = 0;
	s_pAppli = this;
	m_ctx.m_pExecCtx = this;
}

xbString	xbAppBase::GetAppLabel() const
{
	if (m_appLabel.IsValid()) return m_appLabel;
	m_appLabel = xbGetProductName();
	return m_appLabel;
}
xbString	xbAppBase::GetAppName() const
{
	return xbGetFileName();
}

xbString	xbAppBase::GetAppPath() const
{
	return xbGetFilePath();
}

xbString	xbAppBase::GetAppDir() const
{
	return xbGetFileDir();
}

xbAppBase::~xbAppBase()
{
	m_ctx.m_pExecCtx = NULL;
	xbRdbgAssert(m_cLockExceptionDlg == 0);
	xbRdbgAssert(m_cLockDlgUse == 0);
	xbRdbgAssert(m_cLockDlgDisp == 0);
	xbRdbgAssert(s_pAppli == this);
	s_pAppli = NULL;
}


xbEstack::xbEstack(bool bResumed,bool bCtxSet)
	:xbEadv(xbHR_FAIL,xbEctor_base)
{
	m_bResumed = bResumed;
	m_bCtxSet = bCtxSet;
	//no call to traceExcept here: 
	//	because of stack status, use the less possible the stack to avoid crash 
}

xbString xbSEHlabel(DWORD dwExcept)
{
	switch (dwExcept)
	{
	case EXCEPTION_ACCESS_VIOLATION: return "Access Violation"; break;
 	case EXCEPTION_DATATYPE_MISALIGNMENT: return "Datatype Misalignment"; break;
 	case EXCEPTION_BREAKPOINT: return "Breakpoint"; break;
 	case EXCEPTION_SINGLE_STEP: return "Single Step"; break;
 	case EXCEPTION_ARRAY_BOUNDS_EXCEEDED: return "Array Bounds Exceeded"; break;
 	case EXCEPTION_FLT_DENORMAL_OPERAND: return "Flt Denormal Operand"; break;
 	case EXCEPTION_FLT_DIVIDE_BY_ZERO: return "Flt Divide By ZERO"; break;
 	case EXCEPTION_FLT_INEXACT_RESULT: return "Flt Inexact Result"; break;
 	case EXCEPTION_FLT_INVALID_OPERATION: return "Flt Invalid Operation"; break;
 	case EXCEPTION_FLT_OVERFLOW: return "Flt Overflow"; break;
 	case EXCEPTION_FLT_STACK_CHECK: return "Flt Stack Check"; break;
 	case EXCEPTION_FLT_UNDERFLOW: return "Flt Underflow"; break;
 	case EXCEPTION_INT_DIVIDE_BY_ZERO: return "Int Divide By Zero"; break;
 	case EXCEPTION_INT_OVERFLOW: return "Int Overflow"; break;
 	case EXCEPTION_PRIV_INSTRUCTION: return "Priv Instruction"; break;
 	case EXCEPTION_IN_PAGE_ERROR: return "In Page Error"; break;
 	case EXCEPTION_ILLEGAL_INSTRUCTION: return "Illegal Instruction"; break;
 	case EXCEPTION_NONCONTINUABLE_EXCEPTION: return "Noncontinuable Exception"; break;
	case EXCEPTION_STACK_OVERFLOW: return "Stack Overflow"; break;
 	case EXCEPTION_INVALID_DISPOSITION: return "Invalid Disposition"; break;
 	case EXCEPTION_GUARD_PAGE: return "Guard Page"; break;
 	case EXCEPTION_INVALID_HANDLE: return "Invalid Handle"; break;
	}
	return xbFormat("SEH code: %08lx",dwExcept);
}

void xbSEHtranslate( unsigned int sehCode, struct _EXCEPTION_POINTERS *psehInfo)
{
	xbThreadContext *pThCtx = xbThreadContext::Current();

	if (sehCode == EXCEPTION_STACK_OVERFLOW)
	{
		if(pThCtx)
		{
			if (!pThCtx->m_pCurEstack)
				pThCtx->m_pCurEstack = xbDnew xbEstack(false,true);
			
			throw pThCtx->m_pCurEstack;
		}

		xbThrow xbEstack();
	}
	xbSEHtranslate2( pThCtx, sehCode, psehInfo);
}

extern xbString xbStackDump(EXCEPTION_POINTERS *ep);

void xbSEHtranslate2( xbThreadContext *pThCtx, unsigned int sehCode, struct _EXCEPTION_POINTERS *psehInfo)
{
	if(pThCtx)
	{
		if (!pThCtx->m_pCurEseh)
			pThCtx->m_pCurEseh = xbDnew xbEseh(E_POINTER,xbSEHlabel(sehCode),xbStackDump(psehInfo),true);

		throw pThCtx->m_pCurEseh;
	}

	xbThrow xbEseh(E_POINTER,xbSEHlabel(sehCode),xbStackDump(psehInfo));
}

void xbUnexpected()
{
	::xbBeep(xbMBicon_Stop);
	xbRelOutput("[PB] Unexpected exception (doesn't match throw list) -> application aborted");

#if defined(_DEBUG) || defined(_RELDBG)
	::xbDebugBreak();
#endif
	abort();
}

void xbTerminated()
{
	::xbBeep(xbMBicon_Stop);
	xbRelOutput("[PB] Exception problem (exception on exception, ...) -> application aborted");

#if defined(_DEBUG) || defined(_RELDBG)
	xbDebugBreak();
#endif

	abort();
}

bool xbIsSysDebug()
{
	return xbIsDebuggerPresent() || g_xbExceptionEngine.cfgGetBool("sysDebug",false,false);
}

/*
#define MIN_STACK_REQ_WIN9X 17

#if defined (_M_IA64)

#include <intrin.h>

//
// IA-64 has a large context record and needs an extra page
// to do the unwind of a stack overflow exception.
// The BSP may also need to be reset.
//

#define MIN_STACK_REQ_WINNT 3
#define MIN_BSP_REQ_WINNT 2
#else  // defined (_M_IA64)
#define MIN_STACK_REQ_WINNT 2
#endif  // defined (_M_IA64)

int __cdecl xbResetstkoflw(void)
{
    LPBYTE pStack, pStackBase, pMaxGuard, pMinGuard;
#if defined (_M_IA64)
    LPBYTE pBspBase;
    DWORD BspRegionSize;
#endif  // defined (_M_IA64)
    MEMORY_BASIC_INFORMATION mbi;
    SYSTEM_INFO si;
    DWORD PageSize;
    DWORD RegionSize;
    DWORD flNewProtect;
    DWORD flOldProtect;
    BOOL
    (*SetThreadStackGuaranteePointer) (
        ULONG *StackSizeInBytes
    );
    unsigned int osplatform = 0;

    // Use _alloca() to get the current stack pointer
#pragma warning(push)
#pragma warning(disable:6255)
    // prefast(6255): This alloca is safe and we do not want a __try here
    pStack = (LPBYTE)_alloca(1);
#pragma warning(pop)

    // Find the base of the stack.
	xbRdbgAssert(pStack != NULL);

    if (VirtualQuery(pStack, &mbi, sizeof mbi) == 0) 
	{
		ulong err = ::GetLastError();
        return 0;
    }

    pStackBase = (LPBYTE)mbi.AllocationBase;

    GetSystemInfo(&si);
    PageSize = si.dwPageSize;
    RegionSize = 0;

    // Enable the new guard page.
    if (_get_osplatform(&osplatform) != 0)
		return 0;
    if (osplatform == VER_PLATFORM_WIN32_NT) {

        //
        // Note: if the GuaranteedStackBytes TEB field is 0
        // (on older OS versions or if SetThreadStackGuarantee is
        // not being used) we will use the default value of
        // RegionSize (2 pages for ia64, 1 page for other platforms).
        //

        ULONG StackSizeInBytes;
        HMODULE ModuleHandle;

        //
        // Don't call SetThreadStackGuarantee directly as older kernel32.dll
        // versions do not have this export.
        //

        ModuleHandle = GetModuleHandle ("kernel32.dll");

        if (ModuleHandle != NULL) {

            SetThreadStackGuaranteePointer = (BOOL (*)(ULONG *))GetProcAddress (ModuleHandle, "SetThreadStackGuarantee");

            if (SetThreadStackGuaranteePointer != NULL) {
                StackSizeInBytes = 0;       // Indicate just querying
                if (SetThreadStackGuaranteePointer (&StackSizeInBytes) == TRUE &&
                    StackSizeInBytes > 0) {
                    RegionSize = StackSizeInBytes;
                }
            }
        }
    }

    flNewProtect = (osplatform == VER_PLATFORM_WIN32_WINDOWS)
                   ? PAGE_NOACCESS
                   : PAGE_READWRITE | PAGE_GUARD;


    RegionSize = (RegionSize + PageSize - 1) & ~(PageSize - 1);

    //
    // If there is a stack guarantee (RegionSize nonzero), then increase
    // our guard page size by 1 so that even a subsequent fault that occurs
    // midway (instead of at the beginning) through the first guard page
    // will have the extra page to preserve the guarantee.
    //

    if (RegionSize != 0) {
        RegionSize += PageSize;
    }

#if defined (_M_IA64)

    //
    // Reset the backstore stack pages.
    //

    //
    // Calculate the top of the BSP stack, by getting the size of the normal
    // stack and adding it to the StackBase.
    //

    pBspBase = (LPBYTE)(((ULONG_PTR)(((PNT_TIB)NtCurrentTeb())->StackBase) - (ULONG_PTR) mbi.AllocationBase) +
        (ULONG_PTR)(((PNT_TIB)NtCurrentTeb())->StackBase));

    //
    // Get the current BSP and round up since the BSP grows up.
    //

    pMinGuard = (LPBYTE) ((__getReg(__REG_IA64_RsBSP) + PageSize) & ~(ULONG_PTR)(PageSize - 1));

    //
    // The highest BSP address is the top of the BSP stack less one page for
    // the guard.
    //

    pMaxGuard = pBspBase - PageSize;

    BspRegionSize = RegionSize;

    if (BspRegionSize < MIN_BSP_REQ_WINNT * PageSize) {
        BspRegionSize = MIN_BSP_REQ_WINNT * PageSize;
    }

    if (((ULONG_PTR)pMaxGuard < BspRegionSize) ||
        (pMaxGuard - BspRegionSize) < pMinGuard) {

        //
        // The current BSP is already in the highest guard region.
        //

        return 0;
    }

    if (VirtualAlloc (pMinGuard, BspRegionSize, MEM_COMMIT, PAGE_READWRITE) == NULL ||
        VirtualProtect (pMinGuard, BspRegionSize, flNewProtect, &flOldProtect) == 0) {
        return 0;
    }

#endif  // defined (_M_IA64)

    if (RegionSize < MIN_STACK_REQ_WINNT * PageSize) {
        RegionSize = MIN_STACK_REQ_WINNT * PageSize;
    }

    //
    // Find the page(s) just below where the stack pointer currently points.
    // This is the highest potential guard page.
    //

    pMaxGuard = (LPBYTE) (((DWORD_PTR)pStack & ~(DWORD_PTR)(PageSize - 1))
                       - RegionSize);

    //
    // If the potential guard page is too close to the start of the stack
    // region, abandon the reset effort for lack of space.  Win9x has a
    // larger reserved stack requirement.
    //

    pMinGuard = pStackBase + (
                              (osplatform == VER_PLATFORM_WIN32_WINDOWS)
                              ? MIN_STACK_REQ_WIN9X
                              : PageSize);

    if (pMaxGuard < pMinGuard) {
        return 0;
    }

    // Set the new guard page just below the current stack page.

    if (VirtualAlloc (pMaxGuard, RegionSize, MEM_COMMIT, PAGE_READWRITE) == NULL ||
        VirtualProtect (pMaxGuard, RegionSize, flNewProtect, &flOldProtect) == 0) {
        return 0;
    }

    return 1;
}
*/

void xbProcessException(xbEbase *pE,bool bDel,bool bResumeStack)
{
	if(!pE)
		return;
	if (bDel || bResumeStack)
		pE->exceptionProcess();
	if (bDel) 
		pE->Delete();
}

void xbEstack::exceptionProcess(bool bDtor)
{
	if (!m_bResumed)
	{
		xbRelOutputLow("-> stack overflow");

		if (!xbResetstkoflw())
		{
			xbRelOutputLow("[PB] stack overflow: unrecoverable error");
			if (!bDtor)
				throw this;
		}
		else
			m_bResumed = true;
	}
	if (m_bCtxSet)
	{
		xbThreadContext *pThCtx = xbThreadContext::Current();
		if(pThCtx && pThCtx->m_pCurEstack == this)
			pThCtx->m_pCurEstack = NULL;
		m_bCtxSet=false;
	}
}

void xbEseh::exceptionProcess(bool bDtor)
{
	if (m_bCtxSet)
	{
		xbThreadContext *pThCtx = xbThreadContext::Current();
		if(pThCtx && pThCtx->m_pCurEseh == this)
			pThCtx->m_pCurEseh = NULL;
		m_bCtxSet=false;
	}
}

xbEadv::~xbEadv()
{
	eProtectBegin();
	if (m_pInnerE)
		m_pInnerE->Delete();
	eProtectEndTrace();
}

void xbEadv::SetInnerE(xbEbase *pInnerE)
{
	xbRdbgAssert(m_pInnerE != pInnerE);
	if (m_pInnerE)
	{
		m_pInnerE->Delete();
		m_pInnerE = NULL;
	}
	xbRdbgAssert(pInnerE);
	if (!pInnerE)
		return;

	xbEsysLow *pElow;
	if (xbCastIn(pInnerE,&pElow))
	{
		AddCtx(xbGetMsg("raw",pElow));
		AddCtx(xbGetCtx("raw",pElow));
		pElow->Delete();
		return;
	}

	m_pInnerE = pInnerE;
}

xbEbase *xbEadv::GetInnerE() const
{
	return m_pInnerE;
}

#pragma warning(pop)
