我需要自动化命令行应用程序。它要求用户输入密码。我通过STDIN发送密码的所有approches都失败了。现在我试图用一个使用.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
的{{1}}函数,其中包含的内容如下:
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
属性获取进程的句柄。但是也不可能使用这种方式将输入发送到输入缓冲区。
我发现了这些问题,但没有帮助:
如何将“PAGE DOWN”写入控制台输入缓冲区? (1475353)
Java - 将输入传递到外部C / C ++应用程序(1421273)
使用stdin管道控制Windows控制台应用程序(723424)
使用StraceNtX.exe我在应用程序等待输入时得到了此输出:
myProcess.Handle
任何人都可以告诉我,还有什么可以尝试或如何以正确的方式做到这一点? 谢谢!
根据Tim Robinsons的回答,我现在已经有了这个代码,但是它不起作用:
[T4024] GetConsoleMode(f, 12d35c, 12d3af, 77bff894, ...) = 1
[T4024] SetConsoleMode(f, 0, 12d3af, 77bff894, ...) = 1
[T4024] ReadConsoleInputA(f, 12d348, 1, 12d360, ...) = 1
我的程序是一个命令行应用程序,应该充当包装器。
输入是发送但是在某种程度上它没有输入密码字段,但是在密码字段下面显示了一个新的promt(甚至没有显示输入)。
蒂姆,你能举个例子吗?答案 0 :(得分: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
答案 1 :(得分:0)
WriteConsoleInput
需要一个控制台句柄,而不是一个进程句柄。你的问题是如何掌握这个控制台句柄。
如果您的进程是控制台应用程序,并且在启动子进程时没有重定向任何内容,则子进程将附加到您自己的进程控制台。在这种情况下,您可以:
GetStdHandle(STD_INPUT_HANDLE)
以获取您自己的控制台句柄WriteConsoleInput
如果你有一个GUI应用程序,你可以使用AllocConsole
为自己分配一个控制台。
编辑:我最初没有注意到,但这不是WriteConsoleInput
的正确定义。它需要一个INPUT_RECORD
数组,而不是一个字符串。我复制the function prototype from pinvoke.net后,我自己的runas.exe实验工作正常。