1
0
mirror of https://github.com/winscp/winscp.git synced 2026-10-24 02:28:55 +03:00
Files
winscp/source/core/Exceptions.cpp
Martin Prikryl ba680d6e2e Avoid redundantly capturing exception stack trace
Jcl captures exception stack trace on its own, so it's redundant to do it again. And mainly, capturing it the second time somehow does not work with Clang

Source commit: 92a3533d082138b3e5c3729ac35f5fbf0b09b57f
2025-09-03 12:19:54 +02:00

540 lines
16 KiB
C++

//---------------------------------------------------------------------------
#include <CorePCH.h>
#pragma hdrstop
#include "Exceptions.h"
#include "Configuration.h"
//---------------------------------------------------------------------------
static std::unique_ptr<TCriticalSection> IgnoredExceptionsCriticalSection(TraceInitPtr(new TCriticalSection()));
typedef std::set<UnicodeString> TIgnoredExceptions;
static TIgnoredExceptions IgnoredExceptions;
//---------------------------------------------------------------------------
static UnicodeString __fastcall NormalizeClassName(const UnicodeString & ClassName)
{
return ReplaceStr(ClassName, L".", L"::").LowerCase();
}
//---------------------------------------------------------------------------
void __fastcall IgnoreException(const std::type_info & ExceptionType)
{
TGuard Guard(IgnoredExceptionsCriticalSection.get());
// We should better use type_index as a key, instead of a class name,
// but type_index is not available in 32-bit version of STL in XE6.
IgnoredExceptions.insert(NormalizeClassName(UnicodeString(AnsiString(ExceptionType.name()))));
}
//---------------------------------------------------------------------------
static bool __fastcall WellKnownException(
Exception * E, UnicodeString * AMessage, const wchar_t ** ACounterName, Exception ** AClone, bool Rethrow)
{
UnicodeString Message;
const wchar_t * CounterName = NULL; // shut up
std::unique_ptr<Exception> Clone;
bool Result = true;
bool IgnoreException = false;
if (!IgnoredExceptions.empty())
{
TGuard Guard(IgnoredExceptionsCriticalSection.get());
UnicodeString ClassName = NormalizeClassName(E->QualifiedClassName());
IgnoreException = (IgnoredExceptions.find(ClassName) != IgnoredExceptions.end());
}
if (IgnoreException)
{
Result = false;
}
// EAccessViolation is EExternal
else if (dynamic_cast<EAccessViolation*>(E) != NULL)
{
if (Rethrow)
{
throw EAccessViolation(E->Message);
}
UnicodeString S = E->Message;
UnicodeString Dummy;
if (!ExtractMainInstructions(S, Dummy))
{
S = UnicodeString();
}
Message = MainInstructions(LoadStr(ACCESS_VIOLATION_ERROR3)) + S;
CounterName = L"AccessViolations";
Clone.reset(new EAccessViolation(E->Message));
}
// EIntError and EMathError are EExternal
// EClassNotFound is EFilerError
else if ((dynamic_cast<EListError*>(E) != NULL) ||
(dynamic_cast<EStringListError*>(E) != NULL) ||
(dynamic_cast<EIntError*>(E) != NULL) ||
(dynamic_cast<EMathError*>(E) != NULL) ||
(dynamic_cast<EVariantError*>(E) != NULL) ||
(dynamic_cast<EInvalidOperation*>(E) != NULL) ||
(dynamic_cast<EFilerError*>(E) != NULL))
{
if (Rethrow)
{
throw EIntError(E->Message);
}
Message = MainInstructions(E->Message);
CounterName = L"InternalExceptions";
Clone.reset(new EIntError(E->Message));
}
else if (dynamic_cast<EExternal*>(E) != NULL)
{
if (Rethrow)
{
throw EExternal(E->Message);
}
Message = MainInstructions(E->Message);
CounterName = L"ExternalExceptions";
Clone.reset(new EExternal(E->Message));
}
else if (dynamic_cast<EHeapException*>(E) != NULL)
{
if (Rethrow)
{
throw EHeapException(E->Message);
}
Message = MainInstructions(E->Message);
CounterName = L"HeapExceptions";
Clone.reset(new EHeapException(E->Message));
}
else
{
Result = false;
}
if (Result)
{
if (AMessage != NULL)
{
(*AMessage) = Message;
}
if (ACounterName != NULL)
{
(*ACounterName) = CounterName;
}
if (AClone != NULL)
{
(*AClone) = DebugNotNull(Clone.release());
}
}
return Result;
}
//---------------------------------------------------------------------------
static bool __fastcall ExceptionMessage(Exception * E, bool Count,
bool Formatted, UnicodeString & Message, bool & InternalError)
{
bool Result = true;
const wchar_t * CounterName = NULL;
InternalError = false; // see also IsInternalException
// this list has to be in sync with CloneException
if (dynamic_cast<EAbort *>(E) != NULL)
{
Result = false;
}
else if (WellKnownException(E, &Message, &CounterName, NULL, false))
{
InternalError = true;
}
else if (E->Message.IsEmpty())
{
Result = false;
}
else
{
Message = E->Message;
}
if (!Formatted)
{
Message = UnformatMessage(Message);
}
if (InternalError)
{
Message = FMTLOAD(REPORT_ERROR, (Message));
}
if (Count && (CounterName != NULL) && (Configuration->Usage != NULL))
{
Configuration->Usage->Inc(CounterName);
UnicodeString ExceptionDebugInfo =
E->ClassName() + L":" + GetExceptionDebugInfo(E);
Configuration->Usage->Set(LastInternalExceptionCounter, ExceptionDebugInfo);
}
return Result;
}
//---------------------------------------------------------------------------
bool __fastcall IsInternalException(Exception * E)
{
// see also InternalError in ExceptionMessage
return WellKnownException(E, NULL, NULL, NULL, false);
}
//---------------------------------------------------------------------------
bool __fastcall ExceptionMessage(Exception * E, UnicodeString & Message)
{
bool InternalError;
return ExceptionMessage(E, true, false, Message, InternalError);
}
//---------------------------------------------------------------------------
bool __fastcall ExceptionMessageFormatted(Exception * E, UnicodeString & Message)
{
bool InternalError;
return ExceptionMessage(E, true, true, Message, InternalError);
}
//---------------------------------------------------------------------------
bool __fastcall ShouldDisplayException(Exception * E)
{
UnicodeString Message;
return ExceptionMessageFormatted(E, Message);
}
//---------------------------------------------------------------------------
TStrings * __fastcall ExceptionToMoreMessages(Exception * E)
{
TStrings * Result = NULL;
UnicodeString Message;
if (ExceptionMessage(E, Message))
{
Result = new TStringList();
Result->Add(Message);
ExtException * ExtE = dynamic_cast<ExtException *>(E);
if ((ExtE != NULL) && (ExtE->MoreMessages != NULL))
{
Result->AddStrings(ExtE->MoreMessages);
}
}
return Result;
}
//---------------------------------------------------------------------------
bool __fastcall ExceptionFullMessage(Exception * E, UnicodeString & Message)
{
bool Result = ExceptionMessage(E, Message);
if (Result)
{
Message += L"\n";
ExtException * EE = dynamic_cast<ExtException *>(E);
if ((EE != NULL) && (EE->MoreMessages != NULL))
{
Message += EE->MoreMessages->Text + L"\n";
}
}
return Result;
}
//---------------------------------------------------------------------------
UnicodeString __fastcall GetExceptionHelpKeyword(Exception * E)
{
UnicodeString HelpKeyword;
ExtException * ExtE = dynamic_cast<ExtException *>(E);
UnicodeString Message; // not used
bool InternalError = false;
if (ExtE != NULL)
{
HelpKeyword = ExtE->HelpKeyword;
}
else if ((E != NULL) && ExceptionMessage(E, false, false, Message, InternalError) &&
InternalError)
{
HelpKeyword = HELP_INTERNAL_ERROR;
}
return HelpKeyword;
}
//---------------------------------------------------------------------------
UnicodeString __fastcall MergeHelpKeyword(const UnicodeString & PrimaryHelpKeyword, const UnicodeString & SecondaryHelpKeyword)
{
if (!PrimaryHelpKeyword.IsEmpty() &&
!IsInternalErrorHelpKeyword(SecondaryHelpKeyword))
{
// we have to yet decide what we have both
// PrimaryHelpKeyword and SecondaryHelpKeyword
return PrimaryHelpKeyword;
}
else
{
return SecondaryHelpKeyword;
}
}
//---------------------------------------------------------------------------
bool __fastcall IsInternalErrorHelpKeyword(const UnicodeString & HelpKeyword)
{
return
(HelpKeyword == HELP_INTERNAL_ERROR);
}
//---------------------------------------------------------------------------
__fastcall ExtException::ExtException(Exception * E) :
Exception("")
{
AddMoreMessages(E);
FHelpKeyword = GetExceptionHelpKeyword(E);
}
//---------------------------------------------------------------------------
__fastcall ExtException::ExtException(Exception* E, UnicodeString Msg, UnicodeString HelpKeyword):
Exception(Msg)
{
AddMoreMessages(E);
FHelpKeyword = MergeHelpKeyword(HelpKeyword, GetExceptionHelpKeyword(E));
}
//---------------------------------------------------------------------------
__fastcall ExtException::ExtException(UnicodeString Msg, Exception* E, UnicodeString HelpKeyword) :
Exception("")
{
// "copy exception"
AddMoreMessages(E);
// and append message to the end to more messages
if (!Msg.IsEmpty())
{
if (Message.IsEmpty())
{
Message = Msg;
}
else
{
if (FMoreMessages == NULL)
{
FMoreMessages = new TStringList();
}
FMoreMessages->Append(UnformatMessage(Msg));
}
}
FHelpKeyword = MergeHelpKeyword(GetExceptionHelpKeyword(E), HelpKeyword);
}
//---------------------------------------------------------------------------
__fastcall ExtException::ExtException(UnicodeString Msg, UnicodeString MoreMessages,
UnicodeString HelpKeyword) :
Exception(Msg),
FHelpKeyword(HelpKeyword)
{
if (!MoreMessages.IsEmpty())
{
FMoreMessages = TextToStringList(MoreMessages);
}
}
//---------------------------------------------------------------------------
__fastcall ExtException::ExtException(UnicodeString Msg, TStrings* MoreMessages,
bool Own, UnicodeString HelpKeyword) :
Exception(Msg),
FHelpKeyword(HelpKeyword)
{
if (Own)
{
FMoreMessages = MoreMessages;
}
else
{
FMoreMessages = new TStringList();
FMoreMessages->Assign(MoreMessages);
}
}
//---------------------------------------------------------------------------
void __fastcall ExtException::AddMoreMessages(Exception* E)
{
if (E != NULL)
{
if (FMoreMessages == NULL)
{
FMoreMessages = new TStringList();
}
ExtException * ExtE = dynamic_cast<ExtException *>(E);
if (ExtE != NULL)
{
if (ExtE->MoreMessages != NULL)
{
FMoreMessages->Assign(ExtE->MoreMessages);
}
}
UnicodeString Msg;
ExceptionMessageFormatted(E, Msg);
// new exception does not have own message, this is in fact duplication of
// the exception data, but the exception class may being changed
if (Message.IsEmpty())
{
Message = Msg;
}
else if (!Msg.IsEmpty())
{
FMoreMessages->Insert(0, UnformatMessage(Msg));
}
AppendExceptionStackTrace(E, FMoreMessages);
if (FMoreMessages->Count == 0)
{
delete FMoreMessages;
FMoreMessages = NULL;
}
}
}
//---------------------------------------------------------------------------
__fastcall ExtException::~ExtException()
{
delete FMoreMessages;
}
//---------------------------------------------------------------------------
ExtException * __fastcall ExtException::CloneFrom(Exception * E)
{
return new ExtException(E, L"");
}
//---------------------------------------------------------------------------
ExtException * __fastcall ExtException::Clone()
{
return CloneFrom(this);
}
//---------------------------------------------------------------------------
void __fastcall ExtException::Rethrow()
{
throw ExtException(this, L"");
}
//---------------------------------------------------------------------------
UnicodeString __fastcall SysErrorMessageForError(int LastError)
{
UnicodeString Result;
if (LastError != 0)
{
Result = FORMAT(System_Sysconst_SOSError, (LastError, SysErrorMessage(LastError), UnicodeString()));
}
return Result;
}
//---------------------------------------------------------------------------
UnicodeString __fastcall LastSysErrorMessage()
{
return SysErrorMessageForError(GetLastError());
}
//---------------------------------------------------------------------------
__fastcall EOSExtException::EOSExtException(UnicodeString Msg) :
ExtException(Msg, LastSysErrorMessage())
{
}
//---------------------------------------------------------------------------
__fastcall EOSExtException::EOSExtException(UnicodeString Msg, int LastError) :
ExtException(Msg, SysErrorMessageForError(LastError))
{
}
//---------------------------------------------------------------------------
__fastcall EFatal::EFatal(Exception* E, UnicodeString Msg, UnicodeString HelpKeyword) :
ExtException(Msg, E, HelpKeyword),
ReopenQueried(false),
InactiveTerminationMessage(false)
{
EFatal * F = dynamic_cast<EFatal *>(E);
if (F != NULL)
{
ReopenQueried = F->ReopenQueried;
InactiveTerminationMessage = F->InactiveTerminationMessage;
}
}
//---------------------------------------------------------------------------
__fastcall ECRTExtException::ECRTExtException(UnicodeString Msg) :
EOSExtException(Msg, errno)
{
}
//---------------------------------------------------------------------------
ExtException * __fastcall EFatal::Clone()
{
return new EFatal(this, L"");
}
//---------------------------------------------------------------------------
void __fastcall EFatal::Rethrow()
{
throw EFatal(this, L"");
}
//---------------------------------------------------------------------------
ExtException * __fastcall ESshTerminate::Clone()
{
return new ESshTerminate(this, L"", Operation, TargetLocalPath, DestLocalFileName);
}
//---------------------------------------------------------------------------
void __fastcall ESshTerminate::Rethrow()
{
throw ESshTerminate(this, L"", Operation, TargetLocalPath, DestLocalFileName);
}
//---------------------------------------------------------------------------
__fastcall ECallbackGuardAbort::ECallbackGuardAbort() : EAbort(L"callback abort")
{
}
//---------------------------------------------------------------------------
Exception * __fastcall CloneException(Exception * E)
{
Exception * Result;
// this list has to be in sync with ExceptionMessage
ExtException * Ext = dynamic_cast<ExtException *>(E);
if (Ext != NULL)
{
Result = Ext->Clone();
}
else if (dynamic_cast<ECallbackGuardAbort *>(E) != NULL)
{
Result = new ECallbackGuardAbort();
}
else if (dynamic_cast<EAbort *>(E) != NULL)
{
Result = new EAbort(E->Message);
}
else if (WellKnownException(E, NULL, NULL, &Result, false))
{
// noop
}
else
{
// we do not expect this to happen
if (DebugAlwaysFalse(IsInternalException(E)))
{
// to save exception stack trace
Result = ExtException::CloneFrom(E);
}
else
{
Result = new Exception(E->Message);
}
}
return Result;
}
//---------------------------------------------------------------------------
void __fastcall RethrowException(Exception * E)
{
// this list has to be in sync with ExceptionMessage
if (dynamic_cast<ExtException *>(E) != NULL)
{
dynamic_cast<ExtException *>(E)->Rethrow();
}
else if (dynamic_cast<ECallbackGuardAbort *>(E) != NULL)
{
throw ECallbackGuardAbort();
}
else if (dynamic_cast<EAbort *>(E) != NULL)
{
throw EAbort(E->Message);
}
else if (WellKnownException(E, NULL, NULL, NULL, true))
{
// noop, should never get here
}
else
{
throw ExtException(E, L"");
}
}
//---------------------------------------------------------------------------
UnicodeString __fastcall AddContextToExceptionMessage(const Exception & E, const UnicodeString & NewContext)
{
UnicodeString MainMessage;
UnicodeString Context = E.Message;
if (!ExtractMainInstructions(Context, MainMessage))
{
MainMessage = E.Message;
Context = NewContext;
}
else
{
Context += L"\n" + NewContext;
}
UnicodeString Result = MainInstructions(MainMessage) + L"\n" + Context;
return Result;
}