Allow2010 Posted March 9, 2012 Author Posted March 9, 2012 thank you, i am a bit busy right now, will take a look asap and will report back...again thank you very much for your help !
Allow2010 Posted March 9, 2012 Author Posted March 9, 2012 Your example works fine so far, but what i still do not get: wehn closing the external cmd window also the autoit app closes... i was thinking that the Func _ConsoleEventHandler($dwCtrlType) was there to prevent this, but it seems this does not work... here is my minimal version, i do not understand why it closes when i press ctrl-c... expandcollapse popup#NoTrayIcon #Region ;**** Directives created by AutoIt3Wrapper_GUI **** #AutoIt3Wrapper_Res_requestedExecutionLevel=asInvoker #AutoIt3Wrapper_AU3Check_Stop_OnWarning=y #EndRegion ;**** Directives created by AutoIt3Wrapper_GUI **** ;#AutoIt3Wrapper_Change2CUI=y If Not @Compiled Then Exit #include <windowsconstants.au3> 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() ;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 $PID=InputBox("PID","Enter the PID of the Console") If $PID <> @AutoItPID Then DllCall("kernel32.dll", "bool", "AttachConsole", "dword", $PID) 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 _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
Allow2010 Posted March 10, 2012 Author Posted March 10, 2012 (edited) I found a little utility that sends a ctrl-break. I had to collect the child pid due to what I was running and I ran this app (SendSignal.exe - you'll find a link in google). I found it and it works well, made SendSignal in autoit, here is the source code for anyone who prefers to create his own exe. The only problem is that i still have to call it as an external exe which slows down the process a bit If $Cmdline[0] = 1 Then DllCall("kernel32.dll", "bool", "AttachConsole", "dword", $Cmdline[1]) DllCall("kernel32.dll", "bool", "GenerateConsoleCtrlEvent", "dword", 0, "dword", 0) EndIf Edited March 10, 2012 by Allow2010
rover Posted March 11, 2012 Posted March 11, 2012 (edited) Your example works fine so far, but what i still do not get: wehn closing the external cmd window also the autoit app closes... i was thinking that the Func _ConsoleEventHandler($dwCtrlType) was there to prevent this, but it seems this does not work... The attached console is shared continuously in that script, so closing either console closes both processes. The event handler can only reliably block Ctrl-C/Break (Blocking exit only works (unreliably) in XP and is no longer supported as of Vista - see MSDN) The example and UDF below fixes this by only attaching to the console to send Ctrl-C. If you stay attached to a console or cmd.exe prompt or just want to prevent a console/prompt from being closed when it is running a process you can disable the X button and remove the menu close option, but user can still Ctrl-C or enter Exit. Local $hCon = DllCall("Kernel32.dll", "hwnd", "GetConsoleWindow");get attached console window handle ;or WinGetHandle("[TITLE:title; CLASS:ConsoleWindowClass]") Local $hMenu = DllCall('user32.dll', 'int', 'GetSystemMenu', 'hwnd', $hCon[0], 'int', 0) DllCall('user32.dll', 'int', 'EnableMenuItem', 'hwnd', $hMenu[0], 'int', 0xF060, 'int', 0x00000002) ; $SC_CLOSE/$MF_DISABLED DllCall("user32.dll", "int", "RemoveMenu", "hwnd", $hMenu[0] , "int", 0xF060, "int", 0x0) here is my minimal version, i do not understand why it closes when i press ctrl-c... You have to register the event handler after attaching to the console. I found it and it works well, made SendSignal in autoit, here is the source code for anyone who prefers to create his own exe. The only problem is that i still have to call it as an external exe which slows down the process a bit This can be done in the main script without an external exe. I've reworked the example and created a UDF that will work with a process run from the script or an existing console app/cmd window Example initializes the event handler after attaching to the console, then frees the console after sending Ctrl-C, so the script does not exit when the attached console does. Example sends Ctrl-C to 7-Zip archiving files in a selected folder and a cmd.exe window (open a command window and run a process) Edit: changes to udf for issue with undeclared var error with callback param dwCtrlType (removed as it is not required) (callback is created in a new thread, so there are problems with variables and functions) added option to loop sending ctrl-c Edit: Updated UDF with an optional mode that eliminates the requirement for a callback event handler, added sleep param, initial unresponsive console/process checking and param error checking expandcollapse popup#NoTrayIcon Opt("MustDeclareVars", 1) If Not @Compiled Then Exit _Main() Func _Main() GUICreate("Send Ctrl-C to console process", 600, 400) Local $iMemo = GUICtrlCreateEdit("", 2, 28, 596, 370, 0x00200000) GUICtrlSetLimit(-1, 1048576) Local $cCtrl_C = GUICtrlCreateButton("Ctrl-C", 2, 2, 120, 25) GUISetState() Cmd("cmd.exe /c dir c: /s", $iMemo, $cCtrl_C) Cmd("ping.exe -t " & @IPAddress1, $iMemo, $cCtrl_C) Local $sTargetFolder = @TempDir ;run 7-Zip and sends Ctrl-C to cancel Local $Archiv = $sTargetFolder & "_test.zip" Cmd(@ProgramFilesDir & "7-Zip7z a -tzip " & $Archiv & " " & $sTargetFolder & "*", $iMemo, $cCtrl_C) ;Send Ctrl-C to a console app or cmd.exe console running a process ;Enter pid of console Local $PID = 0 ;Local $aCon = WinList("[CLASS:ConsoleWindowClass]") ;get first console ;Local $PID = WinGetProcess($aCon[1][1]) _ConsoleSendCtrlC($PID) While GUIGetMsg() <> -3 WEnd Exit EndFunc ;==>_Main 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, $fRead = True GUICtrlSetData($cEdit, "Running commandline: " & @CRLF & $sCmd & @CRLF & @CRLF) While 1 If $fRead Then $sData = StdoutRead($iPid) If @error Then $fRead = Not $fRead GUICtrlSetData($cEdit, @CRLF & "+++Process finished - Exit Code: " & _GetExitCode($h_Process) & @CRLF, 1) Return Else $sData = StringReplace($sData, @CR & @CR, @CR) If $sData Then GUICtrlSetData($cEdit, $sData & @CRLF, 1) EndIf EndIf Switch GUIGetMsg() Case -3 Exit Case $cBtn _ConsoleSendCtrlC($iPid) GUICtrlSetData($cEdit, @CRLF & "!!!Process terminated - Exit Code: " & _GetExitCode($h_Process) & @CRLF, 1) Return EndSwitch WEnd EndFunc ;==>Cmd Func _GetExitCode(ByRef $hProc) If Not IsArray($hProc) Then Return -1 Local $retval = -1 Local $i_ExitCode = DllCall('kernel32.dll', 'ptr', 'GetExitCodeProcess', 'ptr', $hProc[0], 'int*', 0) If Not @error Then $retval = $i_ExitCode[2] DllCall('kernel32.dll', 'ptr', 'CloseHandle', 'ptr', $hProc[0]) Return $retval EndFunc ;==>_GetExitCode ; #FUNCTION# ==================================================================================================================== ; Name ..........: _ConsoleSendCtrlC ; Description....: Send Ctrl-C to cancel a process running as a console or a process running in a cmd.exe prompt. ; Syntax ........: _ConsoleSendCtrlC($PID, $iMode = 0, $iDll = "kernel32.dll") ; Parameters.....: hWnd - Process ID of a ConsoleWindowClass window ; $iEvent - 0 = Send CTRL_C_EVENT=0 ; $iEvent - 1 = Send CTRL_BREAK_EVENT=1 ; $iMode - 0 = No Callback required - Block CTRL_C_EVENT only for calling process ; $iMode - 1 = Callback required - Block CTRL_C_EVENT and CTRL_BREAK_EVENT for calling process ; $iLoop - Number of times to send Ctrl-C/Break event (Run in loop with ProcessExists check and 10ms Sleep) ; $iSlp - Sleep value - Default 10ms ; $iDll - Optional handle from DllOpen() ; Return values..: Succcess - 1 ; Failure - -1 and sets @error ; Author.........: rover 2k12 ; Remarks .......: Allows cancelled process to do cleanup before exiting. ; Event handler blocking of Ctrl-C/Break prevents script exiting when GenerateConsoleCtrlEvent called on a shared process console ; ; Note: It is preferable to send a CTRL_C_EVENT ($iEvent = 0) without the callback ($iMode = 0). (callback is created in a thread) ; ; Note: Console window and process checking is untested and unfinished in this version (Return = -1 and Error = 2 indicates console window is unresponsive) ; Script crash has occurred when sending Ctrl+C in a tight loop to an unresponsive process ; Test console window or process reponsiveness and send Ctrl+C one or more times with long sleep then if still unreponsive use ProcessClose. ; ; Related .......: _ConsoleEventHandler ; Example .......: Yes ; ; Optional: Callback Event Handler Function (for $iMode = 1 only) ; Func _ConsoleEventHandler() ; Return 1 ; Ctrl-C/Break exiting blocked ; EndFunc ;==>_ConsoleEventHandler ; ; =============================================================================================================================== Func _ConsoleSendCtrlC($PID, $iEvent = 0, $iMode = 0, $iLoop = 1, $iSlp = 10, $iDll = "kernel32.dll") ;Author: rover 2k12 Local $aRet = DllCall($iDll, "bool", "AttachConsole", "dword", $PID) If @error Or $aRet[0] = 0 Then Return SetError(1, 0, -1) ;sets GetLastError to ERROR_GEN_FAILURE (31) if process does not exist ;sets GetLastError to ERROR_ACCESS_DENIED (5) if already attached to a console ;sets GetLastError to ERROR_INVALID_HANDLE (6) if process does not have a console $aRet = DllCall("Kernel32.dll", "hwnd", "GetConsoleWindow") ;must be attached to console to get hWnd If Not @error And IsHWnd($aRet[0]) Then $aRet = DllCall('user32.dll', 'int', 'IsHungAppWindow', 'hwnd', $aRet[0]);from Yashieds' WinAPIEx.au3 If Not @error And $aRet[0] Then DllCall($iDll, "bool", "FreeConsole") Return SetError(2, 0, -1) EndIf Else Return SetError(3, 0, -1) EndIf Local $iCTRL = 0, $iRet = 1, $pCB = 0, $iCB = 0, $iErr = 0, $iCnt = 0 ;check params $iLoop = Int($iLoop) If $iLoop < 1 Then $iLoop = 1 If $iEvent Then $iCTRL = 1 If $iSlp < 10 Then $iSlp = 10 If $iMode Then Local $sCB = "_ConsoleEventHandler" Call($sCB) If @error <> 0xDEAD And @extended <> 0xBEEF Then Local $iCB = DllCallbackRegister($sCB, "long", "") If Not $iCB Then DllCall($iDll, "bool", "FreeConsole") Return SetError(4, 0, -1) EndIf $pCB = DllCallbackGetPtr($iCB) Else $iCTRL = 0 EndIf EndIf $aRet = DllCall($iDll, "bool", "SetConsoleCtrlHandler", "ptr", $pCB, "bool", 1) If @error Or $aRet[0] = 0 Then If $iMode Then DllCallbackFree($iCB) DllCall($iDll, "bool", "FreeConsole") Return SetError(5, @error, -1) EndIf ;send ctrl-c, free console and unregister event handler Do $aRet = DllCall($iDll, "bool", "GenerateConsoleCtrlEvent", "dword", $iCTRL, "dword", 0) If @error Or $aRet[0] = 0 Then $iRet = -1 $iCnt += 1 Sleep($iSlp) Until $iCnt = $iLoop Or ProcessExists($PID) = 0 $aRet = DllCall($iDll, "bool", "FreeConsole") If Not @error Then $iErr = $aRet[0] If $iMode Then Sleep(50) ;10ms o.k, but margin for error better (DllCallbackFree can crash script if called too soon after console freed when using callback) $aRet = DllCall($iDll, "bool", "SetConsoleCtrlHandler", "ptr", 0, "bool", 0) If $iMode Then DllCallbackFree($iCB) Return SetError(@error, $iErr, $iRet) EndFunc ;==>_ConsoleSendCtrlC Func _ConsoleEventHandler() ;single dword param $dwCtrlType in callback can sometimes cause - Error: Variable used without being declared. (event handler is run in a new thread) ;Optional: Callback Event Handler Function (for $iMode = 1 only) Return 1 ; Ctrl-C/Break exiting blocked EndFunc ;==>_ConsoleEventHandler UDF: _ConsoleSendCtrlC() expandcollapse popup; #FUNCTION# ==================================================================================================================== ; Name ..........: _ConsoleSendCtrlC ; Description....: Send Ctrl-C to cancel a process running as a console or a process running in a cmd.exe prompt. ; Syntax ........: _ConsoleSendCtrlC($PID, $iMode = 0, $iDll = "kernel32.dll") ; Parameters.....: hWnd - Process ID of a ConsoleWindowClass window ; $iEvent - 0 = Send CTRL_C_EVENT=0 ; $iEvent - 1 = Send CTRL_BREAK_EVENT=1 ; $iMode - 0 = No Callback required - Block CTRL_C_EVENT only for calling process ; $iMode - 1 = Callback required - Block CTRL_C_EVENT and CTRL_BREAK_EVENT for calling process ; $iLoop - Number of times to send Ctrl-C/Break event (Run in loop with ProcessExists check and 10ms Sleep) ; $iSlp - Sleep value - Default 10ms ; $iDll - Optional handle from DllOpen() ; Return values..: Succcess - 1 ; Failure - -1 and sets @error ; Author.........: rover 2k12 ; Remarks .......: Allows cancelled process to do cleanup before exiting. ; Event handler blocking of Ctrl-C/Break prevents script exiting when GenerateConsoleCtrlEvent called on a shared process console ; ; Note: It is preferable to send a CTRL_C_EVENT ($iEvent = 0) without the callback ($iMode = 0). (callback is created in a thread) ; ; Note: Console window and process checking is untested and unfinished in this version (Return = -1 and Error = 2 indicates console window is unresponsive) ; Script crash has occurred when sending Ctrl+C in a tight loop to an unresponsive process ; Test console window or process reponsiveness and send Ctrl+C one or more times with long sleep then if still unreponsive use ProcessClose. ; ; Related .......: _ConsoleEventHandler ; Example .......: Yes ; ; Optional: Callback Event Handler Function (for $iMode = 1 only) ; Func _ConsoleEventHandler() ; Return 1 ; Ctrl-C/Break exiting blocked ; EndFunc ;==>_ConsoleEventHandler ; ; =============================================================================================================================== Func _ConsoleSendCtrlC($PID, $iEvent = 0, $iMode = 0, $iLoop = 1, $iSlp = 10, $iDll = "kernel32.dll") ;Author: rover 2k12 Local $aRet = DllCall($iDll, "bool", "AttachConsole", "dword", $PID) If @error Or $aRet[0] = 0 Then Return SetError(1, 0, -1) ;sets GetLastError to ERROR_GEN_FAILURE (31) if process does not exist ;sets GetLastError to ERROR_ACCESS_DENIED (5) if already attached to a console ;sets GetLastError to ERROR_INVALID_HANDLE (6) if process does not have a console $aRet = DllCall("Kernel32.dll", "hwnd", "GetConsoleWindow") ;must be attached to console to get hWnd If Not @error And IsHWnd($aRet[0]) Then $aRet = DllCall('user32.dll', 'int', 'IsHungAppWindow', 'hwnd', $aRet[0]);from Yashieds' WinAPIEx.au3 If Not @error And $aRet[0] Then DllCall($iDll, "bool", "FreeConsole") Return SetError(2, 0, -1) EndIf Else Return SetError(3, 0, -1) EndIf Local $iCTRL = 0, $iRet = 1, $pCB = 0, $iCB = 0, $iErr = 0, $iCnt = 0 ;check params $iLoop = Int($iLoop) If $iLoop < 1 Then $iLoop = 1 If $iEvent Then $iCTRL = 1 If $iSlp < 10 Then $iSlp = 10 If $iMode Then Local $sCB = "_ConsoleEventHandler" Call($sCB) If @error <> 0xDEAD And @extended <> 0xBEEF Then Local $iCB = DllCallbackRegister($sCB, "long", "") If Not $iCB Then DllCall($iDll, "bool", "FreeConsole") Return SetError(4, 0, -1) EndIf $pCB = DllCallbackGetPtr($iCB) Else $iCTRL = 0 EndIf EndIf $aRet = DllCall($iDll, "bool", "SetConsoleCtrlHandler", "ptr", $pCB, "bool", 1) If @error Or $aRet[0] = 0 Then If $iMode Then DllCallbackFree($iCB) DllCall($iDll, "bool", "FreeConsole") Return SetError(5, @error, -1) EndIf ;send ctrl-c, free console and unregister event handler Do $aRet = DllCall($iDll, "bool", "GenerateConsoleCtrlEvent", "dword", $iCTRL, "dword", 0) If @error Or $aRet[0] = 0 Then $iRet = -1 $iCnt += 1 Sleep($iSlp) Until $iCnt = $iLoop Or ProcessExists($PID) = 0 $aRet = DllCall($iDll, "bool", "FreeConsole") If Not @error Then $iErr = $aRet[0] If $iMode Then Sleep(50) ;10ms o.k, but margin for error better (DllCallbackFree can crash script if called too soon after console freed when using callback) $aRet = DllCall($iDll, "bool", "SetConsoleCtrlHandler", "ptr", 0, "bool", 0) If $iMode Then DllCallbackFree($iCB) Return SetError(@error, $iErr, $iRet) EndFunc ;==>_ConsoleSendCtrlC Func _ConsoleEventHandler() ;single dword param $dwCtrlType in callback can sometimes cause - Error: Variable used without being declared. (event handler is run in a new thread) ;Optional: Callback Event Handler Function (for $iMode = 1 only) Return 1 ; Ctrl-C/Break exiting blocked EndFunc ;==>_ConsoleEventHandler Edited March 11, 2012 by rover I see fascists...
Allow2010 Posted March 11, 2012 Author Posted March 11, 2012 thank you very much. From you post above i am a bit irritated: you say that >The event handler can only reliably block Ctrl-C/Break (Blocking exit only works (unreliably) in XP and is no longer supported as of Vista - see MSDN) and as my script ist for Vista or newer this will not work, won't it? So i will have to stay with the external app?! I will try it out as soon as i can...
Allow2010 Posted March 11, 2012 Author Posted March 11, 2012 (edited) When i use it it just works fine, but i use a loop to call it more than once to make sure the cmd windows gets it. $counter = 0 While ProcessExists($enginepid) And $counter < 10 _ConsoleSendCtrlC($enginepid) ;Run(@ScriptDir & "CCBSendSignal.exe " & $enginepid, @ScriptDir, @SW_HIDE) If Not ProcessExists($enginepid) Then ExitLoop Else $counter = $counter + 1 Sleep(1000) EndIf WEnd when it is called a few time the exe script crashs with a message Error: Variable used without being declared. i can not figure out what var this is, but it must have to do with calling the function several times and also with the fact that the cmd window is closing while the func is beeing called... So probaly some more errorchecking un the func is needed... Edited March 11, 2012 by Allow2010
rover Posted March 11, 2012 Posted March 11, 2012 thank you very much.From you post above i am a bit irritated:you say that>The event handler can only reliably block Ctrl-C/Break (Blocking exit only works (unreliably) in XP and is no longer supported as of Vista - see MSDN)and as my script ist for Vista or newer this will not work, won't it?So i will have to stay with the external app?!I will try it out as soon as i can...Only the CTRL_CLOSE_EVENT can no longer be blocked, the UDF does not use it.Ctrl-C/Break is blocked I see fascists...
rover Posted March 11, 2012 Posted March 11, 2012 When i use it it just works fine, but i use a loop to call it more than once to make sure the cmd windows gets it. $counter = 0 While ProcessExists($enginepid) And $counter < 10 _ConsoleSendCtrlC($enginepid) ;Run(@ScriptDir & "CCBSendSignal.exe " & $enginepid, @ScriptDir, @SW_HIDE) If Not ProcessExists($enginepid) Then ExitLoop Else $counter = $counter + 1 Sleep(1000) EndIf WEnd when it is called a few time the exe script crashs with a message Error: Variable used without being declared. i can not figure out what var this is, but it must have to do with calling the function several times and also with the fact that the cmd window is closing while the func is beeing called... So probaly some more errorchecking un the func is needed... I think know what causes that error message, the event handler may need ByRef on that variable. It may be a better idea to have an option in the UDF to loop a set number of times and optionally check if the process exists (I left that out, as a process launched with run or a console app you can check, but a cmd.exe window still exists after the process it was running is closed) give me a few minutes and I'll update the udf I see fascists...
Allow2010 Posted March 11, 2012 Author Posted March 11, 2012 (edited) thanks... maybe it is wise to mention, that the app i want to close does not react if i send ctrl+c too early, so i decided to loop up to 10 times/seconds and if the process still exists i kill it with process close...so i can try to shut down nicely, but if all else fails i kill it... Edited March 11, 2012 by Allow2010
rover Posted March 11, 2012 Posted March 11, 2012 (edited) thanks...maybe it is wise to mention, that the app i want to close does not react if i send ctrl+c too early, so i decided to loop up to 10 times/seconds and if the process still exists i kill it with process close...so i can try to shut down nicely, but if all else fails i kill it...O.K. the udf is updated with an option to loop with a processexists check and the issue with the event handler thread undeclared var error is fixed(perhaps a sleep value param should be added?)You jumped to a conclusion regarding the event handler.Might I give you a friendly reminder that everyone here volunteers their free time and code.I do consider OS version issues as much as I can.try the update, it should fix the issue with undeclared var errorI'm off for a morning walk, will check in later.be seeing you... Edited March 11, 2012 by rover I see fascists...
Allow2010 Posted March 11, 2012 Author Posted March 11, 2012 You jumped to a conclusion regarding the event handler.Might I give you a friendly reminder that everyone here volunteers their free time and code.I do consider OS version issues as much as I can.i think we have a missunderstanding here (maybe because my english is no perfect)...I know and value very much everyones help here and in no way was my intention to criticise your work. Sorry if it sounded like this.I just do not understand if i can/should use your solution (UDF) in an app that is trageted for Win Vista or higher or if it is unwise to do so...Maybe if it really is problematic one might consider to exit the function with an error code when run on not supported OS and/or some info in the header of the function.Thank you very much for the update....yes a sleep value parameter would be nice, and when done, please consider to post it to the example scripts forum and add the url to it to the header for everyone else, as i am sure other will need this...Thanks again !
Allow2010 Posted March 11, 2012 Author Posted March 11, 2012 (edited) it still crashes inside the do...until loop This only happends when i try to shutdown the console app while it is in a state where it does not react to ctrl+c, so it takes several "GenerateConsoleCtrlEvent" I believe it happends when the process already stopped and it tries to do $aRet = DllCall($iDll, "bool", "GenerateConsoleCtrlEvent", "dword", $iMode, "dword", 0) Edit: no it can not be caused by an no longer running process. The process sometimes still runs after the crash... Edited March 11, 2012 by Allow2010
rover Posted March 11, 2012 Posted March 11, 2012 Perhaps dissapointed would have been a better choice. anyway, nevermind. I've now tested this UDF on Win 7 x64 as well as XP x86 (the earlier code was tested on both, not that there is anything in the code that would be an OS issue) If you are concerned about OS compatibility, there is nothing used here that wont work in XP, Vista, 2003, 2008, 2008R2 and Win 7, x86 or x64bit These are the relevant MSDN pages for: SetConsoleCtrlHandler Function http://msdn.microsoft.com/en-us/library/windows/desktop/ms686016(v=vs.85).aspx Registering a Control Handler Function http://msdn.microsoft.com/en-us/library/windows/desktop/ms685049(v=vs.85).aspx Can you tell me what the problematic program is and does, or give me a download link? Regarding the crashing with that process, there are a few things to try: 1 - Get return, error and extended from _ConsoleSendCtrlC() and add to a msgbox, traytip, tooltip etc. Add _WinAPI_GetLastErrorMessage() after _ConsoleSendCtrlC and display in a msgbox, traytip, tooltip etc. 2 - Just for testing purposes, try commenting out DllCallbackFree($iCB), We can see if it crashes without DllCallbackFree. 3 - Comment out the open process and getexitcode line. 4 - Increase the sleep (modify the 10ms sleep in the udf Do loop) time between calls to GenerateConsoleCtrlEvent, and limit the number of attempts to cancel before resorting to ProcessClose. (I will add a sleep param to the UDF) 5 - Maybe something like IsAppHung or some other API that can check the unresponsiveness of a process before bombarding it with Ctrl-C, then based on that, try a reasonable number of GenerateConsoleCtrlEvent calls, then if that doesn't work launch the external exe, then last resort ProcessClose. This is certainly a good worst case example to test with. I see fascists...
Allow2010 Posted March 11, 2012 Author Posted March 11, 2012 Thanks for you reply...i have to leave now, but will come back to this tomorrow...
rover Posted March 12, 2012 Posted March 12, 2012 After a review of the MSDN page for SetConsoleCtrlHandler, I've updated the UDF in post #24 and added an optional mode that eliminates the requirement for a callback event handler (CTRL_C_EVENT only) Let's see if this eliminates the crashing with an unresponsive console. I'll see what other options there are for testing an unresponsive console/cmd window/process. Does anyone have a suggestion? I see fascists...
Allow2010 Posted March 12, 2012 Author Posted March 12, 2012 (edited) Thanks for all your work!i did a quick try with the new version using_ConsoleSendCtrlC($enginepid, 0, 20,1000)and it still crashes...sorryi will now prepare a minimal test example and will send it to you...Edit: oh no, did not see your change...call must be_ConsoleSendCtrlC($enginepid, 0, 0, 20,1000)testing again... Edited March 12, 2012 by Allow2010
Allow2010 Posted March 12, 2012 Author Posted March 12, 2012 (edited) juhuu.-) It no longer crashes:-)) there is just one small issue, which probably can not be solved: the Obfuscater does not like Call() and throws warnings and errors... i have to comment out the block If $iMode Then Local $sCB = "_ConsoleEventHandler" Call($sCB) If @error <> 0xDEAD And @extended <> 0xBEEF Then Local $iCB = DllCallbackRegister($sCB, "long", "") If Not $iCB Then DllCall($iDll, "bool", "FreeConsole") Return SetError(4, 0, -1) EndIf $pCB = DllCallbackGetPtr($iCB) Else $iCTRL = 0 EndIf EndIf This is no problem for me as i do not need it. But if the usage of call() can be prevented, it would be nice... Thank you so much for your help....i am sure i no longer need the external exe... Edited March 12, 2012 by Allow2010
rover Posted March 12, 2012 Posted March 12, 2012 (edited) Good to hear it finally stopped crashing. Eliminating the event handler callback (in a new thread) was the solution. How long did it have to loop before the process finally closed? I've removed the hung app check as I don't know if that would even work on a console, it was just an experiment. The Call() check for the event handler is removed. The callback is required for Ctrl-Break blocking, but I don't know if there is any usage where Ctrl-Break would be useful. Try this version and call it with a DllOpen(kernel32.dll) handle Let me know how many times you had to loop (place a MsgBox with $iCnt after the loop exits) Try sleep lower than 1000ms Edit: a few modifications, added link expandcollapse popup; #FUNCTION# ==================================================================================================================== ; Name ..........: _ConsoleSendCtrlC ; Description....: Send Ctrl+C (SIGINT) to cancel a process running as a console or a process running in a cmd.exe prompt ; Allows cancelled process to do cleanup before exiting ; Syntax ........: _ConsoleSendCtrlC($PID, $iLoop = 1, $iSlp = 10, $iDll = "kernel32.dll") ; Parameters.....: $PID - Process ID of a console or ConsoleWindowClass window ; $iLoop - Number of times to send CTRL_C_EVENT - Default = 1 (Run in loop with ProcessExists check and 10ms Sleep) ; $iSlp - Delay time in ms between each CTRL_C_EVENT - Default = 10ms ; $iDll - Optional handle from DllOpen() - For performance reasons use a dll handle even if not using loop values >1 ; Return values..: Succcess - 1 ; Failure - -1 and sets @error ; Author.........: rover 2k12 ; Remarks .......: Blocking of Ctrl+C prevents script exiting when GenerateConsoleCtrlEvent called on a shared console process ; Note: Only blocks CTRL_C_EVENT, will not block CTRL_BREAK_EVENT ; If console unresponsive with default params, set $iLoop >1 and $iSlp >=10 <=1000 (If still unresponsive use ProcessClose) ; MSDN ..........: SetConsoleCtrlHandler can also enable an inheritable attribute that causes the calling process to ignore CTRL+C signals. ; If GenerateConsoleCtrlEvent sends a CTRL+C signal to a process for which this attribute is enabled, the handler functions for that process are not called. ; CTRL+BREAK signals always cause the handler functions to be called. ; Example .......: Yes ; [http://www.autoitscript.com/forum/topic/138191-end-dos-process-with-ctrlc-from-script-help-needed/page__view__findpost__p__970554] ; =============================================================================================================================== Func _ConsoleSendCtrlC($PID, $iLoop = 1, $iSlp = 10, $iDll = "kernel32.dll") ;Author: rover 2k12 Local $aRet = DllCall($iDll, "bool", "AttachConsole", "dword", $PID) If @error Or $aRet[0] = 0 Then Return SetError(1, 0, -1) Local $iRet = 1, $iExt = -1, $iCnt = 0 ;check params $iLoop = Int($iLoop) If $iLoop < 1 Then $iLoop = 1 $iSlp = Int($iSlp) If $iSlp < 10 Then $iSlp = 10 $aRet = DllCall($iDll, "bool", "SetConsoleCtrlHandler", "ptr", 0, "bool", 1) If @error Or $aRet[0] = 0 Then DllCall($iDll, "bool", "FreeConsole") Return SetError(2, 0, -1) EndIf ;Send SIGINT, free console and reset Ctrl+C blocking Do $aRet = DllCall($iDll, "bool", "GenerateConsoleCtrlEvent", "dword", 0, "dword", 0) If @error Or $aRet[0] = 0 Then $iRet = -1 $iCnt += 1 Sleep($iSlp) Until $iCnt = $iLoop Or ProcessExists($PID) = 0 $aRet = DllCall($iDll, "bool", "FreeConsole") If Not @error Then $iExt = $aRet[0] $aRet = DllCall($iDll, "bool", "SetConsoleCtrlHandler", "ptr", 0, "bool", 0) If @error Or $aRet[0] = 0 Then Return SetError(3, $iExt, $iRet) Return SetError(4, $iExt, $iRet) EndFunc ;==>_ConsoleSendCtrlC Edited March 16, 2012 by rover I see fascists...
Allow2010 Posted March 13, 2012 Author Posted March 13, 2012 Thanks, i tested it it looped 7 times with a deley of 1000 i laso tried a delay of 10 and it looped 500+ times and still everything works fine... I then used dllopen and dllclose and it also worked fine... Compiling with obfuscator is no longer a problem. Thanks !!
rover Posted March 13, 2012 Posted March 13, 2012 Thanks, i tested itit looped 7 times with a deley of 1000i laso tried a delay of 10 and it looped 500+ times and still everything works fine...I then used dllopen and dllclose and it also worked fine...Compiling with obfuscator is no longer a problem.Thanks !!As a native AutoIt code solution that sounds good, however...Earlier on, (in XP) I tested TaskKill /PID from one cmd prompt to interrupt another cmd.exe and got an access denied error message - said to use /f force kill switchso I discounted using that, though it did interrupt a process launched by Run() from a script, but wouldn't interrupt an external process. (I don't recall what I did differently at the time)On further investigation, it looks like taskkill might work after all.(some differences though, taskkill interrupts the process in a cmd.exe prompt but also closes it)Run("TaskKill /PID " & $Pid, @ScriptDir, @SW_HIDE)See if TaskKill works for youOh well, at least the udf is a native autoit code solution...The difference is, on a cmd.exe prompt, taskkill interrupts the process (7-Zip deleted the temp file) but the cmd prompt also closed._ConsoleSendCtrlC only interrupted the process, cmd.exe remained open. I see fascists...
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