A. Kabrun A. Kabrun - 5 months ago 42
Vb.net Question

Connecting to OpenVPN in VB.NET

I am wondering on how to connect to OpenVPN with VB.NET, I have attempted using the following code but then in the command line I get an error saying "Options error: In [CMD-LINE]:1: Error opening configuration file: C:\Users\Minetro300\Documents\connection.ovpn
Use --help for more information.".

IO.File.WriteAllText((System.Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments) & "\vShield" & "\connection.ovpn"), My.Resources.OpenVPNCertificate)
IO.File.WriteAllText((System.Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments) & "\vShield" & "\connection.bat"), "openvpn " & System.Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments) & "\connection.ovpn")

Dim connect As System.Diagnostics.Process
connect = New System.Diagnostics.Process()
connect.StartInfo.FileName = System.Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments) & "\vShield" & "\connection.bat"
connect.StartInfo.WindowStyle = ProcessWindowStyle.Normal

connect.Start()
connect.WaitForExit()

Answer Source

I was going to say that I don't know the reason for your error but here are some suggestions to write better code and then I realised that one of those suggestions would have solved your issue. Look at the path of the file you specify in this line:

IO.File.WriteAllText((System.Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments) & "\vShield" & "\connection.ovpn", My.Resources.OpenVPNCertificate)

and now look at the path you specify for what I assume is supposed to be the same file in this line:

IO.File.WriteAllText((System.Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments) & "\vShield" & "\connection.bat"), "openvpn " & System.Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments) & "\connection.ovpn")

The path you specify to read the file from is not the same as the path you specify to write the file to, so is it any wonder that it can't read the file?

The advice I was going to give you about improving your code is to not use long expressions multiple times but, instead, use them once and assign the result to a variable, then reuse that variable. If you had done that then you could not have messed up the path the second time. So:

Dim ovpnFilePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), "vShield\connection.ovpn"

You can now use that ovpnFilePath variable wherever you need that path and then you won't try to read from a different path to that which you wrote to.

That suggested code incorporates several other improvements too. Firstly, importing namespaces that you use often and then not using them in your code. As an example, System is already imported so you don't need to use System.Environment in code like you are. What's worse, though, is that you use System.Environment in some places and just Environment in others, sometimes immediately adjacent. That's bad coding. If I see two different things in your code then I should be able to assume that they are different, not the same thing written two different ways for no good reason. You should also be importing the System.IO namespace and not qualifying File with IO repeatedly.

Importing the System.IO namespace means that you can also use the Path class unqualified and you should be using that class to combine partial paths rather than using string concatenation. Further to that, NEVER concatenate two literals together. It's just silly and complicates your code for no good reason.

Looking more closely at your code, I see that you use two file paths multiple times and both files are in the same folder. I would tend to do this for those paths:

Dim folderPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), "vShield")
Dim ovpnFilePath = Path.Combine(folderPath, "connection.ovpn")
Dim batFilePath = Path.Combine(folderPath, "connection.bat")

That will make your code much less complex and thus easier to read. It will eliminate your current issue and make it less likely that you'll make more.

EDIT: Here is your original code written "properly":

Dim folderPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), "vShield")
Dim ovpnFilePath = Path.Combine(folderPath, "connection.ovpn")
Dim batFilePath = Path.Combine(folderPath, "connection.bat")

File.WriteAllText(ovpnFilePath, My.Resources.OpenVPNCertificate)
File.WriteAllText(batFilePath, String.Format("openvpn ""{0}""", ovpnFilePath))

Using connect As New Process(batFilePath)
    connect.StartInfo.WindowStyle = ProcessWindowStyle.Normal    
    connect.Start()
    connect.WaitForExit()
End Using

As you can see, it's much easier to read for one thing. It doesn't perform the same operations over and over so it nullifies the chance of making the mistake that caused you to post this question in the first place, i.e. use different paths when writing and reading a file.

It also gets rid of unnecessary qualifying namespaces. System and System.Diagnostics are already imported by default, so no need to do anything with them. This code does assume that you have also imported System.IO, which you can do at the file or the project level.

I've also used String.Format to create the commandline as it makes it cleaner to wrap the file path in double-quotes. That's not strictly necessary in this case but it ensures that the commandline will still work if the file path has spaces in it, which is completely possible.

Finally, it correct disposes the Process object that you create by doing so with a Using block, which causes the object created at the Using statement to be disposed at the End Using statement. You should always dispose any objects you create that support it.