Что нового

Эфективное прерываение потока (StdoutRead)

beliy

Продвинутый
Сообщения
372
Репутация
72
На днях захотелось сделать оболочку для командной строки, наиболее удобно как мне показалось это сделать через StdoutRead. Да и захотелось разобраться с потоками. Но в процессе столкнулся с такими проблемами:

1) Как эффективно прервать поток, если он выполняется очень долго или он изначально бесконечный (например, "ping" с параметром -t )
2) Найти аналог ProcessWaitClose, при котором можно определить окончание выполнения команды и при этом данные получать по мере их получения (по умолчанию). Если использую ProcessWaitClose, то данные поступают только после окончания.
Вот пример кода:

Код:
;#Include <Constants.au3>
;#Include <EditConstants.au3>
#Include <GUIConstantsEx.au3>
#Include <GUIEdit.au3>
#Include <ComboConstants.au3>
#Include <WindowsConstants.au3>
#Include <Encoding.au3>

$hGUI = GUICreate('CustomCMD', 600, 400)
$comand_label = GUICtrlCreateLabel("Команда: ", 16, 11, 55, 17)
$Combo = GUICtrlCreateCombo("Выберите команду", 80, 8, 401, 25, $CBS_DROPDOWNLIST + $WS_VSCROLL)
GUICtrlSetData(-1, "ping ya.ru|ping ya.ru -t|tracert ya.ru", "ping ya.ru")
$btn_Run = GUICtrlCreateButton("Старт", 496, 6, 89, 25)
$btn_Stop = GUICtrlCreateButton("Стоп", 496, 6, 89, 25)
GUICtrlSetState(-1, $GUI_HIDE)
$Console = GUICtrlCreateEdit('', 0, 50, 600, 350, BitOR($ES_AUTOVSCROLL, $ES_READONLY, $ES_WANTRETURN, $WS_VSCROLL))
GUICtrlSetFont(-1, 9, 400, 0, 'Courier New')
GUICtrlSetBkColor(-1, 0)
GUICtrlSetColor(-1, 0x00D000)
_GUICtrlEdit_SetLimitText(-1, 8 * 1024 * 1024) ; 8 MB (ANSI)
GUISetState()


;$Config = IniReadSection("option.ini", "combobox")

;If @error Then
;	MsgBox(4096, "Error", "Ошибка чтения INI файла.")
;Else
;	For $i = 1 To $Config[0][0]
;		GUICtrlSetData($Combo, $Config[$i][1] )
;	Next
;EndIf

While 1
   Local $iPid
    $aRead = StdoutRead($iPid)
    If $aRead Then
        GUICtrlSetData($Console, GUICtrlRead($Console) & _Encoding_CyrillicTo1251($aRead))
    EndIf
   
    Switch GUIGetMsg()
        Case $GUI_EVENT_CLOSE
            ExitLoop
        Case $btn_Run
			$iPid = Run(@ComSpec & " /c " & StringReplace(GUICtrlRead($Combo), '"', '""'), @ScriptDir, Default, 2)
            GUISetCursor(1, 1)
            GUICtrlSetState($btn_Run, $GUI_HIDE)
			GUICtrlSetState($btn_Stop, $GUI_SHOW)
            GUICtrlSetState($Combo, $GUI_DISABLE)
            GUICtrlSetData($Console, '')
			ProcessWaitClose($iPID)
			 GUISetCursor(2, 0)
            GUICtrlSetState($btn_Run, $GUI_SHOW)
			GUICtrlSetState($btn_Stop, $GUI_HIDE)
            GUICtrlSetState($Combo, $GUI_ENABLE)
		 Case $btn_Stop
			MsgBox(4096, "Stop", "Stop")
			ProcessClose($iPid)
    EndSwitch      
Wend


Заранее благодарю за оказанную помощь...
 

inververs

AutoIT Гуру
Сообщения
2,135
Репутация
465
На второй вопрос ответ прямо в справке функции
Код:
StdoutRead



Добавлено:
Сообщение автоматически объединено:

На первый - убить процесс.
 
Автор
B

beliy

Продвинутый
Сообщения
372
Репутация
72
На второй вопрос ответ прямо в справке функции

Код:
StdoutRead

пересмотрел несколько раз, но ответа там так и не нашел...

На первый - убить процесс.
Логично, но с помощью
Код:
ProcessClose($iPid)

у меня не получается...
 

joiner

Модератор
Локальный модератор
Сообщения
3,557
Репутация
628
$iPid - это идентификатор cmd, а при том же пинге запускается cmd.exe и ping.exe. придется завершать оба процесса. но опять же, надо знать какой процесс запускается той или иной командой. ну помимо cmd.exe




Добавлено:
Сообщение автоматически объединено:

beliy [?]
пересмотрел несколько раз, но ответа там так и не нашел...
считывание должно происходить в цикле, поэтому
Код:
ProcessWaitClose($iPID)

не нужен
в справке пример считывания
или
http://autoit-script.ru/index.php/topic,12932.msg83043.html#msg83043
 
Автор
B

beliy

Продвинутый
Сообщения
372
Репутация
72
$iPid - это идентификатор cmd, а при том же пинге запускается cmd.exe и ping.exe. придется завершать оба процесса. но опять же, надо знать какой процесс запускается той или иной командой. ну помимо cmd.exe

есть идеи как отлавливать PID запущенного в cmd приложения?

считывание должно происходить в цикле, поэтому

Код:
ProcessWaitClose($iPID)

не нужен

Согласен, но мне нужно по окончанию выполнения внести эти изменения:
Код:
GUISetCursor(2, 0)
GUICtrlSetState($btn_Run, $GUI_SHOW)
GUICtrlSetState($btn_Stop, $GUI_HIDE)
GUICtrlSetState($Combo, $GUI_ENABLE)

Вот и ищу более подходящую альтернативу тому же ProcessWaitClose
 

joiner

Модератор
Локальный модератор
Сообщения
3,557
Репутация
628
beliy [?]
Вот и ищу более подходящую альтернативу тому же ProcessWaitClose
так?

Код:
#include <GUIConstantsEx.au3>
#include <GUIEdit.au3>
#include <ComboConstants.au3>
#include <WindowsConstants.au3>
#include <Encoding.au3>
#include <Array.au3>
#include <Constants.au3>

$hGUI = GUICreate('CustomCMD', 600, 400)
$comand_label = GUICtrlCreateLabel("Команда: ", 16, 11, 55, 17)
$Combo = GUICtrlCreateCombo("Выберите команду", 80, 8, 401, 25, $CBS_DROPDOWNLIST + $WS_VSCROLL)
GUICtrlSetData(-1, "ping ya.ru|ping ya.ru -t|tracert ya.ru", "ping ya.ru")
$btn_Run = GUICtrlCreateButton("Старт", 496, 6, 89, 25)
$btn_Stop = GUICtrlCreateButton("Стоп", 496, 6, 89, 25)
GUICtrlSetState(-1, $GUI_HIDE)
$Console = GUICtrlCreateEdit('', 0, 50, 600, 350, BitOR($ES_AUTOVSCROLL, $ES_READONLY, $ES_WANTRETURN, $WS_VSCROLL))
GUICtrlSetFont(-1, 9, 400, 0, 'Courier New')
GUICtrlSetBkColor(-1, 0)
GUICtrlSetColor(-1, 0x00D000)
_GUICtrlEdit_SetLimitText(-1, 8 * 1024 * 1024) ; 8 MB (ANSI)
GUISetState()


While 1
    Local $iPid
    $aRead = StdoutRead($iPid)
    If $aRead Then
        GUICtrlSetData($Console, GUICtrlRead($Console) & _Encoding_CyrillicTo1251($aRead))
    EndIf

    Switch GUIGetMsg()
        Case $GUI_EVENT_CLOSE
            ExitLoop
        Case $btn_Run
            GUISetCursor(1, 1)
            GUICtrlSetState($btn_Run, $GUI_HIDE)
            GUICtrlSetState($btn_Stop, $GUI_SHOW)
            GUICtrlSetState($Combo, $GUI_DISABLE)
            GUICtrlSetData($Console, '')
            _read_cmd()
            GUISetCursor(2, 0)
            GUICtrlSetState($btn_Run, $GUI_SHOW)
            GUICtrlSetState($btn_Stop, $GUI_HIDE)
            GUICtrlSetState($Combo, $GUI_ENABLE)
        Case $btn_Stop
            MsgBox(4096, "Stop", "Stop")
            ProcessClose($iPid)
    EndSwitch
WEnd


Func _read_cmd()
    $comand = GUICtrlRead($Combo)
    $sBuffer = ''
    $sRead = ''
    $iPid = Run(@ComSpec & ' /k ' & $comand, @SystemDir, @SW_HIDE, $STDOUT_CHILD)
    ConsoleWrite($iPid & @CRLF)
    If Not $iPid Then Exit -33
    While 1
        $sBuffer = StdoutRead($iPid)
        If @error Then ExitLoop
        If $sBuffer Then
            $sRead &= $sBuffer
        EndIf
        Sleep(2)
		    $sRead = _Encoding_CyrillicTo1251($sRead)
    $aRead = StringSplit($sRead, @LF)
    $Lines_r = _ArrayToString($aRead, @CRLF, 1)
    GUICtrlSetData($Console, '')
    GUICtrlSetData($Console, $Lines_r & @CRLF)
    WEnd

EndFunc   ;==>_read_cmd
 
Автор
B

beliy

Продвинутый
Сообщения
372
Репутация
72
не совсем, визуально получается также как и с ProcessWaitClose, т.е. результат появляется по окончанию, а не в онлайн режиме...

В принципе избавиться от ProcessWaitClose получилось через обработку @error после StdoutRead, но появилось сильное мерцание для $btn_Run и $Combo (тестировал на Win7).

Пример кода:
Код:
#Include <GUIConstantsEx.au3>
#Include <GUIEdit.au3>
#Include <ComboConstants.au3>
#Include <WindowsConstants.au3>
#Include <Encoding.au3>

$hGUI = GUICreate('CustomCMD', 600, 400)
$comand_label = GUICtrlCreateLabel("Команда: ", 16, 11, 55, 17)
$Combo = GUICtrlCreateCombo("Выберите команду", 80, 8, 401, 25, $CBS_DROPDOWNLIST + $WS_VSCROLL)
GUICtrlSetData(-1, "ping ya.ru|ping ya.ru -t|tracert ya.ru", "ping ya.ru")
$btn_Run = GUICtrlCreateButton("Старт", 496, 6, 89, 25)
$btn_Stop = GUICtrlCreateButton("Стоп", 496, 6, 89, 25)
GUICtrlSetState(-1, $GUI_HIDE)
$Console = GUICtrlCreateEdit('', 0, 50, 600, 350, BitOR($ES_AUTOVSCROLL, $ES_READONLY, $ES_WANTRETURN, $WS_VSCROLL))
GUICtrlSetFont(-1, 9, 400, 0, 'Courier New')
GUICtrlSetBkColor(-1, 0)
GUICtrlSetColor(-1, 0x00D000)
_GUICtrlEdit_SetLimitText(-1, 8 * 1024 * 1024) ; 8 MB (ANSI)
GUISetState()


;$Config = IniReadSection("option.ini", "combobox")

;If @error Then
;	MsgBox(4096, "Error", "Ошибка чтения INI файла.")
;Else
;	For $i = 1 To $Config[0][0]
;		GUICtrlSetData($Combo, $Config[$i][1] )
;	Next
;EndIf

While 1
   Local $iPid
    $aRead = StdoutRead($iPid)
	If @error Then 
	  GUISetCursor(2, 0)
	  GUICtrlSetState($btn_Run, $GUI_SHOW)
	  GUICtrlSetState($btn_Stop, $GUI_HIDE)
	  GUICtrlSetState($Combo, $GUI_ENABLE)
   EndIf
    If $aRead Then
        GUICtrlSetData($Console, GUICtrlRead($Console) & _Encoding_CyrillicTo1251($aRead))
    EndIf
   
    Switch GUIGetMsg()
        Case $GUI_EVENT_CLOSE
            ExitLoop
        Case $btn_Run
			$iPid = Run(@ComSpec & " /c " & StringReplace(GUICtrlRead($Combo), '"', '""'), @ScriptDir, Default, 2)
            GUISetCursor(1, 1)
            GUICtrlSetState($btn_Run, $GUI_HIDE)
			GUICtrlSetState($btn_Stop, $GUI_SHOW)
            GUICtrlSetState($Combo, $GUI_DISABLE)
            GUICtrlSetData($Console, '')
			;ProcessWaitClose($iPID)
			 ;GUISetCursor(2, 0)
            ;GUICtrlSetState($btn_Run, $GUI_SHOW)
			;GUICtrlSetState($btn_Stop, $GUI_HIDE)
            ;GUICtrlSetState($Combo, $GUI_ENABLE)
		 Case $btn_Stop
			MsgBox(4096, "Stop", "Stop")
			ProcessClose($iPid)
    EndSwitch      
Wend
 

joiner

Модератор
Локальный модератор
Сообщения
3,557
Репутация
628
beliy
смотри код в предыдущем моем посте. я его изменил. чтение в реальном времени. не мерцает. только вот косяк - кодировка пляшет
 
Автор
B

beliy

Продвинутый
Сообщения
372
Репутация
72
2 joiner
да мерцание для $btn_Run и $Combo исчезло, но появилось мерцание $Console + перестала срабатывать $btn_Stop + как ты уже писал иногда бывают чудеса с кодировкой...


Добавлено:
Сообщение автоматически объединено:


Все, таки получилось осилить текущие проблеммы.

Конечный код выглядит так:

Код:
#Include <GUIConstantsEx.au3>
#Include <GUIEdit.au3>
#Include <ComboConstants.au3>
#Include <WindowsConstants.au3>
#Include <Encoding.au3>

$iSec = 0

$hGUI = GUICreate('CustomCMD', 600, 400)
$comand_label = GUICtrlCreateLabel("Команда: ", 16, 11, 55, 17)
$Combo = GUICtrlCreateCombo("Выберите команду", 80, 8, 401, 25, $CBS_DROPDOWNLIST + $WS_VSCROLL)
$btn_Run = GUICtrlCreateButton("Старт", 496, 6, 89, 25)
$btn_Stop = GUICtrlCreateButton("Стоп", 496, 6, 89, 25)
GUICtrlSetState(-1, $GUI_HIDE)
$Console = GUICtrlCreateEdit('', 0, 50, 600, 350, BitOR($ES_AUTOVSCROLL, $ES_READONLY, $ES_WANTRETURN, $WS_VSCROLL))
GUICtrlSetFont(-1, 9, 400, 0, 'Courier New')
GUICtrlSetBkColor(-1, 0)
GUICtrlSetColor(-1, 0x00D000)
_GUICtrlEdit_SetLimitText(-1, 8 * 1024 * 1024) ; 8 MB (ANSI)
GUISetState()


$Config = IniReadSection("option.ini", "combobox")

If @error Then
	MsgBox(4096, "Error", "Ошибка чтения INI файла.")
Else
	For $i = 1 To $Config[0][0]
		GUICtrlSetData($Combo, $Config[$i][1], $Config[$i][1])
	Next
EndIf

While 1
   Local $iPid
    $aRead = StdoutRead($iPid)
   If @error Then 
	  If @SEC <> $iSec Then
		 $iSec = @SEC
			GUISetCursor(2, 0)
			GUICtrlSetState($btn_Run, $GUI_SHOW)
			GUICtrlSetState($btn_Stop, $GUI_HIDE)
			GUICtrlSetState($Combo, $GUI_ENABLE)
	  EndIf
   EndIf
    If $aRead Then
        GUICtrlSetData($Console, GUICtrlRead($Console) & _Encoding_CyrillicTo1251($aRead))
    EndIf
   
    Switch GUIGetMsg()
        Case $GUI_EVENT_CLOSE
            ExitLoop
		 Case $btn_Run
			$iPid = Run(StringReplace(GUICtrlRead($Combo), '"', '""'), @ScriptDir, Default, 2)
            GUISetCursor(1, 1)
            GUICtrlSetState($btn_Run, $GUI_HIDE)
			GUICtrlSetState($btn_Stop, $GUI_SHOW)
            GUICtrlSetState($Combo, $GUI_DISABLE)
            GUICtrlSetData($Console, '')
		 Case $btn_Stop
			ProcessClose($iPid)
    EndSwitch      
Wend


INI-файл (option.ini):
Код:
[combobox]
option1=ping ya.ru
option2=ping ya.ru -t
option3=tracert ya.ru

Всем спасибо за помощь. Предложения по оптимизации с радостью рассмотрю...
 
Верх