0 votes
by (120 points)

I'm trying to use your ssh component in WPF to run remote changes into a unix box.

I can achieve what I want but not without locking up the user interface.

I see there is a runcommandasync. But I can't get the text returned from that. Is there a way to get the returned text?

ReadUntilPrompt works good but I get a freeze while this is running.
Is there a ssh scripting ReadUntilPrompt async method?

Here is my code:

Private Async Sub writechanges_Click(sender As Object, e As RoutedEventArgs)
    Dim ssh As Rebex.Net.Ssh = New Rebex.Net.Ssh
    Await ssh.ConnectAsync("sane.eng.com")
    ssh.Login(usr, pw)
    Dim terminal = New Rebex.TerminalEmulation.VirtualTerminal(80, 25)
    Await terminal.BindAsync(ssh)
    sshterm.Text = terminal.Screen.ToString()
    Dim scripting As Rebex.TerminalEmulation.Scripting = Await ssh.StartScriptingAsync
    scripting.SendCommand("17041") 'no async method
    scripting.ReadUntilPrompt() 'no async method freezes until done reading

End Sub

1 Answer

0 votes
by (15.2k points)

Hi,

unfortunately, there are no async method on Scripting class for sending commands and receiving theirs output. You have to write a workaround for it. Here is a sample code:

Using ssh As Rebex.Net.Ssh = New Rebex.Net.Ssh()
    Await ssh.ConnectAsync("sane.eng.com")

    Await ssh.LoginAsync(usr, pw)

    Dim terminal = New Rebex.TerminalEmulation.VirtualTerminal(80, 25)

    Await terminal.BindAsync(ssh)

    ' there Is no text on screen in this point, so this call does Not matter
    'sshterm.Text = string.Join("\n", terminal.Screen.GetRegionText(0, 0, terminal.Screen.Columns, terminal.Screen.Rows));

    ' use scripting of bound terminal
    Dim scripting As Rebex.TerminalEmulation.Scripting = terminal.Scripting

    ' workaround for synchronous api
    Await Task.Run(Function()
                        ' detect prompt first
                        scripting.DetectPrompt()

                        ' if detection of prompt does not work, set the prompt directrly
                        'scripting.Prompt = "<prompt>" ' actual prompt
                        'scripting.Prompt = "regex:[Pp]rompt" ' actual prompt described with regular expression

                        scripting.SendCommand("17041") 'no async method

                        Return scripting.ReadUntilPrompt()
                    End Function
                    )

    sshterm.Text = String.Join(Of String)(vbCrLf, terminal.Screen.GetRegionText(0, 0, terminal.Screen.Columns, terminal.Screen.Rows))
End Using

I also corrected some mistakes:

  • ssh.StartScriptingAsync creates new VirtualTerminal, so the previous one do not receive any output to its screen.
  • Also, Screen.ToString() does not convert its content to its string, you have to call Screen.GetRegionText(...) to read desired screen area.
  • Before sending a command, you have to set which prompt the server uses. You can do that by calling scripting.DetectPrompt(). It can be called for prompts like '>', 'ftp>' or 'user@workingDir$ ' and similar. At the end this method sets scripting.Prompt property, so you can set this property to whatever the prompt is. That is sufficient.
  • Since ssh is local variable, you should call ssh.Disconnect() or at least ssh.Dispose() when you are done with it, so I added that Using statement.
...