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.
2528 lines
70 KiB
2528 lines
70 KiB
/*===================================================================
|
|
Microsoft Denali
|
|
|
|
Microsoft Confidential.
|
|
Copyright 1996 Microsoft Corporation. All Rights Reserved.
|
|
|
|
Component: Error handling
|
|
|
|
File: Error.cpp
|
|
|
|
Owner: AndrewS
|
|
|
|
This file contains general error reporting routines for Denali.
|
|
===================================================================*/
|
|
#include "denpre.h"
|
|
#pragma hdrstop
|
|
|
|
#include <psapi.h>
|
|
|
|
#include "debugger.h"
|
|
#include "asperror.h"
|
|
#include "memchk.h"
|
|
|
|
#define DELIMITER "~"
|
|
#define MAX_HEADERSIZE 128
|
|
#define MAX_TEMPLATELEN 128
|
|
|
|
//The order of ErrTemplate_Index should be exactly the same order as the IDS_BROWSER_TEMPLATE
|
|
//in the aspresource.h, and as the same order we output the template to the browser.
|
|
//Implementation will loop through the index and picking the string from the resource file.
|
|
//Implementation will also loop through the index and write the string to browser.
|
|
#define ErrTemplate_BEGIN 0
|
|
#define ErrTemplate_ENGINE_BEGIN 1
|
|
#define ErrTemplate_ENGINE_END 2
|
|
#define ErrTemplate_ERROR_BEGIN 3
|
|
#define ErrTemplate_ERROR_END 4
|
|
#define ErrTemplate_SHORT_BEGIN 5
|
|
#define ErrTemplate_SHORT_END 6
|
|
#define ErrTemplate_FILE_BEGIN 7
|
|
#define ErrTemplate_FILE_END 8
|
|
#define ErrTemplate_LINE_BEGIN 9
|
|
#define ErrTemplate_LINE_END 10
|
|
#define ErrTemplate_CODE_BEGIN 11
|
|
#define ErrTemplate_CODE_END 12
|
|
#define ErrTemplate_LONG_BEGIN 13
|
|
#define ErrTemplate_LONG_END 14
|
|
#define ErrTemplate_END 15
|
|
#define ErrTemplateMAX 16
|
|
|
|
const DWORD dwDefaultMask = 0x6; // toNTLog(OFF), toIISLog(ON), toBrowser(ON)
|
|
|
|
CHAR g_szErrTemplate[ErrTemplateMAX][MAX_TEMPLATELEN];
|
|
const LPSTR szErrSysTemplate[] = { "<html><body><h1> HTTP/1.1 ",
|
|
"</h1></body></html>"};
|
|
CErrInfo g_ErrInfoOOM, g_ErrInfoUnExpected;
|
|
CHAR szIISErrorPrefix[20];
|
|
DWORD cszIISErrorPrefix;
|
|
CPINFO g_SystemCPInfo; // global System CodePage default info.
|
|
|
|
static char s_szContentTypeTextHtml[] = "Content-type: text/html\r\n\r\n";
|
|
|
|
CHAR *SzScodeToErrorCode(HRESULT hrError);
|
|
void FreeNullifySz(CHAR **szIn);
|
|
BOOL FIsResStrFormatted(char *szIn);
|
|
|
|
extern LONG g_nOOMErrors;
|
|
|
|
/*===================================================================
|
|
FreeNullifySz
|
|
|
|
Free the memory allocated in szIn, and Nullify the szIn.
|
|
===================================================================*/
|
|
void FreeNullifySz(CHAR **szIn)
|
|
{
|
|
if(*szIn)
|
|
{
|
|
free(*szIn);
|
|
*szIn = NULL;
|
|
}
|
|
}
|
|
|
|
/*===================================================================
|
|
ErrHandleInit
|
|
|
|
PreLoad strings for
|
|
1> OOM
|
|
2> Browser Output Template
|
|
Returns:
|
|
HRESULT
|
|
===================================================================*/
|
|
HRESULT ErrHandleInit(void)
|
|
{
|
|
INT iEntry, iEntryID;
|
|
HRESULT hr;
|
|
|
|
// Retrieve global system codepage and stores it.
|
|
GetCPInfo(CP_ACP, &g_SystemCPInfo);
|
|
|
|
//Init g_szErrTemplate
|
|
//Loop through, and load strings from resource file.
|
|
for (iEntry = ErrTemplate_BEGIN, iEntryID = IDS_BROWSER_TEMPLATE_BEGIN;
|
|
iEntry < ErrTemplateMAX; iEntry ++, iEntryID++)
|
|
{
|
|
CchLoadStringOfId(iEntryID, (CHAR *)g_szErrTemplate[iEntry], MAX_TEMPLATELEN);
|
|
}
|
|
|
|
g_ErrInfoOOM.m_szItem[Im_szErrorCode] = (CHAR *)malloc(sizeof(CHAR)*20*g_SystemCPInfo.MaxCharSize);
|
|
g_ErrInfoOOM.m_szItem[Im_szShortDescription] = (CHAR *)malloc(sizeof(CHAR)*256*g_SystemCPInfo.MaxCharSize);
|
|
g_ErrInfoOOM.m_szItem[Im_szLongDescription] = (CHAR *)malloc(sizeof(CHAR)*512*g_SystemCPInfo.MaxCharSize);
|
|
|
|
if (!g_ErrInfoOOM.m_szItem[Im_szErrorCode] ||
|
|
!g_ErrInfoOOM.m_szItem[Im_szShortDescription] ||
|
|
!g_ErrInfoOOM.m_szItem[Im_szLongDescription])
|
|
{
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
hr = LoadErrResString(IDE_OOM,
|
|
&g_ErrInfoOOM.m_dwMask,
|
|
g_ErrInfoOOM.m_szItem[Im_szErrorCode],
|
|
g_ErrInfoOOM.m_szItem[Im_szShortDescription],
|
|
g_ErrInfoOOM.m_szItem[Im_szLongDescription]);
|
|
|
|
|
|
g_ErrInfoUnExpected.m_szItem[Im_szErrorCode] = (CHAR *)malloc(sizeof(CHAR)*20*g_SystemCPInfo.MaxCharSize);
|
|
g_ErrInfoUnExpected.m_szItem[Im_szShortDescription] = (CHAR *)malloc(sizeof(CHAR)*256*g_SystemCPInfo.MaxCharSize);
|
|
g_ErrInfoUnExpected.m_szItem[Im_szLongDescription] = (CHAR *)malloc(sizeof(CHAR)*512*g_SystemCPInfo.MaxCharSize);
|
|
|
|
if (!g_ErrInfoUnExpected.m_szItem[Im_szErrorCode] ||
|
|
!g_ErrInfoUnExpected.m_szItem[Im_szShortDescription] ||
|
|
!g_ErrInfoUnExpected.m_szItem[Im_szLongDescription])
|
|
{
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
hr = LoadErrResString(IDE_UNEXPECTED,
|
|
&g_ErrInfoUnExpected.m_dwMask,
|
|
g_ErrInfoUnExpected.m_szItem[Im_szErrorCode],
|
|
g_ErrInfoUnExpected.m_szItem[Im_szShortDescription],
|
|
g_ErrInfoUnExpected.m_szItem[Im_szLongDescription]);
|
|
|
|
cszIISErrorPrefix = CchLoadStringOfId(IDS_IISLOG_PREFIX , szIISErrorPrefix, 20);
|
|
return hr;
|
|
}
|
|
/*===================================================================
|
|
ErrHandleUnInit
|
|
|
|
Unit the global err-handling data.
|
|
|
|
Free up the OOM CErrInfo.
|
|
|
|
Side Effect:
|
|
|
|
Free up memory.
|
|
===================================================================*/
|
|
HRESULT ErrHandleUnInit(void)
|
|
{
|
|
FreeNullifySz((CHAR **)&g_ErrInfoOOM.m_szItem[Im_szErrorCode]);
|
|
FreeNullifySz((CHAR **)&g_ErrInfoOOM.m_szItem[Im_szShortDescription]);
|
|
FreeNullifySz((CHAR **)&g_ErrInfoOOM.m_szItem[Im_szLongDescription]);
|
|
|
|
FreeNullifySz((CHAR **)&g_ErrInfoUnExpected.m_szItem[Im_szErrorCode]);
|
|
FreeNullifySz((CHAR **)&g_ErrInfoUnExpected.m_szItem[Im_szShortDescription]);
|
|
FreeNullifySz((CHAR **)&g_ErrInfoUnExpected.m_szItem[Im_szLongDescription]);
|
|
return S_OK;
|
|
}
|
|
/*===================================================================
|
|
Constructor
|
|
|
|
===================================================================*/
|
|
CErrInfo::CErrInfo()
|
|
{
|
|
for (UINT iErrInfo = 0; iErrInfo < Im_szItemMAX; iErrInfo++)
|
|
m_szItem[iErrInfo] = NULL;
|
|
m_bstrLineText = NULL;
|
|
m_nColumn = -1;
|
|
|
|
m_dwMask = 0;
|
|
m_pIReq = NULL;
|
|
m_pHitObj = NULL;
|
|
|
|
m_dwHttpErrorCode = 0;
|
|
m_dwHttpSubErrorCode = 0;
|
|
}
|
|
|
|
/*===================================================================
|
|
CErrInfo::ParseResourceString
|
|
|
|
Parse Resource String to get default mask, error type, short description,
|
|
and long description.
|
|
|
|
Assume resource string is proper formmated.
|
|
Format of resource string
|
|
DefaultMask~errortype~shortdescription~longdescription
|
|
|
|
In case we can not allocate szResourceString(szResourceString), we use default.
|
|
Returns:
|
|
Nothing
|
|
===================================================================*/
|
|
HRESULT CErrInfo::ParseResourceString(CHAR *szResourceString)
|
|
{
|
|
CHAR *szToken = NULL;
|
|
INT cfield = 0;
|
|
INT iItem = 0;
|
|
INT iIndex = 0;
|
|
INT rgErrInfoIndex[3] = {Im_szErrorCode, Im_szShortDescription, Im_szLongDescription};
|
|
|
|
if(NULL == szResourceString)
|
|
{
|
|
m_dwMask = dwDefaultMask;
|
|
for(iItem = 0, iIndex = 0; iIndex < 3; iIndex++)
|
|
{
|
|
iItem = rgErrInfoIndex[iIndex];
|
|
m_szItem[iItem] = g_ErrInfoUnExpected.m_szItem[iItem];
|
|
}
|
|
return S_OK;
|
|
}
|
|
//Mask
|
|
szToken = (char *)_mbstok((unsigned char *)szResourceString, (unsigned char *)DELIMITER);
|
|
if(szToken != NULL)
|
|
{
|
|
m_dwMask = atoi(szToken);
|
|
cfield++;
|
|
}
|
|
else
|
|
{
|
|
m_dwMask = dwDefaultMask;
|
|
}
|
|
|
|
//3 String Items, ErrorCode,ShortDescription,LongDescription
|
|
for(iItem = 0, iIndex = 0; iIndex < 3; iIndex++)
|
|
{
|
|
szToken = (char *)_mbstok(NULL, (unsigned char *)DELIMITER);
|
|
iItem = rgErrInfoIndex[iIndex];
|
|
if (szToken != NULL)
|
|
{
|
|
m_szItem[iItem] = szToken;
|
|
cfield++;
|
|
}
|
|
else
|
|
{
|
|
// Long Description is optional.
|
|
if (Im_szLongDescription != iItem)
|
|
{
|
|
m_szItem[iItem] = g_ErrInfoUnExpected.m_szItem[iItem];
|
|
}
|
|
cfield++;
|
|
}
|
|
}
|
|
|
|
//check wether we have wrong format of resource string.
|
|
Assert(cfield == 4);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
/*===================================================================
|
|
CErrInfo::LogError(void)
|
|
|
|
Perform all the switch logic in this functions. Send error to NT Log,
|
|
IIS Log, or Browser.
|
|
When reach this point, we assume all the strings have allocated, and will not be used after this
|
|
function.
|
|
|
|
Side effects:
|
|
|
|
Returns:
|
|
HRESULT
|
|
===================================================================*/
|
|
HRESULT CErrInfo::LogError(void)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
HRESULT hr_ret = S_OK;
|
|
UINT iEInfo = 0;
|
|
BOOL fIISLogFailed, fDupToNTLog;
|
|
|
|
#if DBG
|
|
// Print details about the error to debug window; don't bother if
|
|
// info is NULL (happens for things like 404 not found, etc.)
|
|
|
|
if (m_szItem[Im_szEngine] != NULL && m_szItem[Im_szFileName] != NULL)
|
|
{
|
|
DBGERROR((DBG_CONTEXT, "%s error in %s at line %s\n",
|
|
m_szItem[Im_szEngine],
|
|
m_szItem[Im_szFileName],
|
|
m_szItem[Im_szLineNum]? m_szItem[Im_szLineNum] : "?"));
|
|
|
|
DBGPRINTF((DBG_CONTEXT, " %s: %s\n",
|
|
m_szItem[Im_szErrorCode],
|
|
m_szItem[Im_szShortDescription]));
|
|
}
|
|
else
|
|
DBGERROR((DBG_CONTEXT, "ASP Error: %s\n", m_szItem[Im_szShortDescription]? m_szItem[Im_szShortDescription] : "?"));
|
|
#endif
|
|
|
|
// Attach ASP error to HitObj (if exists and in 'executing' state)
|
|
if (m_pHitObj && m_pHitObj->FExecuting())
|
|
{
|
|
CASPError *pASPError = new CASPError(this);
|
|
if (pASPError)
|
|
m_pHitObj->SetASPError(pASPError);
|
|
}
|
|
|
|
hr = LogErrortoIISLog(&fIISLogFailed, &fDupToNTLog);
|
|
if (FAILED(hr))
|
|
{
|
|
hr_ret = hr;
|
|
}
|
|
|
|
//fIISLogFailed, if it is TRUE, then, this error was upgraded, and should be a WARNING type in
|
|
//NT event log.
|
|
hr = LogErrortoNTEventLog(fIISLogFailed, fDupToNTLog);
|
|
if (FAILED(hr))
|
|
{
|
|
hr_ret = hr;
|
|
}
|
|
|
|
hr = LogErrortoBrowserWrapper();
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
hr_ret = hr;
|
|
}
|
|
|
|
if (m_pHitObj)
|
|
{
|
|
m_pHitObj->SetExecStatus(eExecFailed);
|
|
}
|
|
//In case of an error, hr_ret is the last error reported from the 3 logging functions.
|
|
return hr_ret;
|
|
|
|
}
|
|
|
|
/*===================================================================
|
|
CErrInfo::LogErrortoNTEventLog
|
|
|
|
Log Error/Event to NT Event Log.
|
|
|
|
Returns:
|
|
Nothing
|
|
===================================================================*/
|
|
HRESULT CErrInfo::LogErrortoNTEventLog
|
|
(
|
|
BOOL fIISLogFailed,
|
|
BOOL fDupToNTLog
|
|
)
|
|
{
|
|
CHAR szErrNTLogEntry[4096];
|
|
CHAR szStringTemp[MAX_PATH];
|
|
INT cch = 0;
|
|
|
|
if(Glob(fLogErrorRequests))
|
|
{
|
|
//Is the error serious enough to get into NT log
|
|
if(ERR_FLogtoNT(m_dwMask) || fIISLogFailed || fDupToNTLog)
|
|
{
|
|
szErrNTLogEntry[0] = '\0';
|
|
|
|
if (fIISLogFailed)
|
|
{
|
|
cch = CchLoadStringOfId(IDS_LOG_IISLOGFAILED, szStringTemp, MAX_PATH);
|
|
strncat(szErrNTLogEntry, szStringTemp, cch);
|
|
}
|
|
|
|
if (m_szItem[Im_szFileName] != NULL)
|
|
{
|
|
cch = CchLoadStringOfId(IDS_LOGTOEVENTLOG_FILE, szStringTemp, MAX_PATH);
|
|
strncat(szErrNTLogEntry, szStringTemp, cch);
|
|
strncat(szErrNTLogEntry, m_szItem[Im_szFileName], 512);
|
|
}
|
|
strncat(szErrNTLogEntry, " ", 1);
|
|
|
|
if (m_szItem[Im_szLineNum] != NULL)
|
|
{
|
|
cch = CchLoadStringOfId(IDS_LOGTOEVENTLOG_LINE, szStringTemp, MAX_PATH);
|
|
strncat(szErrNTLogEntry, szStringTemp, cch);
|
|
strncat(szErrNTLogEntry, m_szItem[Im_szLineNum], 48);
|
|
}
|
|
strncat(szErrNTLogEntry, " ", 1);
|
|
|
|
//Ok, do we have something to log.
|
|
if (m_szItem[Im_szShortDescription] != NULL)
|
|
{
|
|
// ShortDescription does not have ". " at the end.
|
|
// Therefore, the next strncat need to concatenate two sentences together with
|
|
// a period ". ".
|
|
char szTempPeriod[] = ". ";
|
|
|
|
strncat(szErrNTLogEntry, m_szItem[Im_szShortDescription], 512);
|
|
strncat(szErrNTLogEntry, szTempPeriod, 512);
|
|
}
|
|
else
|
|
{
|
|
DWORD dwMask;
|
|
CHAR szDenaliNotWorking[MAX_PATH];
|
|
|
|
LoadErrResString(IDE_UNEXPECTED, &dwMask, NULL, szDenaliNotWorking, NULL);
|
|
strncat(szErrNTLogEntry, szDenaliNotWorking, strlen(szDenaliNotWorking));
|
|
}
|
|
|
|
//Ok, do we have something to log.
|
|
if (m_szItem[Im_szLongDescription] != NULL)
|
|
{
|
|
strncat(szErrNTLogEntry, m_szItem[Im_szLongDescription], 512);
|
|
}
|
|
|
|
if (fIISLogFailed || fDupToNTLog)
|
|
MSG_Warning((LPCSTR)szErrNTLogEntry);
|
|
else
|
|
MSG_Error((LPCSTR)szErrNTLogEntry);
|
|
}
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
/*===================================================================
|
|
CErrInfo::LogErrortoIISLog
|
|
|
|
Log Error/Event to IIS Log.
|
|
|
|
If we fail to log the message then upgrade logging to NT event Log
|
|
with entries indicate the error and the IIS log failed.
|
|
|
|
Also do the upgrade if the global setting says so.
|
|
|
|
Returns:
|
|
Nothing
|
|
===================================================================*/
|
|
HRESULT CErrInfo::LogErrortoIISLog
|
|
(
|
|
BOOL *pfIISLogFailed,
|
|
BOOL *pfDupToNTLog
|
|
)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
const LPSTR szIISDelimiter = "|";
|
|
const DWORD cszIISDelimiter = 1; // strlen("|");
|
|
const LPSTR szIISNoInfo = "-";
|
|
const DWORD cszIISNoInfo = 1; // strlen("-");
|
|
const CHAR chProxy = '_';
|
|
CIsapiReqInfo *pIReq = NULL;
|
|
|
|
*pfIISLogFailed = FALSE;
|
|
*pfDupToNTLog = FALSE;
|
|
|
|
if (m_pIReq == NULL && m_pHitObj == NULL)
|
|
return S_OK;
|
|
|
|
//Try to write to IISLog via pIReq->QueryPszLogData()
|
|
if (ERR_FLogtoIIS(m_dwMask))
|
|
{
|
|
//get pIReq
|
|
if (m_pHitObj)
|
|
{
|
|
pIReq = m_pHitObj->PIReq();
|
|
}
|
|
|
|
if (NULL == pIReq)
|
|
{
|
|
pIReq = m_pIReq;
|
|
}
|
|
|
|
if (pIReq == NULL)
|
|
{
|
|
*pfIISLogFailed = TRUE;
|
|
return E_FAIL;
|
|
}
|
|
|
|
// Setup the sub-string array
|
|
const DWORD crgsz = 3;
|
|
LPSTR rgsz[crgsz];
|
|
|
|
rgsz[0] = m_szItem[Im_szLineNum];
|
|
rgsz[1] = m_szItem[Im_szErrorCode];
|
|
rgsz[2] = m_szItem[Im_szShortDescription];
|
|
|
|
// Allocate the log entry string
|
|
CHAR *szLogEntry = NULL;
|
|
DWORD cszLogEntry = (cszIISDelimiter * crgsz) + 1;
|
|
DWORD dwIndex;
|
|
|
|
for (dwIndex = 0; dwIndex < crgsz; dwIndex++)
|
|
{
|
|
if (rgsz[dwIndex])
|
|
cszLogEntry += strlen(rgsz[dwIndex]);
|
|
else
|
|
cszLogEntry += cszIISNoInfo;
|
|
}
|
|
|
|
szLogEntry = new CHAR[cszLogEntry];
|
|
if (NULL == szLogEntry) {
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
// Copy the entry, proxy bad characters
|
|
CHAR *szSource = NULL;
|
|
CHAR *szDest = szLogEntry;
|
|
|
|
// Start with a delimiter to separate us from
|
|
// the request query
|
|
memcpy(szDest, szIISDelimiter, cszIISDelimiter);
|
|
szDest += cszIISDelimiter;
|
|
|
|
for (dwIndex = 0; dwIndex < crgsz; dwIndex++)
|
|
{
|
|
szSource = rgsz[dwIndex];
|
|
if (szSource)
|
|
{
|
|
while (*szSource)
|
|
{
|
|
if (isleadbyte(*szSource))
|
|
{
|
|
*szDest++ = *szSource++;
|
|
*szDest++ = *szSource++;
|
|
}
|
|
else if ((*szSource == ',') ||
|
|
(*szSource == ' ') ||
|
|
(*szSource == '\r') ||
|
|
(*szSource == '\n') ||
|
|
(*szSource == '\"'))
|
|
{
|
|
*szDest++ = chProxy;
|
|
szSource++;
|
|
}
|
|
else
|
|
*szDest++ = *szSource++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
memcpy(szDest, szIISNoInfo, cszIISNoInfo);
|
|
szDest += cszIISNoInfo;
|
|
}
|
|
|
|
if ((dwIndex + 1) < crgsz)
|
|
{
|
|
// Another sub-string comming, use a delimiter
|
|
memcpy(szDest, szIISDelimiter, cszIISDelimiter);
|
|
szDest += cszIISDelimiter;
|
|
}
|
|
}
|
|
*szDest = '\0';
|
|
|
|
// Log it
|
|
BOOL fResult = TRUE;
|
|
|
|
fResult = SUCCEEDED(pIReq->AppendLogParameter(szLogEntry));
|
|
|
|
// Set "500" error in log.
|
|
if (pIReq->ECB()->dwHttpStatusCode == 200) // error content sent, OK, but really an error
|
|
pIReq->ECB()->dwHttpStatusCode = 500;
|
|
|
|
// Release log string
|
|
delete [] szLogEntry;
|
|
|
|
// If any error occurred while writing to log, upgrade to NT Event log
|
|
if (!fResult)
|
|
{
|
|
m_dwMask = ERR_SetLogtoNT(m_dwMask);
|
|
*pfIISLogFailed = TRUE;
|
|
}
|
|
|
|
// Even if successful we might still want the message
|
|
// in the NT event log if the global setting to do so is on.
|
|
else if (Glob(fDupIISLogToNTLog))
|
|
{
|
|
if (!ERR_FLogtoNT(m_dwMask))
|
|
{
|
|
// Need to remember the flag in order to insert
|
|
// the upgraded IIS log error as NT log warnings.
|
|
// The errors already destined for NT log should
|
|
// stay as errors.
|
|
m_dwMask = ERR_SetLogtoNT(m_dwMask);
|
|
*pfDupToNTLog = TRUE;
|
|
}
|
|
}
|
|
|
|
hr = S_OK;
|
|
}
|
|
|
|
return(hr);
|
|
}
|
|
|
|
/*===================================================================
|
|
CErrInfo::LogErrortoBrowserWrapper
|
|
|
|
Just a Wrapper around Log Error/Event to Browser.
|
|
|
|
In this function, pIReq or pResponse is resolved.
|
|
|
|
NOTE:
|
|
Unfortunately, this function can not tell pResponse is inited or not.
|
|
In case when pResponse has not been inited, pResponse is not NULL, but things
|
|
in pResponse are invalid.
|
|
Therefore, caller need to provide pIReq in case where pResponse has not been inited.
|
|
|
|
Returns:
|
|
HRESULT
|
|
===================================================================*/
|
|
HRESULT CErrInfo::LogErrortoBrowserWrapper()
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
//
|
|
// Must have passed in either an CIsapiReqInfo or a HITOBJ. Otherwise, there is nothing we can do.
|
|
// This condition occurs in case of HandleOOMError
|
|
//
|
|
if (m_pIReq == NULL && m_pHitObj == NULL)
|
|
return E_FAIL;
|
|
|
|
|
|
// Remember response object if any
|
|
CResponse *pResponse = m_pHitObj ? m_pHitObj->PResponse() : NULL;
|
|
|
|
CIsapiReqInfo *pIReq =
|
|
(m_pHitObj && pResponse && m_pHitObj->PIReq()) ?
|
|
m_pHitObj->PIReq() : m_pIReq;
|
|
if (!pIReq)
|
|
return E_FAIL;
|
|
|
|
// Do custom errors only if response headers aren't written already
|
|
// ALSO: No custom error if called from global.asa, with intrinsic objects hidden.
|
|
// (Appln_OnStart & Session_OnStart)
|
|
//
|
|
// for errors in Appln_OnEnd or Session_OnEnd, these are not browser requests
|
|
// and so pResponse == NULL in this case.
|
|
|
|
if (!pResponse || !pResponse->FHeadersWritten())
|
|
{
|
|
BOOL fIntrinsicsWereHidden = FALSE;
|
|
if (m_pHitObj)
|
|
{
|
|
fIntrinsicsWereHidden = m_pHitObj->FRequestAndResponseIntrinsicsHidden();
|
|
m_pHitObj->UnHideRequestAndResponseIntrinsics();
|
|
}
|
|
|
|
BOOL fCustom = FALSE;
|
|
hr = LogCustomErrortoBrowser(pIReq, &fCustom);
|
|
|
|
if (fIntrinsicsWereHidden)
|
|
m_pHitObj->HideRequestAndResponseIntrinsics();
|
|
|
|
if (fCustom)
|
|
return hr;
|
|
}
|
|
|
|
// No custom error - do regular error from this object
|
|
|
|
if (m_szItem[Im_szHeader])
|
|
{
|
|
BOOL fRet = pIReq->SendHeader
|
|
(
|
|
m_szItem[Im_szHeader],
|
|
strlen(m_szItem[Im_szHeader]) + 1,
|
|
s_szContentTypeTextHtml,
|
|
sizeof(s_szContentTypeTextHtml),
|
|
FALSE
|
|
);
|
|
|
|
if (!fRet)
|
|
return E_FAIL;
|
|
}
|
|
|
|
if (pResponse)
|
|
hr = LogErrortoBrowser(pResponse);
|
|
else
|
|
hr = LogErrortoBrowser(pIReq);
|
|
|
|
return hr;
|
|
}
|
|
|
|
/*===================================================================
|
|
CErrInfo::LogCustomErrortoBrowser
|
|
|
|
Called by LogErrortoBrowserWrapper.
|
|
|
|
Parameters
|
|
pIReq
|
|
pfCustomErrorProcessed
|
|
|
|
Returns:
|
|
HRESULT
|
|
===================================================================*/
|
|
HRESULT CErrInfo::LogCustomErrortoBrowser
|
|
(
|
|
CIsapiReqInfo *pIReq,
|
|
BOOL *pfCustomErrorProcessed
|
|
)
|
|
{
|
|
// Custom errors when HttpErrorCode is specified (404 or 500),
|
|
// or '500;100' ASP scripting error case
|
|
BOOL fTryErrorTransfer = FALSE;
|
|
DWORD dwCode, dwSubCode;
|
|
|
|
if (m_dwHttpErrorCode == 404 || m_dwHttpErrorCode == 500 || m_dwHttpErrorCode == 401)
|
|
{
|
|
dwCode = m_dwHttpErrorCode;
|
|
dwSubCode = m_dwHttpSubErrorCode;
|
|
}
|
|
else if (m_dwHttpErrorCode == 0 && m_pHitObj &&
|
|
m_pHitObj->FHasASPError() && // there's an error on this page
|
|
m_pHitObj->FExecuting() && // while executing
|
|
!m_pHitObj->FInTransferOnError() && // not inside transfer-on-error already
|
|
m_pHitObj->PAppln() && m_pHitObj->PResponse() && m_pHitObj->PServer() &&
|
|
m_pHitObj->PAppln()->QueryAppConfig()->pCLSIDDefaultEngine()) // engine in the registry is valid
|
|
{
|
|
dwCode = 500;
|
|
dwSubCode = 100;
|
|
fTryErrorTransfer = TRUE;
|
|
}
|
|
else
|
|
{
|
|
// no need to try
|
|
*pfCustomErrorProcessed = FALSE;
|
|
return S_OK;
|
|
}
|
|
|
|
// Get custom error from W3SVC
|
|
|
|
|
|
STACK_BUFFER( tempParamBuf, MAX_PATH );
|
|
TCHAR *szBuf = (TCHAR *)tempParamBuf.QueryPtr();
|
|
DWORD dwLen = MAX_PATH;
|
|
BOOL fIsFileError;
|
|
BOOL fSendErrorBody;
|
|
|
|
BOOL fRet = pIReq->GetCustomError(dwCode, dwSubCode, dwLen, szBuf, &dwLen, &fIsFileError, &fSendErrorBody);
|
|
if (!fRet && (GetLastError() == ERROR_INSUFFICIENT_BUFFER)) {
|
|
if (tempParamBuf.Resize(dwLen) == TRUE) {
|
|
szBuf = (TCHAR *)tempParamBuf.QueryPtr();
|
|
fRet = pIReq->GetCustomError(dwCode, dwSubCode, dwLen, szBuf, &dwLen, &fIsFileError, &fSendErrorBody);
|
|
}
|
|
}
|
|
if (fRet) {
|
|
|
|
|
|
if (fSendErrorBody == FALSE) {
|
|
// suppress all output through intrinsic
|
|
if (m_pHitObj && m_pHitObj->PResponse())
|
|
m_pHitObj->PResponse()->SetIgnoreWrites();
|
|
}
|
|
if (fIsFileError) {
|
|
// Verify that the error file can be read
|
|
if (FAILED(AspGetFileAttributes(szBuf)))
|
|
fRet = FALSE;
|
|
}
|
|
else {
|
|
// Avoid circular client redirections
|
|
// (check if the current URL is the same as error URL
|
|
if (_tcsicmp(szBuf, pIReq->QueryPszPathInfo()) == 0)
|
|
fRet = FALSE;
|
|
}
|
|
}
|
|
|
|
if (!fRet) {
|
|
// no custom error found
|
|
*pfCustomErrorProcessed = FALSE;
|
|
return S_OK;
|
|
}
|
|
|
|
// There's a custom error - use it
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
if (fIsFileError)
|
|
{
|
|
|
|
if (fSendErrorBody)
|
|
// in case of file errors mime type follows the file path
|
|
// in the returned buffer
|
|
hr = WriteCustomFileError(pIReq, szBuf, szBuf+_tcslen(szBuf)+1);
|
|
}
|
|
else if (fTryErrorTransfer)
|
|
{
|
|
// transfer to URL
|
|
|
|
// need to Map Path first
|
|
TCHAR szTemplate[MAX_PATH];
|
|
WCHAR *pErrorURL;
|
|
#if UNICODE
|
|
pErrorURL = szBuf;
|
|
#else
|
|
CMBCSToWChar convStr;
|
|
if (FAILED(convStr.Init(szBuf))) {
|
|
*pfCustomErrorProcessed = FALSE;
|
|
return S_OK;
|
|
}
|
|
pErrorURL = convStr.GetString();
|
|
#endif
|
|
|
|
if (FAILED(m_pHitObj->PServer()->MapPathInternal(0, pErrorURL, szTemplate))) {
|
|
// could use custom error
|
|
*pfCustomErrorProcessed = FALSE;
|
|
return S_OK;
|
|
}
|
|
Normalize(szTemplate);
|
|
|
|
// do the transfer
|
|
m_pHitObj->SetInTransferOnError();
|
|
hr = m_pHitObj->ExecuteChildRequest(TRUE, szTemplate, szBuf);
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
// error while reporting error -- report both
|
|
LogErrortoBrowser(m_pHitObj->PResponse());
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// client redirect to URL
|
|
hr = WriteCustomURLError(pIReq, szBuf);
|
|
}
|
|
|
|
if (fIsFileError || !fTryErrorTransfer)
|
|
{
|
|
// suppress all output through intrinsic
|
|
if (m_pHitObj && m_pHitObj->PResponse())
|
|
m_pHitObj->PResponse()->SetIgnoreWrites();
|
|
}
|
|
|
|
*pfCustomErrorProcessed = TRUE;
|
|
return hr;
|
|
}
|
|
|
|
/*===================================================================
|
|
CErrInfo::WriteCustomFileError
|
|
|
|
Dumps the content of a custom error file to the browser
|
|
|
|
Returns:
|
|
NONE.
|
|
===================================================================*/
|
|
HRESULT CErrInfo::WriteCustomFileError
|
|
(
|
|
CIsapiReqInfo *pIReq,
|
|
TCHAR *szPath,
|
|
TCHAR *szMimeType
|
|
)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
char *szStatus = m_szItem[Im_szHeader];
|
|
char *pszMBCSMimeType;
|
|
|
|
#if UNICODE
|
|
CWCharToMBCS convStr;
|
|
|
|
if (FAILED(hr = convStr.Init(szMimeType, 65001))) {
|
|
return hr;
|
|
}
|
|
else {
|
|
pszMBCSMimeType = convStr.GetString();
|
|
}
|
|
#else
|
|
pszMBCSMimeType = szMimeType;
|
|
#endif
|
|
|
|
if (szStatus == NULL) {
|
|
// no status set -- get it from the response object if available
|
|
CResponse *pResponse = m_pHitObj ? m_pHitObj->PResponse() : NULL;
|
|
if (pResponse)
|
|
szStatus = pResponse->PCustomStatus();
|
|
}
|
|
|
|
hr = CResponse::SyncWriteFile(pIReq,
|
|
szPath,
|
|
pszMBCSMimeType,
|
|
szStatus); // NULL is OK - means 200
|
|
|
|
return hr;
|
|
}
|
|
|
|
/*===================================================================
|
|
CErrInfo::WriteCustomURLError
|
|
|
|
Sends client redirect to the custom URL error
|
|
|
|
Returns:
|
|
NONE.
|
|
===================================================================*/
|
|
HRESULT CErrInfo::WriteCustomURLError(
|
|
CIsapiReqInfo *pIReq,
|
|
TCHAR *sztURL)
|
|
{
|
|
// Header is
|
|
// Location: redirect_URL?code;http://original_url
|
|
|
|
HRESULT hr = S_OK;
|
|
char *szURL;
|
|
#if UNICODE
|
|
CWCharToMBCS convRedirURL;
|
|
|
|
if (FAILED(hr = convRedirURL.Init(sztURL,65001))) {
|
|
return hr;
|
|
}
|
|
|
|
szURL = convRedirURL.GetString();
|
|
#else
|
|
szURL = sztURL;
|
|
#endif
|
|
|
|
// code
|
|
char szCode[8];
|
|
if (m_dwHttpErrorCode > 0 && m_dwHttpErrorCode < 1000)
|
|
ltoa(m_dwHttpErrorCode, szCode, 10);
|
|
else
|
|
return E_FAIL;
|
|
|
|
// get the current URL
|
|
char szServer[128];
|
|
DWORD dwServerSize = sizeof(szServer);
|
|
|
|
STACK_BUFFER( tempHeader, 256 );
|
|
if (!pIReq->GetServerVariableA("SERVER_NAME", szServer, &dwServerSize))
|
|
return E_FAIL; // shouldn't happen
|
|
char *szOrigURL;
|
|
#if UNICODE
|
|
CWCharToMBCS convOrigStr;
|
|
|
|
if (FAILED(hr = convOrigStr.Init(pIReq->QueryPszPathInfo(), 65001))) {
|
|
return hr;
|
|
}
|
|
|
|
szOrigURL = convOrigStr.GetString();
|
|
#else
|
|
szOrigURL = pIReq->QueryPszPathInfo();
|
|
#endif
|
|
|
|
// estimate of the length
|
|
DWORD cchHeaderMax = strlen(szURL)
|
|
+ strlen(szServer)
|
|
+ strlen(szOrigURL)
|
|
+ 80; // decorations
|
|
|
|
if (tempHeader.Resize(cchHeaderMax) == FALSE) {
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
char *szHeader = (char *)tempHeader.QueryPtr();
|
|
|
|
// construct the redirection header
|
|
char *szBuf = szHeader;
|
|
szBuf = strcpyExA(szBuf, "Location: ");
|
|
szBuf = strcpyExA(szBuf, szURL);
|
|
szBuf = strcpyExA(szBuf, "?");
|
|
szBuf = strcpyExA(szBuf, szCode);
|
|
szBuf = strcpyExA(szBuf, ";http://");
|
|
szBuf = strcpyExA(szBuf, szServer);
|
|
szBuf = strcpyExA(szBuf, szOrigURL);
|
|
szBuf = strcpyExA(szBuf, "\r\n\r\n");
|
|
Assert(strlen(szHeader) < cchHeaderMax);
|
|
|
|
// set the status
|
|
static char s_szRedirected[] = "302 Object moved";
|
|
pIReq->SetDwHttpStatusCode(302);
|
|
|
|
// send the header
|
|
BOOL fRet = pIReq->SendHeader(s_szRedirected,
|
|
sizeof(s_szRedirected),
|
|
szHeader,
|
|
strlen(szHeader) + 1,
|
|
FALSE);
|
|
|
|
return (fRet ? S_OK : E_FAIL);
|
|
}
|
|
|
|
/*===================================================================
|
|
CErrInfo::WriteHTMLEncodedErrToBrowser
|
|
|
|
Log Error/Event to Browser with HTMLEncoded via either pResponse or pIReq.
|
|
|
|
Either pResponse or pIReq must be valid.
|
|
|
|
Returns:
|
|
NONE.
|
|
===================================================================*/
|
|
void CErrInfo::WriteHTMLEncodedErrToBrowser
|
|
(
|
|
const CHAR *StrIn,
|
|
CResponse *pResponse,
|
|
CIsapiReqInfo *pIReq
|
|
)
|
|
{
|
|
CHAR szHTMLEncoded[2*MAX_RESSTRINGSIZE];
|
|
LPSTR pszHTMLEncoded = NULL;
|
|
LPSTR pStartszHTMLEncoded = NULL;
|
|
DWORD nszHTMLEncoded = 0;
|
|
BOOL fStrAllocated = FALSE;
|
|
|
|
nszHTMLEncoded = HTMLEncodeLen(StrIn, CP_ACP, NULL);
|
|
|
|
|
|
if (nszHTMLEncoded > 0)
|
|
{
|
|
if (nszHTMLEncoded > 2 * MAX_RESSTRINGSIZE)
|
|
{
|
|
pszHTMLEncoded = new char[nszHTMLEncoded+2];
|
|
if (pszHTMLEncoded)
|
|
{
|
|
fStrAllocated = TRUE;
|
|
}
|
|
else
|
|
{
|
|
HandleOOMError(NULL, NULL);
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
pszHTMLEncoded = &szHTMLEncoded[0];
|
|
|
|
pStartszHTMLEncoded = pszHTMLEncoded;
|
|
pszHTMLEncoded = HTMLEncode(pszHTMLEncoded, StrIn, CP_ACP, NULL);
|
|
|
|
nszHTMLEncoded--; // take out the count for '\0'.
|
|
if (pResponse)
|
|
pResponse->WriteSz((CHAR *)pStartszHTMLEncoded, nszHTMLEncoded);
|
|
else
|
|
CResponse::StaticWrite(pIReq, pStartszHTMLEncoded, nszHTMLEncoded);
|
|
}
|
|
|
|
if (fStrAllocated)
|
|
delete [] pStartszHTMLEncoded;
|
|
|
|
return;
|
|
}
|
|
|
|
/*===================================================================
|
|
CErrInfo::LogErrortoBrowser
|
|
|
|
Log Error/Event to Browser via pResponse.
|
|
|
|
We will output
|
|
1> default ScriptErrorMessage or
|
|
2> Error Info/Default Template/has long description available or
|
|
3> Error Info/Default Template/no long description available
|
|
|
|
Returns:
|
|
HRESULT
|
|
===================================================================*/
|
|
HRESULT CErrInfo::LogErrortoBrowser(CResponse *pResponse)
|
|
{
|
|
INT cch = 0;
|
|
INT cLine = 0;
|
|
INT iErrTemplate = 0;
|
|
|
|
Assert(NULL != pResponse);
|
|
|
|
// When the error code is zero, then it's coming from a 500 error code path.
|
|
// (HandleSysError presets the code to 404 or 204.)
|
|
//
|
|
if (!pResponse->FHeadersWritten() && (m_dwHttpErrorCode == 500 || m_dwHttpErrorCode == 0))
|
|
pResponse->put_Status(L"500 Internal Server Error");
|
|
|
|
if(ERR_FIsSysFormat(m_dwMask))
|
|
{
|
|
DWORD cChHeader = strlen(szErrSysTemplate[0]);
|
|
DWORD cChTail = strlen(szErrSysTemplate[1]);
|
|
|
|
pResponse->WriteSz((CHAR *)szErrSysTemplate[0], cChHeader);
|
|
WriteHTMLEncodedErrToBrowser((CHAR *)m_szItem[Im_szShortDescription], pResponse, NULL);
|
|
pResponse->WriteSz((CHAR *)szErrSysTemplate[1], cChTail);
|
|
return S_OK;
|
|
}
|
|
|
|
if (!(m_pHitObj->QueryAppConfig())->fScriptErrorsSentToBrowser())
|
|
{
|
|
cch = strlen((CHAR *)((m_pHitObj->QueryAppConfig())->szScriptErrorMessage()));
|
|
GlobStringUseLock();
|
|
pResponse->WriteSz((CHAR *)((m_pHitObj->QueryAppConfig())->szScriptErrorMessage()),cch);
|
|
GlobStringUseUnLock();
|
|
}
|
|
else
|
|
{
|
|
// line 0 is the begin line.
|
|
cch = strlen((CHAR *)g_szErrTemplate[ErrTemplate_BEGIN]);
|
|
pResponse->WriteSz((CHAR *)g_szErrTemplate[ErrTemplate_BEGIN], cch);
|
|
|
|
//7 standard items(file, line, engine, error#, short description, code, long description)
|
|
//If any info missing(is NULL), we skip.
|
|
for (cLine = 0; cLine < 7; cLine++)
|
|
{
|
|
if (NULL == m_szItem[cLine])
|
|
continue;
|
|
|
|
iErrTemplate = cLine * 2 + 1;
|
|
/* BUG 78782 (IIS Active) */
|
|
//WriteHTMLEncodedErrToBrowser((CHAR *)g_szErrTemplate[iErrTemplate], pResponse, NULL);
|
|
pResponse->WriteSz((CHAR *)g_szErrTemplate[iErrTemplate], strlen((CHAR *)g_szErrTemplate[iErrTemplate]));
|
|
|
|
|
|
WriteHTMLEncodedErrToBrowser((CHAR *)m_szItem[cLine], pResponse, NULL);
|
|
|
|
iErrTemplate++;
|
|
/* BUG 78782 (IIS Active) */
|
|
//WriteHTMLEncodedErrToBrowser((CHAR *)g_szErrTemplate[iErrTemplate], pResponse, NULL);
|
|
pResponse->WriteSz((CHAR *)g_szErrTemplate[iErrTemplate], strlen((CHAR *)g_szErrTemplate[iErrTemplate]));
|
|
}
|
|
|
|
//ouput the end line
|
|
cch = strlen((CHAR *)g_szErrTemplate[ErrTemplate_END]);
|
|
pResponse->WriteSz((CHAR *)g_szErrTemplate[ErrTemplate_END], cch);
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
/*===================================================================
|
|
CErrInfo::LogErrortoBrowser
|
|
|
|
Log Error/Event to Browser via pIReq.
|
|
|
|
We will output
|
|
1> default ScriptErrorMessage or
|
|
2> Error Info/Default Template/has long description available or
|
|
3> Error Info/Default Template/no long description available
|
|
|
|
Returns:
|
|
HRESULT
|
|
===================================================================*/
|
|
HRESULT CErrInfo::LogErrortoBrowser(CIsapiReqInfo *pIReq)
|
|
{
|
|
INT cLine = 0;
|
|
INT iErrTemplate = 0;
|
|
|
|
Assert(NULL != pIReq);
|
|
|
|
//HTTP type error, 204, 404, 500
|
|
//mimic IIS error reporting
|
|
//And send out the header.
|
|
if(ERR_FIsSysFormat(m_dwMask))
|
|
{
|
|
CResponse::StaticWrite(pIReq, szErrSysTemplate[0]);
|
|
WriteHTMLEncodedErrToBrowser((CHAR *)m_szItem[Im_szShortDescription], NULL, pIReq);
|
|
CResponse::StaticWrite(pIReq, szErrSysTemplate[1]);
|
|
return S_OK;
|
|
}
|
|
|
|
if (!(m_pHitObj->QueryAppConfig())->fScriptErrorsSentToBrowser())
|
|
{
|
|
GlobStringUseLock();
|
|
CResponse::StaticWrite(pIReq, (CHAR *)((m_pHitObj->QueryAppConfig())->szScriptErrorMessage()));
|
|
GlobStringUseUnLock();
|
|
}
|
|
else
|
|
{
|
|
// line 0 is the begin line.
|
|
CResponse::StaticWrite(pIReq, g_szErrTemplate[ErrTemplate_BEGIN]);
|
|
|
|
//7 standard items(file, line, engine, error#, short description, code, long description)
|
|
//If any info missing(is NULL), we skip.
|
|
for (cLine = 0; cLine < 5; cLine++)
|
|
{
|
|
if (NULL == m_szItem[cLine])
|
|
continue;
|
|
|
|
iErrTemplate = cLine * 2 + 1;
|
|
WriteHTMLEncodedErrToBrowser((CHAR *)g_szErrTemplate[iErrTemplate], NULL, pIReq);
|
|
|
|
WriteHTMLEncodedErrToBrowser((CHAR *)m_szItem[cLine], NULL, pIReq);
|
|
|
|
iErrTemplate++;
|
|
WriteHTMLEncodedErrToBrowser((CHAR *)g_szErrTemplate[iErrTemplate], NULL, pIReq);
|
|
}
|
|
|
|
//ouput the end line
|
|
CResponse::StaticWrite(pIReq, g_szErrTemplate[ErrTemplate_END]);
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
/*===================================================================
|
|
CchLoadStringOfId
|
|
|
|
Loads a string from the string table.
|
|
|
|
Returns:
|
|
sz - the returned string
|
|
INT - 0 if string load failed, otherwise number of characters loaded.
|
|
===================================================================*/
|
|
INT CchLoadStringOfId
|
|
(
|
|
UINT id,
|
|
CHAR *sz,
|
|
INT cchMax
|
|
)
|
|
{
|
|
INT cchRet;
|
|
|
|
// The handle to the DLL instance should have been set up when we were loaded
|
|
if (g_hResourceDLL == (HINSTANCE)0)
|
|
{
|
|
// Totally bogus
|
|
Assert(FALSE);
|
|
return(0);
|
|
}
|
|
|
|
cchRet = LoadStringA(g_hResourceDLL, id, sz, cchMax);
|
|
|
|
IF_DEBUG(ERROR)
|
|
{
|
|
// For debugging purposes, if we get back 0, get the last error info
|
|
if (cchRet == 0)
|
|
{
|
|
DWORD err = GetLastError();
|
|
DBGERROR((DBG_CONTEXT, "Failed to load string resource. Id = %d, error = %d\n", id, err));
|
|
DBG_ASSERT(FALSE);
|
|
}
|
|
}
|
|
|
|
return(cchRet);
|
|
}
|
|
|
|
|
|
/*===================================================================
|
|
CwchLoadStringOfId
|
|
|
|
Loads a string from the string table as a UNICODE string.
|
|
|
|
Returns:
|
|
sz - the returned string
|
|
INT - 0 if string load failed, otherwise number of characters loaded.
|
|
===================================================================*/
|
|
INT CwchLoadStringOfId
|
|
(
|
|
UINT id,
|
|
WCHAR *sz,
|
|
INT cchMax
|
|
)
|
|
{
|
|
INT cchRet;
|
|
|
|
// The handle to the DLL instance should have been set up when we were loaded
|
|
if (g_hResourceDLL == (HINSTANCE)0)
|
|
{
|
|
// Totally bogus
|
|
Assert(FALSE);
|
|
return(0);
|
|
}
|
|
|
|
cchRet = LoadStringW(g_hResourceDLL, id, sz, cchMax);
|
|
|
|
IF_DEBUG(ERROR)
|
|
{
|
|
// For debugging purposes, if we get back 0, get the last error info
|
|
if (cchRet == 0)
|
|
{
|
|
DWORD err = GetLastError();
|
|
DBGERROR((DBG_CONTEXT, "Failed to load string resource. Id = %d, error = %d\n", id, err));
|
|
DBG_ASSERT(FALSE);
|
|
}
|
|
}
|
|
|
|
return(cchRet);
|
|
}
|
|
|
|
/*===================================================================
|
|
HandleSysError
|
|
|
|
Dumps the error to the client and/or to the log
|
|
Loads a string from the string table as a UNICODE string.
|
|
|
|
Returns:
|
|
sz - the returned string
|
|
INT - 0 if string load failed, otherwise number of characters loaded.
|
|
===================================================================*/
|
|
HRESULT HandleSysError( DWORD dwHttpError,
|
|
DWORD dwHttpSubError,
|
|
UINT ErrorID,
|
|
UINT ErrorHeaderID,
|
|
CIsapiReqInfo *pIReq,
|
|
CHitObj *pHitObj)
|
|
{
|
|
CErrInfo SysErrInfo;
|
|
CErrInfo *pErrInfo;
|
|
CHAR szResourceStr[MAX_RESSTRINGSIZE];
|
|
CHAR szHeader[MAX_HEADERSIZE];
|
|
INT cch;
|
|
|
|
pErrInfo = (CErrInfo *)&SysErrInfo;
|
|
|
|
pErrInfo->m_pHitObj = pHitObj;
|
|
pErrInfo->m_pIReq = pIReq;
|
|
if (ErrorHeaderID != 0)
|
|
{
|
|
cch = CchLoadStringOfId(ErrorHeaderID, szHeader, MAX_HEADERSIZE);
|
|
pErrInfo->m_szItem[Im_szHeader] = szHeader;
|
|
}
|
|
else
|
|
{
|
|
pErrInfo->m_szItem[Im_szHeader] = NULL;
|
|
}
|
|
|
|
if (ErrorID != 0)
|
|
cch = CchLoadStringOfId(ErrorID, szResourceStr, MAX_RESSTRINGSIZE);
|
|
|
|
pErrInfo->ParseResourceString(szResourceStr);
|
|
|
|
pErrInfo->m_dwMask = ERR_SetSysFormat(pErrInfo->m_dwMask);
|
|
|
|
pErrInfo->m_dwHttpErrorCode = dwHttpError;
|
|
pErrInfo->m_dwHttpSubErrorCode = dwHttpSubError;
|
|
|
|
pErrInfo->LogError();
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
/*===================================================================
|
|
Handle500Error
|
|
|
|
Based on ErrorID determines headerID, code, sub-code, and
|
|
calls HandleSysError()
|
|
|
|
Returns:
|
|
HRESULT
|
|
===================================================================*/
|
|
HRESULT Handle500Error( UINT errorId,
|
|
CIsapiReqInfo *pIReq)
|
|
{
|
|
UINT headerId;
|
|
DWORD dwHttpSubError;
|
|
|
|
switch (errorId)
|
|
{
|
|
case IDE_SERVER_TOO_BUSY:
|
|
headerId = IDH_500_SERVER_ERROR;
|
|
dwHttpSubError = SUBERRORCODE500_SERVER_TOO_BUSY;
|
|
break;
|
|
|
|
case IDE_SERVER_SHUTTING_DOWN:
|
|
headerId = IDH_500_SERVER_ERROR;
|
|
dwHttpSubError = SUBERRORCODE500_SHUTTING_DOWN;
|
|
break;
|
|
|
|
case IDE_GLOBAL_ASA_CHANGED:
|
|
headerId = IDH_500_SERVER_ERROR;
|
|
dwHttpSubError = SUBERRORCODE500_RESTARTING_APP;
|
|
break;
|
|
|
|
case IDE_INVALID_APPLICATION:
|
|
headerId = IDH_500_SERVER_ERROR;
|
|
dwHttpSubError = SUBERRORCODE500_INVALID_APP;
|
|
break;
|
|
|
|
case IDE_GLOBAL_ASA_FORBIDDEN:
|
|
headerId = IDH_500_SERVER_ERROR;
|
|
dwHttpSubError = SUBERRORCODE500_GLOBALASA_FORBIDDEN;
|
|
break;
|
|
|
|
default:
|
|
headerId = IDH_500_SERVER_ERROR;
|
|
dwHttpSubError = SUBERRORCODE500_SERVER_ERROR;
|
|
break;
|
|
}
|
|
|
|
pIReq->SetDwHttpStatusCode(500);
|
|
return HandleSysError(500, dwHttpSubError, errorId, headerId, pIReq, NULL);
|
|
}
|
|
|
|
/*===================================================================
|
|
HandleOOMError
|
|
|
|
Handle OOM error with special care, because we can not do any dynamic allocation.
|
|
|
|
if pIReq or pHitObj is NULL, nothing will be reported to browser
|
|
|
|
Returns:
|
|
Nothing
|
|
===================================================================*/
|
|
HRESULT HandleOOMError( CIsapiReqInfo *pIReq,
|
|
CHitObj *pHitObj)
|
|
{
|
|
CErrInfo OOMErrInfo;
|
|
CErrInfo *pErrInfo;
|
|
|
|
// Note the OOM occurred
|
|
|
|
InterlockedIncrement(&g_nOOMErrors);
|
|
|
|
pErrInfo = (CErrInfo *)&OOMErrInfo;
|
|
|
|
pErrInfo->m_pIReq = pIReq;
|
|
pErrInfo->m_pHitObj = pHitObj;
|
|
pErrInfo->m_dwMask = g_ErrInfoOOM.m_dwMask;
|
|
pErrInfo->m_szItem[Im_szErrorCode] = g_ErrInfoOOM.m_szItem[Im_szErrorCode];
|
|
pErrInfo->m_szItem[Im_szShortDescription] = g_ErrInfoOOM.m_szItem[Im_szShortDescription];
|
|
pErrInfo->m_szItem[Im_szLongDescription] = g_ErrInfoOOM.m_szItem[Im_szLongDescription];
|
|
|
|
pErrInfo->LogError();
|
|
|
|
return S_OK;
|
|
}
|
|
/*===================================================================
|
|
HandleError
|
|
|
|
Handle reporting of errors given ErrorID, FileName, and LineNum.
|
|
|
|
If Caller provide ErrCode or LongDescription, the default value will be overwriten.
|
|
|
|
Strings passed in will be freed. That is, consider the function as a sink. Caller
|
|
should not use strings after the call.
|
|
|
|
Returns:
|
|
Nothing
|
|
===================================================================*/
|
|
HRESULT HandleError( UINT ErrorID,
|
|
CHAR *szFileName,
|
|
CHAR *szLineNum,
|
|
CHAR *szEngine,
|
|
CHAR *szErrCode,
|
|
CHAR *szLongDes,
|
|
CIsapiReqInfo *pIReq,
|
|
CHitObj *pHitObj,
|
|
va_list *pArgs)
|
|
{
|
|
CErrInfo SysErrInfo;
|
|
CErrInfo *pErrInfo;
|
|
CHAR szResourceStr[MAX_RESSTRINGSIZE];
|
|
CHAR szUnformattedResStr[MAX_RESSTRINGSIZE];
|
|
HRESULT hr = S_OK;
|
|
|
|
pErrInfo = (CErrInfo *)&SysErrInfo;
|
|
|
|
pErrInfo->m_szItem[Im_szFileName] = szFileName;
|
|
pErrInfo->m_szItem[Im_szLineNum] = szLineNum;
|
|
pErrInfo->m_szItem[Im_szEngine] = szEngine;
|
|
|
|
pErrInfo->m_pHitObj = pHitObj;
|
|
pErrInfo->m_pIReq = pIReq;
|
|
|
|
//Load resource string according to the resource ID.
|
|
|
|
if (pArgs) {
|
|
CchLoadStringOfId(ErrorID, szUnformattedResStr, MAX_RESSTRINGSIZE);
|
|
vsprintf(szResourceStr, szUnformattedResStr, *pArgs);
|
|
}
|
|
else {
|
|
CchLoadStringOfId(ErrorID, szResourceStr, MAX_RESSTRINGSIZE);
|
|
}
|
|
|
|
pErrInfo->ParseResourceString(szResourceStr);
|
|
|
|
//NOTE: if ErrorCode/LongDescription not NULL, caller want to overwrite.
|
|
if (szErrCode)
|
|
{
|
|
pErrInfo->m_szItem[Im_szErrorCode] = szErrCode;
|
|
}
|
|
if(szLongDes)
|
|
{
|
|
pErrInfo->m_szItem[Im_szLongDescription] = szLongDes;
|
|
}
|
|
|
|
hr = pErrInfo->LogError();
|
|
|
|
//free up the inputs
|
|
FreeNullifySz((CHAR **)&szFileName);
|
|
FreeNullifySz((CHAR **)&szLineNum);
|
|
FreeNullifySz((CHAR **)&szEngine);
|
|
FreeNullifySz((CHAR **)&szErrCode);
|
|
FreeNullifySz((CHAR **)&szLongDes);
|
|
|
|
return hr;
|
|
}
|
|
/*===================================================================
|
|
HandleError
|
|
|
|
Handle reporting of errors given all the info.
|
|
|
|
This is basically a cover over HandleErrorSz which called from OnScriptError.
|
|
|
|
Strings passed in will be freed. That is, consider the function as a sink. Caller
|
|
should not use strings after the call.
|
|
|
|
Returns:
|
|
Nothing
|
|
===================================================================*/
|
|
HRESULT HandleError( CHAR *szShortDes,
|
|
CHAR *szLongDes,
|
|
DWORD dwMask,
|
|
CHAR *szFileName,
|
|
CHAR *szLineNum,
|
|
CHAR *szEngine,
|
|
CHAR *szErrCode,
|
|
CIsapiReqInfo *pIReq,
|
|
CHitObj *pHitObj)
|
|
{
|
|
CErrInfo SysErrInfo;
|
|
CErrInfo *pErrInfo;
|
|
HRESULT hr = S_OK;
|
|
|
|
pErrInfo = (CErrInfo *)&SysErrInfo;
|
|
|
|
pErrInfo->m_dwMask = dwMask;
|
|
|
|
pErrInfo->m_szItem[Im_szHeader] = NULL; // Caller has already sent out header
|
|
pErrInfo->m_szItem[Im_szFileName] = szFileName;
|
|
pErrInfo->m_szItem[Im_szLineNum] = szLineNum;
|
|
pErrInfo->m_szItem[Im_szEngine] = szEngine;
|
|
pErrInfo->m_szItem[Im_szErrorCode] = szErrCode;
|
|
pErrInfo->m_szItem[Im_szShortDescription] = szShortDes;
|
|
pErrInfo->m_szItem[Im_szLongDescription] = szLongDes;
|
|
|
|
pErrInfo->m_pHitObj = pHitObj;
|
|
pErrInfo->m_pIReq = pIReq;
|
|
|
|
hr = pErrInfo->LogError();
|
|
|
|
//free up the inputs
|
|
FreeNullifySz((CHAR **)&szFileName);
|
|
FreeNullifySz((CHAR **)&szLineNum);
|
|
FreeNullifySz((CHAR **)&szEngine);
|
|
FreeNullifySz((CHAR **)&szErrCode);
|
|
FreeNullifySz((CHAR **)&szShortDes);
|
|
FreeNullifySz((CHAR **)&szLongDes);
|
|
|
|
return hr;
|
|
}
|
|
/*===================================================================
|
|
HandleError
|
|
|
|
Handle reporting of errors given the IActiveScriptError and PFNLINEMAP.
|
|
|
|
This is basically a cover over HandleErrorSz which called from OnScriptError.
|
|
|
|
Returns:
|
|
Nothing
|
|
===================================================================*/
|
|
HRESULT HandleError( IActiveScriptError *pscripterror,
|
|
CTemplate *pTemplate,
|
|
DWORD dwEngineID,
|
|
CIsapiReqInfo *pIReq,
|
|
CHitObj *pHitObj )
|
|
{
|
|
UINT cchBuf = 0;
|
|
CHAR *szOrigin = NULL;
|
|
CHAR *szDesc = NULL;
|
|
CHAR *szLine = NULL;
|
|
CHAR *szPrefix = NULL;
|
|
UINT cchOrigin = 0;
|
|
UINT cchDesc = 0;
|
|
UINT cchLineNum = 0;
|
|
UINT cchLine = 0;
|
|
EXCEPINFO excepinfo = {0};
|
|
CHAR *szResult = NULL;
|
|
BSTR bstrLine = NULL;
|
|
HRESULT hr;
|
|
DWORD dwSourceContext = 0; // Don't trust this one
|
|
ULONG ulLineError = 0;
|
|
BOOLB fGuessedLine = FALSE; // see bug 379
|
|
CHAR *szLineNumT = NULL;
|
|
LPTSTR szPathInfo = NULL;
|
|
LPTSTR szPathTranslated = NULL;
|
|
LONG ichError = -1;
|
|
CErrInfo SysErrInfo;
|
|
CErrInfo *pErrInfo;
|
|
wchar_t wszUnknownException[128];
|
|
wchar_t wszUnknownEngine[32];
|
|
CWCharToMBCS convStr;
|
|
|
|
pErrInfo = (CErrInfo *)&SysErrInfo;
|
|
|
|
pErrInfo->m_pIReq = pIReq;
|
|
pErrInfo->m_pHitObj = pHitObj;
|
|
|
|
if (pscripterror == NULL)
|
|
return E_POINTER;
|
|
|
|
hr = pscripterror->GetExceptionInfo(&excepinfo);
|
|
if (FAILED(hr))
|
|
goto LExit;
|
|
|
|
// bug 99543 If details are deferrred, use the callback to get
|
|
// detailed information.
|
|
if (excepinfo.pfnDeferredFillIn)
|
|
excepinfo.pfnDeferredFillIn(&excepinfo);
|
|
|
|
// if error is OOM, then increment the global counter.
|
|
|
|
if ((excepinfo.wCode == ERROR_OUTOFMEMORY)
|
|
|| ((excepinfo.wCode == 0) && (excepinfo.scode == ERROR_OUTOFMEMORY)))
|
|
InterlockedIncrement(&g_nOOMErrors);
|
|
|
|
|
|
hr = pscripterror->GetSourcePosition(&dwSourceContext, &ulLineError, &ichError);
|
|
if (FAILED(hr))
|
|
goto LExit;
|
|
|
|
// Intentionally ignore any error
|
|
(VOID)pscripterror->GetSourceLineText(&bstrLine);
|
|
|
|
if (pTemplate == NULL)
|
|
goto LExit;
|
|
|
|
// call GetScriptSourceInfo to get path-info (of file) and actual line number where error occurred
|
|
// bug 379: if GetScriptSourceInfo returns fGuessedLine = TRUE, it means we gave it a non-authored line,
|
|
// so we adjust below by not printing bstrLine in the error msg
|
|
if (ulLineError > 0)
|
|
pTemplate->GetScriptSourceInfo(dwEngineID, ulLineError, &szPathInfo, &szPathTranslated, &ulLineError, NULL, &fGuessedLine);
|
|
else
|
|
{
|
|
// ulLineError was zero - no line # specified, so assume main file (usually this will be "out of memory"
|
|
// so effect will be to display the script that was running when this occurred.
|
|
//
|
|
szPathInfo = pTemplate->GetSourceFileName(SOURCEPATHTYPE_VIRTUAL);
|
|
szPathTranslated = pTemplate->GetSourceFileName(SOURCEPATHTYPE_PHYSICAL);
|
|
}
|
|
|
|
// if we have HitObj use it to get the virtual path to avoid
|
|
// displaying of wrong path for shared templates
|
|
//
|
|
// first verify that PathTranslated == main file path; this file could be
|
|
// an include file, in which case PszPathInfo is incorrect.
|
|
//
|
|
if (!pTemplate->FGlobalAsa() && szPathTranslated && _tcscmp(szPathTranslated, pTemplate->GetSourceFileName()) == 0 && pHitObj != NULL && pHitObj->PIReq())
|
|
szPathInfo = pHitObj->PSzCurrTemplateVirtPath();
|
|
|
|
#if UNICODE
|
|
pErrInfo->m_szItem[Im_szFileName] = StringDupUTF8(szPathInfo);
|
|
#else
|
|
pErrInfo->m_szItem[Im_szFileName] = StringDupA(szPathInfo);
|
|
#endif
|
|
szLineNumT = (CHAR *)malloc(10*sizeof(CHAR));
|
|
if (szLineNumT)
|
|
{
|
|
// convert the line number
|
|
_ltoa(ulLineError, szLineNumT, 10);
|
|
}
|
|
pErrInfo->m_szItem[Im_szLineNum] = szLineNumT;
|
|
|
|
|
|
// is the scode one of the VBScript of JavaScript errors (this needs to be lang independent)
|
|
// excepinfo.bstrDescription now has the formatted error string.
|
|
if (excepinfo.bstrSource && excepinfo.bstrDescription)
|
|
{
|
|
// Bug 81954: Misbehaved objects may throw an exception without providing any information
|
|
wchar_t *wszDescription;
|
|
if (excepinfo.bstrDescription[0] == L'\0')
|
|
{
|
|
HRESULT hrError;
|
|
|
|
if (0 == excepinfo.wCode)
|
|
hrError = excepinfo.scode;
|
|
else
|
|
hrError = excepinfo.wCode;
|
|
|
|
wszUnknownException[0] = '\0';
|
|
// Bug 91847 Attempt to get a description via FormatMessage()
|
|
if (!HResultToWsz(hrError, wszUnknownException, 128))
|
|
CwchLoadStringOfId(IDE_SCRIPT_UNKNOWN, wszUnknownException, sizeof(wszUnknownException)/sizeof(WCHAR));
|
|
wszDescription = wszUnknownException;
|
|
}
|
|
else
|
|
wszDescription = excepinfo.bstrDescription;
|
|
|
|
wchar_t *wszSource;
|
|
if (excepinfo.bstrSource[0] == L'\0')
|
|
{
|
|
wszUnknownEngine[0] = '\0';
|
|
CwchLoadStringOfId(IDS_DEBUG_APP, wszUnknownEngine, sizeof(wszUnknownEngine)/sizeof(WCHAR));
|
|
wszSource = wszUnknownEngine;
|
|
}
|
|
else
|
|
wszSource = excepinfo.bstrSource;
|
|
|
|
CHAR *ch = NULL;
|
|
|
|
// convert the Source to ascii
|
|
|
|
if (convStr.Init(wszSource) != NO_ERROR) {
|
|
szOrigin = NULL;
|
|
}
|
|
else {
|
|
szOrigin = convStr.GetString(TRUE);
|
|
}
|
|
if (NULL != szOrigin)
|
|
{
|
|
// Remove the word "error"from the string, if any, because we will
|
|
//print out "error" when we print out the errorID
|
|
cchOrigin = strlen(szOrigin);
|
|
if (cchOrigin > 5) // 5 is strlen("error")
|
|
{
|
|
ch = szOrigin + cchOrigin - 5;
|
|
if (!strncmp(ch, "error", 5))
|
|
{// we found the word "error", truncate the szOrigin by null out the word "error"
|
|
*ch = '\0';
|
|
}
|
|
}
|
|
ch = NULL;
|
|
}
|
|
pErrInfo->m_szItem[Im_szEngine] = szOrigin;
|
|
|
|
|
|
// convert the sDescription to ascii
|
|
if (convStr.Init(wszDescription) != NO_ERROR) {
|
|
szDesc = NULL;
|
|
}
|
|
else {
|
|
szDesc = convStr.GetString(TRUE);
|
|
}
|
|
|
|
//check whether the szDesc is Denali/formatted error resource string or other unformatted string
|
|
if (FALSE == FIsResStrFormatted(szDesc))
|
|
{
|
|
//unformatted string.
|
|
pErrInfo->m_dwMask = dwDefaultMask;
|
|
if (0 == excepinfo.wCode)
|
|
pErrInfo->m_szItem[Im_szErrorCode] = SzScodeToErrorCode(excepinfo.scode);
|
|
else
|
|
pErrInfo->m_szItem[Im_szErrorCode] = SzScodeToErrorCode(excepinfo.wCode);
|
|
|
|
pErrInfo->m_szItem[Im_szShortDescription] = StringDupA(szDesc);
|
|
pErrInfo->m_szItem[Im_szLongDescription] = NULL;
|
|
}
|
|
else
|
|
{
|
|
pErrInfo->ParseResourceString(szDesc);
|
|
|
|
char *szTempErrorCode = SzScodeToErrorCode(excepinfo.scode);
|
|
char *szTempErrorASPCode = StringDupA(pErrInfo->m_szItem[Im_szErrorCode]);
|
|
int nstrlen = strlen(szTempErrorCode) + strlen(szTempErrorASPCode);
|
|
|
|
pErrInfo->m_szItem[Im_szErrorCode] = new char[nstrlen+4];
|
|
|
|
if(pErrInfo->m_szItem[Im_szErrorCode])
|
|
sprintf(pErrInfo->m_szItem[Im_szErrorCode], "%s : %s", szTempErrorASPCode, szTempErrorCode);
|
|
|
|
if (szTempErrorCode)
|
|
delete [] szTempErrorCode;
|
|
|
|
if(szTempErrorASPCode)
|
|
delete [] szTempErrorASPCode;
|
|
|
|
//pErrInfo->m_szItem[Im_szErrorCode] = StrDup(pErrInfo->m_szItem[Im_szErrorCode]);
|
|
pErrInfo->m_szItem[Im_szShortDescription] = StringDupA(pErrInfo->m_szItem[Im_szShortDescription]);
|
|
pErrInfo->m_szItem[Im_szLongDescription] = StringDupA(pErrInfo->m_szItem[Im_szLongDescription]);
|
|
}
|
|
|
|
/*
|
|
* If we didnt guess a line, and we have a line of source code to display
|
|
* then attempt to display it and hopefully a line of ------^ to point to the error
|
|
*/
|
|
if (!fGuessedLine && bstrLine != NULL)
|
|
{
|
|
INT cchDBCS = 0; // Number of DBCS characters in source line
|
|
CHAR *pszTemp = NULL; // Temp sz pointer used to calculate cchDBCS
|
|
// convert the source code line
|
|
|
|
if (FAILED(hr = convStr.Init(bstrLine))) {
|
|
goto LExit;
|
|
}
|
|
szLine = convStr.GetString();
|
|
|
|
cchLine = strlen(szLine);
|
|
if (0 == cchLine)
|
|
goto LExit;
|
|
|
|
// Check for DBCS character, and cchLine -= NumberofDBCScharacter, such that
|
|
// the ----^ will point to the right position.
|
|
pszTemp = szLine;
|
|
while(*pszTemp != NULL)
|
|
{
|
|
if (IsDBCSLeadByte(*pszTemp))
|
|
{
|
|
cchDBCS++;
|
|
pszTemp += 2; // skip 2 bytes
|
|
}
|
|
else
|
|
{
|
|
pszTemp++; // single byte
|
|
}
|
|
}
|
|
|
|
// compute the size of the source code indicator:
|
|
// "<source line> + '\r\n' + <error pos>*'-' + '^'
|
|
// 3 chars. without source line and '-'
|
|
LONG ichErrorT = ichError;
|
|
cchBuf += cchLine + ichErrorT + 3;
|
|
|
|
// allocate the result buffer
|
|
szResult = new(char[cchBuf + 2]);
|
|
if (szResult == NULL)
|
|
goto LExit;
|
|
|
|
// fill up the buffer
|
|
ch = szResult;
|
|
|
|
// append the <PRE>
|
|
// bug 87118, moved to template for a proper HTML encoding
|
|
|
|
// <source line>
|
|
if (cchLine)
|
|
strncpy(ch, szLine, cchLine);
|
|
ch += cchLine;
|
|
|
|
// stick the "----^" string on the end
|
|
if (ichErrorT > -1)
|
|
{
|
|
// prepend the '\n'
|
|
strncpy(ch, "\r\n", 2);
|
|
ch += 2;
|
|
// put in the "---"'s, and shrink "---" by #ofDBCS
|
|
ichErrorT -= cchDBCS;
|
|
while (ichErrorT-- > 0)
|
|
*ch++ = '-';
|
|
|
|
*ch++ = '^';
|
|
}
|
|
|
|
// append the </PRE>
|
|
// bug 87118, moved to template for a proper HTML encoding
|
|
|
|
// terminate the string
|
|
*ch++ = '\0';
|
|
pErrInfo->m_szItem[Im_szCode] = szResult;
|
|
|
|
// add line and column to error object
|
|
pErrInfo->m_nColumn = ichError;
|
|
pErrInfo->m_bstrLineText = bstrLine;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Not VBS or other Engine errors/Unknown error
|
|
// Load Default
|
|
// try to compute a specific error message
|
|
HRESULT hr_def;
|
|
hr_def = GetSpecificError(pErrInfo, excepinfo.scode);
|
|
CHAR *szShortDescription = new CHAR[256];
|
|
|
|
// if that failed try to compute a generic error
|
|
if (FAILED(hr_def))
|
|
{
|
|
pErrInfo->m_dwMask = dwDefaultMask;
|
|
if (0 == excepinfo.wCode)
|
|
{
|
|
pErrInfo->m_szItem[Im_szErrorCode] = SzScodeToErrorCode(excepinfo.scode);
|
|
// Bug 91847 Attempt to get a description via FormatMessage()
|
|
if ((szShortDescription != NULL) &&
|
|
!HResultToSz(excepinfo.scode, szShortDescription, 256))
|
|
{
|
|
// Displaying the error number twice would be redundant, delete it
|
|
delete [] szShortDescription;
|
|
szShortDescription = NULL;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pErrInfo->m_szItem[Im_szErrorCode] = SzScodeToErrorCode(excepinfo.wCode);
|
|
// Bug 91847 Attempt to get a description via FormatMessage()
|
|
if ((szShortDescription != NULL) &&
|
|
!HResultToSz(excepinfo.wCode, szShortDescription, 256))
|
|
{
|
|
// Displaying the error number twice would be redundant, delete it
|
|
delete [] szShortDescription;
|
|
szShortDescription = NULL;
|
|
}
|
|
}
|
|
pErrInfo->m_szItem[Im_szEngine] = NULL;
|
|
pErrInfo->m_szItem[Im_szShortDescription] = szShortDescription;
|
|
pErrInfo->m_szItem[Im_szLongDescription] = NULL;
|
|
}
|
|
}
|
|
|
|
LExit:
|
|
if (excepinfo.bstrSource)
|
|
{
|
|
SysFreeString(excepinfo.bstrSource);
|
|
}
|
|
|
|
if (excepinfo.bstrDescription)
|
|
{
|
|
SysFreeString(excepinfo.bstrDescription);
|
|
}
|
|
|
|
if (excepinfo.bstrHelpFile)
|
|
{
|
|
SysFreeString(excepinfo.bstrHelpFile);
|
|
}
|
|
|
|
pErrInfo->LogError();
|
|
|
|
if (bstrLine)
|
|
{
|
|
SysFreeString(bstrLine);
|
|
}
|
|
|
|
FreeNullifySz((CHAR **)&szDesc);
|
|
|
|
for(INT iErrInfo = 0; iErrInfo < Im_szItemMAX; iErrInfo++)
|
|
{
|
|
FreeNullifySz((CHAR **)&pErrInfo->m_szItem[iErrInfo]);
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
/*===================================================================
|
|
LoadErrResString
|
|
|
|
Loads an error string(formatted) from the string table.
|
|
|
|
Returns:
|
|
pdwMask
|
|
szErrorCode
|
|
szShortDes
|
|
szLongDes
|
|
|
|
if any of the szVariable is NULL, that particular string value will not be loaded.
|
|
|
|
S_OK if successes.
|
|
E_FAIL if fails.
|
|
Side Effect
|
|
NONE
|
|
===================================================================*/
|
|
HRESULT LoadErrResString(
|
|
UINT ErrID/*IN*/,
|
|
DWORD *pdwMask,
|
|
CHAR *szErrorCode,
|
|
CHAR *szShortDes,
|
|
CHAR *szLongDes)
|
|
{
|
|
CHAR *szToken = NULL;
|
|
CHAR szResTemp[2*MAX_RESSTRINGSIZE]; //ResourceTempString
|
|
INT cch = 0;
|
|
|
|
cch = CchLoadStringOfId(ErrID, szResTemp, MAX_RESSTRINGSIZE);
|
|
|
|
//Mask
|
|
szToken = (char *)_mbstok((unsigned char *)szResTemp, (unsigned char *)DELIMITER);
|
|
if (NULL != szToken)
|
|
*pdwMask = atoi(szToken);
|
|
else
|
|
Assert(FALSE);
|
|
|
|
//ErrorCode
|
|
szToken = (char *)_mbstok(NULL, (unsigned char *)DELIMITER);
|
|
if (NULL != szToken && NULL != szErrorCode)
|
|
{
|
|
cch = strlen(szToken);
|
|
memcpy(szErrorCode, szToken, cch);
|
|
szErrorCode[cch] = '\0';
|
|
}
|
|
|
|
//ShortDescription
|
|
szToken = (char *)_mbstok(NULL, (unsigned char *)DELIMITER);
|
|
if (NULL != szToken && NULL != szShortDes)
|
|
{
|
|
cch = strlen(szToken);
|
|
memcpy(szShortDes, szToken, cch);
|
|
szShortDes[cch] = '\0';
|
|
}
|
|
|
|
//LongDescription
|
|
szToken = (char *)_mbstok(NULL, (unsigned char *)DELIMITER);
|
|
if (NULL != szToken && NULL != szLongDes)
|
|
{
|
|
cch = strlen(szToken);
|
|
memcpy(szLongDes, szToken, cch);
|
|
szLongDes[cch] = '\0';
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
/*===================================================================
|
|
SzScodeToErrorCode
|
|
|
|
Conver Scode to string
|
|
|
|
Returns:
|
|
Composed string
|
|
|
|
Side Effects:
|
|
***ALLOCATES MEMORY -- CALLER MUST FREE***
|
|
===================================================================*/
|
|
CHAR *SzScodeToErrorCode
|
|
(
|
|
HRESULT hrError
|
|
)
|
|
{
|
|
CHAR *szResult = NULL;
|
|
CHAR szBuf[17];
|
|
CHAR *szNumber;
|
|
CHAR *szError;
|
|
INT iC;
|
|
INT cch;
|
|
|
|
// put a bunch of zeros into the buffer
|
|
for (iC = 0; iC < 16; ++iC)
|
|
szBuf[iC] = '0';
|
|
|
|
// szNumber points half way into the buffer
|
|
szNumber = &szBuf[8];
|
|
|
|
// get the error szNumber as a hex string
|
|
_ltoa(hrError, szNumber, 16);
|
|
|
|
// back up szNumber to allow a total of 8 digits
|
|
szNumber -= 8 - strlen(szNumber);
|
|
|
|
cch = strlen(szNumber) + 1;
|
|
|
|
szError = new(CHAR[cch]);
|
|
if (szError != NULL)
|
|
{
|
|
szError[0] = '\0';
|
|
strcat(szError, szNumber);
|
|
szResult = szError;
|
|
}
|
|
else
|
|
{
|
|
HandleOOMError(NULL, NULL);
|
|
}
|
|
|
|
return(szResult);
|
|
}
|
|
|
|
/*===================================================================
|
|
SzComposeSpecificError
|
|
|
|
Compose a specific error for an HRESULT of the form:
|
|
<string> <error-number>
|
|
|
|
This is our last resort if there is not more useful information to be had.
|
|
|
|
Returns:
|
|
Composed string
|
|
|
|
Side Effects:
|
|
***ALLOCATES MEMORY -- CALLER MUST FREE***
|
|
===================================================================*/
|
|
HRESULT GetSpecificError
|
|
(
|
|
CErrInfo *pErrInfo,
|
|
HRESULT hrError
|
|
)
|
|
{
|
|
HRESULT hr_return = E_FAIL;
|
|
UINT idErr;
|
|
|
|
switch (hrError)
|
|
{
|
|
case DISP_E_MEMBERNOTFOUND:
|
|
idErr = IDE_SCRIPT_METHOD_NOT_FOUND;
|
|
break;
|
|
|
|
case DISP_E_UNKNOWNNAME:
|
|
idErr = IDE_SCRIPT_UNKNOWN_NAME;
|
|
break;
|
|
|
|
case DISP_E_UNKNOWNINTERFACE:
|
|
idErr = IDE_SCRIPT_UNKNOWN_INTERFACE;
|
|
break;
|
|
|
|
case DISP_E_PARAMNOTOPTIONAL:
|
|
idErr = IDE_SCRIPT_MISSING_PARAMETER;
|
|
break;
|
|
|
|
default:
|
|
// Not one of the errors we know how to handle specially. E_FAIL will be returned.
|
|
idErr = 0;
|
|
break;
|
|
}
|
|
|
|
// build a szResult string if we find a match
|
|
if (idErr != 0)
|
|
{
|
|
hr_return = LoadErrResString(idErr,
|
|
&(pErrInfo->m_dwMask),
|
|
pErrInfo->m_szItem[Im_szErrorCode],
|
|
pErrInfo->m_szItem[Im_szShortDescription],
|
|
pErrInfo->m_szItem[Im_szLongDescription]
|
|
);
|
|
}
|
|
|
|
return(hr_return);
|
|
}
|
|
|
|
/*===================================================================
|
|
HResultToWsz
|
|
|
|
Tranlates a HRESULT to a description string of the HRESULT. Attempts
|
|
to use FormatMessage() to get a
|
|
|
|
Parameters:
|
|
hrIn The error to lookup
|
|
wszOut String to output the description to
|
|
cdwOut Number of WCHARs wszOut can hold
|
|
|
|
Returns:
|
|
TRUE if a description string was found
|
|
FALSE if the error number was output instead
|
|
|
|
Notes:
|
|
Added to resolve bug 91847. When unexpected errors are processed
|
|
the naked error number was output, which developers would then
|
|
have to look up in winerror.h.
|
|
===================================================================*/
|
|
BOOL HResultToWsz(HRESULT hrIn, WCHAR *wszOut, DWORD cdwOut)
|
|
{
|
|
LANGID langID = LANG_NEUTRAL;
|
|
DWORD dwFound = 0;
|
|
HMODULE hMsgModule = NULL;
|
|
|
|
#ifdef USE_LOCALE
|
|
LANGID *pLangID;
|
|
|
|
pLangID = (LANGID *)TlsGetValue(g_dwTLS);
|
|
|
|
if (NULL != pLangID)
|
|
langID = *pLangID;
|
|
#endif
|
|
|
|
if (HRESULT_FACILITY(hrIn) == (HRESULT)FACILITY_INTERNET)
|
|
hMsgModule = GetModuleHandleA("METADATA");
|
|
else
|
|
hMsgModule = GetModuleHandleA("ASP");
|
|
|
|
dwFound = FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_FROM_HMODULE,
|
|
hMsgModule,
|
|
hrIn,
|
|
langID,
|
|
wszOut,
|
|
cdwOut,
|
|
NULL);
|
|
|
|
|
|
if (!dwFound)
|
|
{
|
|
// Error not found, make a string out of the error number
|
|
WCHAR *wszResult = NULL;
|
|
WCHAR wszBuf[17];
|
|
WCHAR *wszNumber;
|
|
WCHAR *wszError;
|
|
INT iC;
|
|
|
|
// put a bunch of zeros into the buffer
|
|
for (iC = 0; iC < 16; ++iC)
|
|
wszBuf[iC] = L'0';
|
|
|
|
// wszNumber points half way into the buffer
|
|
wszNumber = &wszBuf[8];
|
|
|
|
// get the error wszNumber as a hex string
|
|
_ltow(hrIn, wszNumber, 16);
|
|
|
|
// back up szNumber to allow a total of 8 digits
|
|
wszNumber -= 8 - wcslen(wszNumber);
|
|
|
|
// Copy the result to wszOut
|
|
wcsncpy(wszOut, wszNumber, cdwOut);
|
|
|
|
return FALSE;
|
|
}
|
|
else
|
|
return TRUE;
|
|
}
|
|
|
|
HMODULE GetModuleHandleForHRESULT(HRESULT hrIn)
|
|
{
|
|
char szModuleName[MAX_PATH];
|
|
DWORD pathLen = 0;
|
|
char *pch;
|
|
|
|
szModuleName[0] = '\0';
|
|
|
|
if (g_fOOP) {
|
|
|
|
strcat(szModuleName, "INETSRV\\");
|
|
}
|
|
|
|
if (HRESULT_FACILITY(hrIn) == (HRESULT)FACILITY_INTERNET)
|
|
strcat(szModuleName, "METADATA.DLL");
|
|
else
|
|
strcat(szModuleName, "ASP.DLL");
|
|
|
|
return(LoadLibraryExA(szModuleName, NULL, LOAD_LIBRARY_AS_DATAFILE));
|
|
}
|
|
|
|
/*===================================================================
|
|
HResultToSz
|
|
|
|
Tranlates a HRESULT to a description string of the HRESULT. Attempts
|
|
to use FormatMessage() to get a
|
|
|
|
Parameters:
|
|
hrIn The error to lookup
|
|
szOut String to output the description to
|
|
cdwOut Number of WCHARs wszOut can hold
|
|
|
|
Returns:
|
|
TRUE if a description string was found
|
|
FALSE if the error number was output instead
|
|
|
|
Notes:
|
|
Added to resolve bug 91847. When unexpected errors are processed
|
|
the naked error number was output, which developers would then
|
|
have to look up in winerror.h.
|
|
===================================================================*/
|
|
BOOL HResultToSz(HRESULT hrIn, CHAR *szOut, DWORD cdwOut)
|
|
{
|
|
LANGID langID = LANG_NEUTRAL;
|
|
HMODULE hMsgModule = NULL;
|
|
BOOL bFound = FALSE;
|
|
|
|
#ifdef USE_LOCALE
|
|
LANGID *pLangID;
|
|
|
|
pLangID = (LANGID *)TlsGetValue(g_dwTLS);
|
|
|
|
if (NULL != pLangID)
|
|
langID = *pLangID;
|
|
#endif
|
|
|
|
hMsgModule = GetModuleHandleForHRESULT(hrIn);
|
|
|
|
HRESULT hr = GetLastError();
|
|
|
|
bFound = FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_FROM_HMODULE,
|
|
hMsgModule,
|
|
hrIn,
|
|
langID,
|
|
szOut,
|
|
cdwOut,
|
|
NULL);
|
|
|
|
// make one additional check before giving up. If the facility of the error is
|
|
// WIN32, then retry the call after masking out the facility code to get standard
|
|
// WIN32 errors. I.E. 80070005 is really just 5 - access denied
|
|
|
|
if (!bFound && (HRESULT_FACILITY(hrIn) == (HRESULT)FACILITY_WIN32)) {
|
|
|
|
bFound = FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_FROM_HMODULE,
|
|
NULL,
|
|
hrIn & 0xffff,
|
|
langID,
|
|
szOut,
|
|
cdwOut,
|
|
NULL);
|
|
}
|
|
|
|
if (hMsgModule)
|
|
FreeLibrary(hMsgModule);
|
|
|
|
if (!bFound )
|
|
{
|
|
// Error not found, make a string out of the error number
|
|
CHAR *szResult = NULL;
|
|
CHAR szBuf[17];
|
|
CHAR *szNumber;
|
|
CHAR *szError;
|
|
INT iC;
|
|
|
|
// put a bunch of zeros into the buffer
|
|
for (iC = 0; iC < 16; ++iC)
|
|
szBuf[iC] = L'0';
|
|
|
|
// wszNumber points half way into the buffer
|
|
szNumber = &szBuf[8];
|
|
|
|
// get the error wszNumber as a hex string
|
|
_ltoa(hrIn, szNumber, 16);
|
|
|
|
// back up szNumber to allow a total of 8 digits
|
|
szNumber -= 8 - strlen(szNumber);
|
|
|
|
// Copy the result to wszOut
|
|
strncpy(szOut, szNumber, cdwOut);
|
|
|
|
return FALSE;
|
|
}
|
|
else
|
|
return TRUE;
|
|
}
|
|
|
|
/*===================================================================
|
|
FIsResStrFormatted
|
|
|
|
Check for formatted resource string.
|
|
|
|
RETURN:
|
|
TRUE/FALSE
|
|
===================================================================*/
|
|
BOOL FIsResStrFormatted(char *szIn)
|
|
{
|
|
BOOL freturn = FALSE;
|
|
UINT cDelimiter = 0;
|
|
CHAR *pch;
|
|
|
|
if(szIn)
|
|
{
|
|
pch = szIn;
|
|
while(*pch)
|
|
{
|
|
if ('~' == *pch)
|
|
cDelimiter++;
|
|
pch = CharNextA(pch);
|
|
}
|
|
|
|
if(3 == cDelimiter)
|
|
return TRUE;
|
|
}
|
|
return freturn;
|
|
|
|
}
|
|
|
|
/*===================================================================
|
|
HandleErrorMissingFilename
|
|
|
|
In several circumstances we want to report an error, but
|
|
we have no filename, and the normal method for getting
|
|
the filename from the template wont work because we have
|
|
no line number info either (e.g. Script timeout, GPF, control GPF, etc)
|
|
|
|
Get the filename from the CIsapiReqInfo (if possible) and report the error.
|
|
|
|
Returns:
|
|
Nothing
|
|
===================================================================*/
|
|
VOID HandleErrorMissingFilename
|
|
(
|
|
UINT errorID,
|
|
CHitObj *pHitObj,
|
|
BOOL fAddlInfo /* = FALSE */,
|
|
...
|
|
)
|
|
{
|
|
va_list args;
|
|
|
|
if (fAddlInfo)
|
|
va_start(args, fAddlInfo);
|
|
|
|
CHAR *szFileName = NULL;
|
|
|
|
if (pHitObj && pHitObj->PSzCurrTemplateVirtPath())
|
|
{
|
|
#if UNICODE
|
|
szFileName = StringDupUTF8(pHitObj->PSzCurrTemplateVirtPath());
|
|
#else
|
|
szFileName = StringDupA(pHitObj->PSzCurrTemplateVirtPath());
|
|
#endif
|
|
}
|
|
|
|
char szEngine[64];
|
|
CchLoadStringOfId(IDS_ENGINE, szEngine, sizeof szEngine);
|
|
|
|
char *pszEngine = new char [strlen(szEngine) + 1];
|
|
if (pszEngine)
|
|
{
|
|
// If the alloc failed, we will pass NULL to HandleError for pszEngine, which is fine.
|
|
// (old code used to pass NULL) All that will happen is that the AspError.Category == "". Oh Well.
|
|
// TODO: change this function to return an HRESULT.
|
|
//
|
|
strcpy(pszEngine, szEngine);
|
|
}
|
|
|
|
HandleError(errorID, szFileName, NULL, pszEngine, NULL, NULL, NULL, pHitObj, fAddlInfo ? &args : NULL);
|
|
}
|
|
|
|
/*===================================================================
|
|
DebugError
|
|
|
|
Handle a script error by invoking the debugger.
|
|
|
|
Returns:
|
|
fails if debugger cannot be invoked.
|
|
If this function fails, no other action is taken.
|
|
(Therefore, the caller should make sure error is reported in
|
|
some other way)
|
|
===================================================================*/
|
|
HRESULT DebugError(IActiveScriptError *pScriptError, CTemplate *pTemplate, DWORD dwEngineID, IDebugApplication *pDebugApp)
|
|
{
|
|
EXCEPINFO excepinfo = {0};
|
|
BSTR bstrLine = NULL;
|
|
DWORD dwSourceContext = 0;
|
|
ULONG ulLineError = 0;
|
|
ULONG ichLineError = 0; // character offset of the line in the source
|
|
ULONG cchLineError = 0; // length of the source line
|
|
BOOLB fGuessedLine = FALSE; // see bug 379
|
|
LPTSTR szPathInfo = NULL;
|
|
LPTSTR szPathTranslated = NULL;
|
|
LONG ichError = -1;
|
|
HRESULT hr = S_OK;
|
|
IDebugDocumentContext *pDebugContext = NULL;
|
|
wchar_t *wszErrNum, *wszShortDesc, *wszLongDesc; // Used to tokenize resource strings
|
|
|
|
if (pScriptError == NULL || pTemplate == NULL || pDebugApp == NULL)
|
|
return E_POINTER;
|
|
|
|
if (FAILED(pScriptError->GetSourcePosition(&dwSourceContext, &ulLineError, &ichError)))
|
|
return E_FAIL;
|
|
|
|
if (FAILED(pScriptError->GetExceptionInfo(&excepinfo)))
|
|
return E_FAIL;
|
|
|
|
// call template object to get line number and character offset where error occurred
|
|
// (It returns both - caller discards whichever it does not want)
|
|
// bug 379: if pTemplate returns fGuessedLine == TRUE, it means we gave it a non-authored
|
|
// line, so we adjust below by not printing bstrLine in the error msg
|
|
pTemplate->GetScriptSourceInfo(dwEngineID, ulLineError, &szPathInfo, &szPathTranslated, NULL, &ichLineError, NULL);
|
|
|
|
// Create a new document context for this statement
|
|
// CONSIDER: character count that we return is bogus - however our debugging
|
|
// client (Caesar's) does not use this information anyway.
|
|
//
|
|
// If this is in the main file, create a document context based on the CTemplate compiled source
|
|
if (_tcscmp(szPathTranslated, pTemplate->GetSourceFileName()) == 0)
|
|
pDebugContext = new CTemplateDocumentContext(pTemplate, ichLineError, 1);
|
|
|
|
// source refers to an include file, so create a documet context based on cached CIncFile dependency graph
|
|
else
|
|
{
|
|
CIncFile *pIncFile;
|
|
if (FAILED(g_IncFileMap.GetIncFile(szPathTranslated, &pIncFile)))
|
|
{
|
|
hr = E_FAIL;
|
|
goto LExit;
|
|
}
|
|
|
|
pDebugContext = new CIncFileDocumentContext(pIncFile, ichLineError, 1);
|
|
pIncFile->Release();
|
|
}
|
|
|
|
if (pDebugContext == NULL)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
goto LExit;
|
|
}
|
|
|
|
// Yes it does, bring up the debugger on this line
|
|
hr = InvokeDebuggerWithThreadSwitch
|
|
(
|
|
pDebugApp,
|
|
DEBUGGER_UI_BRING_DOC_CONTEXT_TO_TOP,
|
|
pDebugContext
|
|
);
|
|
if (FAILED(hr))
|
|
goto LExit;
|
|
|
|
|
|
// pop up a message box with the error description
|
|
// Bug 81954: Misbehaved objects may throw an exception without providing any information
|
|
wchar_t wszExceptionBuffer[256];
|
|
wchar_t *wszDescription;
|
|
if (excepinfo.bstrDescription == NULL || excepinfo.bstrDescription[0] == L'\0')
|
|
{
|
|
HRESULT hrError;
|
|
|
|
if (0 == excepinfo.wCode)
|
|
hrError = excepinfo.scode;
|
|
else
|
|
hrError = excepinfo.wCode;
|
|
|
|
// Bug 91847 Attempt to get a description via FormatMessage()
|
|
if (!HResultToWsz(hrError, wszExceptionBuffer, 128))
|
|
CwchLoadStringOfId(IDE_SCRIPT_UNKNOWN, wszExceptionBuffer, sizeof(wszExceptionBuffer)/sizeof(WCHAR));
|
|
wszDescription = wszExceptionBuffer;
|
|
}
|
|
else
|
|
wszDescription = excepinfo.bstrDescription;
|
|
|
|
wchar_t wszSource[35];
|
|
CwchLoadStringOfId(IDS_SCRIPT_ERROR, wszSource, sizeof(wszSource)/sizeof(WCHAR));
|
|
|
|
// See if this is resource formatted string, and if it is, get pointers to short & long string
|
|
// resource formatted strings are delimited by '~' characters There are three '~' characters
|
|
// in the resource formatted string
|
|
//
|
|
wszErrNum = wcschr(wszDescription, L'~');
|
|
if (wszErrNum)
|
|
{
|
|
wszShortDesc = wcschr(wszErrNum + 1, L'~');
|
|
if (wszShortDesc)
|
|
{
|
|
wszLongDesc = wcschr(wszShortDesc + 1, L'~');
|
|
|
|
// OK. If all three tests succeeded, we know this is a resource formatted string,
|
|
// and we have pointers to all three segments. Replace each '~' with two newlines.
|
|
// First: Load resource strings
|
|
|
|
wchar_t wszErrorBegin[20], wszErrorEnd[5];
|
|
wchar_t *pwchEnd;
|
|
|
|
CwchLoadStringOfId(IDS_DEBUGGER_TEMPLATE_BEGIN, wszErrorBegin, sizeof(wszErrorBegin)/sizeof(WCHAR));
|
|
CwchLoadStringOfId(IDS_DEBUGGER_TEMPLATE_END, wszErrorEnd, sizeof(wszErrorEnd)/sizeof(WCHAR));
|
|
|
|
// Tokenize string by setting '~' characters to NULL and incrementing ptrs
|
|
|
|
*wszErrNum++ = *wszShortDesc++ = *wszLongDesc++ = L'\0';
|
|
|
|
// Build the string
|
|
|
|
pwchEnd = strcpyExW(wszExceptionBuffer, excepinfo.bstrSource);
|
|
*pwchEnd++ = L' ';
|
|
pwchEnd = strcpyExW(pwchEnd, wszErrorBegin);
|
|
pwchEnd = strcpyExW(pwchEnd, wszErrNum);
|
|
pwchEnd = strcpyExW(pwchEnd, wszErrorEnd);
|
|
*pwchEnd++ = L'\n';
|
|
*pwchEnd++ = L'\n';
|
|
|
|
pwchEnd = strcpyExW(pwchEnd, wszShortDesc);
|
|
*pwchEnd++ = L'\n';
|
|
*pwchEnd++ = L'\n';
|
|
|
|
pwchEnd = strcpyExW(pwchEnd, wszLongDesc);
|
|
|
|
wszDescription = wszExceptionBuffer;
|
|
}
|
|
}
|
|
|
|
MessageBoxW(NULL, wszDescription, wszSource, MB_SERVICE_NOTIFICATION | MB_TOPMOST | MB_OK | MB_ICONEXCLAMATION);
|
|
|
|
LExit:
|
|
if (pDebugContext)
|
|
pDebugContext->Release();
|
|
|
|
if (excepinfo.bstrSource)
|
|
SysFreeString(excepinfo.bstrSource);
|
|
|
|
if (excepinfo.bstrDescription)
|
|
SysFreeString(excepinfo.bstrDescription);
|
|
|
|
if (excepinfo.bstrHelpFile)
|
|
SysFreeString(excepinfo.bstrHelpFile);
|
|
|
|
return hr;
|
|
}
|
|
|