0

I'm learning C# and I'm creating a simple WinForms application, and what it does is starting a simple OpenVPN client:

    void button_Connect_Click(object sender, EventArgs e)
    {
        var proc = new Process();
        proc.StartInfo.FileName = "CMD.exe";
        proc.StartInfo.UseShellExecute = false;
        proc.StartInfo.WorkingDirectory = @"C:\Program Files (x86)\OpenVPN\bin";
        proc.StartInfo.Arguments = "/c openvpn.exe --config config.ovpn --auto-proxy";

        // set up output redirection
        proc.StartInfo.RedirectStandardOutput = true;
        proc.StartInfo.RedirectStandardError = true;
        // Input
        proc.StartInfo.RedirectStandardInput = true;

        // Other
        proc.EnableRaisingEvents = true;
        proc.StartInfo.CreateNoWindow = false;
        // see below for output handler
        proc.ErrorDataReceived += proc_DataReceived;
        proc.OutputDataReceived += proc_DataReceived;

        proc.Start();

        StreamWriter myStreamWriter = proc.StandardInput;
        proc.BeginErrorReadLine();
        proc.BeginOutputReadLine();

        proc.WaitForExit();
    }

    void proc_DataReceived(object sender, DataReceivedEventArgs e)
    {
        // output will be in string e.Data
        if (e.Data != null)
        {
            string Data = e.Data.ToString();
            if (Data.Contains("Enter Auth Username"))
            {
                myStreamWriter("myusername");
            }
            //MessageBox.Show(Data);
        }
    }

What it does now, is send all the output of the CMD to my program, which run commands depending on the output.

My current issue is, I need to write to the stream. I use myStreamWriter in proc_DataReceived, however it's not in the same context so it doesn't work.

I get the following error: The name 'myStreamWriter' does not exist in the current context, which is obviously non existent in that scope.

How do I make this work? Get/set properties? Like I said I'm quite new to C# so any help is appreciated.

Devator
  • 3,686
  • 4
  • 33
  • 52

3 Answers3

2

In your form, add this field:

public class Form1Whatevver: Form {
    private StreamWriter myStreamWriter;
}

Then in your code:

proc.Start();

this.myStreamWriter = proc.StandardInput;

proc.BeginErrorReadLine();
proc.BeginOutputReadLine();

Now, myStreamWriter will be available to proc_DataReceived()

void proc_DataReceived(object sender, DataReceivedEventArgs e)
    {
        // output will be in string e.Data
        if (e.Data != null)
        {
            string Data = e.Data.ToString();
            if (Data.Contains("Enter Auth Username"))
            {
                if (myStreamWriter != null) // Just in case
                {
                    myStreamWriter("myusername");
                }
            }
            //MessageBox.Show(Data);
        }
    }
Jason Evans
  • 28,906
  • 14
  • 90
  • 154
1

The concept you are talking about is Scope not context.


The are several ways you could alter your code so that you can access myStreamWriter from proc_DataReceived.

Arguably the simplest, is to pass the object you wish to effect as a parameter to the function. This is often a good approach in the perspective of future multi-threading and a drive towards a functional style.

    void proc_DataReceived(
        object sender, 
        DataReceivedEventArgs e,
        StreamWriter writer)
    {
        ...
        writer("myusername");
        ...
    }

However proc_DataReceived is obviously an event handler and you probably don't want to change the signature of the function.


If you declare myStreamWriter as a Private Member Variable at the class level you will be able to access from anywhere within the class. The this is optional but good form.

class YourForm : Form
{
    private StreamWriter myStreamWriter;

    void button_Connect_Click(...
    {
        ...
        this.myStreamWriter = proc.StandardInput;
        ...
    }

    void proc_DataRecieved(...
    {
        ...
        this.myStreamWriter("myusername");
        ...

    }
}

A property implementation is not necessary unless you want to access the StreamWriter from outside the class/form.

BenMorel
  • 34,448
  • 50
  • 182
  • 322
Jodrell
  • 34,946
  • 5
  • 87
  • 124
1

The simple quick-fix is to define myStreamWriter as a field in the class (i.e. outside the scopes of your methods), then instantiate it from within one of the scopes:

private StreamWriter _myStreamWriter;

void button_Connect_Click(object sender, EventArgs e)
{
    // Do some stuff

    _myStreamWriter = proc.StandardInput;

    // Do some stuff
}

Having done that should let you use it wherever you want within the class.

Kjartan
  • 18,591
  • 15
  • 71
  • 96