SOH Characters crashing my script. Topic is solved

Get help with using AutoHotkey (v1.1 and older) and its commands and hotkeys
x32
Posts: 177
Joined: 25 Nov 2016, 16:44

SOH Characters crashing my script.

17 Jul 2018, 06:41

So this is a weird one. I'm using AHKsock to transfer information between two computers on my home network. I've hacked his Example 3, which is a chat box, to send commands from this computer and receive information back from the other one. The script on this computer always locks up after some random interval, always when receiving information.

You can see in the image below how it request and receives information. The last line has two extra characters at the beginning which cause the script to lock up. When I paste these characters into Notepad++ they show as SOH,
SOH - Start of Header - First character of a message header.
This script is only modified with extra buttons to quickly send commands it's really hasn't been modified much. The problem always happens when receiving info, I believe it is not the info being sent as I have a record of what the other side sent and those characters never appear. I'll add my code below but it's a big script and that's not even including the library. Hopefully someone can tell me where these phantom characters are coming from and how to stop them.

Image

Code: Select all

/*! TheGood
    AHKsock - A simple AHK implementation of Winsock.
    AHKsock Example 3 - Chatting
    http://www.autohotkey.com/forum/viewtopic.php?p=355775
    Last updated: August 24th, 2010
    
    In this example, the two programs exchange messages with each other. There are no predefined server and client here. If
    the first instance can't seem to connect to the peer, it'll start listening for a peer. Then, once the second instance
    is started and given the address of the first peer, a connection is established and chatting may begin.
    
    This is the flow of execution:
        1. When started, an InputBox asks the user for the peer's address.
        2. An attempt to connect to the given address on port 27015 is then made.
        3. a> If the connect attempt fails, the program starts listening on port 27015 for a peer. Once a peer connects on
              port 27015, a connection is established.
           b> If the connect attempt succeeds, a connection is established.
        4. Once the program receives the CONNECTED event, the Edit control is enabled to allow input.
        5. As the user types text and sends it, the program sends the data to the connected peer using AHKsock_ForceSend.
        6. When one of the peer exits, the connection is closed.
        7. Once the remaining peer receives the DISCONNECTED event, it disables the Edit control and starts listening for
           another peer.
    
    As you can see from the flow of execution, the first peer to start up will always fail to connect because the second
    peer hasn't been started yet. Once the second peer is started, it attempts to connect to the first peer, and a
    connection is thus established.
    
    This example uses OutputDebug to output its log (e.g. errors). See the command's docs for more details.
*/
    ;We'll need to allow more than one instance to test it on the same machine
    #SingleInstance, Force
    
    ;Needed if AHKsock isn't in one of your lib folders
    ;#Include %A_ScriptDir%\AHKsock.ahk
    
    ;Set up an error handler (this is optional)
    AHKsock_ErrorHandler("AHKsockErrors")
    
    ;Set up an OnExit routine
    OnExit, GuiClose
    
    ;Set default value to invalid handle
    iPeerSocket := -1
    
    ;Set up the GUI
    Gui, +OwnDialogs +AlwaysOnTop
	
    Gui, Add, Edit, x10 r10 w480 vtxtDialog ReadOnly hwndhtxtDialog
    
    /*! Limit the edit control text length to 65535 because that's the maximum length we can express with the stream
    processing we use here, because the string's length must fit in the low-word of a 32-bit integer. See the comment block
    in the StreamProcessor function for more details
    */
    Gui, Add, Edit, x10 y150 w250 vtxtInput hwndhtxtInput ReadOnly gtxtInput Limit65535
    Gui, Add, Button, x265 y150 w45 h22 vbtnSend hwndhbtnSend Default Disabled gbtnSend, Send
	Gui, Add, Button, x315 y150 w45 h22 gclear, Clear

	Gui, Add, Button, x365 y150 w60 h22 gtimeron, Start Scroll
	Gui, Add, Button, x430 y150 w60 h22 gtimeroff, Stop Scroll
    
	Gui, Add, Button, x10 y180 w80 h25 vbtnstatus gstatus, Get Status
	Gui, Add, Button, x95 y180 w80 h25 vPauseResume gPauseResume, Pause/Resume
	Gui, Add, Button, x180 y180 w80 h25 vPrintStop gPrintStop, Stop
	
	Gui, Add, Button, x315 y180 w45 h25 gguitwo, Options	
	
	Gui, Add, Button, x365 y180 w125 h25 vturncamon gcam, Cam Viewer
	
	
	Gui, Add, GroupBox, x10 y210 w480 h30, 
	Gui, Add, Text, x20 y220 w440 vlblStatus hwndhlblStatus, Not connected...
	
    Gui, +MinSize
    Gui, Show, w500 h250, SD3 MCP Status
	
	
    ;Ask for peer address
    ;InputBox, sName, Peer hostname or IP address, Please enter your peer's hostname or IP address:,,, 100, 1000, 0,,, 192.168.0.4 ; localhost

    ;If ErrorLevel
    ;    ExitApp
	
	IniRead, sName, s3dremote.ini, nfo, print_comp_ip

    ;Try to connect
    GuiControl,, lblStatus, Trying to connect to %sName%... ;Update status
    If (i := AHKsock_Connect(sName, 27015, "Peer"))
        OutputDebug, % "AHKsock_Connect() failed with return value = " i " and ErrorLevel = " ErrorLevel
    
    /*! While the connection is attempted, we should start listening for peers. The reason we should start listening before
    the connect operation is over is because otherwise, the second client to start might not be able to connect to its peer
    if it (the peer) is still waiting for the connect operation to finish, and is thus not listening for connections yet.
    
    The only time we shouldn't start listening before the connection attempt is over is if we're trying to connect on the
    same machine. Otherwise, the listening socket we would create would accept the client socket we created inside the same
    script. In other words, the script would be connected to itself.
    */
    
    /*! We need to find out if the target computer is the same machine. The method used below will cover most common ways to
    connect to the same machine, but it is not foolproof. For example, if the computer is on a LAN and port 27015 is
    forwarded to this machine, then connecting on the router's address will not be seen as the same machine, which would
    result in the script connecting to itself. However, because of the fail-safes we have in place, the connection will be
    immediately closed as soon as it is established (see the comment block in the ACCEPTED event in the Peer() function).
    */
    
    ;First we have to translate sName to an IP address if it is not already one (AHKsock_GetAddrInfo would return the same
    ;IP address if sName is already an IP). Note that here, we do not do any error-checking because if AHKsock_Connect
    ;accepted sName with no error, then AHKsock_GetAddrInfo will work fine as well.
    AHKsock_GetAddrInfo(sName, sConnectIP, True)
    
    ;Then, we also get the IP address of the computer
    AHKsock_GetAddrInfo(A_ComputerName, sComputerIP, True)
    
    /*! The connection attempt is to same machine if
        - the target IP is the loopback address, or
        - the target IP is the computer's IP
    */
    bSameMachine := (sConnectIP = "127.0.0.1") Or (sConnectIP = sComputerIP)
    
    ;Only start listening if we're not trying to connect to the same machine
    If Not bSameMachine And (i := AHKsock_Listen(27015, "Peer")) {
        OutputDebug, % "AHKsock_Listen() failed with return value = " i " and ErrorLevel = " ErrorLevel
        bCantListen := True ;So that if the connect() attempt fails, we exit.
    }
Return
/*
GuiSize:
    Anchor(htxtDialog, "wh")
    Anchor(htxtInput, "wy")
    Anchor(hbtnSend, "xy")
    Anchor(hlblStatus, "wy", 1)
Return
*/
GuiEscape:
GuiClose:
    
    ;So that we don't go back to listening on disconnect
    bExiting := True
    
    /*! If the GUI is closed, this function will be called twice:
        - Once non-critically when the GUI event fires (GuiEscape or GuiClose) (graceful shutdown will occur), and
        - Once more critically during the OnExit sub (after the previous GUI event calls ExitApp)
        
        But if the application is exited using the Exit item in the tray's menu, graceful shutdown will be impossible
        because AHKsock_Close() will only be called once critically during the OnExit sub.
    */
    AHKsock_Close()
ExitApp

txtInput:
    
    /*! This GUI event will be called everytime the text in the Edit control changes. The idea is to send a notification to
    the peer when the user starts/stops typing. We do this using the typing update frame. See the StreamProcessor function
    for more information on frames.
    */
    
    ;Check if we're connected
    If (iPeerSocket = -1)
        Return
    
    ;Get text entered, if any
    GuiControlGet, sText,, txtInput
    
    ;Create a boolean value
    bTypingUpdate := sText <> ""
    
    ;Send a typing update frame only if the boolean value is different from the last one we sent. We do this check so that
    ;we don't drown the peer with updates everytime the user types an additional character in the Edit control box.
    If (bTypingUpdate = bLastTypingUpdate)
        Return
    
    /*! It is more appropriate to use AHKsock_ForceSend here because the thread is not critical, and because the amount of
    data to send is very small. We thus don't have to worry about the function taking time to return.
    */
    
    ;Send a typing update frame: high-word is 1, low-word is a boolean value (False = 0, True = 1)
    VarSetCapacity(iFrame, 4, 0), NumPut((1 << 16) + bTypingUpdate, iFrame, 0, "UInt")
    If (i := AHKsock_ForceSend(iPeerSocket, &iFrame, 4)) {
        OutputDebug, % "AHKsock_ForceSend failed with return value = " i " and error code = " ErrorLevel " at line " A_LineNumber
        ExitApp
    }
    
    ;Remember the boolean value of the typing update frame we just sent
    bLastTypingUpdate := sText ? 1 : 0
    
Return

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

PauseResume:
GuiControl, , txtInput, PauseResume
GoTo, btnSend
Return

PrintStop:
GuiControl, , txtInput, PrintStop
GoTo, btnSend
Return

guitwo:
Run, sd3options.ahk
Return

cam:
IfWinExist, Printer Cam Viewer
	WinClose, Printer Cam Viewer

Else, Run, cam.ahk
Return

timeron:
SetTimer, GetStatus, 5000
GoSub, GetStatus
Return

timeroff:
SetTimer, GetStatus, Off
Return

clear:
GuiControl, , txtDialog,
Return

GetStatus:
status:
GuiControl, , txtInput, Status

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
btnSend:
    
    /*! This GUI event will be called everytime the user clicks on the Send button, or when he presses the Enter key after
    typing text in the Edit control. The logic is rather straight-forward. We first send a string length frame to tell the
    peer how long the string coming will be, and we then send the string. See the StreamProcessor function for more
    information on frames.
    */
    
    ;Check if we're connected
    If (iPeerSocket = -1)
        Return
    
    ;Get the text to send
    GuiControlGet, sText,, txtInput
    
    ;Make sure we even have something to send
    If Not sText
        Return
    
    ;Get text length
    iTextLength := StrLen(sText) * 2
    
    ;First send a string length frame: high-word is 2, low-word is the length of the string
    VarSetCapacity(iFrame, 4, 0), NumPut((2 << 16) + iTextLength, iFrame, 0, "UInt")
    If (i := AHKsock_ForceSend(iPeerSocket, &iFrame, 4)) {
        OutputDebug, % "AHKsock_ForceSend failed with return value = " i " and error code = " ErrorLevel " at line " A_LineNumber
        ExitApp
    }
    
    ;Send the actual string now, excluding the null terminator
    If (i := AHKsock_ForceSend(iPeerSocket, &sText, iTextLength)) {
        OutputDebug, % "AHKsock_ForceSend failed with return value = " i " and error code = " ErrorLevel " at line " A_LineNumber
        ExitApp
    }
    
    ;Data was sent. Add it to the dialog.
    AddDialog(&sText)
    
    ;Clear the Edit control and give focus
    GuiControl,, txtInput
    GuiControl, Focus, txtInput
    
Return

AddDialog(ptrText, bYou = True) {
    Global htxtDialog
    
    ;Append the interlocutor
    sAppend := bYou ? "Request > " : "Received > "
    InsertText(htxtDialog, &sAppend)
    
    ;Append the new text
    InsertText(htxtDialog, ptrText)
    
    ;Append a new line
    sAppend := "`r`n"
    InsertText(htxtDialog, &sAppend)
    
    ;Scroll to bottom
    SendMessage, 0x0115, 7, 0,, ahk_id %htxtDialog% ;WM_VSCROLL
}

Peer(sEvent, iSocket = 0, sName = 0, sAddr = 0, sPort = 0, ByRef bData = 0, bDataLength = 0) {
    Global iPeerSocket, bExiting, bSameMachine, bCantListen
    Static iIgnoreDisconnect
    
    If (sEvent = "ACCEPTED") {
        OutputDebug, % "A client with IP " sAddr " connected!"
        
        ;We now have an established connection with a peer
        
        /*! As a fail-safe, we need to make sure that AHKsock_Connect didn't also succeed (which would mean that we are
        already connected to another peer). This should (in theory) never happen because if the CONNECTED event fires before
        an ACCEPTED event, then we would stop listening (see comment block in CONNECTED event), which means that we would
        not be able to accept any other peer. Similarly, if the ACCEPTED event fires before the CONNECTED event, then it
        means that the peer has received the CONNECTED event before an ACCEPTED event, and the same logic applies. 
        
        But to be on the safe side, if somehow both an ACCEPTED and a CONNECTED event occur, the socket reported in the
        second event to occur between the two will be closed, and the other socket (which was reported in the first event)
        will be kept.
        
        This also allows us to prevent cases where the connection attempt was to the same machine, but we could not detect
        it (bSameMachine is False). In such a case, both events will fire (referring to each other's endpoint socket), which
        means that the fail-safe will close the connection when the second of the two events fire.
        */
        If (iPeerSocket <> -1) {
            OutputDebug, % "We already have a peer! Disconnecting..."
            AHKsock_Close(iSocket) ;Close the socket
            iIgnoreDisconnect += 1 ;So that we don't react when this peer disconnects
            Return
        }
        
        ;Remember the socket
        iPeerSocket := iSocket
        
        ;Stop listening (see comment block in CONNECTED event)
        AHKsock_Listen(27015)
        
        ;Allow input and set focus
        GuiControl, Enable, btnSend
        GuiControl, -ReadOnly, txtInput
        GuiControl, Focus, txtInput
        
        ;Update status
        GuiControl,, lblStatus, Connected to %sAddr%!
        
    } If (sEvent = "CONNECTED") {
        
        ;Check if the connection attempt was successful
        If (iSocket = -1) {
            OutputDebug, % "AHKsock_Connect() failed."
            
            ;Check if we are not currently listening, and if we already tried to listen and failed.
            If bCantListen
                ExitApp
            
            OutputDebug, % "Listening for a peer..."
            GuiControl,, lblStatus, Waiting for a peer... ;Update status
            
            ;If the connection attempt was on this computer, we can start listening now since the connect attempt is
            ;over and we thus run no risk of ending up connected to ourselves. 
            If bSameMachine {
                If (i := AHKsock_Listen(27015, "Peer")) {
                    OutputDebug, % "AHKsock_Listen() failed with return value = " i " and ErrorLevel = " ErrorLevel
                    ExitApp
                }
            }
            
            ;The connect attempt failed, but we are now listening for clients. We can leave now.
            Return
            
        } Else OutputDebug, % "AHKsock_Connect() successfully connected on IP " sAddr "."
        
        ;We now have an established connection with a peer
        
        ;This is the same fail-safe as in the ACCEPTED event (see comment block there)
        If (iPeerSocket <> -1) {
            OutputDebug, % "We already have a peer! Disconnecting..."
            AHKsock_Close(iSocket) ;Close the socket
            iIgnoreDisconnect += 1 ;So that we don't react when this peer disconnects
            Return
        }
        
        ;Remember the socket
        iPeerSocket := iSocket
        
        /*! We can stop listening now, because we don't want to have more than one peer connnected to us. Note that it's
        possible that we are not even listening anyways (for example, if the connect attempt succeeded, but we weren't
        listening because it's on the same machine). In such a case, calling AHKsock_Listen to stop listening is useless but
        harmless. Therefore, there's no point in checking if we're actually listening before stopping.
        */
        AHKsock_Listen(27015)
        
        ;Allow input and set focus
        GuiControl, Enable, btnSend
        GuiControl, -ReadOnly, txtInput
        GuiControl, Focus, txtInput
        
        ;Update status
        GuiControl,, lblStatus, Connected to %sName%!
        
    } Else If (sEvent = "DISCONNECTED") {
        
        ;Check if we're supposed to ignore this event
        If iIgnoreDisconnect {
            iIgnoreDisconnect -= 1
            Return
        }
        
        ;Reset variable
        iPeerSocket := -1
        
        ;Delete any past data the stream processor had stored
        StreamProcessor()
        
        ;We should go back to listening (unless we're in the process of leaving)
        If Not bExiting {
            
            OutputDebug, % "The peer disconnected! Going back to listening..."
            
            GuiControl,, lblStatus, Waiting for a peer... ;Update status
            If (i := AHKsock_Listen(27015, "Peer")) {
                OutputDebug, % "AHKsock_Listen() failed with return value = " i " and ErrorLevel = " ErrorLevel
                ExitApp
            }
            
            ;Disable input and clear textbox
            GuiControl, Disable, btnSend
            GuiControl, +ReadOnly, txtInput
            GuiControl,, txtInput
            
        } Else OutputDebug, % "The peer disconnected! Exiting..."
        
    } Else If (sEvent = "RECEIVED") {
        
        ;OutputDebug, % "Received " bDataLength " bytes" ;FOR DEBUGGING PURPOSES ONLY
        ;OutputDebug, % "bData = " MCode_Bin2Hex(&bData, bDataLength) ;FOR DEBUGGING PURPOSES ONLY
        
        ;Send to the stream processor
        StreamProcessor(bData, bDataLength)
    }
}

/*! The StreamProcessor is, in abstract terms, a function that receives as input the data from RECEIVE events, and that
    outputs and processes frames. The idea is that no matter how the data frames that the peer sends is broken up across
    RECEIVE events, the StreamProcessor will properly cut and put together the pieces into the individual frames to process.
    
    More specifically, the StreamProcessor's role is in threefold:
    - To stock up data that we received in past RECEIVE events but that could not be processed because it didn't represent
      a complete data frame.
    - To append newly arrived data to any incomplete data frame that we stocked up in past RECEIVE events.
    - To process any complete frame, whether it be from multiple past RECEIVE events, or wholly contained in the data from
      the RECEIVE event that just arrived.
    
    The static variable bPastData is the variable used to stock up data from past RECEIVE events that could not be
    processed, and the static variable bPastDataLength holds the length of bPastData.
    
    Frame are 4 bytes long. There are two types of frames:
    - Typing update (TU) frames: Updates the typing status of the peer. These frames occur when the peer starts typing in
      the Edit control, or empties the Edit control. See the code found in the txtInput label to see it made and sent.
    - String length (SL) frames: Frames that contain the length of the string that follows it. These frames occur when the
      peer sends a string of text (by pressing the Send button or the Enter key). See the code found in the btnSend label to
      see it made and sent.
*/

/*! To better understand how StreamProcessor works, imagine the following scenario (which is entirely possible, given the
    streaming nature of TCP):
        A) We first receive a RECEIVE event with 2 bytes, which is half of a TU frame.
        B) We then receive a RECEIVE event with 3 bytes, the first 2 bytes belonging to the other half of the TU frame, and
           the last 1 byte belonging to the first first byte of an SL frame.
        C) We then receive a RECEIVE event with 7 bytes, the first 3 bytes belonging to the last 3 bytes of the SL
           frame and the following 4 bytes representing the beginning of a string of 8 bytes.
        D) And finally we receive a RECEIVE event with 4 bytes, representing the end of the string.
    
    This is what would happen in the StreamProcessor after each RECEIVE event occurs (following the flow of the function):
        A) 1. There is no past data stored in bPastData to prepend.
           2. Since 2 < 4, we have less than a frame to process. We keep the 2 bytes we have in bPastData, and leave.
           
        B) 1. We prepend the 2 bytes in bPastData to the 3 bytes we just received. In total, we now have 5 bytes to process.
           2. We enter the processing loop and process the TU frame. There is 1 byte left to process. Since 1 < 4, we exit
              the While loop.
           3. We could not process the 1 byte, which is part of an incomplete frame, so we save it in bPastData and leave.
           
        C) 1. We prepend the 1 byte in bPastData to the 7 bytes we just received. In total, we now have 8 bytes to process.
           2. We enter the processing loop and try to process the SL frame. Because only part of the string is present, we
              can't process the frame. So we save all 8 bytes in bPastData and leave.
              
        D) 1. We prepend the 8 bytes in bPastData to the 4 bytes we just received.
           2. We enter the processing loop and try to process the SL frame. The whole string is present, so we add the
              string to the dialog textbox and exit the loop.
           3. There are no bytes that we could not process, so there's nothing to save in bPastData. We leave.
*/
StreamProcessor(ByRef bNewData = 0, bNewDataLength = -1) {
    Static bPastData, bPastDataLength := 0
    
    ;Check if we're just erasing any previous data we had
    If (bNewDataLength = -1) {
        If bPastDataLength { ;Check if there's data to erase
            VarSetCapacity(bPastData, 0)
            bPastDataLength := 0
        }
        
        ;OutputDebug, % "SP - Deleting any previous data" ;FOR DEBUGGING PURPOSES ONLY
        Return
    }
    
    ;Check if we have any past data to prepend before entering the processing loop
    If bPastDataLength {
        
        ;OutputDebug, % "SP - Prepending previous data of " bPastDataLength " bytes" ;FOR DEBUGGING PURPOSES ONLY
        
        ;The length of the data to process will be the length of both old and new data combined
        bDataLength := bPastDataLength + bNewDataLength
        
        ;Prep the variable which will hold both the previous data and the new data
        VarSetCapacity(bData, bDataLength)
        
        ;Copy the old data into the beginning of bData, and then the new data right after
        CopyBinData(&bPastData, &bData, bPastDataLength)
        CopyBinData(&bNewData, &bData + bPastDataLength, bNewDataLength)
        
        ;We can now delete any old data we had because we're about to process it
        VarSetCapacity(bPastData, 0) ;Clear the variable to free some memory since it won't be used
        bPastDataLength := 0 ;Reset the value
        
        ;Now bData contains the newly received data as well as any previous data that could not be fully processed before
        
        ;Set the data pointer to the new data we just created
        bDataPointer := &bData
        
        /*! The advantage of using a data pointer is so that the code that follows after can work regardless of whether
        the data to process is in bNewData (if we had nothing to prepend), or in bData (if we had to create it to
        prepend some past data). The variable bDataLength holds the length of the data to which bDataPointer points.
        */        
        
    ;Set the data pointer to the newly arrived data
    } Else bDataPointer := &bNewData, bDataLength := bNewDataLength
    
    ;Now bDataPointer points to the data to process, whether it be only the new data that arrived, or old and new data which
    ;have been concatenated. The length of the data to which bDataPointer points is in bDataLength.
    
    ;Check if we have at least one frame to process
    If (bDataLength < 4) {
        
        ;OutputDebug, % "SP - Less than a frame to process. Saving " bDataLength " bytes and leaving" ;FOR DEBUGGING PURPOSES ONLY
        
        ;We only have part of one frame. Keep what we have and leave.
        VarSetCapacity(bPastData, bPastDataLength := bDataLength)
        CopyBinData(bDataPointer, &bPastData, bDataLength)
        Return
    }
    
    ;Start processing the stream of frames
    iOffset := 0
    While (iOffset + 4 <= bDataLength) {
        
        ;Get frame type and data
        iFrame     := NumGet(bDataPointer+0, iOffset, "UInt")
        iFrameType := iFrame >> 16 ;High-word
        iFrameData := iFrame & 0xFFFF ;Low-word        
        
        /*! The type of the frame is in the high-word, and the data of the frame is in the low-word:
            - Typing update frames have a high-word of 1, and a boolean value in the low-word indicating typing status
            - String length frames have a high-word of 2, and the length of the string that follows in the low-word
        */
        
        ;Check what kind of frame it is
        If (iFrameType = 1) { ;It's a typing update frame
            GuiControl,, lblStatus, % iFrameData ? "Peer is typing..." : "Connected!" ;Update
            iOffset += 4 ;Increase the offset to be at the beginning of the next frame
        } Else If (iFrameType = 2) { ;It's a string length frame
            
            ;Check if the whole string is present
            ;We do + 4 because the string starts right after the frame we're currently processing
            If Not (iFrameData <= bDataLength - (iOffset + 4)) {
                
                ;OutputDebug, % "SP - Part of string missing. Saving " (bDataLength - iOffset) " bytes and leaving" ;FOR DEBUGGING PURPOSES ONLY
                
                ;We only have part of a string. Keep what we have (including the string length frame) and leave.
                ;We include the string length frame so that this loop will be able to handle it the next time
                ;it processes the same data but with more of the string at the end
                VarSetCapacity(bPastData, bPastDataLength := bDataLength - iOffset)
                CopyBinData(bDataPointer + iOffset, &bPastData, bDataLength - iOffset)
                Return
            }
            
            ;Increase the offset to where the string starts (right after the frame we're processing)
            iOffset += 4
            
            ;Get the string and add to dialog
            VarSetCapacity(sTextData, iFrameData + 2, 0)
            CopyBinData(bDataPointer + iOffset, &sTextData, iFrameData)
            
            ;Add the string to the dialog textbox
            AddDialog(&sTextData, False)
            
            ;Move the offset by the length of the string to be at the beginning of the next frame
            iOffset += iFrameData
        }
    }

    ;Check if there are bytes part of an incomplete frame at the end of the stream that could not be processed
    If (n := (bDataLength - iOffset)) And (n < 4) {
        
        ;OutputDebug, % "SP - Less than a frame left at the end of the stream. Saving " n " bytes and leaving" ;FOR DEBUGGING PURPOSES ONLY
        
        ;We only have part of one frame. Keep what we have and leave.
        VarSetCapacity(bPastData, bPastDataLength := n)
        CopyBinData(bDataPointer + iOffset, &bPastData, n)
        Return
    }
}

AHKsockErrors(iError, iSocket) {
    OutputDebug, % "Error " iError " with error code = " ErrorLevel ((iSocket <> -1) ? " on socket " iSocket "." : ".") 
}

CopyBinData(ptrSource, ptrDestination, iLength) {
    If iLength ;Only do it if there's anything to copy
        DllCall("RtlMoveMemory", "Ptr", ptrDestination, "Ptr", ptrSource, "UInt", iLength)
}

/*! TheGood
    Append text to an Edit control
    http://www.autohotkey.com/forum/viewtopic.php?t=56717
*/
InsertText(hEdit, ptrText, iPos = -1) {
    
    If (iPos = -1) {
        SendMessage, 0x000E, 0, 0,, ahk_id %hEdit% ;WM_GETTEXTLENGTH
        iPos := ErrorLevel
    }
    
    SendMessage, 0x00B1, iPos, iPos,, ahk_id %hEdit% ;EM_SETSEL
    SendMessage, 0x00C2, False, ptrText,, ahk_id %hEdit% ;EM_REPLACESEL
}

/*
;Anchor by Titan, adapted by TheGood
;http://www.autohotkey.com/forum/viewtopic.php?p=377395#377395
Anchor(i, a = "", r = false) {
	static c, cs = 12, cx = 255, cl = 0, g, gs = 8, gl = 0, gpi, gw, gh, z = 0, k = 0xffff, ptr
	If z = 0
		VarSetCapacity(g, gs * 99, 0), VarSetCapacity(c, cs * cx, 0), ptr := A_PtrSize ? "Ptr" : "UInt", z := true
	If (!WinExist("ahk_id" . i)) {
		GuiControlGet, t, Hwnd, %i%
		If ErrorLevel = 0
			i := t
		Else ControlGet, i, Hwnd, , %i%
	}
	VarSetCapacity(gi, 68, 0), DllCall("GetWindowInfo", "UInt", gp := DllCall("GetParent", "UInt", i), ptr, &gi)
		, giw := NumGet(gi, 28, "Int") - NumGet(gi, 20, "Int"), gih := NumGet(gi, 32, "Int") - NumGet(gi, 24, "Int")
	If (gp != gpi) {
		gpi := gp
		Loop, %gl%
			If (NumGet(g, cb := gs * (A_Index - 1)) == gp, "UInt") {
				gw := NumGet(g, cb + 4, "Short"), gh := NumGet(g, cb + 6, "Short"), gf := 1
				Break
			}
		If (!gf)
			NumPut(gp, g, gl, "UInt"), NumPut(gw := giw, g, gl + 4, "Short"), NumPut(gh := gih, g, gl + 6, "Short"), gl += gs
	}
	ControlGetPos, dx, dy, dw, dh, , ahk_id %i%
	Loop, %cl%
		If (NumGet(c, cb := cs * (A_Index - 1), "UInt") == i) {
			If a =
			{
				cf = 1
				Break
			}
			giw -= gw, gih -= gh, as := 1, dx := NumGet(c, cb + 4, "Short"), dy := NumGet(c, cb + 6, "Short")
				, cw := dw, dw := NumGet(c, cb + 8, "Short"), ch := dh, dh := NumGet(c, cb + 10, "Short")
			Loop, Parse, a, xywh
				If A_Index > 1
					av := SubStr(a, as, 1), as += 1 + StrLen(A_LoopField)
						, d%av% += (InStr("yh", av) ? gih : giw) * (A_LoopField + 0 ? A_LoopField : 1)
			DllCall("SetWindowPos", "UInt", i, "UInt", 0, "Int", dx, "Int", dy
				, "Int", InStr(a, "w") ? dw : cw, "Int", InStr(a, "h") ? dh : ch, "Int", 4)
			If r != 0
				DllCall("RedrawWindow", "UInt", i, "UInt", 0, "UInt", 0, "UInt", 0x0101) ; RDW_UPDATENOW | RDW_INVALIDATE
			Return
		}
	If cf != 1
		cb := cl, cl += cs
	bx := NumGet(gi, 48, "UInt"), by := NumGet(gi, 16, "Int") - NumGet(gi, 8, "Int") - gih - NumGet(gi, 52, "UInt")
	If cf = 1
		dw -= giw - gw, dh -= gih - gh
	NumPut(i, c, cb, "UInt"), NumPut(dx - bx, c, cb + 4, "Short"), NumPut(dy - by, c, cb + 6, "Short")
		, NumPut(dw, c, cb + 8, "Short"), NumPut(dh, c, cb + 10, "Short")
	Return, true
}
*/
x32
Posts: 177
Joined: 25 Nov 2016, 16:44

Re: SOH Characters crashing my script.  Topic is solved

17 Jul 2018, 09:04

I was able to work out that the characters were in fact being sent from the script on the other computer. Turns out I just needed to add a short pause between when the script gathered the info from the other window and sent it back to this computer.

Return to “Ask for Help (v1)”

Who is online

Users browsing this forum: Google [Bot] and 370 guests