Connecting to OpenVPN in VB.NET - 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()

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.

Related

Directory.Enumerate Files - need to filter by strict file extension

I have the following code:
Dim File_Names_In_Path = Directory.EnumerateFiles(DropBox_Path, "*.csv")
which works fine on newer machines, however on a few older machines it's finding some files that the users have renamed to "*.csvo".
I'm aware of why this happens - with a wildcard and a three letter extension as per Microsoft's documentation.
I don't really have the option of making the users rename their old files.
How can I make the Directory.EnumerateFiles filter results to only pick .csv files?
I think it might be connected to using .where and the file extension but can't just figure how to add this to my code.
If you want it to use the exact case of "csv" then
Dim File_Names_In_Path = Directory.EnumerateFiles(DropBox_Path, "*.csv").
Where(Function(f) Path.GetExtension(f) = ".csv")
or to make it case-insensitive:
Dim File_Names_In_Path = Directory.EnumerateFiles(DropBox_Path, "*.csv").
Where(Function(f) Path.GetExtension(f).Equals(".csv", StringComparison.InvariantCultureIgnoreCase))
Note that the Path.GetExtension Method includes the dot at the start of the extension.

VBA Dynamic Save As, Microsoft Project to Excel

I am trying to create a macro that will export a Microsoft Project file into an excel file. Through the use of macro recording I have got a line of code that accomplishes this using the export wizard, but I want the file path and file name to be dynamic so I can use this macro on different projects. I have been searching many other threads and the Microsoft website with no luck. Is this possible?
Here is what I have:
sub formatAndSave ()
FileSaveAs Name:="C:\Users\XXXXXX\SharePoint\Projects\ProjectType\HxH\myProject.xlsx",_
FormatID:="MSProject.ACE", map:="myMap"
end sub
One idea I tried was:
Active.Workbook.SaveAs FileName:=Title
Any help would be very much appreciated!
For the sake of simplicity, let's assume for all answers below your project is located at c:\projects\myProj.mpp
I think you're after the string replace function. Something like:
Dim excelFilePath As String
excelFilePath = Replace(ActiveProject.FullName, ".mpp", ".xlsx")
Debug.Print excelFilePath
'the output would be c:\projects\myProj.xlsx
If you're unfamiliar with string manipulation in VB/VBA, just search the web for "VBA string manipulation". Microsoft has a decent article here: https://msdn.microsoft.com/en-us/library/aa903372(v=vs.71).aspx
A few other things that may be handy for you are these variables:
ActiveProject.FullName 'shows full path & name, so you'd get "c:\projects\myProj.mpp"
ActiveProject.Path 'shows just the path, so you'd get "c:\projects\"
ActiveProject.Name 'shows just the file name, so you'd get "myProj.mpp"
Finally, one caveat I've seen is that the ActiveProject.FullName and ActiveProject.Name variables may or may not provide the file extension depending on your local windows environment settings. I've observed that if Windows Explorer is configured to hide file extensions, then these variables also withhold the extension; if Explorer is configured to show them, then they are provided in the variables. Make sure your code is robust to both cases, or make sure you have control over the environment where you code will run.

Using the same file error

On a button click I have the following code to write what Is in my textboxes.
Dim file As System.IO.StreamWriter
file = My.Computer.FileSystem.OpenTextFileWriter("C:/Users/Nick/Documents/Dra.txt", False)
file.WriteLine(NameBasic)
file.WriteLine(LastBasic)
file.WriteLine(PhoneBasic)
file.WriteLine(NameEmer1)
On my form load, I load what is in the notepad from what was written, It is saying It is already being used(the file) which is true, how can I have two different functions(write, and read) manipulating the same file with out this error?
The process cannot access the file 'C:\Users\Nick\Documents\Dra.txt' because it is being used by another process.
And here is the code for my onformload
Dim read As System.IO.StreamReader
read = My.Computer.FileSystem.OpenTextFileReader("C:/Users/Nick/Documents/Dra.txt")
lblNameBasic.Text = read.ReadLine
I am sort of stuck on this problem, thank you
You need to close the file when you are done writing to it.
Dim file As System.IO.StreamWriter
file = My.Computer.FileSystem.OpenTextFileWriter("C:/Users/Nick/Documents/Dra.txt", False)
file.WriteLine(NameBasic)
file.WriteLine(LastBasic)
file.WriteLine(PhoneBasic)
file.WriteLine(NameEmer1)
file.Close()
To answer your question:
how can I have two different functions(write, and read) manipulating the same file with out this error?
If you really want to simultaneously read and write to the same file from two processes, you need to open the file with the FileShare.ReadWrite option. The My.Computer.FileSystem methods don't do that.¹
However, I suspect that you don't really wan't to do that. I suspect that you want to write and, after you are finished writing, you want to read. In that case, just closing the file after using it will suffice. The easiest way to do that is to use the Using statement:
Using file = My.Computer.FileSystem.OpenTextFileWriter(...)
file.WriteLine(...)
file.WriteLine(...)
...
End Using
This ensures that the file will be closed properly as soon as the Using block is left (either by normal execution or by an exception). In fact, it is good practice to wrap use of every object whose class implements IDisposable (such as StreamWriter) in a Using statement.
¹ According to the reference source, OpenTextFileWriter creates a New IO.StreamWriter(file, append, encoding), which in turn creates a new FileStream(..., FileShare.Read, ...).

Access not exiting

Recently, my Access .mdb database started intermittently not allowing Access (both Access 2003 and 2007) to quit. If I quit (whether by pressing the X button or from the menu, then it closes the database and appears to exit Access, as well, but then it suddenly reappears (without any database open). The only way for me to exit at that point is from the task manager.
There are two significant changes that I did recently that might be related. 1) I started using the WinSCP .Net assembly to access an ftp server, which I had to install and register for COM from the instructions here. 2) I started using ODBC, first as a linked table, and then from VBA ADO code (see this). I doubt that this second change is causing this problem because I've had the problem both when I was using the linked tables and when with ADO.
This doesn't happen every time I open the database, and I haven't noticed a pattern. What could be causing this strange problem?
Edit - I found the root of the problem. By breaking my ftp download code at various points and seeing whether it will exit, I narrowed it down to the following:
Dim PDFFolders As Recordset
Set PDFFolders = CurrentDb.OpenRecordset("PDFFolders")
Dim syncOptions As TransferOptions
Set syncOptions = New TransferOptions
syncOptions.filemask = "*/*.pdf"
On Error Resume Next 'In case it doesn't exist
Do While Not PDFFolders.EOF
sess.SynchronizeDirectories SynchronizationMode_Local, info!RTFFolder, _
info!BasePDFFolder & "/" & PDFFolders!Name, False, , , _
syncOptions
PDFFolders.MoveNext
Loop
PDFFolders.Close
Set syncOptions = Nothing
Set PDFFolders = Nothing
On Error GoTo 0
If it runs the sess.SynchronizeDirectories statement, then access won't exit, otherwise it does. Looks to me like a bug win WinSCP.
I can do other things like downloading files, creating directories, etc. with no problem, but when it gets to this statement, it doesn't exit access afterwards.
Sticky instances of Access usually result from hanging object references.
If Access hangs the way you described, I would suspect a nasty circular reference.
To investigate on circular references, you basically have two options:
Inspect your code on circular dependencies - That might become tedious and not so easy. But if you know your code base deeply, you might have suspects where to look first.
Add logging to your code - In case you cannot spot the problem via inspection alone, you can consider adding consequent logging of object creation/deletion (through Class_Initialize/Class_Terminate). For larger code bases using classes heavily, this is a good investment to start with.
If your problem is with classes where you cannot control the code (as is your case), you might consider using that external classes only through wrapper classes where you can log creation/deletion. Of course in tricky cases, termination of the wrapper class does not mean termination of the wrapped class.
BTW, I strongly recommend to make sure to set every object reference explicitly to Nothing ASAP:
Set MyObj = GetMyObject()
' Proceed with coding here later
' First write the following line
Set MyObj = Nothing
Special thoughts have to be given in the case of local error handling to make sure to set the reference to Nothing in either case.
A good way to ensure this is using a With-block instead of an explicit variable (if the usage pattern allows to):
With GetMyObject()
' Use the object's members here
End With
With this pattern you save declaring the local variable and can be sure that the object reference does not survive the current method.
I still think it's a bug in WinSCP, but I found a workaround. I noticed that it only happened if I took the information from a table in the database, and not if I put in a hard-coded string. So I just added & vbNullString, which concatenates a blank string, which changes the data type from a Field to a String, and now it doesn't happen anymore.

vb.net application works with files dragged onto the exe, but crashes if there's a space in the file's path

I'm developing an application in vb.net. You drag any type of file onto the exe, and a window pops up with some options for the file, then it saves the file to a different location, works some SQL magic, etc. It works great for the most part.
The only issue I've found is that if the path of the file contains any spaces, the application will crash immediately with the error window: http://i.stack.imgur.com/mVamO.png
I'm using:
Private filename as String = Command$
This is located right inside my form's class declaration, not within a sub/function.
Without this line, my program runs fine (although useless, without accessing the file).
I've also tried (I think this was it, I don't have the code with me at the moment):
Private filename as String = Environment.CommandLine
And it had the same issue.
So, in vb.net, is there a way to drag a file onto an exe and use that path name, even if there are spaces in the path name?
Windows will put double-quotes around the passed command line argument if the path to the dragged file contains spaces. Trouble is, you are using an ancient VB6 way to retrieve the argument, you see the double quotes. Which .NET then objects against, a double quote is not valid in a path name. Use this:
Dim path = Command$.Replace("""", "")
Or the .NET way:
Sub Main(ByVal args() As String)
If args.Length > 0 then
Dim path = args(0)
MsgBox(path)
'' do something with it..
End If
End Sub
If possible, do post your code as it's pretty much anything that can go wrong. Normally, after receiving CommandLine Arg, I would try to use a System.IO.File wrapper and use built-in mechanisms to verify file and then proceed with it further using IO as much as possible. If you are attempting to directly manipulate the file, then the spaces might become an issue.
In addition, there is a way to convert long file path + name to old DOS’s 8.3 magical file path + name. However, I’ll go into R&D after I see what you are doing in code.