Write a variable to a file that has a different type than the function assigned to the variable - vb.net

I have the following code that I am using to parse out a test file. I am getting variable conversion error in Sub Main() when I assign file = Read(). The return value of Read() is a TextFieldParser type. How do I assign the proper variable type to "file" so I can write the output to a text file?
Thanks!
Module Module1
Function Read()
Using MyReader As New FileIO.TextFieldParser("C:\Users\Colin\Desktop\Parse_Me.txt")
Dim currentRow As String
While Not MyReader.EndOfData
Try
currentRow = MyReader.ReadLine()
Console.WriteLine(Parse_me(currentRow))
Catch ex As FileIO.MalformedLineException
MsgBox("Line " & ex.Message &
" is invalid. Skipping")
End Try
End While
Return MyReader
MyReader.Close()
End Using
End Function
Function Parse_me(ByVal test As String)
Dim Set_1, Set_2, Set_3, Set_4, Set_5 As String
Dim new_string As String
Set_1 = test.Substring(0, 4)
Set_2 = test.Substring(7, 2)
Set_3 = test.Substring(11, 1)
Set_4 = test.Substring(14, 4)
Set_5 = test.Substring(20, 4)
new_string = Set_1 & " " & Set_2 & " " & Set_3 & " " & Set_4 & " " & Set_5
Return new_string
End Function
Sub Main()
Dim file As Object
file = Read()
FilePutObject("C:\Users\Colin\Desktop\Parse_Meoutput.txt", file)
End Sub
End Module

Here's how FilePutObject is supposed to work (example taken from MSDN documentation for FilePutObject):
Sub WriteData()
Dim text As String = "test"
FileOpen(1, "test.bin", OpenMode.Binary)
FilePutObject(1, text)
FileClose(1)
End Sub
The 1 act as an identifier for the file. Note also that the file name is passed to FileOpen before calling FilePutObject, and that FileClose is called afterwards. Also note that a string is being written to the file. I don't know which types of data are valid for being passed to FilePutObject, but FileIO.TextFieldParser is definitely not one of them (I just tried it).
Correct me if I'm wrong, but I'm pretty sure that FilePutObject is one of those carry-overs from VB6. If you're writing new code, I would rather use a Stream object for my I/O. For one, it's a lot more .Net-ish (i.e., type-safe, object-oriented, etc). And as far as usability goes, it's a lot clearer how a Stream works, not to mention it doesn't involve passing arbitrary integers as handles to functions in order to identify which file you'd like to work with. And to top it all off, a Stream works whether you want to write to a file, to the console, or send the data to another machine. To sum up, I would definitely look up the Stream class, some of its child classes (like FileStream, and whatever else appeals to you), and some associated types (such as the TextWriter class for conveniently writing text).

Change the definition of the function "read" to:
Function Read() as FileIO.TextFieldParser
and change the declaration of "file" in sub main to:
Dim file as FileIO.TextFieldParser
That way the data type of the function and assignment match.

Related

Add a path to a code VB.net / visual basic

how do I add a path to a code where "HERE_HAS_TO_BE_A_PATH" is. When I do, Im getting an error message. The goal is to be able to specific the path where is the final text file saved.
Thanks!
Here is a code:
Dim newFile As IO.StreamWriter = IO.File.CreateText("HERE_HAS_TO_BE_A_PATH")
Dim fix As String
fix = My.Computer.FileSystem.ReadAllText("C:\test.txt")
fix = Replace(fix, ",", ".")
My.Computer.FileSystem.WriteAllText("C:\test.txt", fix, False)
Dim query = From data In IO.File.ReadAllLines("C:\test.txt")
Let name As String = data.Split(" ")(0)
Let x As Decimal = data.Split(" ")(1)
Let y As Decimal = data.Split(" ")(2)
Let z As Decimal = data.Split(" ")(3)
Select name & " " & x & "," & y & "," & z
For i As Integer = 0 To query.Count - 1
newFile.WriteLine(query(i))
Next
newFile.Close()
1) Use a literal string:
The easiest way is replacing "HERE_HAS_TO_BE_A_PATH" with the literal path to desired output target, so overwriting it with "C:\output.txt":
Dim newFile As IO.StreamWriter = IO.File.CreateText("C:\output.txt")
2) Check permissions and read/write file references are correct:
There's a few reasons why you might be having difficulties, if you're trying to read and write into the root C:\ directory you might be having permissions issues.
Also, go line by line to make sure that the input and output files are correct every time you are using one or the other.
3) Make sure the implicit path is correct for non-fully qualified paths:
Next, when you test run the program, it's not actually in the same folder as the project folder, in case you're using a relative path, it's in a subfolder "\bin\debug", so for a project named [ProjectName], it compiles into this folder by default:
C:\path\to\[ProjectName]\bin\Debug\Program.exe
In other words, if you are trying to type in a path name as a string to save the file to and you don't specify the full path name starting from the C:\ drive, like "output.txt" instead of "C:\output.txt", it's saving it here:
C:\path\to\[ProjectName]\bin\Debug\output.txt
To find out exactly what paths it's defaulting to, in .Net Framework you can check against these:
Application.ExecutablePath
Application.StartupPath
4) Get user input via SaveFileDialogue
In addition to a literal string ("C:\output.txt") if you want the user to provide input, since it looks like you're using .Net Framework (as opposed to .Net Core, etc.), the easiest way to set a file name to use in your program is using the built-in SaveFileDialogue object in System.Windows.Forms (like you see whenever you try to save a file with most programs), you can do so really quickly like so:
Dim SFD As New SaveFileDialog
SFD.Filter = "Text Files|*.txt"
SFD.ShowDialog()
' For reuse, storing file path to string
Dim myFilePath As String = SFD.FileName
Dim newFile As IO.StreamWriter = IO.File.CreateText(myFilePath) ' path var
' Do the rest of your code here
newFile.Close()
5) Get user input via console
In case you ever want to get a path in .Net Core, i.e. with a console, the Main process by default accepts a String array called args(), here's a different version that lets the user add a path as the first parameter when running the program, or if one is not provided it asks the user for input:
Console.WriteLine("Hello World!")
Dim myFilePath = ""
If args.Length > 0 Then
myFilePath = args(0)
End If
If myFilePath = "" Then
Console.WriteLine("No file name provided, please input file name:")
While (myFilePath = "")
Console.Write("File and Path: ")
myFilePath = Console.ReadLine()
End While
End If
Dim newFile As IO.StreamWriter = IO.File.CreateText(myFilePath) ' path var
' Do the rest of your code here
newFile.Close()
6) Best practices: Close & Dispose vs. Using Blocks
In order to keep the code as similar to yours as possible, I tried to change only the pieces that needed changing. Vikyath Rao and Mary respectively pointed out a simplified way to declare it as well as a common best practice.
For more information, check out these helpful explanations:
Can any one explain why StreamWriter is an Unmanaged Resource. and
Should I call Close() or Dispose() for stream objects?
In summary, although streams are managed and should garbage collect automatically, due to working with the file system unmanaged resources get involved, which is the primary reason why it's a good idea to manually dispose of the object. Your ".close()" does this. Overrides for both the StreamReader and StreamWriter classes call the ".dispose()" method, however it is still common practice to use a Using .. End Using block to avoid "running with scissors" as Enigmativity puts it in his post, in other words it makes sure that you don't go off somewhere else in the program and forget to dispose of the open filestream.
Within your program, you could simply replace the "Dim newFile As IO.StreamWriter = IO.File.CreateText("C:\output.txt")" and "newFile.close()" lines with the opening and closing statements for the Using block while using the simplified syntax, like so:
'Dim newFile As IO.StreamWriter = IO.File.CreateText(myFilePath) ' old
Using newFile As New IO.StreamWriter(myFilePath) ' new
Dim fix As String = "Text from somewhere!"
newFile.WriteLine(fix)
' other similar operations here
End Using ' new -- ensures disposal
'newFile.Close() ' old
You can write that in this way. The stream writer automatically creates the file.
Dim newFile As New StreamWriter(HERE_HAS_TO_BE_A_PATH)
PS: I cannot mention all these in the comment section as I have reputations less than 50, so I wrote my answer. Please feel free to tell me if its wrong
regards,
vikyath

vb.net correct error implement System.IDisposable

I am just lost with trying to correct this error
Error BC36010 'Using' operand of type 'Boolean' must implement 'System.IDisposable'
The Error only shows up when I remove the If End If design from the code below and implement Using
Reason for the use of Using End Using was garbage clean up
So the question is how to implement System.IDisposable ?
Public Sub haveFILE()
'Dim path As String = "C:\Users\Me\source\repos\TestForms\TestForms\Resource\"
Using Not My.Computer.FileSystem.FileExists(path & "Check.txt") Then
tbHaveDB.Text = "File NOT Found"
' Create or overwrite the file.
Dim fs As FileStream = File.Create(path & "Check.txt")
fs.Close()
End Using
End Sub
The error is about using
Using Not My.Computer.FileSystem.FileExists(path & "Check.txt") Then
End Using
My.Computer.FileSystem.FileExists(path & "Check.txt") returns boolean. Just don't use using. Use if then in this case. Using is when you create IDisposable object
Your code seem confusing between different blocks. You have half if half using. I think, it needs to be this
if Not My.Computer.FileSystem.FileExists(path & "Check.txt") Then
tbHaveDB.Text = "File NOT Found"
' Create or overwrite the file.
else
using fs As FileStream = File.Create(path & "Check.txt")
' write to stream here
fs.Close()
End Using
End If

Is it possible to build a MessageBox message that is then passed to a message box to include new lines?

I am attempting to generate a message for the user that returns a list of missing checklist items. My question: is there a way to build a message that can then be passed to a MessageBox that includes new lines. I have considered overloading the method to accept various numbers of individual messages, but there has to be a more elegant way to do this. Below is the class that I have designed to handle this message collection, display, and future exportation to a more convenient format.
Public Class clsChecklistMissingItems
Private Shared iWrong As Integer = 0 'Number of items wrong.
Private Shared sMissingItems() As String 'Will use the number of items wrong.
Public Shared Sub CollectItem(ByVal mess As String) 'Saves the message passed to it.
ReDim Preserve sMissingItems(iWrong) 'Resize the array based on the counter.
sMissingItems(iWrong) = mess 'Assign the message to the missing items string array.
iWrong = iWrong + 1 'Increment the counter (may give us +1
End Sub
Public Sub DisplayList() 'Displays the message at the end of the execution.
'Can this be generated procedurally?
MessageBox.Show("There were " & iWrong & " missing or incorrect items." & vbNewLine &
sMissingItems(iWrong))
End Sub End Class
My alternate solution is to write a form that is formatted like a text box that will behave similar to a text box, but will have all of the described functionality.
Using arrays is not the best option. .NET has plenty of built-in collection classes that are far superior to an array, like List<T>. I understand it's tempting to use an array when you're coming from other "flavors" of Visual Basic (VBScript, VBA, etc.) because that's what you're familiar with, but you should learn what's available in the .NET FCL.
You could do something like this using a loop and a StringBuilder to build your list of messages:
Dim wrongItems As New List(Of String)()
' fill the collection however you do it...
wrongItems.AddRange({"Reason 1", "Reason 2", "Reason 3"})
Dim sb As New StringBuilder()
For Each item In wrongItems
sb.AppendLine(item)
Next
MsgBox(String.Format("There were {0} missing or incorrect items.",
wrongItems.Count) & vbNewLine & sb.ToString())
After speaking with my co-worker it was pointed out to me that VB.NET has a carriage return line feed that is designed to be concatenated into a string to represent a new line.
Public Sub DisplayList()
Dim sMessage As String = ""
For i As Integer = 0 To sMissingItems.Length - 1
sMessage = sMessage & sMissingItems(i) & vbCrLf
Next
MessageBox.Show(sMessage)
End Sub
I have not had a chance to implement using a list rather than an array at this point.

ASP.NET How do I wait for file upload/release?

I've got ASP.NET intranet application written in VB. It gets a file from the user, and then depending on a few different cases it may create a few copies of the file as well as move the original.
Unfortunately I've come across a case where I get this error:
Exception Details: System.IO.IOException: The process cannot access the file
'\\some\dir\D09_03_5_180_0.000-6.788.png' because it is being used by
another process.
Which is thrown by My.Computer.FileSystem.CopyFile. And that's fine that it's being used by another process - it may still be saving/downloading from the user or trying to copy while another thread(?) is copying, I don't really care about that, what I want to know:
Is there any way that I can tell VB to wait to copy (also move) the file until the file is no longer in use?
Thanks
Test if the file is in use and the do what you need to do.
Public Sub WriteLogFile(ByVal pText As String, ByVal psPath As String, ByVal psName As String)
Dim strFullFileName As String
Dim Writer As System.IO.StreamWriter
Dim Fs As System.IO.FileStream
Try
Dim DirectoryHandler As New System.IO.DirectoryInfo(psPath)
strFullFileName = psPath & "\" & psName & Date.Today.Month.ToString & "-" & Date.Today.Day.ToString & "-" & Date.Today.Year.ToString & ".txt"
If Not DirectoryHandler.Exists() Then
Try
Monitor.Enter(fsLocker)
DirectoryHandler.Create()
Finally
Monitor.Exit(fsLocker)
End Try
End If
Try
If CheckIfFileIsInUse(strFullFileName) = True Then
Thread.Sleep(500) ' wait for .5 second
WriteLogFile(pText, psPath, psName)
If Not Fs Is Nothing Then Fs.Close()
If Not Writer Is Nothing Then Writer.Close()
Exit Sub
End If
Monitor.Enter(fsLocker)
Fs = New System.IO.FileStream(strFullFileName, IO.FileMode.Append, IO.FileAccess.Write, IO.FileShare.Write)
Writer = New System.IO.StreamWriter(Fs)
Writer.WriteLine(Date.Now.ToString & vbTab & "ProcessID: " & Process.GetCurrentProcess.Id.ToString() & vbTab & pText)
Writer.Close()
Fs.Close()
Finally
Monitor.Exit(fsLocker)
End Try
Catch ex As Exception
Dim evtEMailLog As System.Diagnostics.EventLog = New System.Diagnostics.EventLog()
evtEMailLog.Source = Process.GetCurrentProcess.ProcessName.ToString()
evtEMailLog.WriteEntry(ex.Message, System.Diagnostics.EventLogEntryType.Error)
Finally
If Not Fs Is Nothing Then Fs.Close()
If Not Writer Is Nothing Then Writer.Close()
End Try
End Sub
Public Function CheckIfFileIsInUse(ByVal sFile As String) As Boolean
If System.IO.File.Exists(sFile) Then
Try
Dim F As Short = FreeFile()
FileOpen(F, sFile, OpenMode.Append, OpenAccess.Write, OpenShare.Shared)
FileClose(F)
Catch
Return True
End Try
End If
End Function
Hmm... not directly.
What most implementations are doing, is making a retry of copying the file, with a small timeframe (some seconds)
if you want to make a nice UI, you check via Ajax, if the copying process went well.
Well, it turns out that waiting would not work in this case:
When trying to copy a file you cannot copy a file from one location to the same location or it will throw an error (apparently). Rather than just pretending to copy the file, VB actually tries to copy the file and fails because the copy operation is trying to copy to the file it's copying from (with overwrite:=True at least).
Whoops!

XmlTextWriter responds with System.InvalidOperationException during creation of second element

I am having trouble with XMLTextWriter.WriteStartElement throwing an exception:
System.InvalidOperationException
when trying to write the second element in my XML document. This error comes back as "The Writer is closed". I have not closed it, so I am guessing it has fallen out of scope?? I have created a class to write an XML file using XMLTextWriter as an object within my class. Below is the relevant code. I have found one other post on codeguru that was never answered with the exact same issue. Any ideas for workarounds or otherwise would be appreciated.
Function CreateXML()...
Try
_listDocument = New XmlTextWriter(_xmlDI.FullName & "\\" & currentFilename, Nothing)
CreateHeader()
AddTimeDateNode()
CreateXML = True
Catch xmlErr As XmlException
MsgBox("Unable to create temporary file(" & currentFilename & ") that is used to change your whitelist or blacklist. " & _
"More technical information: " & xmlErr.Message, MsgBoxStyle.Critical, "Can't Continue")
End Try
End Function
Function AddListMember(ByVal listType As String, ByVal listItem As String, ByVal action As String) As Boolean
_listDocument.WriteStartElement(listItem) <-- CODE THROWS EXCEPTION HERE!
_listDocument.WriteAttributeString("listType", listType)
_listDocument.WriteAttributeString("action", action)
_listDocument.WriteString(listItem)
_listDocument.WriteEndElement()
_listDocument.WriteWhitespace(Chr(13) & Chr(10) & "\t")
Return True
End Function
'Sets the XML header
Private Function CreateHeader() As Boolean
_listDocument.WriteStartDocument(False)
_listDocument.WriteWhitespace(Chr(13) & Chr(10))
Return True
End Function
'Add Time Date node
Private Function AddTimeDateNode() As Boolean
_listDocument.WriteStartElement("DateTimeAdded")
_listDocument.WriteString(DateTime.Now.ToString)
_listDocument.WriteEndElement()
_listDocument.WriteWhitespace(Chr(13) & Chr(10))
Return True
End Function
I am calling these functions after instantiating a dimension from ListXML (the name of my class) with the following code:
Dim xmloutput As New ListXML
xmloutput.CreateXML()
xmloutput.AddListMember(xmloutput.ReturnWhiteList, currentItem.SenderEmailAddress, xmloutput.ReturnAddAction)
As far as I can tell, it looks like you're trying to create more than one root element - one for DateTimeAdded and one for your list member.
If you call WriteStartElement in CreateXml() you'd end up with valid XML. You'll need to end that element before you end the document of course.
(And yes, the codeguru post looks like it's trying to do the same thing.)
Basically, this is a valid XML document:
<RootElement>
<FirstElement>
Content
</FirstElement>
<SecondElement>
Content
</SecondElement>
</RootElement>
But this is not:
<FirstElement>
Content
</FirstElement>
<SecondElement>
Content
</SecondElement>
You were trying to do the latter, hence the problem.