.NET: ввод данных во входной буфер процесса

Мне нужно автоматизировать приложение командной строки. Он просит пользователя ввести пароль. Все мои подходы к отправке пароля через 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)

Моя программа представляет собой приложение командной строки, которое должно действовать как оболочка.

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

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


person sc911    schedule 07.12.2009    source источник
comment
Какое приложение требует пароль? Там может быть более простой способ, чем этот.   -  person Tim Robinson    schedule 07.12.2009
comment
Это консольное приложение диспетчера сертификатов Cisco cisco_cert_mgr.exe. Но я уже искал любое другое решение, но, похоже, его нет.   -  person sc911    schedule 07.12.2009


Ответы (2)


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

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

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

person sc911    schedule 09.02.2010

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

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

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

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

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

person Tim Robinson    schedule 07.12.2009