Announcement

Collapse
No announcement yet.

Mysterious GPF and Exception Handling

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

  • Mysterious GPF and Exception Handling

    I was trying to track down a mysterious GPF in my dll that only occurs when I do a precise step of sequences, and have spent days verifying that pointers are good, and all threads are closed, and anything I could think of that might cause the crash, and still can not find it.

    Then I was searching the forums for what else could be a corrupted memory location, or anything else that could GPF, and ran into one that MCM wrote about a year ago Sure its a GPF but....
    In it the point was made that you could raise your own exceptions, so I started thinking. If you can raise one, then there must be a way to read one and act on it?

    Sure enough looking in the Win32 help files, I found "GetExceptionInformation" which I think may be my answer, but could not find any examples to show me just how to use it (or if I even understand the documentation correctly)

    Does anyone have some examples or point me the right direction? I would like to be able to build into my dll that when an unhandled exception occurs I can either take care of it, or log it when it occurs, and not later on (like when closing the program)

    Side Note: Compiled as EXE I get no GPF, Compiled as DLL I get no GPF if I push a button that does my cleanup, but if I programmatically press the button from code, then I get a GPF when closing. (Which is really starting to make me think, that where I am looking is NOT where the exception occurred.
    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? "

  • #2
    Not to be "too" simple, but perhaps you could make the source code available along with any required data files and instructions on "how to make it fail" and you might get some 'fresh eyes' on the problem.

    I remember I had a dumb mistake in one of my programs once, and I got to the point where I was sure there was a compiler problem. So I bundled up the source and data files and mailed in to the PB support dept.

    In less than an hour someone had replied that leaving an orphan underscore at the end of a source code line changes a single-line if into a multi-line IF..END IF block, and if I simply fixed that and added the (now) required END IF the program would work just fine.

    This, too, could be something 'fresh eyes' could spot almost instantly.

    Hell, it's a thought.

    MCM
    Michael Mattias
    Tal Systems (retired)
    Port Washington WI USA
    [email protected]
    http://www.talsystems.com

    Comment


    • #3
      I am sure it's the typical "i will not tell to much"..

      1) I am sure it's VB again?

      2) If so, an unhandled error in VB crashes PB code as well.
      hellobasic

      Comment


      • #4
        Sorry Edwin and MCM, I am sure you guys get the feeling that when I say "Its buried deep" that its a phrase that I hear often when hearing the same questions from someone wanting my help, and my only reply is "I have to see the code" to even BEGIN to get a clue.

        I am sure it's the typical "i will not tell to much"..
        Not that I don't want to, but it truly is buried that deep in code (too bad I did not know then what I have learned since), as you will see from the code below that unfortunately took me about 8 hours to rip out and make functional by itself without the other thousands of lines of code.

        1) I am sure it's VB again?
        Actually yes to begin, but also under Labview, C++, and PB itself (each with their own "quirks" but I think the common flaw is a pointer gone bad? or something I have no knowledge that I may or may not have control over

        Anyways, like I said it took me over 8 hrs to rip down and make a "Functional" demonstration of the problem, so please forgive me the 2nd error I get from my demo (I think its just the migraine and me not thinking, why PB will not fire the close event, so I am not so worried about that problem just yet)

        Anyways, to make things simpler, you will see areas where I hard-coded things like "Assume Com1" rather than the real possibles and their error-checks, etc.

        Also for MCM...I know you hate me for all the Globals, but my next version I am hoping to eliminate the whole "Global - Local" debate, so please bear with me.

        I also stuck as much as I could into as few files as possible. Although my usual habit is to put all my "Declares" in separate *.h files and the associated code into *.inc files so I can just drop them into different projects and not have to re-do working code.

        Anyways, I appreciate some "Fresh-Eyes" over what I may be obviously missing.

        The main Program. Name it "COSMOS" or whatever
        Code:
        '#COMPILE EXE
        #COMPILE DLL
        #DIM ALL
        #DEBUG ERROR ON
        #TOOLS ON
        #INCLUDE "WIN32API.INC"       'Windows Api
        
        '------------------------------------------------------------------------------
        '*** Load all the header files
        '------------------------------------------------------------------------------
        '*** COSMOS.h
             DECLARE FUNCTION WINMAIN (BYVAL hInstance AS DWORD, BYVAL hPrevInstance AS DWORD, BYVAL lpCmdLine AS ASCIIZ PTR, BYVAL iCmdShow AS LONG) AS LONG
             DECLARE FUNCTION LIBMAIN (BYVAL hInstance AS LONG, BYVAL fwdReason AS LONG, BYVAL lpvReserved AS LONG) AS LONG
             DECLARE FUNCTION WndProc (BYVAL hWnd AS DWORD, BYVAL wMsg AS DWORD, BYVAL wParam AS DWORD, BYVAL lParam AS LONG) AS LONG
             DECLARE FUNCTION LoadCosmos ALIAS "LoadCosmos"() AS LONG
             DECLARE FUNCTION UnloadCosmos ALIAS "UnloadCosmos"() AS LONG
             DECLARE FUNCTION GetWindowState ALIAS "GetWindowState"(WindowToCheck AS LONG) AS LONG
             DECLARE FUNCTION ShowCosmos ALIAS "ShowCosmos"() AS LONG
             DECLARE FUNCTION HideCosmos ALIAS "HideCosmos"() AS LONG
             DECLARE FUNCTION IsCosmosVisible ALIAS "IsCosmosVisible"() AS LONG
        
             GLOBAL IsExe            AS LONG
             GLOBAL ParentClosing AS LONG
             GLOBAL hInst        AS DWORD
             GLOBAL hWndMain     AS DWORD
             GLOBAL hWndClient   AS DWORD
             GLOBAL fClosed      AS LONG
             GLOBAL freezeMenu   AS LONG
        '------------------------------------------------------------------------------
        '*** Menubar.h
             GLOBAL hMenu AS LONG
             GLOBAL StatHeight  AS LONG
             %IDM_TOOLS                         = %WM_USER + 100
             %IDM_TRBLIMITSVIEWON               = %WM_USER + 942
        '------------------------------------------------------------------------------
        '*** Statusbar.h
             GLOBAL hStatus      AS DWORD
             %ID_STATUSBAR                      = %WM_USER + 1025&  ' statusbar
        '------------------------------------------------------------------------------
        '*** ErrorHandling.h
             DECLARE FUNCTION StartTrace ALIAS "StartTrace"() AS LONG
             DECLARE FUNCTION EndTrace ALIAS "EndTrace"() AS LONG
             DECLARE FUNCTION ErrorCheck ALIAS "ErrorCheck"(ErrorCode AS LONG)AS LONG     'Error Handling
        
             GLOBAL TraceLog AS STRING
             GLOBAL TraceLogNum AS LONG
             GLOBAL NumOfErrorsCosmos    AS LONG
        '------------------------------------------------------------------------------
        '*** Threading.h
             DECLARE FUNCTION OpenMdiThread ALIAS "OpenMdiThread"() AS LONG       'Added to re-enable the Mdi thread
             DECLARE FUNCTION CloseMdiThread ALIAS "CloseMdiThread"() AS LONG
        
             DECLARE FUNCTION OpenListenThread ALIAS "OpenListenThread"() AS LONG       'Added to re-enable the listen thread
             DECLARE FUNCTION CloseListenThread ALIAS "CloseListenThread"() AS LONG
        
             GLOBAL MdiThread AS LONG    'Added so that the MdiForm can run on its own, and return to parent caller
             GLOBAL MdiThreadDone AS LONG
        
             GLOBAL HwndThread       AS LONG         'Variable for the thread
             GLOBAL CloseThread      AS LONG
             GLOBAL ThreadDone       AS LONG
        '------------------------------------------------------------------------------
        '*** DlgIoState.h
             DECLARE SUB ShowIoStateWindow(TestToRun AS STRING)
             DECLARE SUB HideIoStateWindow()
        
             GLOBAL IoTest AS STRING
             GLOBAL IsDlgIoOpen AS LONG
             GLOBAL HwndDlgIoState  AS DWORD
        
             %Btn_Stop    = 102
        '------------------------------------------------------------------------------
        '*** History.h
             DECLARE CALLBACK FUNCTION HistoryChildrenCallback() AS LONG
             DECLARE CALLBACK FUNCTION HistoryCallBack () AS LONG
             DECLARE FUNCTION LoadHistory(BYVAL hParent&) AS LONG     'Load main program
             DECLARE FUNCTION ShowHistory ALIAS "ShowHistory"(BYVAL hParent&) AS LONG
             DECLARE FUNCTION HideHistory ALIAS "HideHistory"() AS LONG
             DECLARE FUNCTION IsHistoryOpen ALIAS "IsHistoryOpen"() AS LONG
             DECLARE SUB LstHistoryAddLine(FontType AS STRING, FontColor AS STRING, TextToAdd AS STRING, AppendCr AS LONG)    'MUST REMAIN HERE DUE TO CONFLICT OF LOADING PORT FUNCTION OR DLGHistory 1ST
             DECLARE SUB LstHistoryRestoreHistory()
             DECLARE SUB LstHistoryClearHistory()
             DECLARE SUB DefaultHistory()
        
             GLOBAL HistoryHeight AS LONG
             GLOBAL HistoryWidth AS LONG
             GLOBAL hLibHistory AS LONG
             GLOBAL RchEdHistory AS LONG
             GLOBAL hHistory AS LONG        'Handle for subclassed RichEdit
             GLOBAL OldDlgHistoryTxtProc AS LONG  'for address of original RichEdit procedure
             GLOBAL HistoryRtfText AS STRING
             %RchEdHistory = 100
        '------------------------------------------------------------------------------
        '*** Rtf.h
             DECLARE FUNCTION RichEditStreamInString (BYVAL dwCookie AS DWORD, BYVAL pbBuff AS BYTE PTR, _
                                              BYVAL cb AS LONG, pcb AS LONG) AS DWORD
        
             GLOBAL gPos AS LONG, gPtr AS LONG, gTxt AS STRING
        
             DECLARE FUNCTION RtfCommandsStart() AS STRING
             DECLARE FUNCTION RtfCommandsEnd() AS STRING
        
             DECLARE FUNCTION RtfCommandsEndOfLine() AS STRING
        
             DECLARE FUNCTION RtfReplaceCr(CommandString AS STRING) AS STRING
        
             DECLARE FUNCTION RtfFontCourier() AS STRING
             DECLARE FUNCTION RtfFontTimesNewRoman() AS STRING
             DECLARE FUNCTION RtfFontAndale() AS STRING
             DECLARE FUNCTION RtfFontLucidia() AS STRING
             DECLARE FUNCTION RtfFontGeorgia() AS STRING
        
             DECLARE FUNCTION RtfFontBold() AS STRING
             DECLARE FUNCTION RtfFontUnBold() AS STRING
             DECLARE FUNCTION RtfFontItalic() AS STRING
             DECLARE FUNCTION RtfFontUnItalic() AS STRING
             DECLARE FUNCTION RtfFontUnderline() AS STRING
             DECLARE FUNCTION RtfFontUnUnderline() AS STRING
        
             DECLARE FUNCTION RtfFontSize(SizeForFont AS LONG) AS STRING
        
             DECLARE FUNCTION RtfFontMaroon() AS STRING
             DECLARE FUNCTION RtfFontGreen() AS STRING
             DECLARE FUNCTION RtfFontOlive() AS STRING
             DECLARE FUNCTION RtfFontNavy() AS STRING
             DECLARE FUNCTION RtfFontPurple() AS STRING
             DECLARE FUNCTION RtfFontTeal() AS STRING
             DECLARE FUNCTION RtfFontGrey() AS STRING
             DECLARE FUNCTION RtfFontSilver() AS STRING
             DECLARE FUNCTION RtfFontRed() AS STRING
             DECLARE FUNCTION RtfFontLime() AS STRING
             DECLARE FUNCTION RtfFontYellow() AS STRING
             DECLARE FUNCTION RtfFontBlue() AS STRING
             DECLARE FUNCTION RtfFontFuchsia() AS STRING
             DECLARE FUNCTION RtfFontAqua() AS STRING
             DECLARE FUNCTION RtfFontWhite() AS STRING
             DECLARE FUNCTION RtfFontBlack() AS STRING
        '------------------------------------------------------------------------------
        '*** WaitForChar.h
             DECLARE FUNCTION WaitForChar ALIAS "WaitForChar"(CharToWaitFor AS ASCIIZ * %MAX_PATH, OPTIONAL BYVAL TimeOutTime AS LONG) AS LONG
             DECLARE FUNCTION WaitForCharWithMotorPosition ALIAS "WaitForCharWithMotorPosition"(CharToWaitFor AS ASCIIZ * %MAX_PATH, BYVAL MotorNumber AS LONG, OPTIONAL BYVAL ReportToWindowHwnd AS LONG, OPTIONAL BYVAL TimeOutTime AS LONG) AS LONG
        
             GLOBAL WaitingForChar AS LONG
        '------------------------------------------------------------------------------
        '*** PortFunctions.h
             DECLARE FUNCTION OpenPort ALIAS "OpenPort"(BYVAL ComPortNumber AS LONG, BYVAL ComPortBaudRate AS LONG) AS LONG  'Configure and open serial port
             DECLARE FUNCTION IsPortOpen ALIAS "IsPortOpen"() AS LONG    'Determine if port is already open (mostly for labview users)
             DECLARE FUNCTION ClosePort ALIAS "ClosePort"() AS LONG  'Close serial port
             DECLARE FUNCTION SendToPort ALIAS "SendToPort" (CommandOut AS STRING) AS LONG 'Send Command to device via Cptr or actual string
             DECLARE FUNCTION WaitReply(MinNumOfChars AS LONG, TimeoutTime AS LONG)AS LONG
             DECLARE FUNCTION ReadAndThenClearFromPort ALIAS "ReadAndThenClearFromPort"() AS STRING  'Get reply from device
             DECLARE FUNCTION CountCharsAtPort ALIAS "CountCharsAtPort"() AS LONG 'Count Number of Chars at port
             DECLARE FUNCTION SearchForChars ALIAS "SearchForChars"(CharsToFind AS ASCIIZ) AS LONG
             DECLARE FUNCTION ClearPort ALIAS "ClearPort"() AS LONG  'Clear buffer
             DECLARE FUNCTION RemoveFromPort ALIAS "RemoveFromPort"(StringToRemove AS ASCIIZ) AS LONG  'Clear buffer chars
        
             DECLARE FUNCTION ReceiveData(BYVAL HwndTerminal AS DWORD) AS LONG
             DECLARE FUNCTION ReadFromPort ALIAS "ReadFromPort"() AS STRING  'Get reply from device
        
             DECLARE FUNCTION VerifyIfVxm() AS LONG
             DECLARE SUB CheckVxmConnected ALIAS "CheckVxmConnected"()
        
             GLOBAL ErrPort AS LONG
             GLOBAL Buffer           AS STRING       'ComPort Buffer
             GLOBAL CharsInBuffer    AS LONG         'Number of Chars at buffer
             GLOBAL OpeningPort AS LONG          'If opening the port dont show the reply from the verify
             GLOBAL DetectingVxm AS LONG         'If Detecting then continue till false. (Verify Status will make false if passed in as true)
             GLOBAL TracePort AS LONG
             GLOBAL TracePortLog AS STRING
             GLOBAL TracePortLogNum AS LONG
        
             GLOBAL hWndPort AS LONG
        '------------------------------------------------------------------------------
        '*** TroubleshootIo.h
             DECLARE SUB TroubleshootLimits()
             DECLARE SUB StopTroubleshootLimits()
             DECLARE SUB RunningLimitsView()
             DECLARE SUB StoppedLimitsView()
             GLOBAL IoView AS LONG
             GLOBAL LimitState AS STRING
             GLOBAL ChangeIoDisplay AS LONG
        '------------------------------------------------------------------------------
        '*** RTF.inc and RichEdit.inc should be their own Inc files due to size
        #INCLUDE "RichEdit.inc"
        #INCLUDE "Rtf.inc"
        '*** InitControls.inc and Mdi32.inc should be their own Inc files due to size
        #INCLUDE "Mdi32.inc"
        #INCLUDE "COMMCTRL.INC"
        #INCLUDE "InitControls.inc"
        '------------------------------------------------------------------------------
        
        '*** Load the resource file
        #RESOURCE "COSMOS.pbr"
        '------------------------------------------------------------------------------
        '*** If compiling as Exe
        FUNCTION WINMAIN (BYVAL hInstance AS DWORD, BYVAL hPrevInstance AS DWORD, BYVAL lpCmdLine AS ASCIIZ PTR, BYVAL iCmdShow AS LONG) AS LONG
             ON ERROR GOTO ErrHandler
        '     TRACE NEW "MyExeTrace.txt"
        '     TRACE ON
             IsExe = %True
             hInst = hInstance 'store module handle in global variable for later use
             LoadCosmos
        '     TRACE OFF
        '     TRACE CLOSE
             EXIT FUNCTION
        ErrHandler:
             StartTrace
             ErrorCheck ERR
             EndTrace
        END FUNCTION  ' end WinMain
        
        '*** If compiling as Dll
        FUNCTION LIBMAIN (BYVAL hInstance AS LONG, BYVAL fwdReason AS LONG, BYVAL lpvReserved AS LONG) AS LONG
             ON ERROR GOTO ErrHandler
             SELECT CASE fwdReason
                  CASE %DLL_PROCESS_ATTACH
                       FUNCTION = 1   'success!
                       'FUNCTION = 0   'failure!  This will prevent the EXE from running.
        '               TRACE NEW "MyDllTrace.txt"
        '               TRACE ON
                       IsExe = %False      'Flag for Exe
                       hInst = hInstance
                       LoadCosmos
                  CASE %DLL_PROCESS_DETACH
                       UnloadCosmos
                       FUNCTION = 1   'success!
                       'FUNCTION = 0   'failure!
        '               TRACE OFF
        '               TRACE CLOSE
                  CASE %DLL_THREAD_ATTACH
                       FUNCTION = 1   'success!
                       'FUNCTION = 0   'failure!
                  CASE %DLL_THREAD_DETACH
                       FUNCTION = 1   'success!
                       'FUNCTION = 0   'failure!
             END SELECT
             EXIT FUNCTION
        ErrHandler:
             StartTrace
             ErrorCheck ERR
             EndTrace
        END FUNCTION
        
        '------------------------------------------------------------------------------
        ' Main procedure
        '------------------------------------------------------------------------------
        FUNCTION WndProc (BYVAL hWnd AS DWORD, BYVAL wMsg AS DWORD, _
                          BYVAL wParam AS DWORD, BYVAL lParam AS LONG) AS LONG
             ON ERROR GOTO ErrHandler
             LOCAL tRect       AS RECT
             LOCAL cc          AS CLIENTCREATESTRUCT
             LOCAL rc AS RECT
             LOCAL zText AS ASCIIZ * %MAX_PATH
             SELECT CASE wMsg
                  CASE %WM_INITDIALOG     'If initializing (opening the TerminalSimple)
                  CASE %WM_CREATE
                       ParentClosing = %False
                       '------------------------------------------------------------------------
                       ' some of CommCtrl.dll's controls, messages and styles were not included
                       ' in standard Win95A release, so we need to see how to initiate the dll.
                       '------------------------------------------------------------------------
        '               NewComCtl = InitComctl32 (%ICC_BAR_CLASSES)
                       '------------------------------------------------------------------------
        ''*** Create the status bar window
                       hStatus = CreateStatusWindow(%WS_CHILD OR %WS_VISIBLE OR %SBS_SIZEGRIP, "", hWnd, %ID_STATUSBAR)
                       DIM prts(0 TO 11) AS LONG
                       GetClientRect hStatus, rc
        '*** Dimension the parts
                       prts(0) = rc.nRight * 0.05     : prts(1) = rc.nRight * 0.15     'Port:Portnum
                       prts(2) = rc.nRight * 0.16     : prts(3) = rc.nRight * 0.25     'Space:Baud
                       prts(4) = rc.nRight * 0.35     : prts(5) = rc.nRight * 0.36     'Baudratenum:Space
                       prts(6) = rc.nRight * 0.43     : prts(7) = rc.nRight * 0.78     'Status:PortStatus
                       prts(8) = rc.nRight * 0.79     : prts(9) = rc.nRight * 0.85     'Space:VXM
                       prts(10) = rc.nRight * 0.95     : prts(11) = rc.nRight * 1      'VXMStatus:Space
                       Sendmessage hStatus, %SB_SETPARTS, 12, VARPTR(prts(0))
        '*** Port
                       zText = " Port: " : SendMessage hStatus, %SB_SETTEXT, 0, VARPTR(zText)
                       zText = " Not Set" : SendMessage hStatus, %SB_SETTEXT, 1, VARPTR(zText)
        '*** BaudRate
                       zText = " Baudrate: " : SendMessage hStatus, %SB_SETTEXT, 3, VARPTR(zText)
                       zText = " Not Set" : SendMessage hStatus, %SB_SETTEXT, 4, VARPTR(zText)
        '*** Port Status
                       zText = " Status: " : SendMessage hStatus, %SB_SETTEXT, 6, VARPTR(zText)
                       zText = " Not Set" : SendMessage hStatus, %SB_SETTEXT, 7, VARPTR(zText)
        '*** Create MDI Client window
        '               cc.idFirstChild = 1
        '               cc.hWindowMenu  = hMenuWindow  'for file list in Window menu
                       hWndClient = CreateWindowEx(%WS_EX_CLIENTEDGE, "MDICLIENT", BYVAL %NULL, _
                                                     %WS_CHILD OR %WS_CLIPCHILDREN OR _
                                                     %WS_VISIBLE OR %WS_VSCROLL OR %WS_HSCROLL  , _
                                                     0, 0, 0, 0, hWnd, 1, hInst, cc)
                       EXIT FUNCTION
                  CASE %WM_SIZE
                       IF wParam <> %SIZE_MINIMIZED THEN
        '*** Get Toolbar and statusbar height
                            GetWindowRect hStatus, tRect
                            StatHeight = tRect.nBottom - tRect.nTop  'statusbar height
                            GetWindowRect RchEdHistory, tRect
                            HistoryHeight = tRect.nBottom - tRect.nTop
        '*** Set Toolbar and statusbar height
                            SendMessage hStatus, wMsg, wParam, lParam
                            MoveWindow hWndClient, -2, 0, LOWRD(lParam)+ 5 , HIWRD(lParam) - (StatHeight + HistoryHeight) - 10, %TRUE
                            GetWindowRect hWndClient, tRect
                            HistoryWidth = tRect.nRight - tRect.nLeft '- 10
                            SetWindowPos RchEdHistory, 0, 0, HIWRD(lParam) - (StatHeight + HistoryHeight) - 10, HistoryWidth, HistoryHeight, %SWP_NOZORDER
                            InvalidateRect hStatus, BYVAL 0, %TRUE  'now redraw window
                            UpdateWindow hStatus       'Redraw the dialog
                       END IF
                       EXIT FUNCTION
        '*** Form Notifications
                  CASE %WM_NOTIFY
                       EXIT FUNCTION
        '*** Menu Select
                  CASE %WM_MENUSELECT
                       LoadString hInst, wParam, zText, SIZEOF(zText)
                       EXIT FUNCTION
        '*** Paint the form
                  CASE %WM_NCPAINT 'if flag is set, return zero to avoid menu update
                       IF freezeMenu THEN EXIT FUNCTION
        '*** Determine which menu or command was clicked
                  CASE %WM_COMMAND
                       SELECT CASE LOWRD(wParam)          'Equivelant to CBCTL in a callback function
        '****************************************** TROUBLESHOOTING ROUTINES ***************************************************
                            CASE %IDM_TRBLIMITSVIEWON
                                 TroubleshootLimits
                       END SELECT
                  CASE %WM_SYSCOLORCHANGE
        '*** Forward this message to common controls so that they will be properly updated if the user changes the color settings.
                       SendMessage hStatus,  %WM_SYSCOLORCHANGE, wParam, lParam
                  CASE %WM_CLOSE
                       SELECT CASE IsExe
                            CASE %True
                                 UnloadCosmos
                            CASE %False
                                 SELECT CASE ParentClosing          '<--- Only End if the parent process is closing
                                      CASE %True
                                           UnloadCosmos
                                      CASE %False
                                           HideCosmos
                                           EXIT FUNCTION
                                 END SELECT
                       END SELECT
                  CASE %WM_DESTROY
                       SELECT CASE IsExe
                            CASE %True
                                 PostQuitMessage 0                  '<--- Escape message pump if still running
                                 EXIT FUNCTION
                            CASE %False
                       END SELECT
             END SELECT
             FUNCTION = DefFrameProc(hWnd, hWndClient, wMsg, wParam, lParam)
             EXIT FUNCTION
        ErrHandler:
             StartTrace
             ErrorCheck ERR
             EndTrace
        END FUNCTION
        '------------------------------------------------------------------------------
        FUNCTION LoadCosmos ALIAS "LoadCosmos"()EXPORT AS LONG
             ON ERROR GOTO ErrHandler
        ''*** Window variables
             LOCAL hAccel       AS DWORD
             LOCAL Msg          AS TAGMSG
             LOCAL wce          AS WNDCLASSEX
             LOCAL szClassName  AS ASCIIZ * 80
        '*** Desktop variables
             LOCAL DeskTopX AS LONG
             LOCAL DeskTopY AS LONG
             LOCAL AppX AS LONG
             LOCAL AppY AS LONG
        '*** Register Main Window Class
        '     GetClassInfoEx(hInst, "#32770", wce)      'copy dialog class (modeless) into ports class
             szClassName       = "COSMOS"
             wce.cbSize        = SIZEOF(wce)
             wce.style         = %CS_HREDRAW OR %CS_VREDRAW
             wce.lpfnWndProc   = CODEPTR(WndProc)
             wce.cbClsExtra    = 0
             wce.cbWndExtra    = 0
             wce.hInstance     = hInst
             wce.hIcon         = LoadIcon(hInst, "APPICON")
             wce.hCursor       = LoadCursor(%NULL, BYVAL %IDC_ARROW)
             wce.hbrBackground = %NULL       'LoadImage(%Null, ".\COSMOS Intro.bmp" , %IMAGE_BITMAP, 0, 0,%LR_LOADFROMFILE)
             wce.lpszMenuName  = %NULL
             wce.lpszClassName = VARPTR(szClassName)
             wce.hIconSm       = LoadIcon(hInst, BYVAL %IDI_APPLICATION)
             IF RegisterClassEx(wce) = 0 THEN
                  RegisterClass BYVAL (VARPTR(wce) + 4)
             END IF
        '*** Setup main menu
             hMenu       = LoadMenu(hInst, "MAINMENU")           'main menu        '<--- Involves Res File
        '*** Get Desktop properties
             DeskTopX = GetSystemMetrics(%SM_CXSCREEN)
             DeskTopY = GetSystemMetrics(%SM_CYSCREEN)
             SELECT CASE DesktopX
                  CASE < 800
                       AppX = 640
                  CASE 800 TO 1024
                       AppX = 800
                  CASE > 1024
                       AppX = 1024
             END SELECT
             SELECT CASE DesktopY
                  CASE < 600
                       AppY = 480
                  CASE 600 TO 768
                       AppY = 600
                  CASE > 768
                       AppY = 768
             END SELECT
        
        '*** Create main window using the registered class
             LOCAL CurrentVersion AS STRING
             CurrentVersion = "3.1.1"
             hWndMain = CreateWindow("COSMOS", _              ' window class name
                                      "COSMOS (version " + CurrentVersion + ")", _              ' window caption
                                      %WS_OVERLAPPEDWINDOW, _   ' window style
                                      (DeskTopX - AppX) / 2, _  ' initial x position
                                      (DeskTopY - AppY) / 2, _  ' initial y position
                                      AppX, _                    ' initial x size
                                      AppY, _                    ' initial y size
                                      %NULL, _                  ' parent window handle
                                      hMenu, _                  ' window menu handle
                                      hInst, _              ' program instance handle
                                      BYVAL %NULL)              ' creation parameters
             LoadHistory HwndMain
        '*** Load Dialogs
        '            ShowIoStateWindow                         '<--- Breaks the rules list only for keeping track of whats loaded
        OpenPort 1, 9600
             OpenListenThread
             ShowCosmos
        '*** Message handler loop
             SELECT CASE IsExe
                  CASE %False
                  CASE %True
                       WHILE GetMessage(Msg, BYVAL %NULL, 0, 0)
                            IF TranslateMDISysAccel(hWndClient, Msg) = 0 THEN
                                 IF TranslateAccelerator(hWndMain, hAccel, Msg) = 0 THEN
                                      TranslateMessage Msg
                                      DispatchMessage Msg
                                 END IF
                            END IF
                       WEND
             END SELECT
             FUNCTION = msg.wParam
             EXIT FUNCTION
        ErrHandler:
             StartTrace
             ErrorCheck ERR
             EndTrace
        END FUNCTION
        
        FUNCTION UnloadCosmos ALIAS "UnloadCosmos"()EXPORT AS LONG
             LOCAL lResult AS LONG
             LOCAL ErrorBuff AS ASCIIZ * %MAX_PATH
             ClosePort
             ParentClosing = %True
        '     SendMessage(GetDlgCtrlId(%Btn_Stop), %BM_CLICK, 0, 0)
        '     DIALOG END HwndDlgIoState
        '     HideIoStateWindow
        '     DestroyWindow HwndDlgIoState
        '     SendMessage hWndClient, %WM_CLOSE, HwndDlgAbout&, 0
        '     SendMessage(HwndDlgIoState, %WM_SYSCOMMAND, %SC_CLOSE, 0)
             SendMessage(GetDlgItem(HwndDlgIoState, %Btn_Stop), %BM_CLICK, 0, 0)
        '     lResult = GetLastError()           'Check for the error
        '     FormatMessage %FORMAT_MESSAGE_FROM_SYSTEM, BYVAL %NULL, lResult, %NULL, ErrorBuff, SIZEOF(ErrorBuff), BYVAL %NULL  'Format the message
        'MSGBOX ErrorBuff + $CR + STR$(GetDlgItem(HwndDlgIoState, %Btn_Stop)) + $CR + STR$(GetDlgCtrlId(%Btn_Stop))
        
             IF HwndThread THEN CloseListenThread
             DestroyMenu hMenu
             DestroyWindow HwndClient
             DestroyWindow RchEdHistory
             DestroyWindow HwndMain
        '     DestroyWindow GetWindow(HwndMain, %GW_HWNDFIRST)
        '     lResult = GetLastError()           'Check for the error
        '     FormatMessage %FORMAT_MESSAGE_FROM_SYSTEM, BYVAL %NULL, lResult, %NULL, ErrorBuff, SIZEOF(ErrorBuff), BYVAL %NULL  'Format the message
        'MSGBOX ErrorBuff + str$(HwndMain)
                  IF hLibHistory THEN FreeLibrary hLibHistory
        '     lResult = GetLastError()           'Check for the error
        '     FormatMessage %FORMAT_MESSAGE_FROM_SYSTEM, BYVAL %NULL, lResult, %NULL, ErrorBuff, SIZEOF(ErrorBuff), BYVAL %NULL  'Format the message
        'MSGBOX ErrorBuff
        ''MSGBOX "Thread Count = " + STR$(THREADCOUNT) + $CR + "Thread Id = " + STR$(THREADID) + $CR + "MDI Thread = " + STR$(MdiThread)
        END FUNCTION
        
        FUNCTION GetWindowState ALIAS "GetWindowState"(WindowToCheck AS LONG) AS LONG
             SELECT CASE IsWindowVisible(WindowToCheck)
                  CASE 0         'Not Visible = 0
                       FUNCTION = %SW_HIDE
                  CASE ELSE      'Visible
                       SELECT CASE IsZoomed(WindowToCheck)
                           CASE 0         'Not Maximized
                                 SELECT CASE IsIconic(WindowToCheck)
                                      CASE 0    'Not Minimized (Normal) = 1
                                           FUNCTION = %SW_SHOWNORMAL
                                      CASE ELSE      'Minimized = 6
                                           FUNCTION = %SW_MINIMIZE
                                 END SELECT
                            CASE ELSE      'Maximized = 3
                                 FUNCTION = %SW_MAXIMIZE
                       END SELECT
             END SELECT
        END FUNCTION
        
        FUNCTION ShowCosmos ALIAS "ShowCosmos"()EXPORT AS LONG
             SELECT CASE IsWindow(HwndMain)
                  CASE 0         'If COSMOS not loaded then load it
                       LoadCosmos
                  CASE ELSE
             END SELECT
             SELECT CASE GetWindowState(HwndMain)
                  CASE %SW_SHOWNORMAL
                  CASE ELSE
                       ShowWindow HwndMain, %SW_SHOWNORMAL
                       SLEEP 100         'Need this for some reason so window will stay in forground
                       UpdateWindow HwndMain
                       DefaultHistory
             END SELECT
             FUNCTION = %True
        END FUNCTION
        
        FUNCTION HideCosmos ALIAS "HideCosmos"()EXPORT AS LONG
             SELECT CASE IsWindow(HwndMain)
                  CASE 0    'Cosmos does not exist
                  CASE ELSE 'Cosmos exists
                       SELECT CASE GetWindowState(HwndMain)
                            CASE %SW_HIDE
                            CASE ELSE
                                 ShowWindow HwndMain, %SW_HIDE
                       END SELECT
             END SELECT
             FUNCTION = %False
        END FUNCTION
        
        FUNCTION IsCosmosVisible ALIAS "IsCosmosVisible"() EXPORT AS LONG
             SELECT CASE IsWindow(HwndMain)
                  CASE 0
                       FUNCTION = %False
                  CASE ELSE
                       FUNCTION = GetWindowState(HwndMain)
             END SELECT
        END FUNCTION
        
        '-------------------------------------------------------------------------------------------------------------------------
        '*** ErrorHandling.inc
        FUNCTION StartTrace ALIAS "StartTrace"() AS LONG
             TRACE NEW "Error Testing Trace"
             TRACE ON
             TraceLogNum = FREEFILE
             TraceLog = "COSMOS Serial Driver Trace " + DATE$ + ".txt"
             REPLACE ":" WITH "-" IN TraceLog
             OPEN TraceLog FOR APPEND AS #TraceLogNum
             NumOfErrorsCosmos = 0
        END FUNCTION
        
        FUNCTION EndTrace ALIAS "EndTrace"() AS LONG
             TRACE ON
             TRACE CLOSE
             CLOSE TraceLogNum
             SLEEP 100           'Just to slow things down
             SELECT CASE NumOfErrorsCosmos
                  CASE > 0        'Errors occured
                  CASE ELSE       'No errors so kill the log
                       KILL TraceLog
             END SELECT
        END FUNCTION
        
        FUNCTION ErrorCheck ALIAS "ErrorCheck"(ErrorCode AS LONG) AS LONG
             LOCAL i AS LONG
             LOCAL CallStack AS STRING
             TRACE ON
             SELECT CASE ErrorCode
                  CASE 0
                  CASE 1 TO 150, 241 TO 255       'PowerBasic Runtime Errors
                       NumOfErrorsCosmos = NumOfErrorsCosmos + 1
                       FOR i = CALLSTKCOUNT TO 1 STEP -1
                            CallStack = CallStack + CALLSTK$(i) + $CRLF
                       NEXT i
                       PRINT #TraceLogNum, "Thread ID = " + STR$(THREADID)
                       PRINT #TraceLogNum, "Error Number = " + STR$(ErrorCode) + " Error Description = " + ERROR$(ErrorCode)
                       PRINT #TraceLogNum, "Call Stack at Error = " + CallStack
                       PRINT #TraceLogNum, "Errors in Session = " + STR$(NumOfErrorsCosmos) + $CRLF + $CRLF + $CRLF
                       ERRCLEAR
             END SELECT
             TRACE OFF
             FUNCTION = %True
        END FUNCTION
        
        '-------------------------------------------------------------------------------------------------------------------------
        '*** Threading.inc
        FUNCTION OpenListenThread ALIAS "OpenListenThread"() AS LONG       'Added to re-enable the listen thread
             ON ERROR GOTO ErrHandler
             CloseThread = %False
             ERRCLEAR
             IF HwndThread = 0 THEN THREAD CREATE ReceiveData(1) TO HwndThread  'Create a "listen" thread to monitor input from device
             FUNCTION = %True
             EXIT FUNCTION
        ErrHandler:
             StartTrace
             ErrorCheck ERR
             EndTrace
        END FUNCTION
        
        FUNCTION CloseListenThread ALIAS "CloseListenThread"() AS LONG
             CloseThread = %True
             IF HwndThread THEN THREAD CLOSE HwndThread TO ThreadDone
             WaitForSingleObject HwndThread, %INFINITE
             IF HwndThread THEN HwndThread = 0
             FUNCTION = ThreadDone
             EXIT FUNCTION
        END FUNCTION
        '-------------------------------------------------------------------------------------------------------------------------
        '*** DlgIoState.inc
        CALLBACK FUNCTION IoStateCallback()
            LOCAL BtnState AS LONG
            LOCAL StatusFlag AS LONG
            LOCAL StatusText AS STRING
        
            SELECT CASE AS LONG CBMSG
                CASE %WM_MDIACTIVATE
                     SELECT CASE CBWPARAM                 'Deactivated window
                          CASE HwndDlgIoState
                                 LstHistoryClearHistory
                     END SELECT
                     SELECT CASE CBLPARAM                 'Activated window
                          CASE HwndDlgIoState
                                 LstHistoryClearHistory
                     END SELECT
                     CONTROL SEND HwndMain, %RchEdHistory, %EM_SETSEL, 1, 1
                     FUNCTION = %False          '%WM_MDIACTIVATE needs 0 (or %False) returned to indicate the message was processed
                CASE %WM_INITDIALOG       'initialise stuff before dialog is drawn
                    StatusFlag = %True '%False      'e.g.
                    LimitState = ""     'Just to get the status screen to refresh
        
                CASE %WM_COMMAND
                    SELECT CASE AS LONG CBCTL
                        CASE %Btn_Stop
                            IF CBCTLMSG = %BN_CLICKED OR CBCTLMSG = 1 THEN
                                 SELECT CASE UCASE$(IoTest)
                                    CASE "TROUBLESHOOTLIMITS"
                                        StopTroubleshootLimits
                                END SELECT
                                EnableMenuItem hMenu, %IDM_TRBLIMITSVIEWON, %MF_ENABLED
                            END IF
                    END SELECT
                CASE %WM_SYSCOMMAND
                    IF (CBWPARAM AND &HFFF0) = %SC_CLOSE THEN   'Must get the CBWPARAM first or this query-unload equiv will not work
                        SELECT CASE ParentClosing
                            CASE %False
                                FUNCTION = %True        'Blocks the close by not allowing the next call to destroy
                                 DefaultHistory
                         SELECT CASE UCASE$(IoTest)
                            CASE "TROUBLESHOOTLIMITS"
                                StopTroubleshootLimits
                        END SELECT
                        EnableMenuItem hMenu, %IDM_TRBLIMITSVIEWON, %MF_ENABLED
                            CASE %True
                                FUNCTION = %False        'Allows the close on the next call with the destroy
                        END SELECT
                    END IF
                CASE %WM_CLOSE
                CASE %WM_DESTROY
            END SELECT
        '     FUNCTION = DefMDIChildProc(CBHNDL, CBMSG, CBWPARAM, CBLPARAM)           'Need this if Window is to show
        '     FUNCTION = DefFrameProc(HwndClient, CBHNDL, CBMSG, CBWPARAM, CBLPARAM)           'Need this if Window is to show
        END FUNCTION
        
        SUB ShowIoStateWindow(TestToRun AS STRING)
            IsDlgIoOpen = %True
            IoTest = TestToRun
            DIM x&, y&
            DIALOG GET LOC HwndMain TO x&, y&
            LOCAL Style&, ExStyle&
            Style& = %WS_CHILD OR %WS_CAPTION OR %WS_SYSMENU OR %WS_CLIPSIBLINGS  'OR %WS_SYSMENU  %WS_CAPTION OR %WS_SYSMENU OR %DS_CENTER
            ExStyle& = 0
            DIALOG NEW HwndClient, "Limits Viewer", x& - 115, y&, 115, 25, Style&, ExStyle&  TO HwndDlgIoState
            CONTROL ADD BUTTON, HwndDlgIoState, %Btn_Stop, "Exit", 30, 5, 50, 15, %WS_CHILD OR _
                                  %WS_VISIBLE OR %WS_TABSTOP OR %BS_AUTOCHECKBOX OR %BS_PUSHLIKE
            DIALOG SHOW MODELESS HwndDlgIoState, CALL IoStateCallback
        END SUB
        
        SUB HideIoStateWindow()
            ON ERROR GOTO ErrHandler
            ERRCLEAR
            LOCAL Result AS LONG
            SELECT CASE IsWindow(HwndDlgIoState  )
                CASE 0
                CASE ELSE
                    DIALOG SHOW STATE HwndDlgIoState, %SW_HIDE TO Result  'Hide the TerminalSimple
            END SELECT
            EXIT SUB
        ErrHandler:
            StartTrace
            ErrorCheck ERR
            EndTrace
        END SUB
        '-------------------------------------------------------------------------------------------------------------------------
        '*** History.inc
        CALLBACK FUNCTION HistoryChildrenCallback() AS LONG
             ON ERROR GOTO ErrHandler
             LOCAL tRect       AS RECT
             LOCAL ActiveWindow AS LONG
             LOCAL MainHeight AS LONG
             LOCAL MainWidth AS LONG
             SELECT CASE CBMSG
                  CASE %WM_SETFOCUS
                       ActiveWindow = SendMessage(HwndClient, %WM_MDIGETACTIVE, BYVAL %NULL, BYVAL %NULL)
                       CALL HideCaret (CBCTL)
                       FUNCTION = 0 : EXIT FUNCTION
                  CASE %WM_KILLFOCUS
                  CASE %WM_MOVE, %WM_SIZE, %WM_CLOSE, %WM_QUERYENDSESSION
                       GetWindowRect HwndMain, tRect
                       MainHeight = tRect.nBottom - tRect.nTop
                       MainWidth = tRect.nRight - tRect.nLeft
                       GetWindowRect hStatus, tRect
                       StatHeight = tRect.nBottom - tRect.nTop  'statusbar height
                       SendMessage HwndMain, %WM_SIZE, %SIZE_RESTORED, MAK(DWORD, MainWidth - 10 , MainHeight - StatHeight - 20)
                       InvalidateRect hStatus, BYVAL %NULL, %True
                       UpdateWindow hStatus
                  CASE %WM_SIZING
             END SELECT
             FUNCTION = CallWindowProc(OldDlgHistoryTxtProc, CBHNDL, CBMSG, CBWPARAM, CBLPARAM) 'Pass back to original
             EXIT FUNCTION
        ErrHandler:
             StartTrace
             ErrorCheck ERR
             EndTrace
        END FUNCTION
        
        CALLBACK FUNCTION HistoryCallBack () AS LONG
            ON ERROR GOTO ErrHandler
            SELECT CASE CBMSG           'Determine what the event is
                CASE %WM_INITDIALOG     'If initializing (opening the History)
                    FUNCTION = %FALSE   ' Return 0 to stop the dialog box engine setting focus
                CASE %WM_MDIACTIVATE
                     FUNCTION = %False          '%WM_MDIACTIVATE needs 0 (or %False) returned to indicate the message was processed
                CASE %WM_KEYDOWN        'In this case re-set focus because of loss of focus
                CASE %WM_MOUSEACTIVATE
                CASE %WM_DROPFILES
                    FUNCTION = %True
                CASE %WM_MOVE
                CASE %WM_SETREDRAW
                CASE  %WM_MOUSEWHEEL
                CASE %WM_COMMAND
                CASE %WM_SIZE
                    FUNCTION = %True
                CASE %WM_SYSCOMMAND
                    IF (CBWPARAM AND &HFFF0) = %SC_CLOSE THEN   'Must get the CBWPARAM first or this query-unload equiv will not work
                       SELECT CASE ParentClosing
                            CASE %False
                                FUNCTION = %True        'Blocks the close by not allowing the next call to destroy
                            CASE %True
                                FUNCTION = %False        'Allows the close on the next call with the destroy
                        END SELECT
                    END IF
                CASE %WM_DESTROY
                CASE ELSE
             END SELECT
        '     FUNCTION = DefMDIChildProc(CBHNDL, CBMSG, CBWPARAM, CBLPARAM)           'Need this if Window is to show
             FUNCTION = DefFrameProc(CBHNDL, HwndClient, CBMSG, CBWPARAM, CBLPARAM)
             EXIT FUNCTION
        ErrHandler:
             StartTrace
             ErrorCheck ERR
             EndTrace
        END FUNCTION
        
        FUNCTION LoadHistory(BYVAL hParent&) AS LONG     'Load main program
             ON ERROR GOTO ErrHandler
             hLibHistory = LoadLibrary("RICHED32.DLL")      'Load the Richedit Dll
             RchEdHistory = CreateWindowEx(%NULL, _                                             ' extended styles
                            $RICHEDIT_CLASS, _                                        ' class name
                            "", _                                              ' caption
                            %WS_CHILD OR %WS_VISIBLE OR %WS_TABSTOP OR %WS_VSCROLL OR %WS_HSCROLL OR %WS_EX_MDICHILD  OR %WS_EX_CLIENTEDGE     _         ' window styles
                            OR %WS_SIZEBOX OR %ES_DISABLENOSCROLL _    'To allow resizing the History window
                            OR %ES_MULTILINE OR %ES_AUTOVSCROLL OR %ES_AUTOHSCROLL _   ' class styles
                            OR %ES_WANTRETURN OR %ES_NOHIDESEL  OR %ES_LEFT OR %ES_READONLY, _      ' OR %EM_SETREADONLY' class styles
                            0, 0, _                                           ' left, top
                            680, 180, _                                            ' width, height
                            hParent&, %RchEdHistory, _                                       ' handle of parent, control ID
                            hInst, BYVAL %NULL)                             ' handle of instance, creation parameters
        '*** Subclass the RichEditBox
             CONTROL HANDLE HwndMain, %RchEdHistory TO hHistory
             OldDlgHistoryTxtProc = SetWindowLong(hHistory, %GWL_WNDPROC, CODEPTR(HistoryChildrenCallback )) 'Subclass Edit control
             CONTROL SEND HwndMain, %RchEdHistory, %EM_SETBKGNDCOLOR, 0, RGB(255,254,209)    'Post-it Note Color
             FUNCTION=%True
             EXIT FUNCTION
        ErrHandler:
             StartTrace
             ErrorCheck ERR
             EndTrace
        END FUNCTION
        
        SUB LstHistoryAddLine(FontType AS STRING, FontColor AS STRING, TextToAdd AS STRING, AppendCr AS LONG)
             ON ERROR GOTO ErrHandler
             LOCAL LineCount AS LONG
             LOCAL i AS LONG
             LOCAL TempRtfText AS STRING
             LOCAL RemoveRtfText AS STRING
             LOCAL TempText AS STRING
             LOCAL eStream AS EDITSTREAM
             LOCAL ret AS LONG
             SELECT CASE FontColor
                  CASE "Red"
                       SELECT CASE AppendCr
                            CASE %False
                                 REPLACE $CR WITH $CR + RtfCommandsEndOfLine IN TextToAdd
                                 HistoryRtfText = HistoryRtfText + RtfFontRed + TextToAdd
                            CASE %True
                                 REPLACE $CR WITH $CR + RtfCommandsEndOfLine IN TextToAdd
                                 HistoryRtfText = HistoryRtfText + RtfFontRed + RtfCommandsEndOfLine + TextToAdd
                            CASE 2
                                 REPLACE $CR WITH $CR + RtfCommandsEndOfLine IN TextToAdd
                                 HistoryRtfText = HistoryRtfText + RtfFontRed + TextToAdd + RtfCommandsEndOfLine
                       END SELECT
                  CASE "Yellow"
                       SELECT CASE AppendCr
                            CASE %False
                                 REPLACE $CR WITH $CR + RtfCommandsEndOfLine IN TextToAdd
                                 HistoryRtfText = HistoryRtfText + RtfFontYellow + TextToAdd
                            CASE %True
                                 REPLACE $CR WITH $CR + RtfCommandsEndOfLine IN TextToAdd
                                 HistoryRtfText = HistoryRtfText + RtfFontYellow + RtfCommandsEndOfLine + TextToAdd
                            CASE 2
                                 REPLACE $CR WITH $CR + RtfCommandsEndOfLine IN TextToAdd
                                 HistoryRtfText = HistoryRtfText + RtfFontYellow + TextToAdd + RtfCommandsEndOfLine
                       END SELECT
                  CASE "Auqua", "Aqua"    'In the case of typo's in code
                       SELECT CASE AppendCr
                            CASE %False
                                 REPLACE $CR WITH $CR + RtfCommandsEndOfLine IN TextToAdd
                                 HistoryRtfText = HistoryRtfText + RtfFontAqua + TextToAdd
                            CASE %True
                                 REPLACE $CR WITH $CR + RtfCommandsEndOfLine IN TextToAdd
                                 HistoryRtfText = HistoryRtfText + RtfFontAqua + RtfCommandsEndOfLine + TextToAdd
                            CASE 2
                                 REPLACE $CR WITH $CR + RtfCommandsEndOfLine IN TextToAdd
                                 HistoryRtfText = HistoryRtfText + RtfFontAqua + TextToAdd + RtfCommandsEndOfLine
                       END SELECT
                  CASE "Blue"
                       SELECT CASE AppendCr
                            CASE %False
                                 REPLACE $CR WITH $CR + RtfCommandsEndOfLine IN TextToAdd
                                 HistoryRtfText = HistoryRtfText + RtfFontBlue + TextToAdd
                            CASE %True
                                 REPLACE $CR WITH $CR + RtfCommandsEndOfLine IN TextToAdd
                                 HistoryRtfText = HistoryRtfText + RtfFontBlue + RtfCommandsEndOfLine + TextToAdd
                            CASE 2
                                 REPLACE $CR WITH $CR + RtfCommandsEndOfLine IN TextToAdd
                                 HistoryRtfText = HistoryRtfText + RtfFontBlue + TextToAdd + RtfCommandsEndOfLine
                       END SELECT
                  CASE "Green"
                       SELECT CASE AppendCr
                            CASE %False
                                 REPLACE $CR WITH $CR + RtfCommandsEndOfLine IN TextToAdd
                                 HistoryRtfText = HistoryRtfText + RtfFontGreen + TextToAdd
                            CASE %True
                                 REPLACE $CR WITH $CR + RtfCommandsEndOfLine IN TextToAdd
                                 HistoryRtfText = HistoryRtfText + RtfFontGreen + RtfCommandsEndOfLine + TextToAdd
                            CASE 2
                                 REPLACE $CR WITH $CR + RtfCommandsEndOfLine IN TextToAdd
                                 HistoryRtfText = HistoryRtfText + RtfFontGreen + TextToAdd + RtfCommandsEndOfLine
                       END SELECT
                  CASE "Black"
                       SELECT CASE AppendCr
                            CASE %False
                                 REPLACE $CR WITH $CR + RtfCommandsEndOfLine IN TextToAdd
                                 HistoryRtfText = HistoryRtfText + RtfFontBlack + TextToAdd
                            CASE %True
                                 REPLACE $CR WITH $CR + RtfCommandsEndOfLine IN TextToAdd
                                 HistoryRtfText = HistoryRtfText + RtfFontBlack + RtfCommandsEndOfLine + TextToAdd
                            CASE 2
                                 REPLACE $CR WITH $CR + RtfCommandsEndOfLine IN TextToAdd
                                 HistoryRtfText = HistoryRtfText + RtfFontBlack + TextToAdd + RtfCommandsEndOfLine
                       END SELECT
                  CASE "White"
                       SELECT CASE AppendCr
                            CASE %False
                                 REPLACE $CR WITH $CR + RtfCommandsEndOfLine IN TextToAdd
                                 HistoryRtfText = HistoryRtfText + RtfFontWhite + TextToAdd
                            CASE %True
                                 REPLACE $CR WITH $CR + RtfCommandsEndOfLine IN TextToAdd
                                 HistoryRtfText = HistoryRtfText + RtfFontWhite + RtfCommandsEndOfLine + TextToAdd
                            CASE 2
                                 REPLACE $CR WITH $CR + RtfCommandsEndOfLine IN TextToAdd
                                 HistoryRtfText = HistoryRtfText + RtfFontWhite + TextToAdd + RtfCommandsEndOfLine
                       END SELECT
                  CASE "Teal"
                       SELECT CASE AppendCr
                            CASE %False
                                 REPLACE $CR WITH $CR + RtfCommandsEndOfLine IN TextToAdd
                                 HistoryRtfText = HistoryRtfText + RtfFontTeal + TextToAdd
                            CASE %True
                                 REPLACE $CR WITH $CR + RtfCommandsEndOfLine IN TextToAdd
                                 HistoryRtfText = HistoryRtfText + RtfFontTeal + RtfCommandsEndOfLine + TextToAdd
                            CASE 2
                                 REPLACE $CR WITH $CR + RtfCommandsEndOfLine IN TextToAdd
                                 HistoryRtfText = HistoryRtfText + RtfFontTeal + TextToAdd + RtfCommandsEndOfLine
                       END SELECT
                  CASE "Lime"
                       SELECT CASE AppendCr
                            CASE %False
                                 REPLACE $CR WITH $CR + RtfCommandsEndOfLine IN TextToAdd
                                 HistoryRtfText = HistoryRtfText + RtfFontLime + TextToAdd
                            CASE %True
                                 REPLACE $CR WITH $CR + RtfCommandsEndOfLine IN TextToAdd
                                 HistoryRtfText = HistoryRtfText + RtfFontLime + RtfCommandsEndOfLine + TextToAdd
                            CASE 2
                                 REPLACE $CR WITH $CR + RtfCommandsEndOfLine IN TextToAdd
                                 HistoryRtfText = HistoryRtfText + RtfFontLime + TextToAdd + RtfCommandsEndOfLine
                       END SELECT
                  CASE "Fuchsia"
                       SELECT CASE AppendCr
                            CASE %False
                                 REPLACE $CR WITH $CR + RtfCommandsEndOfLine IN TextToAdd
                                 HistoryRtfText = HistoryRtfText + RtfFontFuchsia + TextToAdd
                            CASE %True
                                 REPLACE $CR WITH $CR + RtfCommandsEndOfLine IN TextToAdd
                                 HistoryRtfText = HistoryRtfText + RtfFontFuchsia + RtfCommandsEndOfLine + TextToAdd
                            CASE 2
                                 REPLACE $CR WITH $CR + RtfCommandsEndOfLine IN TextToAdd
                                 HistoryRtfText = HistoryRtfText + RtfFontFuchsia + TextToAdd + RtfCommandsEndOfLine
                       END SELECT
                  CASE "Olive"
                       SELECT CASE AppendCr
                            CASE %False
                                 REPLACE $CR WITH $CR + RtfCommandsEndOfLine IN TextToAdd
                                 HistoryRtfText = HistoryRtfText + RtfFontOlive + TextToAdd
                            CASE %True
                                 REPLACE $CR WITH $CR + RtfCommandsEndOfLine IN TextToAdd
                                 HistoryRtfText = HistoryRtfText + RtfFontOlive + RtfCommandsEndOfLine + TextToAdd
                            CASE 2
                                 REPLACE $CR WITH $CR + RtfCommandsEndOfLine IN TextToAdd
                                 HistoryRtfText = HistoryRtfText + RtfFontOlive + TextToAdd + RtfCommandsEndOfLine
                       END SELECT
                  CASE "Maroon"
                       SELECT CASE AppendCr
                            CASE %False
                                 REPLACE $CR WITH $CR + RtfCommandsEndOfLine IN TextToAdd
                                 HistoryRtfText = HistoryRtfText + RtfFontMaroon + TextToAdd
                            CASE %True
                                 REPLACE $CR WITH $CR + RtfCommandsEndOfLine IN TextToAdd
                                 HistoryRtfText = HistoryRtfText + RtfFontMaroon + RtfCommandsEndOfLine + TextToAdd
                            CASE 2
                                 REPLACE $CR WITH $CR + RtfCommandsEndOfLine IN TextToAdd
                                 HistoryRtfText = HistoryRtfText + RtfFontMaroon + TextToAdd + RtfCommandsEndOfLine
                       END SELECT
             END SELECT
        '*** KEEP THE WINDOW FROM GETTING TOO BIG
             SELECT CASE LEN(HistoryRtfText)
                  CASE > = 20000
                       TempRtfText = HistoryRtfText
                       DO WHILE INSTR(TempRtfText, RtfCommandsEndOfLine)
                            LineCount = LineCount + 1
                            TempRtfText = MID$(TempRtfText, INSTR(TempRtfText, RtfCommandsEndOfLine) + LEN(RtfCommandsEndOfLine))
                            IF ParentClosing = %True THEN EXIT DO
                       LOOP
        'Loop through to determine how much to remove then remove it
                       RemoveRtfText = ""
                       TempRtfText = HistoryRtfText
                       FOR i = 1 TO ROUND(((LineCount * .1) + .5), 0)  'Round to the truly closets whole number
                            RemoveRtfText = RemoveRtfText + MID$(TempRtfText, 1, INSTR(TempRtfText, RtfCommandsEndOfLine) + LEN(RtfCommandsEndOfLine))
                            TempRtfText = MID$(TempRtfText, INSTR(TempRtfText, RtfCommandsEndOfLine) + LEN(RtfCommandsEndOfLine))
                       NEXT i
                       HistoryRtfText = MID$(HistoryRtfText, LEN(RemoveRtfText))
                       SLEEP 1         'JUST TO GIVE THE PROCESSOR A BREAK
                  CASE ELSE
             END SELECT
             eStream.pfnCallback = CODEPTR(RichEditStreamInString) 'pointer to RichEdit callback procedure
             gPos = 1
             gTxt = RtfCommandsStart + RtfFontCourier + RtfFontSize(12) + HistoryRtfText + RtfCommandsEnd
             gPtr = STRPTR(gTxt)
             IF IsWindow(RchEdHistory) THEN ret = SendMessage(RchEdHistory, %EM_STREAMIN, %SF_RTF, VARPTR(eStream)) 'stream in text
             InvalidateRect HwndMain, BYVAL %NULL, %True
             UpdateWindow HwndMain
             CONTROL GET TEXT HwndMain, %RchEdHistory TO TempText
             CONTROL SEND HwndMain, %RchEdHistory, %EM_SETSEL,LEN(TempText), LEN(TempText)
             SELECT CASE TracePort
                  CASE %False
                  CASE %True
                       PRINT #TracePortLogNum, TempText
             END SELECT
             EXIT SUB
        ErrHandler:
             StartTrace
             ErrorCheck ERR
             EndTrace
        END SUB
        
        SUB LstHistoryRestoreHistory()
             LOCAL TempText AS STRING
             LOCAL eStream AS EDITSTREAM
             eStream.pfnCallback = CODEPTR(RichEditStreamInString) 'pointer to RichEdit callback procedure
             gPos = 1
             gTxt = RtfCommandsStart + RtfFontCourier + RtfFontSize(12) + HistoryRtfText + RtfCommandsEnd
             gPtr = STRPTR(gTxt)
             SendMessage RchEdHistory, %EM_STREAMIN, %SF_RTF, VARPTR(eStream) 'stream in text
             CONTROL GET TEXT HwndMain, %RchEdHistory TO TempText
             CONTROL SEND HwndMain, %RchEdHistory, %EM_SETSEL,LEN(TempText), LEN(TempText)
        END SUB
        
        SUB LstHistoryClearHistory()
             HistoryRtfText = ""
             CONTROL SET TEXT HwndMain, %RchEdHistory, ""
             InvalidateRect HwndMain, BYVAL %NULL, %True
             UpdateWindow HwndMain
        END SUB
        
        SUB DefaultHistory()
             LOCAL LongStr AS LONG
             LstHistoryClearHistory
             LstHistoryAddLine "fontc12", "Blue", "COSMOS", %False
             LstHistoryAddLine "fontc12", "Maroon", $TAB + "Select a Tool or File to edit from menu above", %True
        
             LstHistoryAddLine "fontc12", "Blue", "", %True
             LstHistoryAddLine "fontc12", "Blue", "Tools Available", %True
             LstHistoryAddLine "fontc12", "Maroon", $TAB + "File" + $TAB + "---> Create, Edit, Send and Recieve program scripts to, and from the VXM", %True
        
             LongStr = LEN("|- Troubleshooting")
             LstHistoryAddLine "fontc12", "Blue", "", %True
             LstHistoryAddLine "fontc12", "Maroon", $TAB + "Tools -", %True
             LstHistoryAddLine "fontc12", "Maroon", $TAB + SPACE$(LEN("Tools -")) + "|- QuickMoves" + SPACE$(LongStr - LEN("|- QuickMoves")) + " ---> Virtual Jog, Quick Move motor (absolute or relative position), TeachMode", %True
             LstHistoryAddLine "fontc12", "Maroon", $TAB + SPACE$(LEN("Tools -")) + "|- Code Creator" + SPACE$(LongStr - LEN("|- Code Creator")) + " ---> Create program scripts without needing to know VXM commands", %True
             LstHistoryAddLine "fontc12", "Maroon", $TAB + SPACE$(LEN("Tools -")) + "|- Terminal" + SPACE$(LongStr - LEN("|- Terminal")) + " ---> Communicate directly with VXM via Terminal", %True
             LstHistoryAddLine "fontc12", "Maroon", $TAB + SPACE$(LEN("Tools -")) + "|- Tutorials" + SPACE$(LongStr - LEN("|- Tutorials")) + " ---> Tutorials on VXM commands, and common uses", %True
             LstHistoryAddLine "fontc12", "Maroon", $TAB + SPACE$(LEN("Tools -")) + "|- Troubleshooting" + " ---> Got a problem? Troubleshooting tools should solve the problem", %True
        
             LongStr = LEN("|- Program Selector Switch (Thumbwheel)")
             LstHistoryAddLine "fontc12", "Blue", "", %True
             LstHistoryAddLine "fontc12", "Maroon", $TAB + "Setup -", %True
             LstHistoryAddLine "fontc12", "Maroon", $TAB + SPACE$(LEN("Setup -")) + "|- Wizard" + SPACE$(LongStr - LEN("|- Wizard")) + " ---> Walks you through the complete setup necessary to start using the VXM", %True
             LstHistoryAddLine "fontc12", "Maroon", $TAB + SPACE$(LEN("Setup -")) + "|- Port" + SPACE$(LongStr - LEN("|- Port")) + " ---> Setup communication directly, or auto-detect what port your VXM is connected to", %True
             LstHistoryAddLine "fontc12", "Maroon", $TAB + SPACE$(LEN("Setup -")) + "|- Axis" + SPACE$(LongStr - LEN("|- Axis")) + " ---> Setup each axis so the motors move properly", %True
             LstHistoryAddLine "fontc12", "Maroon", $TAB + SPACE$(LEN("Setup -")) + "|- Joystick and Jog" + SPACE$(LongStr - LEN("|- Joystick and Jog")) + " ---> Set VXM to allow Joystick and Jog control", %True
             LstHistoryAddLine "fontc12", "Maroon", $TAB + SPACE$(LEN("Setup -")) + "|- Program Selector Switch (Thumbwheel)"  + " ---> Set VXM to allow 'stand-alone' program selection", %True
             LstHistoryAddLine "fontc12", "Maroon", $TAB + SPACE$(LEN("Setup -")) + "|- Multi-Function User Inputs" + SPACE$(LongStr - LEN("|- Multi-Function User Inputs")) + " ---> Set VXM to allow various options depending on input", %True
             LstHistoryAddLine "fontc12", "Maroon", $TAB + SPACE$(LEN("Setup -")) + "|- Controller Mode" + SPACE$(LongStr - LEN("|- Controller Mode")) + " ---> Set VXM mode, invert motor direction, home motor 1st time run after power-up", %True
        
             LstHistoryAddLine "fontc12", "Blue", "", %True
             LstHistoryAddLine "fontc12", "Maroon", $TAB + "Help -", %True
             LstHistoryAddLine "fontc12", "Maroon", $TAB + SPACE$(LEN("Help -")) + "|- Documentation " + $TAB + "---> VXM Users Manual, QuickStarts, Application Notes", %True
             LstHistoryAddLine "fontc12", "Maroon", $TAB + SPACE$(LEN("Help -")) + "|- Online Support" + $TAB + "---> Various websites with information not covered here", %True
             CONTROL SEND HwndMain, %RchEdHistory, %EM_SETSEL, 1, 1
        END SUB
        '-------------------------------------------------------------------------------------------------------------------------
        
        
        '***************************************************************************
        '*** RTF.inc and RichEdit.inc should be their own *.inc files due to size
        '***************************************************************************
        
        
        '-------------------------------------------------------------------------------------------------------------------------
        '*** WaitForChar.inc
        FUNCTION WaitForChar ALIAS "WaitForChar"(CharToWaitFor AS ASCIIZ * %MAX_PATH, OPTIONAL BYVAL TimeOutTime AS LONG) EXPORT AS LONG
             ON ERROR GOTO ErrHandler
             LOCAL SendText AS STRING
             LOCAL HoldBuffer AS STRING
             LOCAL TempBuffer AS STRING
             LOCAL RemoveChar AS ASCIIZ * %MAX_PATH
             LOCAL TempSearchString AS ASCIIZ * %MAX_PATH
             LOCAL CharWaitingFor AS ASCIIZ * %MAX_PATH
             LOCAL StartTime AS CURRENCY
             IF TimeOutTime <> 0 THEN StartTime = TIMER
             CharWaitingFor = CharToWaitFor
             WaitingForChar = %True
             DO
                  TempSearchString = ReadFromPort
                  SLEEP 10
                  TempSearchString = STRREVERSE$(TempSearchString)
                  SELECT CASE TimeOutTime
                       CASE 0
        '*** Add routine to stop user lockout if waiting forever and control not on or will never return
                            ReadFromPort
                            HoldBuffer = Buffer
                            SendText = "V"
                            SendToPort SendText
                            SLEEP 10
                            ReadFromPort
                            TempBuffer = Buffer
                            REPLACE HoldBuffer WITH "" IN TempBuffer
                            RemoveChar = TempBuffer      'Change to Asciiz to remove from port
                            SELECT CASE TempBuffer       'If no response then exit the wait
                                 CASE ""                     'Just in case try one more time
                                      ReadFromPort
                                      HoldBuffer = Buffer
                                      SendText = "V"
                                      SendToPort SendText
                                      SLEEP 10
                                      ReadFromPort
                                      TempBuffer = Buffer
                                      REPLACE HoldBuffer WITH "" IN TempBuffer
                                      RemoveChar = TempBuffer      'Change to Asciiz to remove from port
                                      SELECT CASE TempBuffer       'If no response then exit the wait
                                           CASE ""                     'Still nothing so break out
                                                FUNCTION = %True               'Changed to true just to shut labview up
                                                WaitingForChar = %False
                                                EXIT DO
                                           CASE ELSE
                                      END SELECT
                                 CASE ELSE
                            END SELECT
        '*** Put error checks 1st then check for replies that normally dont appear
                            SELECT CASE INSTR(TempBuffer, "Ë")
                                 CASE 0
                                 CASE ELSE
                                      FUNCTION = %True               'Changed to true just to shut labview up
                                      WaitingForChar = %False
                                      EXIT DO
                            END SELECT
        
                            SELECT CASE INSTR(TempBuffer, "b")
                                 CASE 0
                                 CASE ELSE
                                      RemoveFromPort RemoveChar
                                      FUNCTION = %True               'Changed to true just to shut labview up
                                      WaitingForChar = %False
                                      EXIT DO
                            END SELECT
                            SELECT CASE INSTR(TempBuffer, "R")
                                 CASE 0
                                 CASE ELSE
                                      RemoveFromPort RemoveChar
                                      FUNCTION = %True               'Changed to true just to shut labview up
                                      WaitingForChar = %False
                                      EXIT DO
                            END SELECT
                            SELECT CASE INSTR(TempBuffer, "J")
                                 CASE 0
                                 CASE ELSE
                                      RemoveFromPort RemoveChar
                                      FUNCTION = %True               'Changed to true just to shut labview up
                                      WaitingForChar = %False
                                      EXIT DO
                            END SELECT
                       CASE ELSE
                            SELECT CASE TIMER
                                 CASE < StartTime + (TimeOutTime/1000)
                                 CASE >= StartTime + (TimeoutTime/1000) + (1/1000)        'Added extra ms so msg below not shown unless char not found
                                      WaitingForChar = %False
                                      EXIT DO
                            END SELECT
                  END SELECT
                  IF ParentClosing = %True THEN TempSearchString = TempSearchString + CharToWaitFor
                  IF ParentClosing = %True THEN EXIT DO
             LOOP UNTIL INSTR(TempSearchString, CharToWaitFor) 'Dont use "OR EscapeFunction = %True" cause a Binary OR is used
             SELECT CASE INSTR(TempSearchString,CharToWaitFor)
                  CASE 0
                       FUNCTION = %False
                  CASE ELSE
                       RemoveFromPort(CharToWaitFor)
                       FUNCTION = %True
             END SELECT
             EXIT FUNCTION
        ErrHandler:
             StartTrace
             ErrorCheck ERR
             EndTrace
        END FUNCTION
        
        FUNCTION WaitForCharWithMotorPosition ALIAS "WaitForCharWithMotorPosition"(CharToWaitFor AS ASCIIZ * %MAX_PATH, BYVAL MotorNumber AS LONG, OPTIONAL BYVAL ReportToWindowHwnd AS LONG, OPTIONAL BYVAL TimeOutTime AS LONG) EXPORT AS LONG
             ON ERROR GOTO ErrHandler
             LOCAL SendText AS STRING
             LOCAL HoldBuffer AS STRING
             LOCAL TempBuffer AS STRING
             LOCAL RemoveChar AS ASCIIZ * %MAX_PATH
             LOCAL TempSearchString AS ASCIIZ * %MAX_PATH
             LOCAL CharWaitingFor AS ASCIIZ * %MAX_PATH
             LOCAL MotorPosition AS STRING
             LOCAL HandleMotorPosition AS LONG
             LOCAL StartTime AS CURRENCY
             HandleMotorPosition = ReportToWindowHwnd
        '     Buffer = ""
             IF TimeOutTime <> 0 THEN StartTime = TIMER
             CharWaitingFor=CharToWaitFor
             DO
                  TempSearchString = ReadFromPort
                  SLEEP 100
                  TempSearchString = STRREVERSE$(TempSearchString)
                  SELECT CASE TimeOutTime
                       CASE 0
                            ReadFromPort
                            HoldBuffer = Buffer
                            SendText = "V"
                            SendToPort SendText
                            SLEEP 100
                            ReadFromPort
                            TempBuffer = Buffer
                            REPLACE HoldBuffer WITH "" IN TempBuffer
                            RemoveChar = TempBuffer      'Change to Asciiz to remove from port
                            SELECT CASE TempBuffer       'If no response then exit the wait
                                 CASE ""                     'Just in case try one more time
                                      ReadFromPort
                                      HoldBuffer = Buffer
                                      SendText = "V"
                                      SendToPort SendText
                                      SLEEP 100
                                      ReadFromPort
                                      TempBuffer = Buffer
                                      REPLACE HoldBuffer WITH "" IN TempBuffer
                                      RemoveChar = TempBuffer      'Change to Asciiz to remove from port
                                      SELECT CASE TempBuffer       'If no response then exit the wait
                                           CASE ""                     'Still nothing so break out
                                                FUNCTION = %True               'Changed to true just to shut labview up
                                                WaitingForChar = %False
                                                EXIT FUNCTION
                                           CASE ELSE
                                      END SELECT
                                 CASE ELSE
                            END SELECT
        '*** Put error checks 1st then check for replies that normally dont appear
                            SELECT CASE INSTR(TempBuffer, "Ë")
                                 CASE 0
                                 CASE ELSE
                                      FUNCTION = %True               'Changed to true just to shut labview up
                                      WaitingForChar = %False
                                      EXIT FUNCTION
                            END SELECT
                            SELECT CASE INSTR(TempBuffer, "b")
                                 CASE 0
                                 CASE ELSE
                                      RemoveFromPort RemoveChar
                                      FUNCTION = %True               'Changed to true just to shut labview up
                                      WaitingForChar = %False
                                      EXIT FUNCTION
                            END SELECT
                            SELECT CASE INSTR(TempBuffer, "R")
                                 CASE 0
                                 CASE ELSE
                                      RemoveFromPort RemoveChar
                                      FUNCTION = %True               'Changed to true just to shut labview up
                                      WaitingForChar = %False
                                      EXIT FUNCTION
                            END SELECT
                            SELECT CASE INSTR(TempBuffer, "J")
                                 CASE 0
                                 CASE ELSE
                                      RemoveFromPort RemoveChar
                                      FUNCTION = %True               'Changed to true just to shut labview up
                                      WaitingForChar = %False
                                      EXIT FUNCTION
                            END SELECT
                       CASE ELSE
                            SELECT CASE TIMER
                                 CASE < StartTime + (TimeOutTime/1000)
                                 CASE >= StartTime + (TimeoutTime/1000) + (1/1000)        'Added extra ms so msg below not shown unless char not found
                                      WaitingForChar = %False
                                      EXIT DO
                            END SELECT
                  END SELECT
                  Buffer = ""
                  SELECT CASE MotorNumber
                       CASE 3 TO 4           'Cant get motor 3 or 4 positions while VXM is busy
                       CASE 1 TO 2
        '                    MotorPosition = GetMotorPosition(MotorNumber)
                          MotorPosition = "+00000000"                      '<--- Purposely faked for demo
                            IF HandleMotorPosition THEN SendMessage HandleMotorPosition, %WM_SETTEXT, 0, BYVAL STRPTR(MotorPosition)  'Send position back to parent caller
                  END SELECT
                  IF ParentClosing = %True THEN TempSearchString = TempSearchString + CharToWaitFor
                  IF ParentClosing = %True THEN EXIT DO
             LOOP UNTIL INSTR(TempSearchString, CharToWaitFor) 'Dont use "OR EscapeFunction = %True" cause a Binary OR is used
             SELECT CASE INSTR(TempSearchString,CharToWaitFor)
                  CASE 0
                       FUNCTION = %False
                  CASE ELSE
                       RemoveFromPort(CharToWaitFor)
                       FUNCTION = %True
             END SELECT
             EXIT FUNCTION
        ErrHandler:
             StartTrace
             ErrorCheck ERR
             EndTrace
        END FUNCTION
        '-------------------------------------------------------------------------------------------------------------------------
        '*** PortFunctions.inc
        FUNCTION OpenPort ALIAS "OpenPort"(BYVAL ComPortNumber AS LONG, BYVAL ComPortBaudRate AS LONG) EXPORT AS LONG  'Configure and open serial port
             LOCAL ComPort AS STRING
             LOCAL BaudRate AS STRING
             LOCAL Dummy AS STRING
             ComPort = "\\.\COM1"                                                    '<--- Purposely set for debug demo
        '     BaudRate = STR$(ComPortBaudRate)          'For Display Purposes
              BaudRate = "9600"                                                    '<--- Purposely set for debug demo
             HwndPort = FREEFILE               'Handle to the file (aka ComPort)
             COMM OPEN ComPort AS HwndPort  'Open the port
             ErrPort = ERR
             SELECT CASE HwndPort          'If Error then
                  CASE 0
                     FUNCTION = %False        'Return false to function call
                       ClosePort
                       EXIT FUNCTION             'Exit if port cannot be opened
                  CASE ELSE
              END SELECT
             SELECT CASE FILEATTR(HwndPort, 0)   'Check if the port is truly Open
                  CASE 0
                       FUNCTION = %False        'Return false to function call
                       ClosePort
                       EXIT FUNCTION
                  CASE ELSE
             END SELECT
             COMM SET HwndPort, BAUD      = ComPortBaudRate  'Set the BaudRate
             COMM SET HwndPort, BYTE      = 8                    ' 8 bits
             COMM SET HwndPort, PARITY   = %False             ' No parity
             COMM SET HwndPort, STOP      = 0                    ' 1 stop bit
             COMM SET HwndPort, TXBUFFER = 2048               ' 4096                ' 4k transmit buffer
             COMM SET HwndPort, RXBUFFER = 2048               ' 4096                ' 4k receive buffer
             COMM SET HwndPort, NULL      = %False               'Do not toss the null valued bytes due to the $ for reading the Output States
             COMM PRINT HwndPort, $NUL       'Issue a CR/LF and flush the receive buffer
             SLEEP 1
             IF FILEATTR(HwndPort, 0) THEN COMM RECV HwndPort, COMM(HwndPort, RXQUE), dummy   'If anything is on the port, then remove it and put it in dummy variable
             FUNCTION = %TRUE          'If it made it this far then return True to function call
             DIALOG DOEVENTS
             OpeningPort = %False
        END FUNCTION
        
        FUNCTION IsPortOpen ALIAS "IsPortOpen"() EXPORT AS LONG
             SELECT CASE FILEATTR(HwndPort, 0)   'Check if the port is truly Open
                  CASE 0
                       FUNCTION = %False        'Return false to function call
                  CASE ELSE
                       FUNCTION = %True
             END SELECT
        END FUNCTION
        
        FUNCTION ClosePort ALIAS "ClosePort"() EXPORT AS LONG  'Close serial port
              LOCAL Dummy AS STRING
             OpeningPort = %False
             IF FILEATTR(HwndPort, 0) THEN COMM RECV HwndPort, COMM(HwndPort, RXQUE), dummy  'Get it
             IF FILEATTR(HwndPort, 0) THEN COMM CLOSE HwndPort               'Close the port (Handle#)
             FUNCTION = %True          'Return True to function call
             HwndPort = %False
             DIALOG DOEVENTS
        END FUNCTION
        
        FUNCTION SendToPort ALIAS "SendToPort" (CommandOut AS STRING) EXPORT AS LONG 'Send Command to device via Cptr or actual string
             ON ERROR GOTO ErrHandler
             LOCAL SendText AS STRING
             LOCAL lResult AS LONG
        '     local StringPointer AS ASCIIZ PTR      'Setup a pointer in case commandout is a pointer
        '     StringPointer = VARPTR(CommandOut)  'Get the location of the pointer
        '     SendText = @StringPointer
             SendText = CommandOut
             IF ParentClosing = %True THEN EXIT FUNCTION
             IF FILEATTR(HwndPort, 0) THEN COMM SEND HwndPort, SendText
             DIALOG DOEVENTS                          'Message Pump for DLL version of the form to react
             FUNCTION = %True
             EXIT FUNCTION
        ErrHandler:
             ErrorCheck ERR
        END FUNCTION
        
        FUNCTION ReceiveData(BYVAL HwndTerminal AS DWORD) AS LONG
             WHILE ISFALSE CloseThread
                   IF ParentClosing = %True THEN EXIT DO
                  IF FILEATTR(HwndPort, 0) THEN CharsInBuffer = COMM(HwndPort, RXQUE) ' Test the RX buffer
                  SELECT CASE CharsInBuffer
                       CASE< 1
                             SLEEP 1
                        CASE ELSE
                            ReadFromPort
                  END SELECT
             WEND
        END FUNCTION
        
        FUNCTION ReadFromPort ALIAS "ReadFromPort"() EXPORT AS STRING  'Get reply from device
             LOCAL lResult AS LONG
             LOCAL TempBuffer        AS STRING        'Temp ComPort Buffer
             IF ParentClosing = %True THEN EXIT FUNCTION
             IF FILEATTR(HwndPort, 0) THEN CharsInBuffer = COMM(HwndPort, RXQUE) ' Test the RX buffer
             SELECT CASE CharsInBuffer
                  CASE< 1
                          SLEEP 1
                  CASE ELSE
                       IF FILEATTR(HwndPort, 0) THEN COMM RECV HwndPort, CharsInBuffer, TempBuffer        ' Read incoming characters.
                       Buffer = Buffer & TempBuffer
             END SELECT
             FUNCTION = Buffer     'Return the buffer value to function call
        END FUNCTION
        
        FUNCTION WaitReply(MinNumOfChars AS LONG, TimeoutTime AS LONG)AS LONG
             LOCAL StartTime AS CURRENCY
             StartTime = TIMER
             DO
                  SELECT CASE CountCharsAtPort
                       CASE > = MinNumOfChars
                            EXIT DO
                       CASE ELSE
                  END SELECT
                  IF TIMER > = StartTime + (TimeOutTime/1000) THEN EXIT DO
              IF ParentClosing = %True THEN EXIT DO
             LOOP
             FUNCTION = %True
        END FUNCTION
        
        FUNCTION ReadAndThenClearFromPort ALIAS "ReadAndThenClearFromPort"() EXPORT AS STRING  'Get reply from device
             TRACE ON
             ON ERROR GOTO ErrHandler
             ERRCLEAR
             FUNCTION = ReadFromPort
             ClearPort
             EXIT FUNCTION
        ErrHandler:
             ErrorCheck ERR
        END FUNCTION
        
        FUNCTION CountCharsAtPort ALIAS "CountCharsAtPort"() EXPORT AS LONG 'Count Number of Chars at port
             TRACE ON
             ON ERROR GOTO ErrHandler
             ERRCLEAR
             IF FILEATTR(HwndPort, 0) THEN CharsInBuffer = COMM(HwndPort, RXQUE) ' Test the RX buffer
             SELECT CASE CharsInBuffer
                  CASE< 1
                  CASE ELSE
                       ReadFromPort
             END SELECT
             FUNCTION = LEN(Buffer)
             EXIT FUNCTION
        ErrHandler:
             ErrorCheck ERR
        END FUNCTION
        
        FUNCTION SearchForChars ALIAS "SearchForChars"(CharsToFind AS ASCIIZ) EXPORT AS LONG
             TRACE ON
             ON ERROR GOTO ErrHandler
             ERRCLEAR
             LOCAL TempString AS STRING
             LOCAL StringPointer AS ASCIIZ PTR      'Setup a pointer in case commandout is a pointer
             StringPointer = VARPTR(CharsToFind)  'Get the location of the pointer
             SELECT CASE StringPointer
                  CASE 0
                       FUNCTION = %False
                       EXIT FUNCTION
                  CASE ELSE
             END SELECT
             [email protected]
        '     @StringPointer = STRREVERSE$(@StringPointer)
             TempString = STRREVERSE$(TempString)
             IF FILEATTR(HwndPort, 0) THEN CharsInBuffer = COMM(HwndPort, RXQUE) ' Test the RX buffer
             SELECT CASE CharsInBuffer
                  CASE< 1
                  CASE ELSE
                       ReadFromPort
             END SELECT
             Buffer = STRREVERSE$(Buffer)
             SELECT CASE INSTR(Buffer,@StringPointer)
                  CASE 0
                       FUNCTION = %False
                  CASE ELSE
                       FUNCTION = %True
             END SELECT
        '     @StringPointer = STRREVERSE$(@StringPointer)
             TempString = STRREVERSE$(TempString)
             Buffer = STRREVERSE$(Buffer)
             EXIT FUNCTION
        ErrHandler:
             ErrorCheck ERR
        END FUNCTION
        
        FUNCTION ClearPort ALIAS "ClearPort"() EXPORT AS LONG  'Clear buffer
             TRACE ON
             ON ERROR GOTO ErrHandler
             ERRCLEAR
             Buffer = ""
             FUNCTION = %True
             EXIT FUNCTION
        ErrHandler:
             ErrorCheck ERR
        END FUNCTION
        
        FUNCTION RemoveFromPort ALIAS "RemoveFromPort"(StringToRemove AS ASCIIZ) EXPORT AS LONG  'Clear buffer chars
             TRACE ON
             ON ERROR GOTO ErrHandler
             ERRCLEAR
             LOCAL TempString AS STRING
             LOCAL StringPointer AS ASCIIZ PTR      'Setup a pointer in case commandout is a pointer
             StringPointer = VARPTR(StringToRemove)  'Get the location of the pointer
             SELECT CASE StringPointer
                  CASE 0
                       FUNCTION = %False
                       EXIT FUNCTION
                  CASE ELSE
             END SELECT
             [email protected]
             TempString = STRREVERSE$(TempString)
             SELECT CASE Buffer  'If buffer is empty then read in case buffer wasnt filled yet
                  CASE ""
                       FUNCTION = %False
                       EXIT FUNCTION
                  CASE ELSE
             END SELECT
             SELECT CASE TempString
                  CASE ""
                       FUNCTION = %False
                       EXIT FUNCTION
                  CASE ELSE
             END SELECT
        '     IF Updating = %False THEN
                  Buffer = STRREVERSE$(Buffer)
                  Buffer = STRDELETE$(Buffer, INSTR(Buffer, TempString), LEN(TempString))
                  TempString = STRREVERSE$(TempString)
                  Buffer = STRREVERSE$(Buffer)
        '     END IF
              EXIT FUNCTION
        ErrHandler:
             ErrorCheck ERR
        END FUNCTION
        
        FUNCTION VerifyIfVxm() AS LONG
        '     ClearPort
        '     SendToPort "ATZ"
        '     SLEEP 100
        '     ReadFromPort
        '     SELECT CASE INSTR(UCASE$(Buffer), "OK")
        '          CASE %False
                       ClearPort
                       SendToPort "K,Q,F,V"  'Force a Kill Offline and back Online
                       SLEEP 100
                       ReadFromPort
                       SELECT CASE LEFT$(Buffer,1)
                            CASE "R","J","B","b"
                                 FUNCTION = %True
                            CASE ELSE
                                 FUNCTION = %False
                       END SELECT
        '          CASE ELSE
        '               FUNCTION = %False
        '     END SELECT
        END FUNCTION
        
        SUB CheckVxmConnected ALIAS "CheckVxmConnected"()
        
             LOCAL TempReply AS STRING
             ClearPort
             IF FILEATTR(HwndPort, 0) THEN COMM SEND HwndPort, "V"
             SLEEP 100
             TempReply = ReadFromPort
             SELECT CASE TempReply
                  CASE ""
                       LstHistoryAddLine "fontc12", "Red", "*** VXM Not turned on or not connected ***", %True
                  CASE ELSE
             END SELECT
        
        END SUB
        '-------------------------------------------------------------------------------------------------------------------------
        '*** TroubleshootIo.inc
        '*** Limits
        SUB TroubleshootLimits()
            LOCAL lResult AS LONG
            LOCAL ErrorBuff AS ASCIIZ * %MAX_PATH
             ON ERROR GOTO ErrHandler
        '*** Ensure nothing is already running
            StopTroubleshootLimits
            HideIoStateWindow
            EnableMenuItem hMenu, %IDM_TRBLIMITSVIEWON, %MF_ENABLED
            IoView = %True
            ShowIoStateWindow(FUNCNAME$)
            DO
                IF IoView = %False THEN EXIT DO
                IF ParentClosing = %True THEN EXIT DO
        '*** Check if change display
                Buffer = ""
        ''*** Limits
                SendToPort "?"
                WaitReply 1, 100    'Wait for 1 char or 100ms
                  ReadFromPort
                  IF Buffer = "" THEN Buffer = "0"
                Buffer = BIN$(ASC(Buffer), 8)
                SELECT CASE Buffer
                     CASE ""
                    CASE = Limitstate
                        ChangeIoDisplay = %False
                    CASE ELSE
                        ChangeIoDisplay = %True
                END SELECT
                Limitstate = Buffer
        ''*** If Stopping Viewer then update screen
                SELECT CASE IoView
                    CASE %False
                        StoppedLimitsView
                    CASE %True
                        RunningLimitsView
                END SELECT
                  DIALOG DOEVENTS
                SLEEP 1
                IF IoView = %False THEN EXIT DO
                IF ParentClosing = %True THEN EXIT DO
            LOOP
            StoppedLimitsView   'When triggered to escape update the stopped view
            EXIT SUB
        ErrHandler:
            StartTrace
            ErrorCheck ERR
            EndTrace
        END SUB
        
        SUB StopTroubleshootLimits()
            IoView = %False
        END SUB
        
        SUB RunningLimitsView()
            DIM Msg AS STRING
            DIM i AS LONG
        '*** User Limits
            SELECT CASE ChangeIoDisplay
                CASE %False
                CASE %True
                    LstHistoryClearHistory
                    Msg = "Limits Viewer" + $CR
                    LstHistoryAddLine "fontc12", "Blue", Msg, %False
        
                    Msg = $TAB + "Press limit switches to see if state changes" + $CR
                    LstHistoryAddLine "fontc12", "Maroon", Msg, %False
                    Msg = $TAB + "Low = 0" + $TAB + "High = 1" + $CR
                    LstHistoryAddLine "fontc12", "Maroon", Msg, %False
        
                    Msg = "Limit 4+" + $TAB + "Limit 4-" + $TAB + "Limit 3+" + $TAB + "Limit 3-" + $TAB + _
                          "Limit 2+" + $TAB + "Limit 2-" + $TAB + "Limit 1+" + $TAB + "Limit 1-" + $CR
                    LstHistoryAddLine "fontc12", "Blue", Msg, %True
                    Msg = ""
                    FOR i = 1 TO LEN(Limitstate)
                        SELECT CASE i
                            CASE 1
                                Msg = Msg + SPACE$(LEN("Limit 4+") / 2) + MID$(Limitstate, i, 1) + SPACE$(LEN("Limit 4+") / 2) + $TAB
                            CASE 2
                                Msg = Msg + SPACE$(LEN("Limit 4-") / 2) + MID$(Limitstate, i, 1) + SPACE$(LEN("Limit 4-") / 2) + $TAB
                            CASE 3
                                Msg = Msg + SPACE$(LEN("Limit 3+") / 2) + MID$(Limitstate, i, 1) + SPACE$(LEN("Limit 3+") / 2) + $TAB
                            CASE 4
                                Msg = Msg + SPACE$(LEN("Limit 3-") / 2) + MID$(Limitstate, i, 1) + SPACE$(LEN("Limit 3-") / 2) + $TAB
                            CASE 5
                                Msg = Msg + SPACE$(LEN("Limit 2+") / 2) + MID$(Limitstate, i, 1) + SPACE$(LEN("Limit 2+") / 2) + $TAB
                            CASE 6
                                Msg = Msg + SPACE$(LEN("Limit 2-") / 2) + MID$(Limitstate, i, 1) + SPACE$(LEN("Limit 2-") / 2) + $TAB
                            CASE 7
                                Msg = Msg + SPACE$(LEN("Limit 1+") / 2) + MID$(Limitstate, i, 1) + SPACE$(LEN("Limit 1+") / 2) + $TAB
                            CASE 8
                                Msg = Msg + SPACE$(LEN("Limit 1-") / 2) + MID$(Limitstate, i, 1) + SPACE$(LEN("Limit 1-") / 2) + $TAB
                        END SELECT
                    NEXT i
                    Msg = Msg + $CR
                    LstHistoryAddLine "fontc12", "Green", Msg, %False
            END SELECT
        END SUB
        
        SUB StoppedLimitsView()
            LstHistoryClearHistory
        END SUB
        '-------------------------------------------------------------------------------------------------------------------------
        '-------------------------------------------------------------------------------------------------------------------------
        '-------------------------------------------------------------------------------------------------------------------------
        These files should be in your PB folders already, so it should "Just Compile", but if not, let me know and I will post a copy
        Code:
        '*** RTF.inc and RichEdit.inc should be their own Inc files due to size
        #INCLUDE "RichEdit.inc"
        #INCLUDE "Rtf.inc"
        '*** InitControls.inc and Mdi32.inc should be their own Inc files due to size
        #INCLUDE "Mdi32.inc"
        #INCLUDE "COMMCTRL.INC"
        #INCLUDE "InitControls.inc"
        The Ripped down version of my resource file - Name it "COSMOS.rc"
        Code:
        #include "resource.h"
        
        //TOOLS
             #define IDM_TOOLS             WM_USER + 100 //TOOLS MENU
             #define IDM_TRBLIMITSVIEWON        WM_USER + 942
        
        MAINMENU MENU LOADONCALL MOVEABLE
        BEGIN
        //*** TOOLS ***
             POPUP "Tools"
             BEGIN
                       MENUITEM "Limits"                                IDM_TRBLIMITSVIEWON
             END
        //*** END TOOLS ***
        END
        My PB test code (sorry so sloppy, but by the end of the day I got a wicked migraine)
        Code:
        #COMPILE EXE
        #DIM ALL
        #INCLUDE "WIN32API.INC"       'Windows Api
        '*** Prototype for LoadLibrary call function
             DECLARE FUNCTION DllShowCosmos() AS LONG
        '***
        
        GLOBAL hDlg  AS DWORD
        %IDD_DIALOG1  =  101
        
        CALLBACK FUNCTION ShowDIALOG1Proc()
             SELECT CASE AS LONG CBMSG
                  CASE %WM_INITDIALOG
                  CASE %WM_NCACTIVATE
                  CASE %WM_COMMAND
                  CASE %WM_CLOSE
                       PostQuitMessage 0             'Stop the message pump
        'MSGBOX FUNCNAME$ + " %WM_CLOSE fired"
            END SELECT
        END FUNCTION
        
        '------------------------------------------------------------------------------
        FUNCTION WINMAIN ( _
                            BYVAL hInstance     AS LONG, _     ' handle of current instance
                            BYVAL hPrevInstance AS LONG, _     ' handle of previous instance(not used in Win32)
                            BYVAL pszCmdLine    AS ASCIIZ PTR, _     ' address of command line
                            BYVAL nCmdShow      AS LONG _      ' show state of window
                         ) AS LONG
             DIALOG NEW %HWND_DESKTOP, "Dialog1", 70, 70, 201, 121, %WS_POPUP OR %WS_BORDER _
                  OR %WS_DLGFRAME OR %WS_CAPTION OR %WS_SYSMENU OR %WS_CLIPSIBLINGS OR _
                  %WS_VISIBLE OR %DS_MODALFRAME OR %DS_3DLOOK OR %DS_NOFAILCREATE OR _
                  %DS_SETFONT, %WS_EX_CONTROLPARENT OR %WS_EX_LEFT OR _
                  %WS_EX_LTRREADING OR %WS_EX_RIGHTSCROLLBAR, TO hDlg
             DIALOG SHOW MODELESS hDlg, CALL ShowDIALOG1Proc
             LOCAL tmsg AS TagMsg
             LOCAL hLib AS DWORD
             LOCAL AddrShowCosmos AS DWORD
             hLib = LoadLibrary( "COSMOS.dll")
             AddrShowCosmos=GetProcAddress(BYVAL hLib, "ShowCosmos")
             IF AddrShowCosmos THEN CALL DWORD AddrShowCosmos USING DllShowCosmos
        '*** Acquire and dispatch messages until a WM_QUIT message is received.
             WHILE ISTRUE GetMessage(tmsg, BYVAL %NULL, 0, 0)
                  TranslateMessage tmsg
                  DispatchMessage tmsg
             WEND
        MSGBOX FUNCNAME$ + " has escaped messagepump"
        END FUNCTION
        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


        • #5
          Test conditions

          Now for the Test Conditions.
          The major one is if you do the following:
          1. Open the test program (it in turn opens the dll (so to speak). )
          2. Click "Tools-->Limits" and the dialog should pop up with the cancel button
          3. Close the parent testing Process without closing the dll interface


          What it should do since this is the only process with the dll loaded is to free all threads, and whatever else I can think of that could cause a memory corruption (but obviously not working for whatever reason)

          The minor test is if you do the following:
          1. Open the test program (it in turn opens the dll (so to speak). )
          2. Close the parent testing Process without closing the dll interface

          the results of this one may be just because I got wooped and tired of trying to tear down, so not a big deal at the moment.

          Barring things I am not noticing, or thinking of, the 2 core sections that I think may be causing the errors may be either or combined ideas?
          1. I have a variable "ParentClosing =" that I thought was a global flag to tell any threads to stop and break out (but maybe this is part of what MCM has been so gently trying to beat into my noggin in the 1st place about variables not available across threads? (But I think in this case each thread should see my flag?)
          2. StreaminString for the RichEdit...(I have no documentation on where, how, or what is happening with this callback so it could be of partial cause?)
          3. Less likely, but perhaps the "COMM" functions? maybe they were not built to handle a constant pounding of "Send/Receive" ......just guessing at the moment


          The interesting thing is the difference between running as an EXE vs running as a DLL (see #COMPILE to switch between the 2 ways)

          Anyways, I REALLY appreciate fresh eyes on what is obviously eluding me.
          (Hopefully my next version will be all SDK and I can avoid mistakes of the "Past/Present/ and hopefully Future"

          Thanx again to anyone helping
          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


          • #6
            I haven't gone thru this in deatil but I can tell you I don't like using a DDT MODELESS dialog with an SDK message loop.

            Try changing that DDTdialog to a SDK dialog and use CreateDialog to create the window and add the IsDialogMessage() call to your message loop in WinMain.

            For as little as that dialog does it shouldn't take you more than twenty minutes.

            Also..
            Code:
            FUNCTION LibMain .....
                ...
                LoadCosmos
            You can't call LoadCosmos on LibMain/Dll_Process_Attach since you are using functions other than those in KERNEL32.DLL

            Better you should set a STATIC flag in fucntion ShowCosmos
            Code:
            FUNCTION ShowCosmos....
            
             STATIC bLoaded AS LONG
            
             ....
             IF ISFALSE bLoaded THEN    ' load it on the first call. 
                CALL          LoadCosmos
                bLoaded  = %TRUE
             END IF

            MCM
            Last edited by Michael Mattias; 1 Dec 2007, 09:41 AM.
            Michael Mattias
            Tal Systems (retired)
            Port Washington WI USA
            [email protected]
            http://www.talsystems.com

            Comment


            • #7
              I too dislike the mix of DDT and SDK, but when this all started, DDT was all I understood. But as time went on, I started understanding SDK and finding it much more useful in a lot of cases.

              Anyways, how would I go about a Modeless Dialog/Window using SDK? The closest I can find is
              GetClassInfoEx(GetModuleHandle(BYVAL %NULL), "#32770", twcx) 'copy dialog class (modeless)
              (I really do not get why modeless is marked as "#32770" though)

              so I made a function in my newer version (trying to avoid pitfalls of the past) as being:
              Code:
              FUNCTION Register_Modeless( _
                        ClassName AS STRING, _        'Class Name
                        BYVAL ClassCallBack AS DWORD, _     'Callback Function
                        ClassInstance AS LONG, _      'Instance
                        ClassIcon AS ASCIIZ, _        'Icon
                        ClassBackGround AS LONG _    'Background
                        ) AS LONG
                   ON ERROR GOTO ErrHandler
                  GetClassInfoEx(GetModuleHandle(BYVAL %NULL), "#32770", twcx)      'copy dialog class (modeless) into ports class
              
                   szClassName        = ClassName
                   twcx.cbSize        = SIZEOF(twcx)                               ' size of WNDCLASSEX structure
                   twcx.style         = %CS_DBLCLKS                                ' class styles
                   twcx.lpfnWndProc   = ClassCallBack                  ' address of window procedure used by class
                   twcx.cbClsExtra    = 0                                          ' extra class bytes
                   twcx.cbWndExtra    = %DLGWINDOWEXTRA                                          ' extra window bytes
                   twcx.hInstance     = ClassInstance                                  ' instance of the EXE/DLL that is registering the window
                   twcx.hIcon         = LoadIcon(ClassInstance, ClassIcon)    ' handle of class icon
                   twcx.hCursor       = LoadCursor(%NULL, BYVAL %IDC_ARROW)        ' handle of class cursor
                   twcx.hbrBackground = ClassBackGround                    ' brush used to fill background of window's client area
                   twcx.lpszMenuName  = %NULL                                      ' resource identifier of the class menu
                   twcx.lpszClassName = VARPTR(szClassName)                        ' class name
                   twcx.hIconSm       = LoadIcon(%NULL, ClassIcon)    ' handle of small icon shown in caption/system Taskbar
                   SELECT CASE RegisterClassEx(twcx)
                        CASE 0
                             FUNCTION = %FALSE
                        CASE ELSE
                             FUNCTION = %TRUE
                   END SELECT
                   EXIT FUNCTION
              ErrHandler:
                   LogError ERR, ERROR$(ERR)
                   RESUME              'Resume once error has been handled
              END FUNCTION
              You can't call LoadCosmos on LibMain/Dll_Process_Attach since you are using functions other than those in KERNEL32.DLL
              I thought the whole purpose of the attach and detach was to setup and cleanup variables, arrays, dialogs, etc?????

              In this case SHowCosmos in the attach was solely for display purposes. But in real was debating on no matter what the 1st function called was (Lets say it was "SendToPort", then the window would automatically pop up when the dll was attached to the process.) Although I have already seen a reason not to show just because I called a function.)

              Anyways if there is a better definition for a modeless dialog in SDK I would love to see it.
              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


              • #8
                With the discussion with Karl-Heinz Voltmann at DLL will not terminate I think it may have just hit me where some of my problems may be.

                In each of my functions if there is a do loop I have a flag in the loop (If ParentClosing = %True then exit do) and a flag to escape the loop based off a variable to stop that particular loop.

                I started thinking about "Forget the code.....think of what the code is DOING", and if I got it right what is happening is
                1. Once I enter my do loop, I am stuck there until either my Flag is set to escape, or I found what I am looking for
                2. Because my dialog is modeless I can push the button to set the flag to escape my loop, and it does because my loop is checking the state of my global variable
                3. If I close and my loop is no longer running then no crash

                So in this case (I may be WAYYYYyyyy off here but) would a modeless dialog be some sort of "Threaded Dialog"?

                But if I close, while the loop is running, and programmatically press the button, I get a crash (I assume because the loop never sees the flag set?), possibly because the the message to stop is on the stack waiting to be processed, and PostQuitMessage 0 stops my message pump to the message to stop is never seen?

                Forgive me if I am wrong (or just partially right) but gut feeling tells me the problem is in my loop because if it is running, I crash, if it is not running I do not crash
                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


                • #9
                  > if there is a better definition for a modeless dialog in SDK I would love to see it.
                  CreateDialog[Indirect][Param]
                  Michael Mattias
                  Tal Systems (retired)
                  Port Washington WI USA
                  [email protected]
                  http://www.talsystems.com

                  Comment


                  • #10
                    Do loop blocking

                    I think I just found a heck of an example why MCM dislikes globals (And I am starting to also now). I found my bug, it was my Do Loop in the "TroubleshootLimits".
                    Even though I do a check for my Global Flag, to see if it = %True, and if it does then exit the loop. Somehow If I physically press the button, then the loop sees that the Flag = %True and exits the loop, but if I programmatically press the button, then the loop never sees the Flag changed from %False to %True.

                    Still unsure as to why? But searching for a cause when I was at wits end finding the cause of the GPF I tripped into a post From back in 2003 where Bob Plymale as asking some Thread Question. Where he was using a Do Loop to wait for a Thread to complete, and MCM pointed out "WaitForSingleObject"

                    Lance Edmonds comment below that sparked a thought in my head
                    You should not hold up the message queue by blocking it waiting for the thread to finish. Doing so largely removes the point of using a secondary thread anyway.
                    Although my problem had nothing to do with threads, I stopped and thought "Could the Do Loop be blocking? because I already started the loop before setting the flag to break out?" (Still not totally sure, because pushing the button Sets the Flag correctly)

                    Anyways, I stopped and thought about the purpose of the loop. The core purpose is to keep sending and receiving certain codes until the button is pressed and the Window is closed. Lance had mentioned Starting and Stopping from the Callback function, and it hit me....I could get rid of my do loop and just call the function each loop of my Callback function (as deemed necessary) since the window is open as long as the test is running.

                    Look ma! No Do Loops
                    Well it certainly made the problem in this case go away. Now I have to figure out why it could not see the flag set to True in the 1st place?
                    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


                    • #11
                      >Look ma! No Do Loops

                      Good for you.

                      Next Stop: "Look Ma, no GLOBALs!"

                      (e,g,GUI + Worker Thread + Abort Demo)

                      Yes, that's right: no GLOBALs (or STATICs for that matter) in that code. It's cut and paste and compile and run.

                      MCM
                      Michael Mattias
                      Tal Systems (retired)
                      Port Washington WI USA
                      [email protected]
                      http://www.talsystems.com

                      Comment


                      • #12
                        Neverrrrrrrrmind.....BAAAAAAAD Idea

                        Turns out I run out of stack space and create an endless loop somewhere.


                        Back to the drawing board
                        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


                        • #13
                          >Turns out I run out of stack space and create an endless loop somewhere

                          OUT OF STACK SPACE ===>>> MAJOR CLUE HERE!

                          I have three dollars sixty-two cents says in one of your message processing code groups you are calling something which results in the same message being automatically generated... causing an endless recursion. Well, it's not really endless... it will end as soon as you run out of stack space and generate a Stack Fault GPF - but it sounds like you have already discovered this.
                          Michael Mattias
                          Tal Systems (retired)
                          Port Washington WI USA
                          [email protected]
                          http://www.talsystems.com

                          Comment


                          • #14
                            MCM....My thoughts EXACTLY

                            The only problem is, that it did not occur until I did away with the Do Loop and just called the function from the callback. So I am in a catch 22 unless I can find a way around.

                            At least I now have a few things to look at. But unfortunately too many pieces (and a couple pieces that I can not verify if my guess is possible, until PB or others can get me documentation to if I may or may not be right)

                            Anyways from my code above, maybe someone can extrapolate and give me something to look at (although I have a ton of avenues but SOMEONE has to have run into something as difficult as this one before)

                            Testing Procedure: (Both in Exe and DLL form)
                            1. Start the program, and have the Limits Viewer run
                            2. While its running, kill the program (or parent process if run as Dll) and from there "SendMessage" to click the button

                            Results:
                            EXE: All ends fine, no GPF or known errors
                            DLL: All ends fine, but GPF/Error (depending on parent process could be approx 5 seconds after closing)

                            Testing Procedure2: (Both in Exe and DLL form)
                            1. Start the program, and have the Limits Viewer run
                            2. "Physically" Press the button to close the test

                            Results:
                            EXE: All ends fine, no GPF or known errors
                            DLL: All ends fine, no GPF or known errors

                            Common Results:
                            EXE: Both tests are the same, whether I physically click the button, or SendMessage to push it
                            DLL: Works if I "Physically" Click...GPF if I SendMessage to do it

                            Either way...my best guesses to cause of problems? (Only guessing here)
                            *** Remember by the way in all cases, I set a Global Variable = %True (meaning 1 and no other value) that if it is set to escape the DO LOOP
                            • Programmatically press "Flag is not seen" within loop (not sure why)
                            • Physically Click, "Flag is seen" (still don't know why different from program click"
                            • "Streamin" may be a blocking function, but commenting this out does not get rid of the problem
                            • "COMM" may be a blocking function but again commenting out does not fix the problem
                            • DO LOOP with 1ms sleep by itself, I get the same problem.
                            • I would have thought threading my COMM RECV could be a cause but since it was commented out, I have to almost ignore it (ALLLLMOSTTT)


                            So I wonder for the missing peices, that I know exist, but no documentation (that I can find anyways)
                            • Is there a list of "Blocking Functions" (and hopefully, what they do, why they block, how to disable the block etc) both on the SDK side and the DDT side?
                            • Shouldn't a global variable be global across threads? (lets not get into the synch and thread aware and accessing in the middle of an update, because to my knowledge at SOME point any thread looking at the variable should see it changed (whether the same thread or a different one)


                            Probably confusing, but at this point, my only answer to the user is ("1st Close all running pieces" and THEN close the parent program
                            (Although I do not like leaving "Bugs known to me in my programs")

                            On a side note...I do Not know about PB 8.04 but PB 8.03 has documented CancelIO and CancelIoEX but neither of which exists in the Win32Api.Inc file (which I tried to replicate from C++ definitions, hoping that was the solution)
                            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


                            • #15
                              >Is there a list of "Blocking Functions"

                              Sure, the entire PB keyword list. They either return, timeout and return, or block.

                              e.g..
                              COMM RECV
                              ..returns on timeout or when something is received, not before, not after.

                              And of course, all the Win32 synchronization functions, eg WaitForxxxx or Sleep[Ex].

                              >Shouldn't a global variable be global across threads?

                              You should know this by now.

                              A global variable is global to the code module. If a thread function is in the code module, it has access to GLOBAL variables.
                              Michael Mattias
                              Tal Systems (retired)
                              Port Washington WI USA
                              [email protected]
                              http://www.talsystems.com

                              Comment

                              Working...
                              X