.NET: ввод данных в буфер ввода процесса - PullRequest
2 голосов
/ 07 декабря 2009

Мне нужно автоматизировать приложение командной строки. Он просит пользователя ввести пароль. Все мои попытки отправить пароль через STDIN не удалось. Теперь я пытаюсь сделать это с помощью программы-оболочки, используя .NET.

Я запускаю приложение, создающее новый процесс, устанавливаю свойства StartInfo и затем запускаю процесс:

Dim app_path As String
Dim app_args As String
Dim myProcess As Process = New Process()
myProcess.StartInfo.FileName = app_path
myProcess.StartInfo.Arguments = app_args
myProcess.StartInfo.UseShellExecute = False
myProcess.Start()

Я пытался использовать свойство StartInfo.RedirectStandardInput, но безуспешно.

Теперь я наткнулся на функцию WriteConsoleInput из kernel32.dll, которую я включил следующим образом:

Declare Function WriteConsoleInput Lib "kernel32.dll" Alias "WriteConsoleInputA" (ByVal hConsoleInput As Integer, ByVal lpBuffer As String, ByVal nNumberOfCharsToWrite As Integer, ByRef lpNumberOfCharsWritten As Integer) As Boolean

Я могу получить дескриптор процесса через свойство myProcess.Handle. Но отправка ввода в буфер ввода с использованием этого способа также была невозможна.

Я нашел эти вопросы, но они не помогли:

  • Как мне записать «PAGE DOWN» во входной буфер консоли? (1475353)

  • Java - передача ввода во внешнее приложение C / C ++ (1421273)

  • Управление консольным приложением Windows с помощью стандартного канала (723424)

Используя StraceNtX.exe Я получил этот вывод в тот момент, когда приложение ожидает ввода:

[T4024] GetConsoleMode(f, 12d35c, 12d3af, 77bff894, ...) = 1
[T4024] SetConsoleMode(f, 0, 12d3af, 77bff894, ...) = 1
[T4024] ReadConsoleInputA(f, 12d348, 1, 12d360, ...) = 1

Может кто-нибудь сказать мне, что еще попробовать или как правильно сделать вышеупомянутое? Спасибо!


Основано на ответе Тима Робинсона. У меня есть этот код, но он не работает:

myProcess = New Process()
myProcess.StartInfo.FileName = app_path
myProcess.StartInfo.Arguments = app_args
myProcess.StartInfo.WindowStyle = ProcessWindowStyle.Normal
myProcess.StartInfo.UseShellExecute = False
myProcess.Start()
' Wait for process requesting passwort input
System.Threading.Thread.Sleep(3000)
Dim len As Integer
len = 0
Dim handle As Integer
handle = GetStdHandle(STD_INPUT_HANDLE)
WriteConsoleInput(handle, "Test", 4, len)

Моя программа - это приложение командной строки, которое должно выполнять функцию оболочки.

Входные данные отправляются, но таким образом, что они не вводятся в поле пароля, а под полем пароля отображается новое приглашение (даже без ввода).

Тим, можешь привести пример?

Ответы [ 2 ]

2 голосов
/ 09 февраля 2010

Извините за поздний ответ, был очень занят.

Вот полный пример кода, урезанный из моего кода. Я надеюсь, что я не удалил что-то важное.

Private Sub runWaitInput(ByVal exe As String, ByVal parameter As String, ByVal trigger As String, ByVal input As String)
    ' runs an process, waits for a certain string in stdout and then enters text '
    Dim proc As Process
    Dim stdOut As StreamReader
    Dim ch As Int32
    Dim buffer As String = ""
    Dim InputRecords(0) As KeyEventStruct

    ' create process '
    proc = New Process()
    proc.StartInfo.FileName = exe
    proc.StartInfo.Arguments = parameter
    proc.StartInfo.UseShellExecute = False
    proc.StartInfo.RedirectStandardOutput = True
    proc.Start()

    ' redirect stdOut '
    stdOut = proc.StandardOutput
    While Not proc.HasExited
        ' read character '
        ch = stdOut.Read()
        Console.Write(Convert.ToChar(ch))
        buffer = buffer & Convert.ToChar(ch)
        ' read output and check for trigger-text '
        If buffer.LastIndexOf(trigger) <> -1 Then
            buffer = ""
            InputRecords(0) = New KeyEventStruct
            InputRecords = generateInputRecord(input & Convert.ToChar(13))
            WriteConsoleInput(STD_INPUT_HANDLE, InputRecords, InputRecords.Count(), New Integer)
        End If
    End While
    Console.Write(stdOut.ReadToEnd())
End Sub

Function generateInputRecord(ByVal str As String) As KeyEventStruct()
    Dim ret(str.Count() - 1) As KeyEventStruct
    For i = 0 To str.Count - 1 Step 1
        With ret(i)
            .EventType = 1
            .bKeyDown = True
            .uChar.AsciiChar = Convert.ToInt32(str(i))
            .dwControlKeyState = 0
            .wRepeatCount = 1
            .wVirtualKeyCode = 0
            .wVirtualScanCode = 0
        End With
    Next
    Return ret
End Function

Используется следующий модуль:

Imports System.Runtime.InteropServices

Module ConsoleUtils

    <Flags()> Public Enum ControlKeyState As Integer
        RightAltPressed = &H1
        LeftAltPressed = &H2
        RightCtrlPressed = &H4
        LeftCtrlPressed = &H8
        ShiftPressed = &H10
        NumLockOn = &H20
        ScrollLockOn = &H40
        CapsLockOn = &H80
        EnhancedKey = &H100
    End Enum

    <StructLayout(LayoutKind.Explicit)> Public Structure CHAR_UNION
        <FieldOffset(0)> Public UnicodeChar As Short
        <FieldOffset(0)> Public AsciiChar As Byte
    End Structure

    <DllImport("kernel32", EntryPoint:="WriteConsoleInputA", CharSet:=CharSet.Auto, SetLastError:=True, ThrowOnUnmappablechar:=True)> _
        Public Function WriteConsoleInput( _
            ByVal hConsoleInput As IntPtr, _
            ByVal lpBuffer() As KeyEventStruct, _
            ByVal nLength As Integer, _
            ByRef lpNumberOfEventsWritten As Integer _
        ) As Boolean
    End Function

    <DllImport("KERNEL32.DLL", EntryPoint:="GetStdHandle", SetLastError:=False, ExactSpelling:=True, CallingConvention:=CallingConvention.StdCall)> _
        Public Function GetStdHandle( _
            ByVal nStdHandle As Integer _
        ) As Integer
    End Function

    <StructLayout(LayoutKind.Sequential)> Public Structure KeyEventStruct
        Public EventType As Short
        <MarshalAs(UnmanagedType.Bool)> Public bKeyDown As Boolean
        Public wRepeatCount As Short
        Public wVirtualKeyCode As Short
        Public wVirtualScanCode As Short
        Public uChar As CHAR_UNION
        Public dwControlKeyState As ControlKeyState
    End Structure

    Public ReadOnly STD_OUTPUT_HANDLE As IntPtr = New IntPtr(GetStdHandle(-11))
    Public ReadOnly STD_INPUT_HANDLE As IntPtr = New IntPtr(GetStdHandle(-10))
    Public ReadOnly STD_ERROR_HANDLE As IntPtr = New IntPtr(GetStdHandle(-12))

End Module

С помощью этой функции вы можете запустить процесс и дождаться строки вывода (т.е. "пароль:"). Затем функция введет предоставленный текст с последующим возвратом.

Надеюсь, это поможет!

С уважением sc911

0 голосов
/ 07 декабря 2009

WriteConsoleInput ожидает дескриптор консоли, а не дескриптор процесса. Ваша проблема в том, как достать этот дескриптор консоли.

Если ваш процесс является консольным приложением и вы ничего не перенаправляете при запуске дочернего процесса, тогда дочерний процесс будет присоединен к консоли вашего собственного процесса. В этом случае вы можете:

  1. Запустить дочерний процесс с отключенным перенаправлением
  2. Позвоните GetStdHandle(STD_INPUT_HANDLE), чтобы получить свой собственный дескриптор консоли
  3. Передать эту ручку консоли на WriteConsoleInput

Если у вас есть приложение с графическим интерфейсом, вы можете назначить себе консоль, используя AllocConsole.

Редактировать: Сначала я не заметил, но это неправильное определение для WriteConsoleInput. Требуется массив INPUT_RECORD, а не строка. Мой собственный эксперимент с runas.exe работает нормально, когда я скопировал прототип функции с pinvoke.net .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...