danls Posted January 15, 2012 Posted January 15, 2012 (edited) 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: expandcollapse popup#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 Edited January 15, 2012 by danls
danls Posted January 16, 2012 Author Posted January 16, 2012 So I'm starting to think maybe I should use the Run command instead of _WinAPI_CreateProcess, which the named pipes example uses. I could then use StdoutRead and StdInWrite to send and receive data from the process, and then send this into the named pipe. But I still can't figure out why the cmd /k process exits when using _WinAPI_CreateProcess as above. Any ideas?
danls Posted January 18, 2012 Author Posted January 18, 2012 No one has any clue about why a created process just exits like this? This is driving me crazy...
Recommended Posts
Create an account or sign in to comment
You need to be a member in order to leave a comment
Create an account
Sign up for a new account in our community. It's easy!
Register a new accountSign in
Already have an account? Sign in here.
Sign In Now