0

I have 2 forms, MainForm and RCONForm. What I am trying to do is to start a process when the programs starts and in this case I have choosen cmd.exe and it works.

The thing is I want to be able to read the output and send input onto the process from another form using 2 textboxes.

The problem is that I can't read anything from the process neither can I send any input to it either.

My MainForm:

Public Class MainForm

#Region "Import Function"

    Dim Functions As New Functions
    Friend WithEvents RCON As Process

#End Region

    Friend Sub AppendOutputText(ByVal text As String)
        If RCONForm.RCONLogText.InvokeRequired Then
            Dim myDelegate As New RCONForm.AppendOutputTextDelegate(AddressOf AppendOutputText)
            Me.Invoke(myDelegate, text)
        Else
            RCONForm.RCONLogText.AppendText(text)
        End If
    End Sub

    Private Sub MainForm_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
        RCON = New Process
        With RCON.StartInfo
            .FileName = "C:\Windows\system32\CMD.exe"
            .UseShellExecute = False
            .CreateNoWindow = True
            .RedirectStandardInput = True
            .RedirectStandardOutput = True
            .RedirectStandardError = True
        End With
        RCON.Start()
        RCON.BeginErrorReadLine()
        RCON.BeginOutputReadLine()
        AppendOutputText("RCON Started at: " & RCON.StartTime.ToString)
    End Sub

    Private Sub RCONButton_Click(sender As Object, e As EventArgs) Handles RCONButton.Click
        Functions.KillOldForm()
        Functions.SpawnForm(Of RCONForm)()
    End Sub

    Private Sub ExitButton_Click(sender As Object, e As EventArgs) Handles ExitButton.Click
        Application.Exit()
    End Sub

    Private Sub ServerButton_Click(sender As Object, e As EventArgs) Handles ServerButton.Click
        Functions.KillOldForm()
        Functions.SpawnForm(Of ServerForm)()
    End Sub
End Class

And my RCONForm:

Public Class RCONForm

    Friend Delegate Sub AppendOutputTextDelegate(ByVal text As String)
    Friend WithEvents RCON As Process = MainForm.RCON

    Friend Sub RCON_ErrorDataReceived(ByVal sender As Object, ByVal e As System.Diagnostics.DataReceivedEventArgs) Handles RCON.ErrorDataReceived
        MainForm.AppendOutputText(vbCrLf & "Error: " & e.Data)
    End Sub

    Friend Sub RCON_OutputDataReceived(ByVal sender As Object, ByVal e As System.Diagnostics.DataReceivedEventArgs) Handles RCON.OutputDataReceived
        MainForm.AppendOutputText(vbCrLf & e.Data)
    End Sub

    Friend Sub RCONCommandText_KeyPress(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyPressEventArgs) Handles RCONCommandText.KeyPress
        If e.KeyChar = Microsoft.VisualBasic.ChrW(Keys.Return) Then
            MainForm.RCON.StandardInput.WriteLine(RCONCommandText.Text)
            MainForm.RCON.StandardInput.Flush()
            RCONCommandText.Text = ""
            e.Handled = True
        End If
    End Sub

    Friend Sub RCONForm_FormClosing(ByVal sender As Object, ByVal e As System.Windows.Forms.FormClosingEventArgs) Handles Me.FormClosing
        MainForm.RCON.StandardInput.WriteLine("EXIT") 'send an EXIT command to the Command Prompt
        MainForm.RCON.StandardInput.Flush()
        MainForm.RCON.Close()
    End Sub

    Private Sub RCONForm_Load(sender As Object, e As EventArgs) Handles MyBase.Load

    End Sub
End Class

My Functions:

#Region "Kill Old Forms"

    Function KillOldForm()
        While MainForm.SpawnPanel.Controls.Count > 0
            MainForm.SpawnPanel.Controls(0).Dispose()
        End While
        Return Nothing
    End Function

#End Region

#Region "Spawn Form"

    Function SpawnForm(Of T As {New, Form})() As T
        Dim spawn As New T() With {.TopLevel = False, .AutoSize = False}
        Try
            spawn.Dock = DockStyle.Fill
            MainForm.SpawnPanel.Controls.Add(spawn)
            spawn.Show()
        Catch ex As Exception
            MsgBox(ex.Message)
        End Try
        Return Nothing
    End Function

#End Region

I have been thinking of using threads and maybe that can solve the issue, or is there a more simple way?

Raven Blue
  • 13
  • 3
  • I really don't like the smell of `Functions` for some reason. Haven't looked at the rest of the question but the design seems odd. – djv Oct 06 '17 at 17:47
  • The `RCONForm` in `Dim myDelegate As New RCONForm.AppendOutputTextDelegate(AddressOf AppendOutputText)` is not the same as any `RCONForm` spawned by `Functions.SpawnForm(Of RCONForm)()` (I assume) because you are really accessing the [default form instance](https://stackoverflow.com/a/4699360/832052) of the form in the first line, and assumedly creating a *new* instance of the form in Spawn. You possibly have many instances of RCONForm open, and possibly only hide them with `Functions.KillOldForm`. I can only imagine since you didn't post the code. – djv Oct 06 '17 at 17:54
  • @djv I spawn my Forms on a panel and use Controls(0).Dispose() to kill the old old forms. – Raven Blue Oct 06 '17 at 18:06
  • `Dim a As New RCONForm()` `a.Foo()` is not the same as `RCONForm.Foo()` - is my point – djv Oct 06 '17 at 18:08
  • Can you post Functions: SpawnForm and KillOldForm code? – djv Oct 06 '17 at 18:15
  • In RCONForm you also access the default instance of MainForm: `MainForm.RCON.StandardInput...`. You should avoid default instances whenever possible. Better OOP design could help you here. – djv Oct 06 '17 at 18:17
  • @djv Yes that has been added!, instead of using ``MainForm.RCON.StandardInput`` I shall do ``Dim a As New MainForm()`` ? – Raven Blue Oct 06 '17 at 18:20
  • Ok your SpawnForm already had the signature I suggested in my answer. Now you just need to `Return spawn` – djv Oct 06 '17 at 18:23

1 Answers1

0

You are using default form instances heavily. You should avoid that if possible. Here's a way to keep track of the proper instance of RCONForm in MainForm

Private myRCONForm As RCONForm

Private Sub RCONButton_Click(sender As Object, e As EventArgs) Handles RCONButton.Click
    Functions.KillOldForm()
    myRCONForm = Functions.SpawnForm(Of RCONForm)()
End Sub

Now SpawnForm is a function, and it would return the form so you can keep a reference to it

Public Function SpawnForm(Of T As {New, Form})() As T
    Dim myForm = New T()
    ' add myForm to the appropriate Panel or whatever
    Return myForm
End Function

Update all access to RCONForm with myRCONForm in MainForm

Also, this is a little flawed, where you check if invocation is required on RCONForm.RCONLogText, then invoke on Me. It can be simplified

' old with default form instance

Friend Sub AppendOutputText(ByVal text As String)
    If RCONForm.RCONLogText.InvokeRequired Then
        Dim myDelegate As New RCONForm.AppendOutputTextDelegate(AddressOf AppendOutputText)
        Me.Invoke(myDelegate, text)

    Else
        RCONForm.RCONLogText.AppendText(text)
    End If
End Sub

' new, with instance reference plus simplified invocation

Friend Sub AppendOutputText(text As String)
    If myRCONForm.RCONLogText.InvokeRequired Then
        myRCONForm.RCONLogText.Invoke(New Action(Of String)(AddressOf AppendOutputText), text)
    Else
        myRCONForm.RCONLogText.AppendText(text)
    End If
End Sub
djv
  • 15,168
  • 7
  • 48
  • 72