CodeGuru Home VC++ / MFC / C++ .NET / C# Visual Basic VB Forums Developer.com
Page 2 of 3 FirstFirst 123 LastLast
Results 16 to 30 of 35
  1. #16
    Join Date
    Sep 2009
    Posts
    23

    Re: Building a GUI for a CLI Application

    So anyways, I ended up having a friend help me with this, and we discovered that the original source needed to have these two highlighted lines added:
    http://pastebin.com/m4ebe6674

    Ive done a bit more work on the code, and this is what I have:
    Code:
    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.Text;
    using System.Windows.Forms;
    using System.IO;
    using System.Threading;
    using System.Diagnostics;
    
    namespace iPhone_Tunnel_GUI
    {
        public partial class Form1 : Form
        {
            string exeFile = @"iphone_tunnel_modded.exe";
            private CLIProcess process = null;
            string args = "21 21";
    
            public Form1()
            {
                InitializeComponent();
            }
    
            private void startButton_Click(object sender, EventArgs e)
            {
                if (statusLabel.Text != "Started")
                {
                    if (!File.Exists(exeFile))
                    {
                        MessageBox.Show("Please make sure iphone_tunnel.exe is in the same directory as this GUI.");
                    }
                    outputBox.Text = String.Empty;
    
                    Thread t = new Thread(StartProcess);
                    t.Start();
                    statusLabel.ForeColor = Color.Green;
                    statusLabel.Text = "Started";
                }
            }
    
            private void stopButton_Click(object sender, EventArgs e)
            {
                if (statusLabel.Text != "Stopped")
                {
                    process.Exit();
                    statusLabel.ForeColor = Color.Red;
                    statusLabel.Text = "Stopped";
                }
            }
    
            public void StartProcess(Object state)
            {
                process = new CLIProcess(exeFile, args);
                process.OutputDataReceived += processData;
                process.Start();
            }
    
            private void processData(Object sender, DataReceivedEventArgs e)
            {
                if (outputBox.InvokeRequired)
                {
                    outputBox.Invoke(new MethodInvoker(delegate()
                    {
                        outputBox.Text += e.Data + "\n";
                    }));
                }
            }
    
            private void port_SelectedIndexChanged(object sender, EventArgs e)
            {
                switch (Convert.ToInt32(port.Text))
                {
                    case 21:
                        portName.Text = "  FTP";
                        if (radioPreset.Checked)
                            args = "21 21";
                        break;
                    case 22:
                        portName.Text = "  SSH";
                        if (radioPreset.Checked)
                            args = "22 22";
                        break;
                    case 80:
                        portName.Text = " HTTP";
                        if (radioPreset.Checked)
                            args = "80 80";
                        break;
                    case 1080:
                        portName.Text = "SOCKS";
                        if (radioPreset.Checked)
                            args = "1080 1080";
                        break;
                    case 5900:
                        portName.Text = "  VNC";
                        if (radioPreset.Checked)
                            args = "5900 5900";
                        break;
                }
    
            }
        }
    }
    However, as you can see from this image:
    http://img215.imageshack.us/img215/2808/output.png
    Whenever I have text show up in the box, it shows these box characters instead of putting it on a new line like I want. How do I fix this?

  2. #17
    Join Date
    Jul 2006
    Posts
    297

    Re: Building a GUI for a CLI Application

    I assumed it had something to do with the CLI app because I knew the event was working for the GUI. Whenever it received data it would update. I tried it with stuff like IPCONFIG and some other CLI applications and didn't have any issues so I was confused why it wasn't working for you. It makes sense now glad you got it working.

  3. #18
    Join Date
    Jul 2006
    Posts
    297

    Re: Building a GUI for a CLI Application

    However, as you can see from this image:
    http://img215.imageshack.us/img215/2808/output.png
    Whenever I have text show up in the box, it shows these box characters instead of putting it on a new line like I want. How do I fix this?
    Well it has to do with the fact that the textbox doesn't reconize the new line character that you're being sent from the CLI. That character you're being sent needs to be replaced with

    Code:
    Environment.NewLine

  4. #19
    Join Date
    Sep 2009
    Posts
    23

    Re: Building a GUI for a CLI Application

    Gotcha, thanks! That did it.

    I have another question. How can I check on the process? Right now, if the process errors out (from invalid arguments), the Stopped text changes to running, and Id like to change it back to stopped (and eventually interperet the error text and give the user tips on what they did wrong). I tried:
    Code:
    if (t.ThreadState == System.Threading.ThreadState.Stopped)
    ...
    
    and 
    
    if (!t.IsAlive)
    ...
    But neither did what I want. What should I use, or is there a way to run something in the thread that fires off an event if the thread terminates on its own (when theres an error)?
    Last edited by SpikedCola; September 3rd, 2009 at 09:16 PM.

  5. #20
    Join Date
    Jan 2006
    Location
    Fox Lake, IL
    Posts
    15,007

    Re: Building a GUI for a CLI Application

    Try changing this:

    Code:
     p.StartInfo.RedirectStandardOutput = true;
    to false.
    David

    CodeGuru Article: Bound Controls are Evil-VB6
    2013 Samples: MS CODE Samples

    CodeGuru Reviewer
    2006 Dell CSP
    2006, 2007 & 2008 MVP Visual Basic
    If your question has been answered satisfactorily, and it has been helpful, then, please, Rate this Post!

  6. #21
    Join Date
    Sep 2009
    Posts
    23

    Re: Building a GUI for a CLI Application

    That will throw an exception when I try to read the data from the console app and display it in the GUI.

    To simplify my question, how can I tell if a thread has terminated unexpectedly, and do something when it terminates? The two lines I tried above didnt produce the desired effect (and if they are the right one(s) to use, I must have used them improperly).

  7. #22
    Join Date
    Jul 2006
    Posts
    297

    Re: Building a GUI for a CLI Application

    You need to attach to the Process's Exited event.

    Change the CLIProcess Start method to this

    Code:
            public void Start()
            {
                _process.EnableRaisingEvents = true;
                _process.Start();
                _process.BeginOutputReadLine();
            }
    And add expose this event handler for the CLIProcess

    Code:
            public event EventHandler Exited
            {
                add
                {
                    _process.Exited += value;
                }
                remove
                {
                    _process.Exited -= value;
                }
            }
    attach the new event just like we did with OutputDataReceived and make a function called processExited to handle the event. Viola! It detects when the application closes.

    Code:
                process = new CLIProcess(exeFile, args);
                process.OutputDataReceived += processData;
                process.Exited = processExited;
                process.Start();

  8. #23
    Join Date
    Sep 2009
    Posts
    23

    Re: Building a GUI for a CLI Application

    Success! I got it! Now I know when the process exits. Thankyou!

    This is what Im using for my Exited method:
    Code:
            private void processExited(Object sender, System.EventArgs e)
            {
                exited = true;
                MessageBox.Show("Process Exited!");
    
                if (statusLabel.InvokeRequired)
                {
                    statusLabel.Invoke(new MethodInvoker(delegate()
                    {
                        statusLabel.ForeColor = Color.Red;
                        statusLabel.Text = "Stopped";
                    }));
                }
    
                if (port.InvokeRequired)
                {
                    port.Invoke(new MethodInvoker(delegate()
                        {
                            port.Enabled = true;
                        }));
                }
    
                if (iphonePort.InvokeRequired)
                {
                    iphonePort.Invoke(new MethodInvoker(delegate()
                        {
                            iphonePort.Enabled = true;
                        }));
                }
    
                if (localPort.InvokeRequired)
                {
                    localPort.Invoke(new MethodInvoker(delegate()
                        {
                            localPort.Enabled = true;
                        }));
                }
    
                if (!radioCustom.Enabled)
                {
                    if (radioCustom.InvokeRequired)
                    {
                        radioCustom.Invoke(new MethodInvoker(delegate()
                            {
                                radioCustom.Enabled = true;
                            }));
                    }
                }
                else if (!radioPreset.Enabled)
                {
                    if (radioPreset.InvokeRequired)
                    {
                        radioPreset.Invoke(new MethodInvoker(delegate()
                            {
                                radioPreset.Enabled = true;
                            }));
                    }
                }
            }
    To change everything back when the program terminates (so the user doesnt have to click stop), I have to call an invoke for every single item. Is there a simpler way of doing this, or is this just how its done?

    Also, incase youre wondering what the exited bool is for, I had to change my StartProcess to this so as not to throw an exception when the process exits and it tries to read:
    Code:
            public void StartProcess(Object state)
            {
                process = new CLIProcess(exeFile, args);
                process.OutputDataReceived += processData;
                process.Exited += processExited;
                process.Start();
                System.Threading.Thread.Sleep(50);
                if (!exited)
                    process.BeginReading();
            }
    I also created a BeginReading method, and changed the Start method like this:
    Code:
           public void Start()
            {
                _process.EnableRaisingEvents = true;
                _process.Start();
                
            }
            public void BeginReading()
            {
                _process.BeginOutputReadLine();
            }
    Last edited by SpikedCola; September 4th, 2009 at 03:29 PM.

  9. #24
    Join Date
    Jul 2006
    Posts
    297

    Re: Building a GUI for a CLI Application

    Yes you can fix this. I'll show you an example later when i get home. Basically instead of exposing the events from the Process in the CLIProcess class we can make our own events. And use the post method to fire the event on the same SyncronizationContext as the GUI. Then you won't need to do anything with the Invoke.

    Also all the Invoke really does is invoke the method on the same context as the GUI. It doesn't matter which item you're running invoke from. So essentially you could modify each of the controls inside the first delegate.

    The first solution I posted is really the best option I can think of though.

  10. #25
    Join Date
    Sep 2009
    Posts
    23

    Re: Building a GUI for a CLI Application

    Another question. Id like to display the error that the program shows in the text box, along with some help to resolve it. Ive tried modifying my code like this, but when the process terminates, I dont get any text in the box. I set a breakpoint at "private void processData" but it never fires, so I think Im missing some code in CLIProcess.cs

    Form1.cs:
    Code:
    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.Text;
    using System.Windows.Forms;
    using System.IO;
    using System.Threading;
    using System.Diagnostics;
    
    namespace iPhone_Tunnel_GUI
    {
        public partial class Form1 : Form
        {
            string exeFile = @"iphone_tunnel_modded.exe";
            private CLIProcess process = null;
            string args = "21 21";
            bool exited = false;
    
            public Form1()
            {
                InitializeComponent();
            }
    
            private void startButton_Click(object sender, EventArgs e)
            {
                if (statusLabel.Text != "Started")
                {
                    if (!File.Exists(exeFile))
                    {
                        MessageBox.Show("Please make sure iphone_tunnel_modded.exe is in the same directory as this GUI.");
                        return;
                    }
                    if (radioPreset.Checked)
                    {
                        switch (Convert.ToInt32(port.Text))
                        {
                            case 21:
                                args = "21 21";
                                break;
                            case 22:
                                args = "22 22";
                                break;
                            case 80:
                                args = "80 80";
                                break;
                            case 1080:
                                args = "1080 1080";
                                break;
                            case 5900:
                                args = "5900 5900";
                                break;
                        }
                        radioCustom.Enabled = false;
                    }
                    else if (radioCustom.Checked)
                    {
                        args = iphonePort.Text + " " + localPort.Text;
                        radioPreset.Enabled = false;
                    }
                    outputBox.Text = String.Empty;
                    Thread t = new Thread(StartProcess);
                    t.Start();
                    statusLabel.ForeColor = Color.Green;
                    statusLabel.Text = "Started";
                    port.Enabled = false;
                    iphonePort.Enabled = false;
                    localPort.Enabled = false;
                }
            }
    
            private void stopButton_Click(object sender, EventArgs e)
            {
                if ((statusLabel.Text != "Stopped") && (statusLabel.Text != "Error"))
                {
                    process.Dispose();
                    statusLabel.ForeColor = Color.Red;
                    statusLabel.Text = "Stopped";
                    port.Enabled = true;
                    iphonePort.Enabled = true;
                    localPort.Enabled = true;
                    if (!radioCustom.Enabled)
                        radioCustom.Enabled = true;
                    else if (!radioPreset.Enabled)
                        radioPreset.Enabled = true;
                }
            }
    
            public void StartProcess(Object state)
            {
                process = new CLIProcess(exeFile, args);
                process.OutputDataReceived += processData;
                process.Exited += processExited;
                exited = false;
                process.Start();
                System.Threading.Thread.Sleep(50);
                if (!exited)
                    process.BeginReading();
            }
    
            private void processExited(Object sender, EventArgs e)
            {
                exited = true;
                //MessageBox.Show("Process Exited!");
                /*if (outputBox.InvokeRequired)
                {
                    outputBox.Invoke(new MethodInvoker(delegate()
                    {
                        outputBox.Text = "Uh oh! The process terminated unexpectedly. Make sure you have entered valid ports, and that you don't have a server running on the same port as the one you're trying to tunnel.";
                    }));
                }*/
                if (statusLabel.InvokeRequired)
                {
                    statusLabel.Invoke(new MethodInvoker(delegate()
                    {
                        statusLabel.ForeColor = Color.Red;
                        statusLabel.Text = "Error";
                    }));
                }
    
                if (port.InvokeRequired)
                {
                    port.Invoke(new MethodInvoker(delegate()
                        {
                            port.Enabled = true;
                        }));
                }
    
                if (iphonePort.InvokeRequired)
                {
                    iphonePort.Invoke(new MethodInvoker(delegate()
                        {
                            iphonePort.Enabled = true;
                        }));
                }
    
                if (localPort.InvokeRequired)
                {
                    localPort.Invoke(new MethodInvoker(delegate()
                        {
                            localPort.Enabled = true;
                        }));
                }
    
                if (!radioCustom.Enabled)
                {
                    if (radioCustom.InvokeRequired)
                    {
                        radioCustom.Invoke(new MethodInvoker(delegate()
                            {
                                radioCustom.Enabled = true;
                            }));
                    }
                }
                else if (!radioPreset.Enabled)
                {
                    if (radioPreset.InvokeRequired)
                    {
                        radioPreset.Invoke(new MethodInvoker(delegate()
                            {
                                radioPreset.Enabled = true;
                            }));
                    }
                }
            }
    
            private void processData(Object sender, DataReceivedEventArgs e)
            {
                if (outputBox.InvokeRequired)
                {
                    outputBox.Invoke(new MethodInvoker(delegate()
                    {
                        if (exited)
                            outputBox.Text = "Uh oh! The process terminated unexpectedly. Make sure you have entered valid ports, and that you don't have a server running on the same port as the one you're trying to tunnel. The error received was:" + Environment.NewLine + e.Data;
    
                        else if (e.Data != "")
                        {
                            if (e.Data == "AFCConnectionOpen = 0")
                                outputBox.Text += e.Data;
                            else
                                outputBox.Text += e.Data + Environment.NewLine;
                        }
                    }));
                }
            }
    
            private void port_SelectedIndexChanged(object sender, EventArgs e)
            {
                switch (Convert.ToInt32(port.Text))
                {
                    case 21:
                        portName.Text = "  FTP";
                        break;
                    case 22:
                        portName.Text = "  SSH";
                        break;
                    case 80:
                        portName.Text = " HTTP";
                        break;
                    case 1080:
                        portName.Text = "SOCKS";
                        break;
                    case 5900:
                        portName.Text = "  VNC";
                        break;
                }
    
            }
        }
    }
    CLIProcess.cs:
    Code:
    using System;
    using System.Collections.Generic;
    using System.Text;
    using System.Diagnostics;
    
    namespace iPhone_Tunnel_GUI
    {
        public class CLIProcess : IDisposable
        {
            private Process _process = null;
    
            public CLIProcess(String path, string args)
            {
                _process = new Process();
                _process.StartInfo.FileName = path;
                _process.StartInfo.Arguments = args;
                _process.StartInfo.UseShellExecute = false;
                _process.StartInfo.RedirectStandardOutput = true;
                _process.StartInfo.RedirectStandardError = true;
                _process.StartInfo.CreateNoWindow = true;
            }
    
            public void Start()
            {
                _process.EnableRaisingEvents = true;
                _process.Start();
                _process.BeginErrorReadLine();
            }
            public void BeginReading()
            {
                _process.BeginOutputReadLine();
            }
    
            public event EventHandler Exited
            {
                add
                {
                    _process.Exited += value;
                }
                remove
                {
                    _process.Exited -= value;
                }
            }
    
            public event DataReceivedEventHandler OutputDataReceived
            {
                add
                {
                    _process.OutputDataReceived += value;
                }
    
                remove
                {
                    _process.OutputDataReceived -= value;
                }
            }
    
            public void Dispose()
            {
                if (!_process.HasExited)
                {
                    _process.Kill();
                    _process.Close();
                }
            }
        }
    }

  11. #26
    Join Date
    Jul 2006
    Posts
    297

    Re: Building a GUI for a CLI Application

    If you can attach your newest project again when i get home I will take a look at the two issues and show you some ways you can solve them.

  12. #27
    Join Date
    Sep 2009
    Posts
    23

    Re: Building a GUI for a CLI Application

    Done & done. Thanks a lot!

    Here's a link to the modded CLI app and the Apple DLL - the zip file is too big to host here. It needs to be extracted to the \bin\Debug directory
    Attached Files Attached Files

  13. #28
    Join Date
    Jul 2006
    Posts
    297

    Re: Building a GUI for a CLI Application

    Ok so like I was saying earlier. One way to get around the Invoke methods is to pass a synchronization context and when you fire the event use the post method of that context. Now, to do that we need to rework the CLI class a little so that we wrap the Process's event instead of just exposing it, like so...

    Code:
    public event DataReceivedEventHandler OutputDataReceived;
    
    protected void OnOutputDataReceived(Object state)
    {
        Object[] args = (Object[])state;
        if (OutputDataReceived != null)
            OutputDataReceived(args[0], (DataReceivedEventArgs)args[1]);
    }
    
    protected void Process_OutputDataReceived(Object sender, DataReceivedEventArgs e)
    {
        Context.Post(OnOutputDataReceived, new Object[] { sender, e });
    }
    Ok so in this situation we're going to register the Process_OutputDataReceived to the original event. When it runs the code it will fire the event we want on the correct context.

    Code:
    public void Start()
    {
        _process.EnableRaisingEvents = true;
        _process.OutputDataReceived += Process_OutputDataReceived;
        _process.Start();
        _process.BeginErrorReadLine();
    }
    Because of this code we can re-work the event handlers in the Form1.cs file and just completely remove all the Invoke functions. I've attached the modified code so you can take a look at it. I hope it all makes sense, if not ask questions
    Attached Files Attached Files

  14. #29
    Join Date
    Sep 2009
    Posts
    23

    Re: Building a GUI for a CLI Application

    Thank you so much! Im not quite sure I understood fully what youve done just yet, but it gives me stuff to look up.

    A couple things - when I hit Stop to kill the process, the GUI closes, and I get a 'TargetInvocationException was unhandled' exception at:
    Code:
            static void Main()
            {
                Application.EnableVisualStyles();
                Application.SetCompatibleTextRenderingDefault(false);
                Application.Run(new Form1());
            }
    The InnerException is "No process is associated with this object".

    Also, I was trying to make this work before, but I couldnt figure it out with my old code, and the new code certainly isnt helping When the process exits, I want to display the text in the text box like I was doing, but I also want to display the error text underneath. As it sits right now, I dont even get the text Ive set up to show when the process exits.

  15. #30
    Join Date
    Jul 2006
    Posts
    297

    Re: Building a GUI for a CLI Application

    which error message did you want to display? i don't understand that part of your question.

Page 2 of 3 FirstFirst 123 LastLast

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  





Click Here to Expand Forum to Full Width

Featured