Announcement

Collapse
No announcement yet.

de-bugging code that uses %EM_GETSEL

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

  • de-bugging code that uses %EM_GETSEL

    Hi, I'm sure there's a win32api expert out there who can fix the bug in my code. Simply stated, when the user highlights (selects) a portion of text in a TEXTBOX, then clicks a button, I want to be able to isolate the highlighted text. I know it's GetSel(), but I can't figure out the exact implementation. Here's what I got so far....
    Thanks, Christopher

    Code:
    #COMPILER PBWIN
    #INCLUDE "Win32api.inc"
    
    CALLBACK FUNCTION GetSelButton()
          DIM lResult AS DWORD
          DIM x AS DWORD PTR
          DIM y AS DWORD PTR
          DIM FullText AS STRING
          CONTROL GET TEXT CBHNDL, 500 TO FullText
          lResult = SendMessage(500, %EM_GETSEL, x, y)
          MSGBOX "selected text is: " + MID$(FullText, @x, @x - @y)
    END FUNCTION
    
    CALLBACK FUNCTION ExitButton()
        DIALOG END CBHNDL, 0
    END FUNCTION
    
    FUNCTION PBMAIN () AS LONG
        DIM hDlg AS DWORD
        DIM lResult AS DWORD
        DIALOG NEW PIXELS,0,"GetSel() example",,,200,150,0,0 TO hDlg
        CONTROL ADD TEXTBOX,hDlg,500,"",30,30,150,50,0
        CONTROL ADD BUTTON,hDlg,501,"Exit",150,110,40,20,0 CALL ExitButton
        CONTROL ADD BUTTON,hDlg,502,"Show Selected Text",20,110,120,20,0 CALL GetSelButton
        DIALOG SHOW MODAL hDlg
    END FUNCTION
    Christopher P. Becker
    signal engineer in the defense industry
    Abu Dhabi, United Arab Emirates

  • #2
    Hi Christopher,

    Several small problems here in fact. But first, here's a version that works:
    Code:
    #COMPILER PBWIN
    #INCLUDE "Win32api.inc"
    
    DECLARE FUNCTION SendMessage_ByRef LIB "user32" ALIAS "SendMessageA" (BYVAL hWnd AS DWORD, BYVAL dwMsg AS DWORD, wParam AS DWORD, lParam AS DWORD) AS LONG
    
    CALLBACK FUNCTION GetSelButton()
       DIM lResult AS DWORD
       DIM x AS DWORD
       DIM y AS DWORD
       DIM FullText AS STRING
       DIM hControl AS DWORD
    
       CONTROL GET TEXT CBHNDL, 500 TO FullText
    
       ' We need the control's handle, not its ID, so...
       CONTROL HANDLE CBHNDL, 500 TO hControl
    
       ' Method using original SendMessage declare in WIN32API.INC
       ' Change conditional compile to use other method
       #IF 1
          lResult = SendMessage(hControl, %EM_GETSEL, x, y)
          ' Returned values are zero-based, we need them one-based for MID$
          x = LO(WORD, lResult) + 1
          y = HI(WORD, lResult) + 1
          IF x = y THEN
             MSGBOX "No selection"
          ELSE
             MSGBOX "Using lResult, selected text is: " & MID$(FullText, x, y - x)
          END IF
       #ELSE
          ' Method using ByRef declaration of SendMessage, so we can get values back in wParam and lParam
          lResult = SendMessage_ByRef(hControl, %EM_GETSEL, x, y)
          IF x = y THEN
             MSGBOX "No selection"
          ELSE
             INCR x : INCR y
             MSGBOX "Using X, Y, selected text is: " & MID$(FullText, x, y - x)
          END IF
       #ENDIF
    END FUNCTION
    
    CALLBACK FUNCTION ExitButton()
       DIALOG END CBHNDL, 0
    END FUNCTION
    
    FUNCTION PBMAIN () AS LONG
       DIM hDlg AS DWORD
       DIM lResult AS DWORD
       DIALOG NEW PIXELS,0,"GetSel() example",,,200,150,0,0 TO hDlg
       CONTROL ADD TEXTBOX,hDlg,500,"ABCDEFGHIJKLMNOPQRSTUVWXYZ",30,30,150,50,0
       CONTROL ADD BUTTON,hDlg,501,"Exit",150,110,40,20,0 CALL ExitButton
       CONTROL ADD BUTTON,hDlg,502,"Show Selected Text",20,110,120,20,0 CALL GetSelButton
       DIALOG SHOW MODAL hDlg
    END FUNCTION
    First problem was, you were sending the message to the control's ID, instead of to its handle - hence the CONTROL HANDLE statement, which gets the one from the other.

    Second problem is that SendMessage is declared (in WIN32API.INC) to send wParam and lParam ByVal - so any changes to them won't get back to you. This means you have a choice - either use the result of the function, which contains the same values in its low and high words as wParam and lParam respectively, or, redeclare SendMessage so that it sends those parameters ByRef. Both methods are shown in the code.

    Third, the wParam and lParam aren't pointers when you get them back - they're just plain DWORDs.

    Lastly, you were subtracting the end from the start, rather than vice-versa.

    Isn't this API stuff fun??

    Hope that helps.

    Regards,

    Pete.

    Comment


    • #3
      Sticking with DDT..
      Code:
            DIM x AS DWORD
            DIM y AS DWORD
            DIM FullText AS STRING
            CONTROL GET TEXT CBHNDL, 500 TO FullText
            CONTROL SEND CBHNDL, 500, %EM_GETSEL, VARPTR(x), VARPTR(y)
            MSGBOX "selected text is: " + MID$(FullText, x+1, y - x)
      Rgds, Dave

      Comment


      • #4
        Pete, Dave,
        Thanks, those were the (fast) answers I was hoping for. I actually was trying to translate some VB/.NET stuff into PB/Win.
        Christopher
        Christopher P. Becker
        signal engineer in the defense industry
        Abu Dhabi, United Arab Emirates

        Comment


        • #5
          As Peter stated, basically you tried to access an invalid memory location.

          I added my error handler and numbered your code, and it boiled down to
          Trace Begins...
          Unhandled WindowsException - 3221225477 - ACCESS VIOLATION <---> The thread tried to read from or write to a virtual address for which it does not have the appropriate access
          Function = PBMAIN()
          GETSELBUTTON()
          TRACEEXCEPTIONHANDLER(<04>ö<12><00> ö<12><00>)
          at line: 0

          TRACEPRINT Exit
          Your original code and my error handler are as follows:
          Original Code (slightly modified):
          Code:
          #COMPILER PBWIN
          #TOOLS OFF
          #INCLUDE "Win32api.inc"
          #INCLUDE "ErrorHandling.inc"
          
          CALLBACK FUNCTION GetSelButton()
          20      DIM lResult AS DWORD
          21      DIM x AS DWORD PTR
          22      DIM y AS DWORD PTR
          23      DIM FullText AS STRING
          24      CONTROL GET TEXT CBHNDL, 500 TO FullText
          25      lResult = SendMessage(500, %EM_GETSEL, x, y)
          26      MSGBOX "selected text is: " + MID$(FullText, @x, @x - @y)
          END FUNCTION
          
          CALLBACK FUNCTION ExitButton()
          20    DIALOG END CBHNDL, 0
          END FUNCTION
          
          FUNCTION PBMAIN () AS LONG
               StartTrace
          20    DIM hDlg AS DWORD
          21    DIM lResult AS DWORD
          22    DIALOG NEW PIXELS,0,"GetSel() example",,,200,150,0,0 TO hDlg
          23    CONTROL ADD TEXTBOX,hDlg,500,"",30,30,150,50,0
          24    CONTROL ADD BUTTON,hDlg,501,"Exit",150,110,40,20,0 CALL ExitButton
          25    CONTROL ADD BUTTON,hDlg,502,"Show Selected Text",20,110,120,20,0 CALL GetSelButton
          26    DIALOG SHOW MODAL hDlg
               EndTrace
          END FUNCTION
          ErrorHandling.inc
          Code:
          '*** ErrorHandling.inc
          '***      Written by: Cliff Nichols - 08-28-2008
          '***      Modified by: Cliff Nichols - 09-18-2008
          '***      Compiler: 8.04/9.0
          '***      Should work on: 7/8/9
          '***      Tested on: 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
          '*** Usage:
          '***      StartTrace - Create a Trace log file
          '***      TraceVariables - Common Variables for each function
          '***
          '***      TraceUnhandled - Steps for anything that you did not think of when writing and testing your code
          '--------------------------------------------------------------------------------
          '*** Now for the FUN Stuff
          '***      PB has 'On Error' for handling errors on the DDT side
          '***           PB can raise an error just by ERR = some number
          '***      Windows has 'GetLastError' to check any errors via SDK
          '***           Windows has to check for errors and handle them (errors are not raised)
          '***      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
          '*** I REALLLLLLY want to know how to do the below without visibly inserting numbers but.....
          '***      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
          '***           Conjunction, junction, whats your function??? :-)
          '*** What makes it all work????
          '*** MACRO's baby, yeah baby yeah...(Finally got this one when larger code all had the same multiple commands and got tedious to type or copy/paste)
          '*** Globals.....Real Men don't need no stinkin Globals :-) (Finally got this one working deep with Windows API...See "Parameter as Reply" discussion)
          '--------------------------------------------------------------------------------
          
          '*** If not already declared in your code, then declare this INC
          #IF NOT %DEF(%ERRORHANDLING)
               %ERRORHANDLING = 1
          '*** Globals
          '***      NONE
          '***           - Use SetGetDebugTraceRunning to keep value in replacement for a global DebugTraceRunning
          '***           - Use SetGetLastFunction to keep value in replacement for a global LastFunction
          '*** Constants
               %UNKNOWN_VALUE = -1
          '*** Constants not found in Win32Api.Inc
               %EXCEPTION_ILLEGAL_INSTRUCTION  = &HC000001D
               %EXCEPTION_STACK_OVERFLOW       = &HC00000FD
          '*************************************************************************************************************
          '*** 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
          '*************************************************************************************************************
          '*** TraceUnhandled
          '***      Macro to replace code at the end of each function being being debugged
          '***           Code will jump to end ON ERROR or if told to jump to end
          '***           Code relies on TraceVariables to compile
          '*************************************************************************************************************
               MACRO TraceUnhandled
                    EXIT FUNCTION                                               'Exit to not Execute Function code again
               ErrHandler:
                    ErrFunc = GetLastError                                      'Get API Last Error
                    TraceLastError FUNCNAME$, ErrFunc, ERR, ERL
                    SetUnhandledExceptionFilter %NULL
               END MACRO
          '*************************************************************************************************************
          '*** TraceVariables
          '***      Macro to replace code at the begginning of each function being being debugged
          '***           Code sets up variables to jump to end ON ERROR or if told to jump to end
          '***           Code relies on TraceUnhandled to compile
          '*************************************************************************************************************
               MACRO TraceVariables
                    ON ERROR GOTO ErrHandler                                    'Avoid using error numbers higher than 240, as they are reserved for use in critical error situations which can never be trapped with ON ERROR.
                    LOCAL DebugTraceRunning AS LONG                             'Variable for if debugging
                    LOCAL LastErrorValue AS LONG                                'Variable for GetLastError
                    LOCAL PbErrorValue AS LONG                                  'Variable for PB ERR
                    LOCAL ErrorLineNumber AS LONG                               'Variable for line number where the error occured (or the last checked line)
                    LOCAL ErrFunc AS LONG                                       'Variable for if there was an error in the function
                    LOCAL ErrorBuff AS ASCIIZ * %MAX_PATH                       'Variable for Win32Api Error Description
                    LOCAL WhatToTrace AS STRING                                 'Variable for what to log for an error
                    SetLastError(0)                                             'Ensure the last Windows error is cleared
                    ERRCLEAR                                                    'Ensure the last PB error is cleared
          '          SetUnhandledExceptionFilter %EXCEPTION_EXECUTE_HANDLER      '<--- Use this exception handler if wanting to use windows default (the typical "*.exe has encountered a problem and needs to close" message)
                    SetUnhandledExceptionFilter CODEPTR (TraceExceptionHandler) '<--- Use this exception handler if wanting to make a better crash-proof program
               END MACRO
          '*************************************************************************************************************
          '*** TraceOn
          '***      Macro to replace code that would be blindly TRACE ON
          '***           Code only turns the trace on if debugging
          '***           Leaving a function will turn TRACE OFF by default (although I like to purposely call it to clean up after myself)
          '*************************************************************************************************************
               MACRO TraceOn
                    ErrFunc = SetGetDebugTraceRunning(%UNKNOWN_VALUE, DebugTraceRunning, %UNKNOWN_VALUE)      '<---Find out if debugging or not
                    SELECT CASE DebugTraceRunning
                         CASE 0                                                 'Not running a trace
                         CASE ELSE
                              TRACE ON                                          'Turn on trace if debugging
                    END SELECT
               END MACRO
          '*************************************************************************************************************
          '*** TraceOff
          '***      Macro to replace code that would be default TRACE OFF
          '***           Code only turns the trace off if debugging
          '***           Leaving a function will turn TRACE OFF by default (although I like to purposely call it to clean up after myself)
          '*************************************************************************************************************
               MACRO TraceOff                                                   '<--- Not needed if exiting function but I am a stickler
                    ErrFunc = SetGetDebugTraceRunning(%UNKNOWN_VALUE, DebugTraceRunning, %UNKNOWN_VALUE)
                    SELECT CASE DebugTraceRunning
                         CASE 0                                                 'Not running a trace
                         CASE ELSE
                              TRACE OFF                                         'Turn off trace if debugging
                    END SELECT
               END MACRO
          '*************************************************************************************************************
          '*** LastErrorCheck
          '***      Macro to replace code that would be multiple lines for Win32Api GetLastError
          '***           Code only traces if debugging
          '***           TraceLastError will decide if a Windows or a PB Error and log accordingly
          '*************************************************************************************************************
               MACRO LastErrorCheck
                    ErrFunc = GetLastError                                      'Get API Last Error
                    TraceLastError FUNCNAME$, ErrFunc, ERR, ERL                 'Pass all known info to the function for logging
               END MACRO
          '*************************************************************************************************************
          '*** NOW FOR THE STANDARD INC SETUP
          '*************************************************************************************************************
          '*** Declares:
               DECLARE FUNCTION SetGetDebugTraceRunning(ValueToSet AS LONG, ValueResults AS LONG, ResetValue AS LONG) AS LONG
               DECLARE FUNCTION SetGetLastFunction(ValueToSet AS STRING, ValueResults AS STRING, ResetValue AS LONG) AS LONG
               DECLARE FUNCTION SetGetOverRideError(ValueToSet AS LONG, ValueResults AS LONG, ResetValue AS LONG) AS LONG
               DECLARE FUNCTION StartTrace ALIAS "StartTrace"() AS LONG
               DECLARE FUNCTION EndTrace ALIAS "EndTrace"() AS LONG
               DECLARE FUNCTION TraceProgramName(ParentAppName AS ASCIIZ * %MAX_PATH) AS LONG
               DECLARE FUNCTION TraceLastError(FunctionName AS STRING, LastErrorValue AS LONG, PbErrorValue AS LONG, ErrorLineNumber AS LONG) AS LONG
               DECLARE FUNCTION TracePrint(WhatToTrace AS STRING) AS LONG
               DECLARE FUNCTION TraceExceptionHandler(BYREF lpEP AS EXCEPTION_POINTERS) AS LONG
          '*** Dependant Inc Files:
          '***      None
          '*** Functions:
          '***      SetGetDebugTraceRunning Replaces the need for global variables so that local variables in other functions are easier to read
               FUNCTION SetGetDebugTraceRunning(ValueToSet AS LONG, ValueResults AS LONG, ResetValue AS LONG) AS LONG
          1         TraceVariables                                              'Macro for Declaring variables for error checking
          2         STATIC FunctionValue AS LONG                                'Static to hold current value
          3         SELECT CASE ResetValue                                      'Decide whether to Set or to Get the current value
          4              CASE %False, %UNKNOWN_VALUE                            'If set to False, or -1 Then Get Current Value
          5                   ValueResults = FunctionValue                      'Return Results as a parameter
          6              CASE = %TRUE                                           'If set to True then Reset the Current Value
          7                   FunctionValue = ValueToSet                        'Reset the value
          8                   ValueResults = FunctionValue                      'Return Results as a parameter
          9         END SELECT
          10        FUNCTION = %False                                           'Return if Function Failed
          11        TraceUnhandled                                              'Exit Function or handle unhandled errors
               END FUNCTION
          '*** SetGetLastFunction Replaces the need for global variables so that local variables in other functions are easier to read
               FUNCTION SetGetLastFunction(ValueToSet AS STRING, ValueResults AS STRING, ResetValue AS LONG) AS LONG
          1         TraceVariables                                              'Macro for Declaring variables for error checking
          2         STATIC FunctionValue AS STRING                              'Static to hold current value
          3         SELECT CASE ResetValue                                      'Decide whether to Set or to Get the current value
          4              CASE %False, %UNKNOWN_VALUE                            'If set to False, or -1 Then Get Current Value
          5                   ValueResults = FunctionValue                      'Return Results as a parameter
          6              CASE = %TRUE                                           'If set to True then Reset the Current Value
          7                   FunctionValue = ValueToSet                        'Reset the value
          8                   ValueResults = FunctionValue                      'Return Results as a parameter
          9         END SELECT
          10        FUNCTION = %False                                           'Return if Function Failed
          11        TraceUnhandled                                              'Exit Function or handle unhandled errors
               END FUNCTION
          '*** SetGetOverRideError Replaces the need for global variables so that local variables in other functions are easier to read
               FUNCTION SetGetOverRideError(ValueToSet AS LONG, ValueResults AS LONG, ResetValue AS LONG) AS LONG
          1         TraceVariables                                              'Macro for Declaring variables for error checking
          2         STATIC FunctionValue AS LONG                              'Static to hold current value
          3         SELECT CASE ResetValue                                      'Decide whether to Set or to Get the current value
          4              CASE %False, %UNKNOWN_VALUE                            'If set to False, or -1 Then Get Current Value
          5                   ValueResults = FunctionValue                      'Return Results as a parameter
          6              CASE = %TRUE                                           'If set to True then Reset the Current Value
          7                   FunctionValue = ValueToSet                        'Reset the value
          8                   ValueResults = FunctionValue                      'Return Results as a parameter
          9         END SELECT
          10        FUNCTION = %False                                           'Return if Function Failed
          11        TraceUnhandled                                              'Exit Function or handle unhandled errors
               END FUNCTION
          '*** Start Tracing
               FUNCTION StartTrace ALIAS "StartTrace"() EXPORT AS LONG
          1         TraceVariables                                              'Macro for Declaring variables for error checking
          2         LOCAL TraceLogName AS ASCIIZ * %MAX_PATH
          3         ErrFunc = TraceProgramName(TraceLogName)                    'Get a trace log name
          4         SELECT CASE ErrFunc                                         'If no error creating the log
          5              CASE %FALSE
          6                   DebugTraceRunning = %TRUE                         'Set variable for if tracing
          7                   TRACE NEW TraceLogName                            'Create the log
          8              CASE ELSE                                              'Some Error Occurred
          9         END SELECT
          10        ErrFunc = SetGetDebugTraceRunning(%TRUE, DebugTraceRunning, %TRUE)    'Set the flag that we are tracing
          11        TraceUnHandled                                              'Macro for Local Variables and Unhandled Errors
               END FUNCTION
          '*** End Tracing
               FUNCTION EndTrace ALIAS "EndTrace"() EXPORT AS LONG
          1          TraceVariables                                             'Macro for common variables
          2          LOCAL TraceLogName AS ASCIIZ * %MAX_PATH
          3          ErrFunc = TraceProgramName(TraceLogName)                   'Get a trace log name
          4          SELECT CASE ErrFunc                                        'If the trace log name exists (no error) then
          5               CASE %FALSE
          6                    DebugTraceRunning = %FALSE
          7                    TRACE CLOSE                                      'Close Trace File
          8               CASE ELSE                                             'Some Error Occurred
          9          END SELECT
          10          ErrFunc = SetGetDebugTraceRunning(%FALSE, DebugTraceRunning, %TRUE)      'Set the flag for we are no longer tracing
          11          TraceUnHandled                                            'Macro for Local Variables and Unhandled Errors
               END FUNCTION
          '*** Set up a log file name
               FUNCTION TraceProgramName(ParentAppName AS ASCIIZ * %MAX_PATH) AS LONG
          1          LOCAL ErrFunc AS LONG
          2          LOCAL ModuleHandle AS DWORD
          '2          ErrFunc = GetModuleFileName(GetModuleHandle(BYVAL %NULL), ParentAppName, %MAX_PATH)       'Returns current path to owning parent
          '3          ParentAppName = MID$(ParentAppName, 1, INSTR(ParentAppName, ".exe") - 1)  'Strip all but the parent '.exe' part
          3          ModuleHandle = GetModuleHandle($NUL)
          4          GetModuleFileName(ModuleHandle, ParentAppName, %MAX_PATH)       'Get Exe Path and Name
          5          ModuleHandle = GetModuleHandle("COSMOS")
          6          GetModuleFileName(ModuleHandle, ParentAppName, %MAX_PATH)       'Get Dll Path and Name
          7          ParentAppName = ParentAppName + " - Error Log.log"          'Append string to show an error log
          8          SELECT CASE ErrFunc
          9               CASE 0                                                 'Function Failed
          10                   FUNCTION = ErrFunc
          11              CASE ELSE                                              'Function Passed
          12                    FUNCTION = %False
          13         END SELECT
               END FUNCTION
          '*** Trace Last Error (if any)
               FUNCTION TraceLastError(FunctionName AS STRING, LastErrorValue AS LONG, PbErrorValue AS LONG, ErrorLineNumber AS LONG) AS LONG
                    LOCAL LastError AS LONG
                    LOCAL WhatToTrace AS STRING
                    LOCAL ErrFunc AS LONG
                    LOCAL DebugTraceRunning AS LONG
                    LOCAL ErrorBuff AS ASCIIZ * %MAX_PATH
          
                    ERR = PbErrorValue                                          'PB Clears ERR at the end of functions so to preserve it I reset it
                    SELECT CASE ERR                                             'Is it a PB Error?
                         CASE 0                                                 'Not a PB Error
                              FormatMessage %FORMAT_MESSAGE_FROM_SYSTEM, BYVAL %NULL, LastErrorValue, %NULL, ErrorBuff, SIZEOF(ErrorBuff), BYVAL %NULL     'Format the message
                              WhatToTrace = $TAB + "Unhandled WindowsError - " + STR$(LastErrorValue) + " - " + ErrorBuff + $CRLF + $TAB + " Function = " + FunctionName + " at line: " + STR$(ErrorLineNumber)
                         CASE ELSE                                              'PB Error
                              LastError = ERR
                              WhatToTrace = WhatToTrace + $CRLF + $TAB + "Unhandled PbError # " + STR$(ERR) + " - " + ERROR$ + $CRLF + $TAB + " Function = " + FunctionName + " at line: " + STR$(ErrorLineNumber)    '<--- ERL not quite working at this time
                              SELECT CASE LastError
                                   CASE 0, %ERR_NOERROR                         'No Error
                                   CASE 5, %ERR_ILLEGALFUNCTIONCALL             'Illegal function call
                                   CASE 6, %ERR_OVERFLOW                        'Overflow                '<---This error is not currently supported.
                                   CASE 7, %ERR_OUTOFMEMORY                     'Out of Memory
                                   CASE 9, %ERR_SUBSCRIPTPOINTEROUTOFRANGE      'Subscript / Pointer out of range
                                   CASE 11, %ERR_DIVISIONBYZERO                 'Division by zero         '<---This error is not currently supported.
                                   CASE 24, %ERR_DEVICETIMEOUT                  'Device TimeOut for UDP or TCP communications
                                   CASE 51, %ERR_INTERNALERROR                  'Internal Error
                                   CASE 52, %ERR_BADFILENAMEORNUMBER            'Bad File Name or Number '<--- Also used for Serial Port Errors
                                   CASE 53, %ERR_FILENOTFOUND                   'File not found
                                   CASE 54, %ERR_BADFILEMODE                    'Bad File Mode
                                   CASE 55, %ERR_FILEISOPEN                     'File is already open    '<--- Also used for Serial Port Errors
                                   CASE 57, %ERR_DEVICEIOERROR                  'Device I/O Error        'Serial Port, TCP, UDP
                                   CASE 58, %ERR_FILEALREADYEXISTS              'File already exists
                                   CASE 61, %ERR_DISKFULL                       'Disk full
                                   CASE 62, %ERR_INPUTPASTEND                   'Input past end
                                   CASE 63, %ERR_BADRECORDNUMBER                'Bad record number
                                   CASE 64, %ERR_BADFILENAME                    'Bad file name
                                   CASE 67, %ERR_TOOMANYFILES                   'Too many files
                                   CASE 68, %ERR_DEVICEUNAVAILABLE              'Device unavailable
                                   CASE 69, %ERR_COMMERROR                      'COMM Error         '<---Serial Port Error
                                   CASE 70, %ERR_PERMISSIONDENIED               'Permission Denied
                                   CASE 71, %ERR_DISKNOTREADY                   'Disk not ready
                                   CASE 72, %ERR_DISKMEDIAERROR                 'Disk media error
                                   CASE 74, %ERR_RENAMEACROSSDISKS              'Rename across disks
                                   CASE 75, %ERR_PATHFILEACCESSERROR            'Path/File access error
                                   CASE 76, %ERR_PATHNOTFOUND                   'Path not found
                                   CASE 99, %ERR_OBJECTERROR                    'Object error
                                   CASE 241, %ERR_GLOBALMEMORYCORRUPT           'Global Memory Corrupt
                                   CASE 242, %ERR_STRINGSPACECORRUPT            'String space corrupt
                                   CASE ELSE                                    'Either a 'Compile-Time' error (handled by PB) or Unknown what to do so just exit the function
                              END SELECT
                    END SELECT
                    TraceOn                                                     'If debugging then trace
                    TracePrint WhatToTrace + $CRLF                              'If debugging then trace what?
               END FUNCTION
          '*** Print to trace file if debugging
               FUNCTION TracePrint(WhatToPrint AS STRING) AS LONG
          1         TraceVariables                                              'Macro for common variables
          2         ErrFunc = SetGetDebugTraceRunning(%UNKNOWN_VALUE, DebugTraceRunning, %UNKNOWN_VALUE)      'Check if Debugging
          3         SELECT CASE DebugTraceRunning
          4              CASE 0                                                 'Not running a trace
          5                   FUNCTION = DebugTraceRunning
          6              CASE ELSE
                              TRACE ON
          7                   TRACE PRINT WhatToPrint + $CRLF                   '<--- Chose string since I do not know what is to be printed + $CRLF for log file
          8                   FUNCTION = %FALSE
          9         END SELECT
          10        TraceUnHandled                                              'Macro for Local Variables and Unhandled Errors
               END FUNCTION
          '--------------------------------------------------------------------------------
          '*** Now for the DOOZYYYYyyyy....Handling GPF's and other major fatal errors
          '*** Commented fields are ones that I have no value for (yet) but MSDN say they exist
          '--------------------------------------------------------------------------------
               FUNCTION TraceExceptionHandler(BYREF lpEP AS EXCEPTION_POINTERS) AS LONG
                    STATIC TerminateInProcess AS LONG
                    LOCAL ErrorRecord AS EXCEPTION_RECORD POINTER
                    LOCAL ErrorCode AS LONG POINTER
                    LOCAL WhatToTrace AS STRING
                    LOCAL i AS LONG                                                  'For homing in on where the exception occurred
                    LOCAL CallStackFunctions AS STRING                               'For homing in on where the exception occurred
          'MSGBOX FUNCNAME$ + $CR + "Event Fired" + $CR + STR$(TerminateInProcess) + str$(@ErrorRecord.ExceptionCode)
                    SELECT CASE TerminateInProcess                              'If Unloading due to error let the OS handle it.
                         CASE %TRUE
                              SetUnhandledExceptionFilter %EXCEPTION_EXECUTE_HANDLER 'Reactivate default Error Handler
                              RaiseException %NULL, %NULL, %NULL, %NULL              'Force normal Error Handler
          '*** Exit the thread (and application if this is the only thread)
                              ExitThread 0
                         CASE %FALSE
                              TerminateInProcess = %TRUE
                              ErrorRecord = lpEP.pExceptionRecord                    'Detect the actual exception record
                              DO UNTIL @ErrorRecord.pExceptionRecord = 0             'Gather the exception record(s)
                                   CALL MoveMemory(@ErrorRecord, @ErrorRecord.pExceptionRecord, SIZEOF(ErrorRecord))
                              LOOP
                              ErrorCode   = @ErrorRecord.ExceptionCode
          '*** Now we have in ErrorCode the ExceptionCode, that raised the crash, log the info
                              SELECT CASE ErrorCode
                                   CASE %EXCEPTION_ACCESS_VIOLATION
                                        WhatToTrace =   "ACCESS VIOLATION" + " <---> " + "The thread tried to read from or write to a virtual address for which it does not have the appropriate access"
                                   CASE %EXCEPTION_ARRAY_BOUNDS_EXCEEDED
                                        WhatToTrace =   "ARRAY BOUNDS EXCEEDED" + " <---> " + "The thread tried to access an array element that is out of bounds and the underlying hardware supports bounds checking."
                                   CASE %EXCEPTION_BREAKPOINT
                                        WhatToTrace = "BREAKPOINT" + " <---> " + "A breakpoint was encountered."
                                   CASE %EXCEPTION_DATATYPE_MISALIGNMENT
                                        WhatToTrace =   "DATATYPE MISALIGNMENT" + " <---> " + "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
                                        WhatToTrace =  "FLOAT DENORMAL OPERAND" + " <---> " + "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
                                        WhatToTrace =  "FLOAT DIVISION BY ZERO" + " <---> " + "The thread tried to divide a floating-point value by a floating-point divisor of zero."
                                   CASE %EXCEPTION_FLT_INEXACT_RESULT
                                        WhatToTrace =  "FLOAT INEXACT RESULT" + " <---> " + "The result of a floating-point operation cannot be represented exactly as a decimal fraction."
                                   CASE %EXCEPTION_FLT_INVALID_OPERATION
                                        WhatToTrace =  "FLOAT INVALID OPERATION" + " <---> " + "This exception represents any floating-point exception not included in this list."
                                   CASE %EXCEPTION_FLT_OVERFLOW
                                        WhatToTrace =  "FLOAT OVERFLOW" + " <---> " + "The exponent of a floating-point operation is greater than the magnitude allowed by the corresponding type."
                                   CASE %EXCEPTION_FLT_STACK_CHECK
                                        WhatToTrace =  "FLOAT STACK CHECK" + " <---> " + "The stack overflowed or underflowed as the result of a floating-point operation."
                                   CASE %EXCEPTION_FLT_UNDERFLOW
                                        WhatToTrace =  "FLOAT UNDERFLOW" + " <---> " + "The exponent of a floating-point operation is less than the magnitude allowed by the corresponding type."
                                   CASE %EXCEPTION_ILLEGAL_INSTRUCTION
                                        WhatToTrace =  "ILLEGAL INSTRUCTION" + " <---> " + "The thread tried to execute an invalid instruction."
                                   CASE %EXCEPTION_IN_PAGE_ERROR
                                        WhatToTrace = "IN PAGE ERROR" + " <---> "
                                        WhatToTrace = WhatToTrace + "The thread tried to access a page that was not present, and the system was unable to load the page."
                                        WhatToTrace = WhatToTrace + "For example, this exception might occur if a network connection is lost while running a program over "
                                        WhatToTrace = WhatToTrace + "the network."
                                   CASE %EXCEPTION_INT_DIVIDE_BY_ZERO
                                        WhatToTrace =   "INTEGER DIVISION BY ZERO" + " <---> " + "The thread tried to divide an integer value by an integer divisor of zero."
                                   CASE %EXCEPTION_INT_OVERFLOW
                                        WhatToTrace =   "INTEGER OVERFLOW" + " <---> " + "The result of an integer operation caused a carry out of the most significant bit of the result."
          '                         CASE %EXCEPTION_INVALID_DISPOSITION
          '                              WhatToTrace =   "INVALID DISPOSITION" + " <---> " + "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
          '                              WhatToTrace =   "NONCONTINUABLE EXCEPTION" + " <---> " + "The thread tried to continue execution after a noncontinuable exception occurred."
                                   CASE %EXCEPTION_PRIV_INSTRUCTION
                                        WhatToTrace =   "PRIVATE INSTRUCTION" + " <---> " + "The thread tried to execute an instruction whose operation is not allowed in the current machine mode."
                                   CASE %EXCEPTION_SINGLE_STEP
                                        WhatToTrace =   "SINGLE STEP" + " <---> " + "A trace trap or other single-instruction mechanism signaled that one instruction has been executed."
                                   CASE %EXCEPTION_STACK_OVERFLOW
                                        WhatToTrace =  "STACK OVERFLOW" + " <---> " + "The thread used up its stack."
                                   CASE ELSE
                                        WhatToTrace =  CHR$(0)
                              END SELECT
          '                    FOR i = CALLSTKCOUNT TO CALLSTKCOUNT - 1 STEP -1
          ''                    CallStackFunctions = CallStackFunctions + CALLSTK$(i)
          '                         CallStackFunctions = CALLSTK$(i)
          '                    NEXT i
                              FOR i = CALLSTKCOUNT TO 1 STEP -1
                              CallStackFunctions = CallStackFunctions + CALLSTK$(i) + $CRLF
          '                         CallStackFunctions = CALLSTK$(i)
                              NEXT i
          '*** Code for writing a crash-log, save opened documents etc...
                              WhatToTrace = $TAB + "Unhandled WindowsException - " + STR$(@ErrorRecord.ExceptionCode) + " - " + WhatToTrace + $CRLF + $TAB + " Function = " + CallStackFunctions + " at line: " + STR$(ERL)
          '                    MSGBOX WhatToTrace
                              TracePrint WhatToTrace
          '*** Exit the thread (and application if this is the only thread)
                              ExitThread 0                  'This will stop the typical "*.exe has encountered a problem and needs to close" error message to send a report to M$
                    END SELECT
               END FUNCTION
          #ENDIF
          I know its a bit late, but maybe it will help you with other troublesome procedures later.
          Engineer's Motto: If it aint broke take it apart and fix it

          "If at 1st you don't succeed... call it version 1.0"

          "Half of Programming is coding"....."The other 90% is DEBUGGING"

          "Document my code????" .... "WHYYY??? do you think they call it CODE? "

          Comment

          Working...
          X