gritts Posted October 19, 2014 Posted October 19, 2014 I have been wracking my brains today trying to figure out how to send a control character to a DOS command I initiate within a script. I am doing this little project to eventually add a GUI to some older DOS based apps still in use. (Not everyone appreciates the fine art of command line execution I guess ) Anywho, to test my scripting and what I was able to dig up, I came up with the following. (Yes, I know AutoIT has a ping function, DOS ping was what I came up with to test sending commands to, it is not the final program) expandcollapse popup#cs ---------------------------------------------------------------------------- AutoIt Version: 3.3.12.0 Author: myName Script Function: Template AutoIt script. #ce ---------------------------------------------------------------------------- ; Script Start - Add your code below here #include <ButtonConstants.au3> #include <EditConstants.au3> #include <GUIConstantsEx.au3> #include <WindowsConstants.au3> #include <Constants.au3> #include <process.au3> Opt("GUIOnEventMode", 1) #Region ### START Koda GUI section ### $Main = GUICreate("Ping Now", 547, 62, 192, 124) GUISetFont(10, 400, 0, "MS Sans Serif") GUISetOnEvent($GUI_EVENT_CLOSE, "MainClose") GUISetOnEvent($GUI_EVENT_MINIMIZE, "MainMinimize") GUISetOnEvent($GUI_EVENT_MAXIMIZE, "MainMaximize") GUISetOnEvent($GUI_EVENT_RESTORE, "MainRestore") $tbPingURL = GUICtrlCreateInput("", 16, 16, 425, 24) GUICtrlSetOnEvent($tbPingURL, "tbPingURLChange") $btnPingStart = GUICtrlCreateButton("Ping Now", 456, 16, 75, 25) GUICtrlSetOnEvent($btnPingStart, "btnPingStartClick") GUISetState(@SW_SHOW) #EndRegion ### END Koda GUI section ### Global $srPID While 1 Sleep(100) WEnd Func btnPingStartClick() Local $sURL = GUICtrlRead($tbpingURL),$stdOutLine MsgBox(0,"Ping URL","Reading URL: "&$sURL) ;ignored as I got tired of typing $srPID = Run(@ComSpec&" /c "&"ping -t yahoo.com","",@SW_HIDE,$STDIN_CHILD + $STDOUT_CHILD) ConsoleWrite("Running with PID: "&$srPID&@CRLF) Sleep(1000) EndFunc Func MainClose() ;ControlSend(@ComSpec,"","",Chr(3),1) ;ConsoleWrite(_ProcessGetName($srPID)) $charout=StdinWrite($srPID,Chr(3)) ConsoleWrite($charout&" - "&@error&@CRLF) StdinWrite($srPID) ;ProcessWait($srPID) ProcessWaitClose($srPID) Exit EndFunc Func MainMaximize() EndFunc Func MainMinimize() EndFunc Func MainRestore() EndFunc Func tbPingURLChange() EndFunc When I close the resulting window, I am expecting a CTRL+c to be sent to the running Ping command. Instead, I get a result code back implying that the command was send but nothing happens after that. Ping continues to run in the background till I end its process through Task Manager. When I end the process, the script closes, provided I attempt to close the GUI window (Func MainClose()). If I do not try to close the GUI first and end the ping process through task manager, I get a '0' as the result code for StdInWrite. The PID returned is NOT that of the child process initiated by the Run command but that of the cmd.exe that is started to run the Ping command within. Leaving off the @comspec portion of the Run statement makes no difference, the ping continues to run till ended. Any suggestions? I noted that other queries about sending control keys to DOS commands have been posted in the past but I didn't really pick up on a solution.
gritts Posted October 24, 2014 Author Posted October 24, 2014 Any suggestions? I prefer no to just end the process as the normal means to close the application is with a "CRTL+c" keypress.
jguinch Posted October 24, 2014 Posted October 24, 2014 Well, I'm not sure to really understand what is your problem... Does this code helps you ? expandcollapse popup#cs ---------------------------------------------------------------------------- AutoIt Version: 3.3.12.0 Author: myName Script Function: Template AutoIt script. #ce ---------------------------------------------------------------------------- ; Script Start - Add your code below here #include <ButtonConstants.au3> #include <EditConstants.au3> #include <GUIConstantsEx.au3> #include <WindowsConstants.au3> #include <Constants.au3> #include <process.au3> Opt("GUIOnEventMode", 1) #Region ### START Koda GUI section ### $Main = GUICreate("Ping Now", 547, 62, 192, 124) GUISetFont(10, 400, 0, "MS Sans Serif") GUISetOnEvent($GUI_EVENT_CLOSE, "MainClose") GUISetOnEvent($GUI_EVENT_MINIMIZE, "MainMinimize") GUISetOnEvent($GUI_EVENT_MAXIMIZE, "MainMaximize") GUISetOnEvent($GUI_EVENT_RESTORE, "MainRestore") $tbPingURL = GUICtrlCreateInput("", 16, 16, 425, 24) GUICtrlSetOnEvent($tbPingURL, "tbPingURLChange") $btnPingStart = GUICtrlCreateButton("Ping Now", 456, 16, 75, 25) GUICtrlSetOnEvent($btnPingStart, "btnPingStartClick") GUISetState(@SW_SHOW) #EndRegion ### END Koda GUI section ### Global $srPID While 1 Sleep(100) WEnd Func btnPingStartClick() Local $sURL = GUICtrlRead($tbpingURL),$stdOutLine MsgBox(0,"Ping URL","Reading URL: "&$sURL) ;ignored as I got tired of typing $srPID = Run(@ComSpec&" /c ping -t google.com","","",$STDIN_CHILD + $STDOUT_CHILD) ConsoleWrite("Running with PID: "&$srPID&@CRLF) EndFunc Func MainClose() ProcessClose($srPID) $sOutput = StdoutRead($srPID) ConsoleWrite($sOutput) Exit EndFunc Func MainMaximize() EndFunc Func MainMinimize() EndFunc Func MainRestore() EndFunc Func tbPingURLChange() EndFunc Spoiler Network configuration UDF, _DirGetSizeByExtension, _UninstallList Firefox ConfigurationArray multi-dimensions, Printer Management UDF
kylomas Posted October 24, 2014 Posted October 24, 2014 The problem is that Ping with the "-t" parm pings the target continuously until a CTRL+C is received by the command window. It seems that "normal" methods do not work on the command window. Do a Google search for lot's of info on it. Forum Rules Procedure for posting code "I like pigs. Dogs look up to us. Cats look down on us. Pigs treat us as equals." - Sir Winston Churchill
gritts Posted October 28, 2014 Author Posted October 28, 2014 @jguinch - Your approach does work and I can use it. I had tested it in a version of my script but I am trying to exhaust my options first before I just kill the process. The process I intend to use this with likes to be closed with a CTRL+c but it can be killed. It closes connections and files when it receives the CTRL+c. I used ping as it seemed the best proxy for the actual programs. I hope to use this method against a proprietary app in our office as a means of creating an eventual front-end of sorts. @kylomas - I did reseach Google for a while and after feeling like I was running in circles I came to the same conclusion. I had read here that others were attempting the same if not similar things I was attempting (CTRL+c to a console window opened with the "Run" command) but did not see where there had been a resolution. (Unless my search criteria never returned the results) It almost sounds like sending the CTRL+c is not possible. I'll keep plugging away at it...
CosmicDan Posted March 5, 2015 Posted March 5, 2015 (edited) @jguinch - Your approach does work and I can use it. I had tested it in a version of my script but I am trying to exhaust my options first before I just kill the process. The process I intend to use this with likes to be closed with a CTRL+c but it can be killed. It closes connections and files when it receives the CTRL+c. I used ping as it seemed the best proxy for the actual programs. I hope to use this method against a proprietary app in our office as a means of creating an eventual front-end of sorts. @kylomas - I did reseach Google for a while and after feeling like I was running in circles I came to the same conclusion. I had read here that others were attempting the same if not similar things I was attempting (CTRL+c to a console window opened with the "Run" command) but did not see where there had been a resolution. (Unless my search criteria never returned the results) It almost sounds like sending the CTRL+c is not possible. I'll keep plugging away at it... Any luck? I've searched for the past hour, Writing Chr(3) to StdIn nor GenerateConsoleCtrlEvent work. Googling this, it seems to be a widespread "feature" of Windows - rather than an AutoIt bug. See here for more (albeit non-AutoIt) discussion on this. Edited March 5, 2015 by JonusC CosmicDan.com
CosmicDan Posted March 6, 2015 Posted March 6, 2015 (edited) Sorry for double post, but it's worthy of a notification to all subscribed. I found a solution to this but with one caveat - it only works when the script is compiled. GenerateConsoleCtrlEvent works, but the console needs to be "attached" first. Example below, all credits to rover 2k12. EDIT: It also doesn't work if it's run by SciTE/Wrapper "#AutoIt3Wrapper_Run_After="%out%"" directive... so it's a very last-resort thing. But still better than doing a "kill process" IMO. expandcollapse popup;coded by rover 2k12 ;Ref: ;<a href='http://stackoverflow.com/questions/813086/can-i-send-a-ctrl-c-sigint-to-an-application-on-windows' class='bbc_url' title='External link' rel='nofollow external'>http://stackoverflow.com/questions/813086/can-i-send-a-ctrl-c-sigint-to-an-application-on-windows</a> #NoTrayIcon ;#AutoIt3Wrapper_Change2CUI=y If Not @Compiled Then Exit #include <windowsconstants.au3> Opt("MustDeclareVars", 1) Global $iCallback OnAutoItExitRegister("_Exit") Global Const $CTRL_BREAK_EVENT = 1 Global Const $CTRL_C_EVENT = 0 Global $fCloseFlag = False $iCallback = _InitConsole() _Main() Func _Main() GUICreate("", 600, 400) Local $iMemo = GUICtrlCreateEdit("", 2, 28, 596, 370, $WS_VSCROLL) GUICtrlSetLimit(-1, 1048576) Local $cCtrl_C = GUICtrlCreateButton("Ctrl-C", 2, 2, 120, 25) GUISetState() Local $iExitCode = Cmd("cmd.exe /c dir c:\ /s", $iMemo, $cCtrl_C) MsgBox(262144, "Exit Code", $iExitCode & @TAB & @TAB & @TAB) GUICtrlSetData($iMemo, "") $iExitCode = Cmd("ping.exe -t " & @IPAddress1, $iMemo, $cCtrl_C) MsgBox(262144, "Exit Code", $iExitCode & @TAB & @TAB & @TAB) ;Local $Archiv = @TempDir & "\" & @HOUR & "_" & @MIN & "_" & @SEC & "_test.zip" ;GUICtrlSetData($iMemo, "") ;$iExitCode = Cmd(@ProgramFilesDir & "\7-Zip\7z a -tzip "&$Archiv&" "&@TempDir & "\*.*", $iMemo, $cCtrl_C) ;MsgBox(262144, "Exit Code", $iExitCode&@TAB&@TAB&@TAB) ;send ctrl-c to console process not launched by script GUICtrlSetData($iMemo, "") DllCall("kernel32.dll", "bool", "FreeConsole") ;free console so we can attach to another console process MsgBox(262144, "Open CMD prompt", "open a single cmd prompt with ping.exe -t yourIP, then continue") Local $aCon = WinList("[CLASS:ConsoleWindowClass]"), $PID For $i = 0 To $aCon[0][0] $PID = WinGetProcess($aCon[$i][1]) If $PID <> @AutoItPID Then DllCall("kernel32.dll", "bool", "AttachConsole", "dword", $PID) Next GUICtrlSetData($iMemo, "Ctrl-C to close existing console") While 1 Switch GUIGetMsg() Case -3 ExitLoop Case $cCtrl_C ;send ctrl-c to script and child process event handler, script event handler will block exit DllCall("kernel32.dll", "bool", "GenerateConsoleCtrlEvent", "dword", $CTRL_C_EVENT, "dword", 0) EndSwitch WEnd DllCall("kernel32.dll", "bool", "FreeConsole") Exit EndFunc ;==>_Main Func _Exit() DllCall("kernel32.dll", "bool", "FreeConsole") DllCall("Kernel32.dll", "bool", "SetConsoleCtrlHandler", "ptr", 0, "bool", 0) DllCallbackFree($iCallback) EndFunc ;==>_Exit Func Cmd($sCmd, $cEdit, $cBtn) Local $iPid = Run($sCmd, @ScriptDir, @SW_HIDE, 2) Local $h_Process = DllCall('kernel32.dll', 'ptr', 'OpenProcess', 'int', 0x400, 'int', 0, 'int', $iPid) Local $sData, $retval = -1 While 1 $sData = StdoutRead($iPid) If @error Then ExitLoop $sData = StringReplace($sData, @CR & @CR, @CR) If $sData Then GUICtrlSetData($cEdit, $sData & @CRLF, 1) #cs If $fCloseFlag Then ;continually send ctrl-c until child process exits (not all child processes will respond to Ctrl-C the first time) DllCall("kernel32.dll", "bool", "GenerateConsoleCtrlEvent", "dword", $CTRL_C_EVENT, "dword", 0) If ProcessExists($iPid) = 0 Then ExitLoop EndIf #ce Switch GUIGetMsg() Case -3 ;ExitLoop Case $cBtn ;send ctrl-c to script and child process event handler, script event handler will block exit DllCall("kernel32.dll", "bool", "GenerateConsoleCtrlEvent", "dword", $CTRL_C_EVENT, "dword", 0) EndSwitch WEnd $fCloseFlag = False If IsArray($h_Process) Then Local $i_ExitCode = DllCall('kernel32.dll', 'ptr', 'GetExitCodeProcess', 'ptr', $h_Process[0], 'int*', 0) If IsArray($i_ExitCode) Then $retval = $i_ExitCode[2] DllCall('kernel32.dll', 'ptr', 'CloseHandle', 'ptr', $h_Process[0]) EndIf Return $retval EndFunc ;==>Cmd Func _InitConsole() DllCall("kernel32.dll", "int", "AllocConsole") ;DllCall("kernel32.dll", "bool", "AttachConsole", "dword", -1) Local $hConsolehWnd = DllCall("Kernel32.dll", "hwnd", "GetConsoleWindow") If @error Then Return WinSetState($hConsolehWnd[0], "", @SW_HIDE) Local $iCB = DllCallbackRegister("_ConsoleEventHandler", "long", "dword") DllCall("Kernel32.dll", "bool", "SetConsoleCtrlHandler", "ptr", DllCallbackGetPtr($iCB), "bool", 1) Return $iCB EndFunc ;==>_InitConsole Func _ConsoleEventHandler($dwCtrlType) Switch $dwCtrlType Case $CTRL_C_EVENT, $CTRL_BREAK_EVENT $fCloseFlag = True ; set flag for checking if child process exists Return True ; exiting blocked, exit must be called from loop ;Return False ;exit normally EndSwitch Return False EndFunc ;==>_ConsoleEventHandler Edited March 6, 2015 by JonusC CosmicDan.com
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