First post and first Autoit script. Gotta say I love this scripting language so far.
I am modifying the Named Pipes server & client examples that come with the latest AutoIt to run from the console rather than Gui. So far so good. But when I try sending a command from the client to the server that takes followup interaction, the command exits after it sends its first batch of output.
As a simple example, I am trying to get the named pipe server to run "cmd.exe dir /k". I want the command prompt to then continue running and taking input after it outputs the first results from "dir", hence using the /k flag instead of /c. However, it sends the output of "dir" back to the client and then the cmd.exe process exits. I cannot figure out why!
I'm not going to give the code for the entire server script since it's quite long, but here are the relevant functions for server.au3:
#include <Timers.au3>
#include <Inet.au3>
#include <FTPEx.au3>
#include <ScreenCapture.au3>
#include <NamedPipes.au3>
#include <WinAPI.au3>
#include <WindowsConstants.au3>
#include <GuiConstantsEx.au3>
; ===============================================================================================================================
; Global constants
; ===============================================================================================================================
Global Const $DEBUGGING = True
Global Const $BUFSIZE = 4096
Global Const $PIPE_NAME = ".pipeAutoIt3"
Global Const $TIMEOUT = 5000
Global Const $WAIT_TIMEOUT = 258
Global Const $ERROR_IO_PENDING = 997
Global Const $ERROR_PIPE_CONNECTED = 535
; ===============================================================================================================================
; Global variables
; ===============================================================================================================================
Global $hEvent, $iMemo, $pOverlap, $tOverlap, $hPipe, $hReadPipe, $iState, $iToWrite, $hProcess, $ProcessID
; ===============================================================================================================================
; This function loops waiting for a connection event or the GUI to close
; ===============================================================================================================================
Func MsgLoop()
Local $iEvent
Do
$iEvent = _WinAPI_WaitForSingleObject($hEvent, 0)
If $iEvent < 0 Then
LogError("MsgLoop ...........: _WinAPI_WaitForSingleObject failed")
Exit
EndIf
If $iEvent = $WAIT_TIMEOUT Then ContinueLoop
Debug("MsgLoop ...........: Instance signaled")
Switch $iState
Case 0
CheckConnect()
Case 1
ReadRequest()
Case 2
CheckPending()
Case 3
RelayOutput()
EndSwitch
Until GUIGetMsg() = $GUI_EVENT_CLOSE
EndFunc ;==>MsgLoop
; ===============================================================================================================================
; This function reads a request message from the client
; ===============================================================================================================================
Func ReadRequest()
Local $pBuffer, $tBuffer, $iRead, $bSuccess
$tBuffer = DllStructCreate("char Text[" & $BUFSIZE & "]")
$pBuffer = DllStructGetPtr($tBuffer)
$bSuccess = _WinAPI_ReadFile($hPipe, $pBuffer, $BUFSIZE, $iRead, $pOverlap)
If $bSuccess And ($iRead <> 0) Then
; The read operation completed successfully
Debug("ReadRequest .......: Read success")
Else
; Wait for read Buffer to complete
If Not _WinAPI_GetOverlappedResult($hPipe, $pOverlap, $iRead, True) Then
LogError("ReadRequest .......: _WinAPI_GetOverlappedResult failed")
ReconnectClient()
Return
Else
; Read the command from the pipe
$bSuccess = _WinAPI_ReadFile($hPipe, $pBuffer, $BUFSIZE, $iRead, $pOverlap)
If Not $bSuccess Or ($iRead = 0) Then
LogError("ReadRequest .......: _WinAPI_ReadFile failed")
ReconnectClient()
Return
EndIf
EndIf
EndIf
; Execute the console command
If Not ExecuteCmd(DllStructGetData($tBuffer, "Text")) Then
ReconnectClient()
Return
EndIf
; Relay console output back to the client
$iState = 3
EndFunc ;==>ReadRequest
; ===============================================================================================================================
; This function relays the console output back to the client
; ===============================================================================================================================
Func RelayOutput()
Local $pBuffer, $tBuffer, $sLine, $iRead, $bSuccess, $iWritten
$tBuffer = DllStructCreate("char Text[" & $BUFSIZE & "]")
$pBuffer = DllStructGetPtr($tBuffer)
; Read data from console pipe
_WinAPI_ReadFile($hReadPipe, $pBuffer, $BUFSIZE, $iRead)
;No output read from process
If $iRead = 0 Then
; First check if process still exists
If ProcessExists($ProcessID) Then
LogMsg("Process still exists")
; 1) continue interacting with command
Else
; 2) close hReadPipe and request new command
LogMsg("RelayOutput .......: Process " & $ProcessID & " !exists..Write done")
_WinAPI_CloseHandle($hReadPipe)
_WinAPI_FlushFileBuffers($hPipe)
ReconnectClient()
Return
EndIf
EndIf
; Get the data and strip out the extra carriage returns
$sLine = StringLeft(DllStructGetData($tBuffer, "Text"), $iRead)
$sLine = StringReplace($sLine, @CR & @CR, @CR)
$iToWrite = StringLen($sLine)
DllStructSetData($tBuffer, "Text", $sLine)
; Relay the data back to the client
$bSuccess = _WinAPI_WriteFile($hPipe, $pBuffer, $iToWrite, $iWritten, $pOverlap)
If $bSuccess And ($iWritten = $iToWrite) Then
Debug("RelayOutput .......: Write success")
Else
If Not $bSuccess And (_WinAPI_GetLastError() = $ERROR_IO_PENDING) Then
Debug("RelayOutput .......: Write pending")
$iState = 2
Else
; An error occurred, disconnect from the client
LogError("RelayOutput .......: Write failed")
ReconnectClient()
EndIf
EndIf
EndFunc ;==>RelayOutput
; ===============================================================================================================================
; Executes a command and returns the results
; ===============================================================================================================================
Func ExecuteCmd($sCmd)
Local $tProcess, $tSecurity, $tStartup, $hWritePipe, $iSuccess
; Set up security attributes
$tSecurity = DllStructCreate($tagSECURITY_ATTRIBUTES)
DllStructSetData($tSecurity, "Length", DllStructGetSize($tSecurity))
DllStructSetData($tSecurity, "InheritHandle", True)
; Create a pipe for the child process's STDOUT
If Not _NamedPipes_CreatePipe($hReadPipe, $hWritePipe, $tSecurity) Then
LogError("ExecuteCmd ........: _NamedPipes_CreatePipe failed")
Return False
EndIf
; Create child process
$tProcess = DllStructCreate($tagPROCESS_INFORMATION)
$tStartup = DllStructCreate($tagSTARTUPINFO)
DllStructSetData($tStartup, "Size", DllStructGetSize($tStartup))
DllStructSetData($tStartup, "Flags", BitOR($STARTF_USESTDHANDLES, $STARTF_USESHOWWINDOW))
DllStructSetData($tStartup, "StdOutput", $hWritePipe)
DllStructSetData($tStartup, "StdError", $hWritePipe)
$iSuccess = _WinAPI_CreateProcess("", $sCmd, 0, 0, True, 0, 0, "", DllStructGetPtr($tStartup), DllStructGetPtr($tProcess))
If Not $iSuccess Then
LogError("ExecuteCmd ........: _WinAPI_CreateProcess failed")
_WinAPI_CloseHandle($hReadPipe)
_WinAPI_CloseHandle($hWritePipe)
Return False
EndIf
_WinAPI_CloseHandle(DllStructGetData($tProcess, 'hThread'))
$hProcess = DllStructGetData($tProcess, 'hProcess')
$ProcessID = DllStructGetData($tProcess, 'ProcessID')
LogMsg("PID: " & $ProcessID)
;_WinAPI_CloseHandle(DllStructGetData($tProcess, "hProcess"))
;_WinAPI_CloseHandle(DllStructGetData($tProcess, "hThread"))
; Close the write end of the pipe so that we can read from the read end
_WinAPI_CloseHandle($hWritePipe)
LogMsg("ExecuteCommand ....: " & $sCmd)
Return True
EndFunc ;==>ExecuteCmd