<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
	<channel>
		<title><![CDATA[Серый форум &mdash; AHK: Запуск консольного приложения с перехватом его ввода и вывода]]></title>
		<link>http://forum.script-coding.com/viewtopic.php?id=3467</link>
		<atom:link href="http://forum.script-coding.com/extern.php?action=feed&amp;tid=3467&amp;type=rss" rel="self" type="application/rss+xml" />
		<description><![CDATA[Недавние сообщения в теме «AHK: Запуск консольного приложения с перехватом его ввода и вывода».]]></description>
		<lastBuildDate>Wed, 30 Oct 2013 04:51:50 +0000</lastBuildDate>
		<generator>PunBB</generator>
		<item>
			<title><![CDATA[Re: AHK: Запуск консольного приложения с перехватом его ввода и вывода]]></title>
			<link>http://forum.script-coding.com/viewtopic.php?pid=76680#p76680</link>
			<description><![CDATA[<p>Это вариант той же функции для современных версий AutoHotkey: x86 ANSI, x86 Unicode и x64. В &quot;классическом&quot; AutoHotkey работать не будет, т.к. используются отсутствующие в нём функции.<br /></p><div class="codebox"><pre><code>
RunCon(CmdLine, Input, ByRef Output)
{
    static BufSizeChar := 1024, hParent := 0
    static Show := 0, Flags := 0x101  ; STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW
    static Buf, BufSizeByte, ProcessInfo, StartupInfo, PipeAttribs
    static piSize, siSize, paSize, flOffset, shOffset, ihOffset
    static inOffset, outOffset, errOffset, thrOffset
    If (!hParent) {
        BufSizeByte := A_IsUnicode ? BufSizeChar * 2 : BufSizeChar
        If (A_PtrSize = 8) {
            piSize := 24, siSize := 104, paSize = 24
            flOffset := 60, shOffset := 64, ihOffset := 16
            inOffset := 80, outOffset := 88, errOffset := 96
            thrOffset := 8
        }
        Else {
            piSize := 16, siSize := 68, paSize = 12
            flOffset := 44, shOffset := 48, ihOffset := 8
            inOffset := 56, outOffset := 60, errOffset := 64
            thrOffset := 4
        }
        VarSetCapacity(Buf, BufSizeByte, 0),    VarSetCapacity(ProcessInfo, piSize, 0)
        VarSetCapacity(StartupInfo, siSize, 0), VarSetCapacity(PipeAttribs, paSize, 0)
        NumPut(siSize, StartupInfo, 0, &quot;uint&quot;), NumPut(Flags, StartupInfo, flOffset, &quot;uint&quot;)
        NumPut(Show, StartupInfo, shOffset, &quot;ushort&quot;)
        NumPut(paSize, PipeAttribs, 0, &quot;uint&quot;), NumPut(1, PipeAttribs, ihOffset, &quot;int&quot;)
        hParent := DllCall(&quot;GetCurrentProcess&quot;, &quot;ptr&quot;)
    }
    DllCall(&quot;CreatePipe&quot;, &quot;ptr *&quot;, hRead1_tmp, &quot;ptr *&quot;, hWrite2
                        , &quot;ptr&quot;, &amp;PipeAttribs, &quot;uint&quot;, 0)
    DllCall(&quot;CreatePipe&quot;, &quot;ptr *&quot;, hRead2, &quot;ptr *&quot;, hWrite1_tmp
                        , &quot;ptr&quot;, &amp;PipeAttribs, &quot;uint&quot;, 0)

    NumPut(hRead2,  StartupInfo, inOffset, &quot;ptr&quot;) 
    NumPut(hWrite2, StartupInfo, outOffset, &quot;ptr&quot;)
    NumPut(hWrite2, StartupInfo, errOffset, &quot;ptr&quot;)
    
    DllCall(&quot;DuplicateHandle&quot;, &quot;ptr&quot;, hParent, &quot;ptr&quot;, hRead1_tmp
                             , &quot;ptr&quot;, hParent, &quot;ptr *&quot;, hRead1
                             , &quot;uint&quot;, 0, &quot;uint&quot;, 0
                             , &quot;uint&quot;, 2)    ; DUPLICATE_SAME_ACCESS
    DllCall(&quot;CloseHandle&quot;, &quot;ptr&quot;, hRead1_tmp)
    DllCall(&quot;DuplicateHandle&quot;, &quot;ptr&quot;, hParent, &quot;ptr&quot;, hWrite1_tmp
                             , &quot;ptr&quot;, hParent, &quot;ptr *&quot;, hWrite1
                             , &quot;uint&quot;, 0, &quot;uint&quot;, 0
                             , &quot;uint&quot;, 2)
    DllCall(&quot;CloseHandle&quot;, &quot;ptr&quot;, hWrite1_tmp)
    
    DllCall(&quot;ExpandEnvironmentStrings&quot;, &quot;str&quot;, CmdLine, &quot;str&quot;, Buf, &quot;uint&quot;, BufSizeChar)
    CmdLine := Buf
    Ret := DllCall(&quot;CreateProcess&quot;, &quot;ptr&quot;, 0, &quot;str&quot;, CmdLine, &quot;ptr&quot;, 0, &quot;ptr&quot;, 0
                                  , &quot;uint&quot;, 1, &quot;uint&quot;, 0, &quot;ptr&quot;, 0, &quot;ptr&quot;, 0
                                  , &quot;ptr&quot;, &amp;StartupInfo, &quot;ptr&quot;, &amp;ProcessInfo)
    If (!Ret) {
        MsgBox,, %A_ThisFunc%, Не удалось создать процесс.
        Output := &quot;&quot;
        Return 1
    }
    hChild := NumGet(ProcessInfo, 0, &quot;ptr&quot;)
    DllCall(&quot;CloseHandle&quot;, &quot;ptr&quot;, NumGet(ProcessInfo, thrOffset, &quot;ptr&quot;))
    DllCall(&quot;CloseHandle&quot;, &quot;ptr&quot;, hRead2)
    DllCall(&quot;CloseHandle&quot;, &quot;ptr&quot;, hWrite2)
    If (Input) {
        InLen := StrLen(Input) + 2
        VarSetCapacity(InBuf, InLen, 0)
        StrPut(Input . &quot;`r`n&quot;, &amp;InBuf, &quot;cp866&quot;)
        DllCall(&quot;WriteFile&quot;, &quot;ptr&quot;, hWrite1, &quot;ptr&quot;, &amp;InBuf, &quot;uint&quot;, InLen
                           , &quot;uint *&quot;, BytesWritten, &quot;uint&quot;, 0)
    }
    DllCall(&quot;CloseHandle&quot;, &quot;ptr&quot;, hWrite1)
    Output := &quot;&quot;
    Loop {
        If not DllCall(&quot;ReadFile&quot;, &quot;ptr&quot;, hRead1, &quot;ptr&quot;, &amp;Buf, &quot;uint&quot;, BufSizeByte
                                 , &quot;uint *&quot;, BytesRead, &quot;uint&quot;, 0)
            Break
        NumPut(0, Buf, BytesRead, &quot;Char&quot;)
        Output .= StrGet(&amp;Buf, &quot;cp866&quot;)
    }
    DllCall(&quot;CloseHandle&quot;, &quot;ptr&quot;, hRead1)
    DllCall(&quot;GetExitCodeProcess&quot;, &quot;ptr&quot;, hChild, &quot;int *&quot;, ExitCode)
    DllCall(&quot;CloseHandle&quot;, &quot;ptr&quot;, hChild)
    Return ExitCode
}
</code></pre></div>]]></description>
			<author><![CDATA[null@example.com (YMP)]]></author>
			<pubDate>Wed, 30 Oct 2013 04:51:50 +0000</pubDate>
			<guid>http://forum.script-coding.com/viewtopic.php?pid=76680#p76680</guid>
		</item>
		<item>
			<title><![CDATA[AHK: Запуск консольного приложения с перехватом его ввода и вывода]]></title>
			<link>http://forum.script-coding.com/viewtopic.php?pid=26598#p26598</link>
			<description><![CDATA[<p><em>Примечание: этот код был написан под &quot;классический&quot; AutoHotkey. Более современный вариант см. в следующем посте.</em></p><p>Идея взята из примера в Platform SDK от MS. Суть перехвата состоит в подмене стандартных потоков ввода и вывода запускаемого приложения на неименованные каналы (anonymous pipes), созданные в родительской программе. Теперь всё, что приложение выводит, будет поступать в канал и считываться родителем, в данном случае скриптом. А то, что скрипт пишет в другой канал, поступит на стандартный ввод дочернего приложения.</p><p>Всё это оформлено в виде функции RunCon, которая принимает три аргумента:<br />1) командная строка запуска программы — с аргументами, если есть;<br />2) ввод в программу — одна или несколько команд (разделять новой строкой);<br />3) имя переменной, куда будет помещён вывод программы.<br />Функция возвращает код выхода дочернего приложения. Если в строке запуска или вводе есть пути с пробелами, их нужно заключать в кавычки.<br /></p><div class="codebox"><pre><code>; Примеры вызова.

CmdLine = cmd.exe
Input = dir

Ret := RunCon(CmdLine, Input, Output)
MsgBox, %Output%
MsgBox, Код выхода: %Ret%

CmdLine = nslookup
Input =                ; Ввод нескольких команд.
(
mail.ru
yandex.ru
google.com
)

Ret := RunCon(CmdLine, Input, Output)
MsgBox, %Output%
MsgBox, Код выхода: %Ret%


CmdLine = ping 127.0.0.1    ; Запуск с аргументом, ввода нет.
Input =

Ret := RunCon(CmdLine, Input, Output)
MsgBox, %Output%
MsgBox, Код выхода: %Ret%


; =========================== Функция ============================

RunCon(CmdLine, Input, ByRef Output)
{
    Static Buf, BufSize, ProcessInfo, StartupInfo, PipeAttribs, hParent, Flags, Show
    If (!hParent) {
        Flags := 0x101  ; STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW
        Show  := 0      ; SW_HIDE
        BufSize := 1024
        VarSetCapacity(Buf, BufSize, 0),    VarSetCapacity(ProcessInfo, 16, 0)
        VarSetCapacity(StartupInfo, 68, 0), VarSetCapacity(PipeAttribs, 12, 0)
        NumPut(68, StartupInfo, 0),         NumPut(Flags, StartupInfo, 44)
        NumPut(Show, StartupInfo, 48, &quot;UShort&quot;)
        NumPut(12, PipeAttribs, 0),         NumPut(1, PipeAttribs, 8)
        hParent := DllCall(&quot;GetCurrentProcess&quot;)
    }
    DllCall(&quot;CreatePipe&quot;, &quot;uint *&quot;, hRead1_tmp, &quot;uint *&quot;, hWrite2
                        , &quot;uint&quot;, &amp;PipeAttribs, &quot;uint&quot;, 0)
    DllCall(&quot;CreatePipe&quot;, &quot;uint *&quot;, hRead2, &quot;uint *&quot;, hWrite1_tmp
                        , &quot;uint&quot;, &amp;PipeAttribs, &quot;uint&quot;, 0)

    NumPut(hRead2,  StartupInfo, 56) 
    NumPut(hWrite2, StartupInfo, 60)
    NumPut(hWrite2, StartupInfo, 64)
    
    DllCall(&quot;DuplicateHandle&quot;, &quot;uint&quot;, hParent, &quot;uint&quot;, hRead1_tmp
                             , &quot;uint&quot;, hParent, &quot;uint *&quot;, hRead1
                             , &quot;uint&quot;, 0, &quot;uint&quot;, 0
                             , &quot;uint&quot;, 2)    ; DUPLICATE_SAME_ACCESS
    DllCall(&quot;CloseHandle&quot;, &quot;uint&quot;, hRead1_tmp)
    DllCall(&quot;DuplicateHandle&quot;, &quot;uint&quot;, hParent, &quot;uint&quot;, hWrite1_tmp
                             , &quot;uint&quot;, hParent, &quot;uint *&quot;, hWrite1
                             , &quot;uint&quot;, 0, &quot;uint&quot;, 0
                             , &quot;uint&quot;, 2)
    DllCall(&quot;CloseHandle&quot;, &quot;uint&quot;, hWrite1_tmp)
    
    DllCall(&quot;ExpandEnvironmentStrings&quot;, &quot;str&quot;, CmdLine, &quot;str&quot;, Buf, &quot;uint&quot;, BufSize)
    CmdLine := Buf
    Ret := DllCall(&quot;CreateProcess&quot;, &quot;uint&quot;, 0, &quot;str&quot;, CmdLine, &quot;uint&quot;, 0, &quot;uint&quot;, 0
                                  , &quot;uint&quot;, 1, &quot;uint&quot;, 0, &quot;uint&quot;, 0, &quot;uint&quot;, 0
                                  , &quot;uint&quot;, &amp;StartupInfo, &quot;uint&quot;, &amp;ProcessInfo)
    If (!Ret) {
        MsgBox,, %A_ThisFunc%, Не удалось создать процесс.
        Output := &quot;&quot;
        Return 1
    }
    hChild := NumGet(ProcessInfo)
    DllCall(&quot;CloseHandle&quot;, &quot;uint&quot;, NumGet(ProcessInfo, 4))
    DllCall(&quot;CloseHandle&quot;, &quot;uint&quot;, hRead2)
    DllCall(&quot;CloseHandle&quot;, &quot;uint&quot;, hWrite2)
    If (Input) {
        DllCall(&quot;CharToOem&quot;, &quot;str&quot;, Input, &quot;str&quot;, Input)
        Input .= &quot;`r`n&quot;
        DllCall(&quot;WriteFile&quot;, &quot;uint&quot;, hWrite1, &quot;str&quot;, Input, &quot;uint&quot;, StrLen(Input)
                           , &quot;uint *&quot;, BytesWritten, &quot;uint&quot;, 0)
    }
    DllCall(&quot;CloseHandle&quot;, &quot;uint&quot;, hWrite1)
    Output := &quot;&quot;
    Loop {
        If not DllCall(&quot;ReadFile&quot;, &quot;uint&quot;, hRead1, &quot;uint&quot;, &amp;Buf, &quot;uint&quot;, BufSize
                                 , &quot;uint *&quot;, BytesRead, &quot;uint&quot;, 0)
            Break
        NumPut(0, Buf, BytesRead, &quot;Char&quot;)
        VarSetCapacity(Buf, -1)
        Output .= Buf
    }
    DllCall(&quot;OemToChar&quot;, &quot;str&quot;, Output, &quot;str&quot;, Output)
    DllCall(&quot;CloseHandle&quot;, &quot;uint&quot;, hRead1)
    DllCall(&quot;GetExitCodeProcess&quot;, &quot;uint&quot;, hChild, &quot;int *&quot;, ExitCode)
    DllCall(&quot;CloseHandle&quot;, &quot;uint&quot;, hChild)
    Return ExitCode
}</code></pre></div><p>Дополнительные пояснения (насколько я сам всё понял). При создании каждого канала создаются два его хэндла — один для чтения, другой для записи. Дочерний процесс создаётся с наследованием хэндлов родителя, но чтобы произошла именно подмена, нужные хэндлы также помещаются в его структуру StartupInfo — читающий хэндл одного канала (hRead2) как стандартный ввод и пишущий другого (hWrite2) как стандарный вывод и стандартный поток ошибок.</p><p>Остающиеся у родителя хэндлы hRead1 и hWrite1 перед созданием дочернего процесса дублируются, чтобы получить их ненаследуемые копии, а наследуемые оригиналы уничтожаются, чтобы дочерний процесс их не получил. Кроме того после создания процесса должны быть уничтожены переданные ему хэндлы hRead2 и hWrite2. Поскольку при наследовании происходит дублирование хэндлов, то ему это уже не повредит.</p><p>Цель всех этих манипуляций в том, чтобы у канальных хэндлов не было дублей. Смысл же заключается в особенностях работы функций ReadFile и WriteFile при&nbsp; чтении из канала и записи в него. ReadFile ждёт, пока в канале что-то появится. И если там ничего уже не появится, т.к. другое приложение закончило передачу, то получится вечное ожидание. Но если у канала закрыть все пишущие хэндлы, ReadFile завершится, возвратив 0. Что же произойдёт, если одна из копий пишущего хэндла попадёт в сам тот процесс, где работает ReadFile? Закрыть эту копию будет некому, а она не даст закрыться каналу, и ReadFile будет ждать.</p><p>Что касается WriteFile, то она может зависнуть, если другой процесс уже прекратил считывание из канала, а буфер канала недостаточно велик, чтобы туда поместились все данные, указанные для записи. WriteFile будет ждать, пока освободится буфер. И опять же, если закрыть все, теперь уже читающие, хэндлы канала, WriteFile сможет завершиться.</p><p>Хэндлы каналов закроются также при завершении процесса, что опять же позволит другому процессу прекратить ожидание.</p>]]></description>
			<author><![CDATA[null@example.com (YMP)]]></author>
			<pubDate>Thu, 30 Jul 2009 12:41:11 +0000</pubDate>
			<guid>http://forum.script-coding.com/viewtopic.php?pid=26598#p26598</guid>
		</item>
	</channel>
</rss>
