My latest attempt at "Global" Error Handling
ErrorHandler.inc
DemoErrorHandling.bas
Just keep ignoring the errors that are created in the demo, and check out the log created for each of those errors.
Discussion can be found here
ErrorHandler.inc
Code:
'*** ErrorHandling.inc '*** Written by: Cliff Nichols - 08-28-2008 '*** Modified by: Cliff Nichols - 06-28-2009 '*** '*** Compilers Tested: 8.04/9.0/9.01 '*** Should work on: 7/8/9 '*** '*** OS'es Tested: XP-Pro SP3, Vista-Ultimate '*** Should work on: 95/98/ME/NT/2K/XP/Vista '*** '*** Purpose - Beefed up Error Handling Routines to attempt to "Bullet-Proof" apps '*** Via a "GLOBAL ERROR HANDLER" (Which in theory does not exist) '*** Concepts: '*** Demonstrate PB Errors - (How each either sets or raises an error window) '*** Demonstrate Windows Errors - (How each either sets or raises an error window) '*** Demonstrate GPF "EXCEPTIONS" - (The errors you see with the cryptic '*** "Had a problem and has to close" for an explaination '*** , but no plain english why the error?) '*** '*** Special Thnx: '*** Paul Dixon - Without whom, I would not have found out about EIP's and beginning to understand '*** how to decipher assembly code (still learning so this INC is not complete...YET!!!! that is) '-------------------------------------------------------------------------------- '*** Now for the FUN Stuff '*** PB has 'On Error' for handling errors on the DDT side '*** PB (DDT) can set an error with ERR = some number (does not raise an error, just sets the value) '*** PB (DDT) can raise an error just by ERROR = some number '*** PB (DDT) can test an error state by checking the value of ERR '*** PB appears to work with 'On Error' for handling errors on the SDK side for most errors '*** Windows uses Exceptions (But you must handle, or ignore to not generate a GPF) '<--- This statement may not be TOTALLY correct, but still is concise '*** Windows (SDK) can set an error with 'SetLastError (does not raise an error, just sets the value) '*** Windows (SDK) can set an error with parameters using 'SetLastErrorEX (does not raise an error, just sets the value) '*** Windows (SDK) can raise an error with 'RaiseException' api call '*** Windows (SDK) can test an error state by 'GetLastError' '*** '*** Windows has "Exceptions" (You've seen them...the default usually shows the typical "*.exe has encountered a problem and needs to close" message) '*** Windows "Exceptions" can be raised, but default handler is typically fatal, but you can override that fatality '*** '*** PB narrowing down to line that caused the error '*** Manually inserting Line Numbers can definitely nail down an error, but either needs to be done by hand, or pre-compile to do it for you '*** ---> Scratch that thought, I now have figured out using a macro that I call "DebugLine" can be used on the same line as code, or a separate line '*** ---> to "Land-Mark" by inserting a line number in your code at compile time (Disperce anywhere you think you want a landmark to home in on an error) '*** '*** PB Line numbers (if inserted) can assist in locating where an error occured (larger projects its smarter to contain line numbers to their functions) '*** PB FUNCNAME$ in conjunction with the line numbers can nail down what line the error occured on '*** '*** Windows narrowing down to line that caused the error '*** Extended Instruction Pointer (EIP) ---> to be worked on and detailed later '*** Conjunction, junction, whats your function??? :-) '*** '*** Out of the ordinary ideas: '*** No Globals - Use functions to store value of "Global" and look up the value when needed '<--- Not 100% on this yet, but no problems either '*** Locating lines of code - Figuring out what line of readable code I am on, from the machine code that I read '*** (only beginning, so take at face value, and please correct me where I am wrong) '*** Macro's - Usually I have no use for them, because if I use the same code in multiple functions, '*** then I just write a function to be called from each function. '*** But in a case like this where variables are only valid locally, '*** I can see using macros to replace my code at compile time can save a ton a space '*** and be more readable in the future '*** '*** '-------------------------------------------------------------------------------- '*** If not already declared in your code, then declare this INC #IF NOT %DEF(%ERRORHANDLING) %ERRORHANDLING = 1 '*** Globals '*** NONE '*** OverRideError flag can be one of the following values: '*** %UNKNOWN_VALUE = Unknown until user input at run-time '*** %NORESUME = Do not resume '*** %RESUME = Resume at same line of code that caused the error. '*** (Error must be corrected or will result in an infinite loop) '*** %RESUMENEXT = Resume at line after code that caused the error. '*** %CRASH = Purposely allow a Crash (GPF) to happen. '*** %UNKNOWN_VALUE = -1 'Unknown for when I do not know the value until runtime '*** <--- I chose RESUMENEXT to be 0 if for the odd chance that a flag is not set (default is 0) that I do NOT cause an infinite loop %RESUME = 1 'Use 1 to resume (this is so that if left out, then not stuck in an infinite loop) %RESUMENEXT = 0 'Use 0 for Resume Next in case flag is not set to avoid infinite loops %NORESUME = -1 'NoResume = UnKnown so that code decides what is best path to follow %CRASH = %INVALID_HANDLE_VALUE 'Purposely allow crash of App '*** Constants not found in Win32Api.Inc (Have to be re-researched because I found %EXCEPTION_ILLEGAL_INSTRUCTION = %STATUS_ILLEGAL_INSTRUCTION ' %EXCEPTION_ILLEGAL_INSTRUCTION = &HC000001D %EXCEPTION_ILLEGAL_INSTRUCTION = %STATUS_ILLEGAL_INSTRUCTION %EXCEPTION_STACK_OVERFLOW = &HC00000FD %EXCEPTION_INVALID_DISPOSITION = &HC0000026 %EXCEPTION_NONCONTINUABLE_EXCEPTION = &HC0000025 '*** Type to hold my settings TYPE ErrorCodes lpEP AS EXCEPTION_POINTERS PTR 'Windows EXCEPTIONS (Errors) SafePointer AS LONG 'Pointer value to ErrHandler ErrorLogNumber AS LONG 'Handle to log file LogError AS LONG 'If logging errors PbErrorValue AS LONG 'PB ERR WinErrorValue AS LONG 'GetLastError AppErrorValue AS LONG 'Error value reguardless of PB Error or Windows Error PbErrorDesc AS ASCIIZ * %MAX_PATH 'PB Description of Error in plain english WinErrorDesc AS ASCIIZ * %MAX_PATH 'Windows Description of Error in plain english AppErrorDesc AS ASCIIZ * %MAX_PATH 'Application Specific Description of Error CodeModule AS ASCIIZ * %MAX_PATH 'Function that caused the error '<--- MOST Important thing I have always been after in larger projects) LineLabelNumber AS ASCIIZ * %MAX_PATH 'Line number (or Variable) that was last passed before the error OverRideError AS LONG 'Variable for if to continue even if there is an error StringValue AS ASCIIZ * %MAX_PATH 'Error value as a string Desc AS ASCIIZ * %MAX_PATH 'Description for loggin purposes to help track down a problem END TYPE '*** Declare Functions '************************************************************************************************************* '*** Macro's - When Compiled, replace the name of the macro with all the functions and processes in the macro '*** As if you had done it yourself in each and every SUB/FUNCTION it was called '*** Do not add line numbers to Macro's unless you want to renumber functions that use the macros '************************************************************************************************************* '*** Using SetUnhandledExceptionFilter you can over-ride the default error handler for windows '*** SetUnhandledExceptionFilter %NULL = Default handling within UnhandledExceptionFilter '*** SetUnhandledExceptionFilter %EXCEPTION_EXECUTE_HANDLER = Return from UnhandledExceptionFilter and execute the associated exception handler. '*** (Usually results in process termination.) '*** SetUnhandledExceptionFilter %EXCEPTION_CONTINUE_EXECUTION = Return from UnhandledExceptionFilter and continue execution from the point of the exception. '*** Note that the filter function is free to modify the continuation state by modifying the exception information supplied through its LPEXCEPTION_POINTERS parameter '*** SetUnhandledExceptionFilter %EXCEPTION_CONTINUE_SEARCH = Proceed with normal execution of UnhandledExceptionFilter . That means obeying the SetErrorMod flags, or invoking the Application Error pop-up message box. '*** '*** OnError - Replacement for "ON ERROR" (Place as 1st line of code in your function) MACRO OnError ON ERROR GOTO ErrHandler 'PB command for On Error LOCAL lpEP AS EXCEPTION_POINTERS 'If Windows Crops up an error LOCAL ErrFunc AS LONG 'If Error within my function LOCAL SoftwarePathName AS ASCIIZ * %MAX_PATH LOCAL ErrorLogNumber AS LONG LOCAL ErrorCode AS ErrorCodes LOCAL Continuable AS LONG LOCAL LinesOfCodeToSkip AS LONG LOCAL DebugLineNumber AS STRING SetGetSafeJump CODEPTR(ErrHandler), ErrorCode.SafePointer, %True 'Set flag for escaping functions that I can not find a way to continue from ' SetGetJumpLines 1, LinesOfCodeToSkip, %TRUE 'Set flag for how many lines of code to skip (default = 1 line) SetUnhandledExceptionFilter( CODEPTR (ExceptionHandler)) 'Over-Ride Windows default and let me decide if a FATAL error or to 'Fix-And-Continue' SetGetErrorCodes VARPTR(ErrorCode), %UNKNOWN_VALUE, %TRUE 'Set a handle to ErrorCodes without using GLOBALS '<--- Not 100% on this yet GetModuleFileName(GetModuleHandle(BYVAL %NULL), SoftwarePathName, %MAX_PATH) 'Get Parent PathName and Name SoftwarePathName = MID$(SoftwarePathName, INSTR(-1, SoftwarePathName,"\") + 1) 'Path Name SoftwarePathName = MID$(SoftwarePathName, 1, INSTR(SoftwarePathName,".") - 1) 'Strip the .Dll or .Exe SetGetLogErrors %UNKNOWN_VALUE, ErrorCode.LogError, %UNKNOWN_VALUE 'Get flag for logging errors SELECT CASE ErrorCode.LogError CASE %FALSE CASE %TRUE SetGetLogFileNumber(%UNKNOWN_VALUE, ErrorCode.ErrorLogNumber, %UNKNOWN_VALUE) SELECT CASE ErrorCode.ErrorLogNumber CASE 0 'No file yet ErrorCode.ErrorLogNumber = FREEFILE OPEN SoftwarePathName + " Error Log.txt" FOR APPEND LOCK SHARED AS #ErrorCode.ErrorLogNumber 'Open the log file SetGetLogFileNumber(ErrorCode.ErrorLogNumber, ErrorCode.ErrorLogNumber, %TRUE) END SELECT END SELECT END MACRO MACRO DebugLine DebugLineNumber = TRIM$(STR$(VAL(DebugLineNumber) + 1)) ErrorCode.LineLabelNumber = DebugLineNumber END MACRO '*** HandleErrors - (Place as last line in your function) MACRO HandleErrors SetGetLogErrors %UNKNOWN_VALUE, ErrorCode.LogError, %UNKNOWN_VALUE 'Get flag for logging errors SELECT CASE ErrorCode.LogError CASE %TRUE CASE %FALSE CLOSE ErrorCode.ErrorLogNumber 'End log file SetGetLogFileNumber(0, ErrorCode.ErrorLogNumber, %TRUE) END SELECT EXIT MACRO 'Exit (do not care if sub/function/method/property ErrHandler: 'Only gets here if an error '*** The following Error Information has to stay in the macro due to the error is cleared when it leaves the function that it was raised in. ErrorCode.PbErrorValue = ERR 'Get PB Last Error ErrorCode.WinErrorValue = GetLastError 'Get API Last Error ExceptionInfo(ErrorCode) 'Fill Information for logging SELECT CASE ErrorCode.OverRideError 'Decide how to continue CASE %UNKNOWN_VALUE Continuable = MSGBOX("Error: " + STR$(ErrorCode.AppErrorValue) + SPACE$(5) + "Desc: " + ErrorCode.AppErrorDesc + $CR _ + " Function: " + SPACE$(5) + CALLSTK$(1) + $CR + $CR + "Ignore this error?", %MB_ICONERROR OR %MB_YESNO, FUNCNAME$ + SPACE$(5) + "Windows Error") SELECT CASE Continuable CASE %IDYES RESUME NEXT CASE %IDNO END SELECT CASE %NORESUME 'Just let default handlers decide what to do CASE %RESUME '<-- NOT recommended unless code added to correct how we got here in the 1st place RESUME CASE %RESUMENEXT 'Resume code at next line of code RESUME NEXT END SELECT END MACRO '******* Test functions to remove the need for GLOBALS, buy calling a function and Set or Get the value ******* FUNCTION SetGetErrorCodes(ValueToSet AS LONG, ValueResults AS LONG, ResetValue AS LONG) AS LONG STATIC FunctionValue AS LONG 'Static to hold current value SELECT CASE ResetValue 'Decide whether to Set or to Get the current value CASE %False, %UNKNOWN_VALUE 'If set to False, or -1 Then Get Current Value ValueResults = FunctionValue 'Return Results as a parameter CASE = %TRUE 'If set to True then Reset the Current Value FunctionValue = ValueToSet 'Reset the value ValueResults = FunctionValue 'Return Results as a parameter END SELECT FUNCTION = %False 'Return if Function Failed END FUNCTION FUNCTION SetGetLogErrors(ValueToSet AS LONG, ValueResults AS LONG, ResetValue AS LONG) AS LONG STATIC FunctionValue AS LONG 'Static to hold current value SELECT CASE ResetValue 'Decide whether to Set or to Get the current value CASE %False, %UNKNOWN_VALUE 'If set to False, or -1 Then Get Current Value ValueResults = FunctionValue 'Return Results as a parameter CASE = %TRUE 'If set to True then Reset the Current Value FunctionValue = ValueToSet 'Reset the value ValueResults = FunctionValue 'Return Results as a parameter END SELECT FUNCTION = %False 'Return if Function Failed END FUNCTION FUNCTION SetGetLogFileNumber(ValueToSet AS LONG, ValueResults AS LONG, ResetValue AS LONG) AS LONG STATIC FunctionValue AS LONG 'Static to hold current value SELECT CASE ResetValue 'Decide whether to Set or to Get the current value CASE %False, %UNKNOWN_VALUE 'If set to False, or -1 Then Get Current Value ValueResults = FunctionValue 'Return Results as a parameter CASE = %TRUE 'If set to True then Reset the Current Value FunctionValue = ValueToSet 'Reset the value ValueResults = FunctionValue 'Return Results as a parameter END SELECT FUNCTION = %False 'Return if Function Failed END FUNCTION FUNCTION SetGetSafeJump(ValueToSet AS LONG, ValueResults AS LONG, ResetValue AS LONG) AS LONG STATIC FunctionValue AS LONG 'Static to hold current value SELECT CASE ResetValue 'Decide whether to Set or to Get the current value CASE %False, %UNKNOWN_VALUE 'If set to False, or -1 Then Get Current Value ValueResults = FunctionValue 'Return Results as a parameter CASE = %TRUE 'If set to True then Reset the Current Value FunctionValue = ValueToSet 'Reset the value ValueResults = FunctionValue 'Return Results as a parameter END SELECT FUNCTION = %False 'Return if Function Failed END FUNCTION '-------------------------------------------------------------------------------- '*** Now for the DOOZYYYYyyyy....Handling GPF's and other major fatal errors '-------------------------------------------------------------------------------- '************************************************************************************************************* '*** ExceptionInfo is information that can be caught by 'ON ERROR' type of errors '************************************************************************************************************* FUNCTION ExceptionInfo(BYREF ErrorCode AS ErrorCodes) AS LONG STATIC TerminateInProcess AS LONG 'In case an error occurs while in the midst of ending my app LOCAL SafeReturnPoint AS LONG '*** Get the PB and Windows Error Descriptions (not caring which caused the error) ErrorCode.PbErrorDesc = ERROR$(ErrorCode.PbErrorValue) 'Set the PB Error Description FormatMessage %FORMAT_MESSAGE_FROM_SYSTEM, BYVAL %NULL, ErrorCode.WinErrorValue, %NULL, ErrorCode.WinErrorDesc, SIZEOF(ErrorCode.WinErrorDesc), BYVAL %NULL 'Format the Windows Error Description ErrorCode.AppErrorDesc = "" 'Clear the Application Specific Description ErrorCode.CodeModule = CALLSTK$(2) 'Figure out what function was the 'bad puppy' that caused the error '*** Revision check for if capable of logging the line Number that the error occurred on, or not SELECT CASE ErrorCode.LineLabelNumber CASE "" SELECT CASE %PB_REVISION CASE > = &H900 ErrorCode.LineLabelNumber = ERL$ CASE < &H900 ErrorCode.LineLabelNumber = "" END SELECT CASE ELSE END SELECT '*** Check if logging errors (Commented out TRACE commands until fixed) SELECT CASE ErrorCode.LogError CASE %FALSE CASE %TRUE TRACE ON ErrorCode.Desc = "" ErrorCode.Desc = "Error PB = " + TRIM$(STR$(ErrorCode.PbErrorValue)) ErrorCode.Desc = ErrorCode.Desc + SPACE$(5) ErrorCode.Desc = ErrorCode.Desc + "Desc = " + ErrorCode.PbErrorDesc REPLACE $CR WITH "" IN ErrorCode.Desc IF ErrorCode.LogError THEN PRINT# ErrorCode.ErrorLogNumber, ErrorCode.Desc ErrorCode.Desc = "" ErrorCode.Desc = "Error Windows = " + TRIM$(STR$(ErrorCode.WinErrorValue)) ErrorCode.Desc = ErrorCode.Desc + SPACE$(5) ErrorCode.Desc = ErrorCode.Desc + "Desc = " + ErrorCode.WinErrorDesc REPLACE $CR WITH "" IN ErrorCode.Desc IF ErrorCode.LogError THEN PRINT# ErrorCode.ErrorLogNumber, ErrorCode.Desc SELECT CASE ErrorCode.PbErrorValue CASE 0 SELECT CASE ErrorCode.WinErrorValue CASE 0 ErrorCode.AppErrorValue = 0 ErrorCode.AppErrorDesc = "The operation completed successfully CASE ELSE ErrorCode.AppErrorValue = ErrorCode.WinErrorValue ErrorCode.AppErrorDesc = ErrorCode.WinErrorDesc END SELECT CASE ELSE ErrorCode.AppErrorValue = ErrorCode.PbErrorValue ErrorCode.AppErrorDesc = ErrorCode.PbErrorDesc END SELECT ErrorCode.Desc = "" ErrorCode.Desc = "Error App = " + TRIM$(STR$(ErrorCode.AppErrorValue)) ErrorCode.Desc = ErrorCode.Desc + SPACE$(5) ErrorCode.Desc = ErrorCode.Desc + "Desc = " + ErrorCode.AppErrorDesc REPLACE $CR WITH "" IN ErrorCode.Desc 'TRACE PRINT ErrorCode.Desc IF ErrorCode.LogError THEN PRINT# ErrorCode.ErrorLogNumber, ErrorCode.Desc ErrorCode.Desc = "" ErrorCode.Desc = ErrorCode.Desc + SPACE$(5) SELECT CASE ErrorCode.LineLabelNumber CASE "" ErrorCode.Desc = ErrorCode.Desc + " Function: " + SPACE$(5) + ErrorCode.CodeModule CASE ELSE ErrorCode.Desc = ErrorCode.Desc + " Function: " + SPACE$(5) + ErrorCode.CodeModule + SPACE$(5) + "at Mile-Marker:" + SPACE$(5) + ErrorCode.LineLabelNumber + $CRLF END SELECT REPLACE $CR WITH "" IN ErrorCode.Desc REPLACE $LF WITH "" IN ErrorCode.Desc IF ErrorCode.LogError THEN PRINT# ErrorCode.ErrorLogNumber, ErrorCode.Desc END SELECT IF ErrorCode.LogError THEN PRINT# ErrorCode.ErrorLogNumber, $CRLF END FUNCTION '************************************************************************************************************* '*** ExceptionHandler is MUCH more difficult. (and Difficult to understand the documentation) '************************************************************************************************************* '*** <--- MEGA-IMPORTANT!!!! '*** According to http://www.debuginfo.com/articles/debugfilters.html '*** Custom filters for unhandled exceptions are not called at all when the application is running under debugger. '*** So this will not be called if using debugger '*** '************************************************************************************************************* FUNCTION ExceptionHandler(BYREF lpEP AS EXCEPTION_POINTERS) AS LONG STATIC TerminateInProcess AS LONG LOCAL ErrorRecord AS EXCEPTION_RECORD POINTER LOCAL ErrorCode AS ErrorCodes POINTER LOCAL TempErrorCode AS LONG LOCAL Continuable AS LONG STATIC MostRecentError AS LONG LOCAL i AS LONG SetGetErrorCodes TempErrorCode, TempErrorCode, %UNKNOWN_VALUE 'Since Windows NEEDS 'lpEP AS EXCEPTION_POINTERS' and only accepts 1 parameter, I had to find a work-around to Exceptions only wanting Exception Pointers ErrorCode = TempErrorCode SELECT CASE @ErrorCode.LineLabelNumber CASE "" SELECT CASE %PB_REVISION CASE > = &H900 @ErrorCode.LineLabelNumber = ERL$ CASE < &H900 @ErrorCode.LineLabelNumber = "" END SELECT CASE ELSE END SELECT @ErrorCode.lpEP = VARPTR(lpEP) SELECT CASE TerminateInProcess 'If App is unloading and a Error appears '<--- To be developed later CASE %TRUE CASE %FALSE 'If App is unloading and a Error appears '<--- To be developed later SELECT CASE @[email protected] 'What Error caused me? CASE 0 CASE ELSE ErrorRecord = @[email protected] 'Detect the actual exception record SELECT CASE @ErrorRecord.pExceptionRecord '<--- Array out of bounds if not checked for null pointer first CASE 0 CASE ELSE DO UNTIL @ErrorRecord.pExceptionRecord = 0 'Gather the exception record(s) CALL MoveMemory(@ErrorRecord, @ErrorRecord.pExceptionRecord, SIZEOF(ErrorRecord)) LOOP END SELECT '*** Alert the log to 'FATAL' Errors that could occur @ErrorCode.Desc = "" SELECT CASE @ErrorRecord.ExceptionCode CASE 0 'No Error @ErrorCode.StringValue = "%NO_ERROR" @ErrorCode.Desc = @ErrorCode.Desc _ + "Error Windows = " + @ErrorCode.StringValue _ + " (" + STR$(@ErrorRecord.ExceptionCode) + ") "_ + $CRLF + SPACE$(5) _ + " Desc = " + "The operation completed successfully" CASE %EXCEPTION_ACCESS_VIOLATION @ErrorCode.StringValue = "%EXCEPTION_ACCESS_VIOLATION" @ErrorCode.Desc = @ErrorCode.Desc _ + "Error Windows = " + @ErrorCode.StringValue _ + " (" + STR$(@ErrorRecord.ExceptionCode) + ") "_ + $CRLF + SPACE$(5) SELECT CASE @ErrorRecord.ExceptionInformation(0) CASE 0 @ErrorCode.Desc = @ErrorCode.Desc + " Desc = " + "The thread tried to READ from virtual address" _ + STR$(@ErrorRecord.ExceptionInformation(1)) + " for which it does not have the appropriate access" CASE 1 @ErrorCode.Desc = @ErrorCode.Desc + " Desc = " + "The thread tried to WRITE to virtual address" _ + STR$(@ErrorRecord.ExceptionInformation(1)) + " for which it does not have the appropriate access" CASE 8 @ErrorCode.Desc = @ErrorCode.Desc + " Desc = " + "The thread tried to cause a user-mode data execution prevention (DEP) violation at virtual address" _ + STR$(@ErrorRecord.ExceptionInformation(1)) END SELECT CASE %EXCEPTION_ARRAY_BOUNDS_EXCEEDED @ErrorCode.StringValue = "%EXCEPTION_ARRAY_BOUNDS_EXCEEDED" @ErrorCode.Desc = @ErrorCode.Desc _ + "Error Windows = " + @ErrorCode.StringValue _ + " (" + STR$(@ErrorRecord.ExceptionCode) + ") "_ + $CRLF + SPACE$(5) _ + " Desc = " + "The thread tried to access an array element that is out of bounds and the underlying hardware supports bounds checking." CASE %EXCEPTION_BREAKPOINT @ErrorCode.StringValue = "%EXCEPTION_BREAKPOINT" @ErrorCode.Desc = @ErrorCode.Desc _ + "Error Windows = " + @ErrorCode.StringValue _ + " (" + STR$(@ErrorRecord.ExceptionCode) + ") "_ + $CRLF + SPACE$(5) _ + " Desc = " + "A breakpoint was encountered." CASE %EXCEPTION_DATATYPE_MISALIGNMENT @ErrorCode.StringValue = "%EXCEPTION_DATATYPE_MISALIGNMENT" @ErrorCode.Desc = @ErrorCode.Desc _ + "Error Windows = " + @ErrorCode.StringValue _ + " (" + STR$(@ErrorRecord.ExceptionCode) + ") "_ + $CRLF + SPACE$(5) _ + " Desc = " + "The thread tried to READ or WRITE data that is misaligned on hardware that does not provide alignment." _ + "For example, 16-bit values must be aligned on 2-byte boundaries" CASE %EXCEPTION_FLT_DENORMAL_OPERAND @ErrorCode.StringValue = "%EXCEPTION_FLT_DENORMAL_OPERAND" @ErrorCode.Desc = @ErrorCode.Desc _ + "Error Windows = " + @ErrorCode.StringValue _ + " (" + STR$(@ErrorRecord.ExceptionCode) + ") "_ + $CRLF + SPACE$(5) _ + " Desc = " + "One of the operands in a floating-point operation is denormal." _ + "A denormal value is one that is too small to represent as a standard floating-point value." CASE %EXCEPTION_FLT_DIVIDE_BY_ZERO @ErrorCode.StringValue = "%EXCEPTION_FLT_DIVIDE_BY_ZERO" @ErrorCode.Desc = @ErrorCode.Desc _ + "Error Windows = " + @ErrorCode.StringValue _ + " (" + STR$(@ErrorRecord.ExceptionCode) + ") "_ + $CRLF + SPACE$(5) _ + " Desc = " + "The thread tried to divide a floating-point value by a floating-point divisor of zero." CASE %EXCEPTION_FLT_INEXACT_RESULT @ErrorCode.StringValue = "%EXCEPTION_FLT_INEXACT_RESULT" @ErrorCode.Desc = @ErrorCode.Desc _ + "Error Windows = " + @ErrorCode.StringValue _ + " (" + STR$(@ErrorRecord.ExceptionCode) + ") "_ + $CRLF + SPACE$(5) _ + " Desc = " + "The result of a floating-point operation cannot be represented exactly as a decimal fraction." CASE %EXCEPTION_FLT_INVALID_OPERATION @ErrorCode.StringValue = "%EXCEPTION_FLT_INVALID_OPERATION" @ErrorCode.Desc = @ErrorCode.Desc _ + "Error Windows = " + @ErrorCode.StringValue _ + " (" + STR$(@ErrorRecord.ExceptionCode) + ") "_ + $CRLF + SPACE$(5) _ + " Desc = " + "This exception represents any floating-point exception not included in this list." CASE %EXCEPTION_FLT_OVERFLOW @ErrorCode.StringValue = "%EXCEPTION_FLT_OVERFLOW" @ErrorCode.Desc = @ErrorCode.Desc _ + "Error Windows = " + @ErrorCode.StringValue _ + " (" + STR$(@ErrorRecord.ExceptionCode) + ") "_ + $CRLF + SPACE$(5) _ + " Desc = " + "The exponent of a floating-point operation is greater than the magnitude allowed by the corresponding type." CASE %EXCEPTION_FLT_STACK_CHECK @ErrorCode.StringValue = "%EXCEPTION_FLT_STACK_CHECK" @ErrorCode.Desc = @ErrorCode.Desc _ + "Error Windows = " + @ErrorCode.StringValue _ + " (" + STR$(@ErrorRecord.ExceptionCode) + ") "_ + $CRLF + SPACE$(5) _ + " Desc = " + "The stack overflowed or underflowed as the result of a floating-point operation." CASE %EXCEPTION_FLT_UNDERFLOW @ErrorCode.StringValue = "%EXCEPTION_FLT_UNDERFLOW" @ErrorCode.Desc = @ErrorCode.Desc _ + "Error Windows = " + @ErrorCode.StringValue _ + " (" + STR$(@ErrorRecord.ExceptionCode) + ") "_ + $CRLF + SPACE$(5) _ + " Desc = " + "The exponent of a floating-point operation is less than the magnitude allowed by the corresponding type." CASE %EXCEPTION_ILLEGAL_INSTRUCTION @ErrorCode.StringValue = "%EXCEPTION_ILLEGAL_INSTRUCTION" @ErrorCode.Desc = @ErrorCode.Desc _ + "Error Windows = " + @ErrorCode.StringValue _ + " (" + STR$(@ErrorRecord.ExceptionCode) + ") "_ + $CRLF + SPACE$(5) _ + " Desc = " + "The thread tried to execute an invalid instruction." CASE %EXCEPTION_IN_PAGE_ERROR @ErrorCode.StringValue = "%EXCEPTION_IN_PAGE_ERROR" @ErrorCode.Desc = @ErrorCode.Desc _ + "Error Windows = " + @ErrorCode.StringValue _ + " (" + STR$(@ErrorRecord.ExceptionCode) + ") "_ + $CRLF + SPACE$(5) SELECT CASE @ErrorRecord.ExceptionInformation(0) CASE 0 @ErrorCode.Desc = @ErrorCode.Desc + " Desc = " + "The thread tried to READ from virtual address" _ + STR$(@ErrorRecord.ExceptionInformation(1)) + " for which it does not have the appropriate access" _ + $CRLF + "NTSTATUS = " + STR$(@ErrorRecord.ExceptionInformation(2)) CASE 1 @ErrorCode.Desc = @ErrorCode.Desc + " Desc = " + "The thread tried to WRITE to virtual address" _ + STR$(@ErrorRecord.ExceptionInformation(1)) + " for which it does not have the appropriate access" _ + $CRLF + "NTSTATUS = " + STR$(@ErrorRecord.ExceptionInformation(2)) CASE 8 @ErrorCode.Desc = @ErrorCode.Desc + " Desc = " + "The thread tried to cause a user-mode data execution prevention (DEP) violation at virtual address" _ + STR$(@ErrorRecord.ExceptionInformation(1)) _ + $CRLF + "NTSTATUS = " + STR$(@ErrorRecord.ExceptionInformation(2)) END SELECT CASE %EXCEPTION_INT_DIVIDE_BY_ZERO @ErrorCode.StringValue = "%EXCEPTION_INT_DIVIDE_BY_ZERO" @ErrorCode.Desc = @ErrorCode.Desc _ + "Error Windows = " + @ErrorCode.StringValue _ + " (" + STR$(@ErrorRecord.ExceptionCode) + ") "_ + $CRLF + SPACE$(5) _ + " Desc = " + "The thread tried to divide an integer value by an integer divisor of zero." CASE %EXCEPTION_INT_OVERFLOW @ErrorCode.StringValue = "%EXCEPTION_INT_OVERFLOW" @ErrorCode.Desc = @ErrorCode.Desc _ + "Error Windows = " + @ErrorCode.StringValue _ + " (" + STR$(@ErrorRecord.ExceptionCode) + ") "_ + $CRLF + SPACE$(5) _ + " Desc = " + "The result of an integer operation caused a carry out of the most significant bit of the result." CASE %EXCEPTION_INVALID_DISPOSITION @ErrorCode.StringValue = "%EXCEPTION_INVALID_DISPOSITION" @ErrorCode.Desc = @ErrorCode.Desc _ + "Error Windows = " + @ErrorCode.StringValue _ + " (" + STR$(@ErrorRecord.ExceptionCode) + ") "_ + $CRLF + SPACE$(5) _ + " Desc = " + "An exception handler returned an invalid disposition to the exception dispatcher. " _ + "Programmers using a high-level language such as C should never encounter this exception." CASE %EXCEPTION_NONCONTINUABLE_EXCEPTION @ErrorCode.StringValue = "%EXCEPTION_NONCONTINUABLE_EXCEPTION" @ErrorCode.Desc = @ErrorCode.Desc _ + "Error Windows = " + @ErrorCode.StringValue _ + " (" + STR$(@ErrorRecord.ExceptionCode) + ") "_ + $CRLF + SPACE$(5) _ + " Desc = " + "The thread tried to continue execution after a noncontinuable exception occurred." CASE %EXCEPTION_PRIV_INSTRUCTION @ErrorCode.StringValue = "%EXCEPTION_PRIV_INSTRUCTION" @ErrorCode.Desc = @ErrorCode.Desc _ + "Error Windows = " + @ErrorCode.StringValue _ + " (" + STR$(@ErrorRecord.ExceptionCode) + ") "_ + $CRLF + SPACE$(5) _ + " Desc = " + "The thread tried to execute an instruction whose operation is not allowed in the current machine mode." CASE %EXCEPTION_SINGLE_STEP @ErrorCode.StringValue = "%EXCEPTION_SINGLE_STEP" @ErrorCode.Desc = @ErrorCode.Desc _ + "Error Windows = " + @ErrorCode.StringValue _ + " (" + STR$(@ErrorRecord.ExceptionCode) + ") "_ + $CRLF + SPACE$(5) _ + " Desc = " + "A trace trap or other single-instruction mechanism signaled that one instruction has been executed." CASE %EXCEPTION_STACK_OVERFLOW @ErrorCode.StringValue = "%EXCEPTION_STACK_OVERFLOW" @ErrorCode.Desc = @ErrorCode.Desc _ + "Error Windows = " + @ErrorCode.StringValue _ + " (" + STR$(@ErrorRecord.ExceptionCode) + ") "_ + $CRLF + SPACE$(5) _ + " Desc = " + "The thread used up its stack." CASE ELSE @ErrorCode.StringValue = "%Unhandled Error: " @ErrorCode.Desc = @ErrorCode.Desc _ + "Error Windows = " + @ErrorCode.StringValue + STR$(@ErrorRecord.ExceptionCode) END SELECT ' 'TRACE PRINT ErrorCode.Desc IF @ErrorCode.LogError THEN PRINT# @ErrorCode.ErrorLogNumber, @ErrorCode.Desc SELECT CASE @ErrorCode.LineLabelNumber CASE "" IF @ErrorCode.LogError THEN PRINT# @ErrorCode.ErrorLogNumber, SPACE$(5) + "Function: " + SPACE$(5) + CALLSTK$(2) CASE ELSE PRINT# @ErrorCode.ErrorLogNumber, "Function: " + SPACE$(5) + CALLSTK$(2) + SPACE$(5) + "at Mile-Marker:" + SPACE$(5) + @ErrorCode.LineLabelNumber + $CRLF END SELECT @ErrorCode.Desc = "CallStack at Error" + $CRLF FOR i = CALLSTKCOUNT TO 1 STEP -1 @ErrorCode.Desc = @ErrorCode.Desc + SPACE$(5) + CALLSTK$(i) + $CRLF NEXT i IF @ErrorCode.LogError THEN PRINT# @ErrorCode.ErrorLogNumber, @ErrorCode.Desc IF @ErrorCode.LogError THEN PRINT# @ErrorCode.ErrorLogNumber, $CRLF END SELECT '*** Code for writing a crash-log, save opened documents etc... '*** In My case, this is where I decide whether to continue, or exit '>>> 1st attempt to RESUME NEXT I thought would be the following line to Resume Execution after the Exception '>>> [email protected] = [email protected] + SIZEOF([email protected]) '>>> Lo and Behold thanx to Paul Dixon's Post at http://www.powerbasic.com/support/pbforums/showthread.php?t=40575#post315530 '>>> Led me to his post in the source code forum at http://www.powerbasic.com/support/pbforums/showthread.php?t=37821 '>>> Although it is for PBCC and using Assembly code, '>>> I did some hunting and pecking and figured out that [email protected] = [email protected] + SIZEOF([email protected]) '>>> Is the flag I need to set to 'Resume Next' (or any address I wish to jump to SELECT CASE @ErrorCode.OverRideError CASE %RESUME FUNCTION = %EXCEPTION_CONTINUE_EXECUTION 'Ignore the Error and continue CASE %RESUMENEXT SELECT CASE IsBadCodePtr([email protected] + 1) 'SIZEOF([email protected]) CASE 0 'Not an error [email protected] = [email protected] + 1 FUNCTION = %EXCEPTION_CONTINUE_EXECUTION 'Ignore the Error and continue (Probably CRASH) CASE ELSE SELECT CASE IsBadCodePtr([email protected]) CASE 0 FUNCTION = %EXCEPTION_CONTINUE_EXECUTION 'Ignore the Error and continue (Probably CRASH) CASE ELSE FUNCTION = %EXCEPTION_EXECUTE_HANDLER 'See if another handler can fix, or crash anyways END SELECT END SELECT CASE %UNKNOWN_VALUE 'Decide what to do after an error Continuable = MSGBOX("Fatal Error " + SPACE$(5) + @ErrorCode.StringValue + SPACE$(5) + "(" + STR$(@ErrorRecord.ExceptionCode) + ")" _ + $CR + "Function: " + SPACE$(5) + CALLSTK$(2) + $CR + $CR + "Ignore this error?", %MB_ICONERROR OR %MB_YESNO, FUNCNAME$ + SPACE$(5) + "Windows Error") SELECT CASE Continuable CASE %IDYES '**************************************** TEMPORARY TILL NEXT VERSION **************************************** LOCAL SafeJumpPoint AS LONG SetGetSafeJump(%UNKNOWN_VALUE, SafeJumpPoint, %UNKNOWN_VALUE) [email protected] = SafeJumpPoint FUNCTION = %EXCEPTION_CONTINUE_EXECUTION 'Ignore the Error and continue (Probably CRASH) EXIT FUNCTION '************************************************************************************************************* FUNCTION = %EXCEPTION_CONTINUE_EXECUTION 'Ignore the Error and continue (Probably CRASH) CASE %IDNO FUNCTION = %EXCEPTION_EXECUTE_HANDLER END SELECT CASE %NORESUME 'Let Windows handle the error FUNCTION = %EXCEPTION_EXECUTE_HANDLER CASE %CRASH FUNCTION = %EXCEPTION_CONTINUE_SEARCH 'Similar to Let Windows handle the error, but if Windows can fix it, it will and then continue END SELECT MostRecentError = @ErrorRecord.ExceptionCode END SELECT END FUNCTION #ENDIF
Code:
#DEBUG ERROR ON 'Set Compiler flag to not forgive me for errors like "Array out of Bounds" #COMPILE EXE 'Compile as executable #DIM ALL 'Declare EVERYTHING!!! (do not let me get away with anything) #INCLUDE "Win32Api.inc" 'Include WinApi #INCLUDE "ErrorHandling.inc" 'Include ErrorHandling '*** DECLARES (because I want to know everything is declared and I did not miss something DECLARE FUNCTION WINMAIN(BYVAL hInstance AS DWORD, BYVAL hPrevInst AS DWORD, BYVAL lpszCmdLine AS ASCIIZ PTR, BYVAL nCmdShow AS LONG) AS LONG DECLARE FUNCTION DemoErrors ALIAS "DemoErrors"() AS LONG '*** Functions FUNCTION WINMAIN(BYVAL hInstance AS DWORD, BYVAL hPrevInst AS DWORD, BYVAL lpszCmdLine AS ASCIIZ PTR, BYVAL nCmdShow AS LONG) AS LONG DemoErrors END FUNCTION FUNCTION DemoErrors ALIAS "DemoErrors"() EXPORT AS LONG OnError 'Macro to turn on Error Handling / Logging 'Macro to turn on Error Handling / Logging SetGetLogErrors %TRUE, ErrorCode.LogError, %TRUE 'Set flag for logging errors '*** PowerBasic Errors DemoPbError 'Demo how PB Set Error code does not necessarily "RAISE" an Error Code DemoRaisePbError 'Demo how PB will "RAISE" an Error Code '*** Windows Errors DemoWinError 'Demo how Windows Set Error code does not necessarily "RAISE" an Error Code '*** Windows EXCEPTION ERRORS (The almighty "Windows has encountered an error in your app and has to close") DemoRaiseWinError 'Demo how Windows will "RAISE" an Error Code '*** Array Out of Bounds DemoArrayOutOfBounds 'Demo Array Out Of Bounds '*** UnderFlow (registers used for the calculation cannot hold the value) DemoUnderflow 'Demo "UnderFlow" (value too small for your data types) '*** OverFlow (Division by Zero) DemoOverflowDivisionByZero 'Demo "OverFlow" (divide by zero) errors '*** CRASH!!!! (GPF) DemoGPF 'Demo the typical "Had an Error and has to close" that we are all aware of MSGBOX "Program Complete" 'Should NEVER get here because of GPF SetGetLogErrors %FALSE, ErrorCode.LogError, %TRUE 'Set flag for logging errors HandleErrors 'Macro to turn off Error Handling / Logging END FUNCTION '*** PB ERR - Error flag is set, but error is NOT raised, '*** So you have to check the value of ERR to see if there was an error FUNCTION DemoPbError()AS LONG OnError 'Macro to turn on Error Handling / Logging PRINT# ErrorCode.ErrorLogNumber, STRING$(40, "-") + SPACE$(5) + FUNCNAME$ + SPACE$(5) + STRING$(40, "-") DebugLine 'Maro to insert line number (Disperce anywhere you think you want a landmark to home in on an error) ERR = 69 'Set Error Code (Error is not raised) PRINT# ErrorCode.ErrorLogNumber, STRING$(3, "*") + " <--- " + "ERR does NOT raise an error, it just sets a flag" + " ---> "+ STRING$(3, "*") 'Notes to print to Log File PRINT# ErrorCode.ErrorLogNumber, STRING$(40, "-") + SPACE$(5) + "END " + FUNCNAME$ + SPACE$(5) + STRING$(40, "-") + $CRLF + $CRLF HandleErrors 'Macro to turn off Error Handling / Logging END FUNCTION '*** PB ERROR - Error flag is set, and error is raised, FUNCTION DemoRaisePbError()AS LONG OnError 'Macro to turn on Error Handling / Logging ErrorCode.OverRideError = %UNKNOWN_VALUE PRINT# ErrorCode.ErrorLogNumber, STRING$(40, "-") + SPACE$(5) + FUNCNAME$ + SPACE$(5) + STRING$(40, "-") DebugLine ERROR 69 'Set Error Code (Error is raised, jump to error handler) PRINT# ErrorCode.ErrorLogNumber, STRING$(3, "*") + " <--- " + "ERROR raises an error, and sets a flag" + " ---> "+ STRING$(3, "*") 'Notes to print to Log File PRINT# ErrorCode.ErrorLogNumber, STRING$(40, "-") + SPACE$(5) + "END " + FUNCNAME$ + SPACE$(5) + STRING$(40, "-") + $CRLF + $CRLF HandleErrors 'Macro to turn off Error Handling / Logging END FUNCTION FUNCTION DemoWinError()AS LONG OnError 'Macro to turn on Error Handling / Logging ErrorCode.OverRideError = %RESUMENEXT PRINT# ErrorCode.ErrorLogNumber, STRING$(40, "-") + SPACE$(5) + FUNCNAME$ + SPACE$(5) + STRING$(40, "-") DebugLine 'Maro to insert line number (Disperce anywhere you think you want a landmark to home in on an error) SetLastError 5 PRINT# ErrorCode.ErrorLogNumber, STRING$(3, "*") + " <--- " + "SetLastError does NOT raise an error, it just merely setting a flag" + " ---> "+ STRING$(3, "*") 'Notes to print to Log File SetLastErrorEx(69, %SLE_MINORERROR) PRINT# ErrorCode.ErrorLogNumber, STRING$(3, "*") + " <--- " + "SetLastErrorEx does NOT raise an error, it just merely setting a flag" + " ---> "+ STRING$(3, "*") 'Notes to print to Log File PRINT# ErrorCode.ErrorLogNumber, STRING$(40, "-") + SPACE$(5) + "END " + FUNCNAME$ + SPACE$(5) + STRING$(40, "-") + $CRLF + $CRLF HandleErrors 'Macro to turn off Error Handling / Logging END FUNCTION FUNCTION DemoRaiseWinError()AS LONG OnError 'Macro to turn on Error Handling / Logging ErrorCode.OverRideError = %RESUMENEXT PRINT# ErrorCode.ErrorLogNumber, STRING$(40, "-") + SPACE$(5) + FUNCNAME$ + SPACE$(5) + STRING$(40, "-") DebugLine RaiseException %NULL, %NULL, %NULL, %NULL 'Bogus Fatal Error for Demo (or not fatal since I have control) DebugLine PRINT# ErrorCode.ErrorLogNumber, STRING$(3, "*") + " <--- " + "Continued from 1st error that was raised" + " ---> "+ STRING$(3, "*") 'Notes to print to Log File '*** Did this twice just to show how you can continue after a Crash (GPF) DebugLine RaiseException %EXCEPTION_ACCESS_VIOLATION, %NULL, %NULL, %NULL 'Bogus Fatal Error for Demo (or not fatal since I have control) DebugLine PRINT# ErrorCode.ErrorLogNumber, STRING$(3, "*") + " <--- " + "Continued from 2nd error that was raised" + " ---> "+ STRING$(3, "*") 'Notes to print to Log File PRINT# ErrorCode.ErrorLogNumber, STRING$(40, "-") + SPACE$(5) + "END " + FUNCNAME$ + SPACE$(5) + STRING$(40, "-") + $CRLF + $CRLF HandleErrors 'Macro to turn off Error Handling / Logging END FUNCTION FUNCTION DemoArrayOutOfBounds()AS LONG OnError 'Macro to turn on Error Handling / Logging ErrorCode.OverRideError = %UNKNOWN_VALUE DIM MyArrayOutOfBounds(0) AS LONG PRINT# ErrorCode.ErrorLogNumber, STRING$(40, "-") + SPACE$(5) + FUNCNAME$ + SPACE$(5) + STRING$(40, "-") DebugLine MyArrayOutOfBounds(1) = 5 DebugLine PRINT# ErrorCode.ErrorLogNumber, STRING$(3, "*") + " <--- " + "Continued from 1st error that was raised" + " ---> "+ STRING$(3, "*") 'Notes to print to Log File DebugLine PRINT# ErrorCode.ErrorLogNumber, STRING$(40, "-") + SPACE$(5) + "END " + FUNCNAME$ + SPACE$(5) + STRING$(40, "-") + $CRLF + $CRLF DebugLine HandleErrors 'Macro to turn off Error Handling / Logging END FUNCTION FUNCTION DemoOverflowDivisionByZero()AS LONG OnError 'Macro to turn on Error Handling / Logging SetGetLogErrors %UNKNOWN_VALUE, ErrorCode.LogError, %UNKNOWN_VALUE 'Get flag for logging errors ErrorCode.OverRideError = %UNKNOWN_VALUE 'Continue after an error? '<--- Do NOTTTTT change this to %RESUME or you will be stuck in a logging loop LOCAL MyOverFlow AS LONG PRINT# ErrorCode.ErrorLogNumber, STRING$(40, "-") + SPACE$(5) + FUNCNAME$ + SPACE$(5) + STRING$(40, "-") MyOverFlow = 4 \ 0 DebugLine PRINT# ErrorCode.ErrorLogNumber, STRING$(3, "*") + " <--- " + "MyOverFlow = 4 \ 0 Results in Pb protecting me (no error)" + " ---> "+ STRING$(3, "*") 'Notes to print to Log File LOCAL a,b,c AS LONG LOCAL x,y,z AS CURRENCY DebugLine 'Maro to insert line number (Disperce anywhere you think you want a landmark to home in on an error) x=0.0 y=1.0 z=y/x 'Floating Point Division (/) by zero, I am protected by PB DebugLine PRINT# ErrorCode.ErrorLogNumber, STRING$(3, "*") + " <--- " + "Floating Point DivByZero: z = y/x Results in PB protecting me (no error)" + " ---> "+ STRING$(3, "*") 'Notes to print to Log File PRINT# ErrorCode.ErrorLogNumber, $LF a=0 b=1 c=b\a 'Integer Division (\) by zero (error) '<--- To be investigated DebugLine PRINT# ErrorCode.ErrorLogNumber, STRING$(3, "*") + " <--- " + "Integer DivByZero: c = b\a Results in allowing an error" + " ---> "+ STRING$(3, "*") 'Notes to print to Log File PRINT# ErrorCode.ErrorLogNumber, STRING$(40, "-") + SPACE$(5) + "END " + FUNCNAME$ + SPACE$(5) + STRING$(40, "-") + $CRLF + $CRLF HandleErrors 'Macro to turn off Error Handling / Logging END FUNCTION FUNCTION DemoUnderflow()AS LONG OnError 'Macro to turn on Error Handling / Logging SetGetLogErrors %UNKNOWN_VALUE, ErrorCode.LogError, %UNKNOWN_VALUE 'Get flag for logging errors ErrorCode.OverRideError = %UNKNOWN_VALUE 'Continue after an error? '<--- Do NOTTTTT change this to %RESUME or you will be stuck in a logging loop LOCAL B AS BYTE PRINT# ErrorCode.ErrorLogNumber, STRING$(40, "-") + SPACE$(5) + FUNCNAME$ + SPACE$(5) + STRING$(40, "-") DO WHILE B < 256 B = B + 1 IF B + 1 = 256 THEN EXIT DO LOOP DebugLine PRINT# ErrorCode.ErrorLogNumber, STRING$(3, "*") + " <--- " + "B + 1 to overflow, PB protecting me (no error)" + " ---> "+ STRING$(3, "*") 'Notes to print to Log File B = 255 + 1 DebugLine PRINT# ErrorCode.ErrorLogNumber, STRING$(3, "*") + " <--- " + "B + 1 to overflow, PB protecting me (no error)" + " ---> "+ STRING$(3, "*") 'Notes to print to Log File DebugLine PRINT# ErrorCode.ErrorLogNumber, STRING$(3, "*") + " <--- " + "B = 255 + 1 to overflow, PB protecting me (no error)" + " ---> "+ STRING$(3, "*") 'Notes to print to Log File PRINT# ErrorCode.ErrorLogNumber, STRING$(40, "-") + SPACE$(5) + "END " + FUNCNAME$ + SPACE$(5) + STRING$(40, "-") + $CRLF + $CRLF HandleErrors 'Macro to turn off Error Handling / Logging END FUNCTION FUNCTION DemoGPF()AS LONG OnError 'Macro to turn on Error Handling / Logging SetGetLogErrors %UNKNOWN_VALUE, ErrorCode.LogError, %UNKNOWN_VALUE 'Get flag for logging errors '*** Let Windows Crash MSGBOX "I will now perform a fatal error, and not recover from it", %MB_ICONINFORMATION, "Fatal Error" ErrorCode.OverRideError = %CRASH PRINT# ErrorCode.ErrorLogNumber, STRING$(40, "-") + SPACE$(5) + FUNCNAME$ + SPACE$(5) + STRING$(40, "-") RaiseException %NULL, %NULL, %NULL, %NULL 'Bogus Fatal Error for Demo (or not fatal since I have control) PRINT# ErrorCode.ErrorLogNumber, "I continued from a crash" 'Should never get here PRINT# ErrorCode.ErrorLogNumber, STRING$(40, "-") + SPACE$(5) + "END " + FUNCNAME$ + SPACE$(5) + STRING$(40, "-") + $CRLF + $CRLF HandleErrors 'Macro to turn off Error Handling / Logging END FUNCTION
Discussion can be found here