Log Writer not creating new line for each entry - vb.net

I get the feeling this is something really simple, but I've tried I don't know how many permutations of vbNewLine, Environment.NewLine, sMessage & vbNewLine (or Environment.Newline) I've tried, or how many pages on this site, or through Google I've looked at but nothing has worked.
I even tried getting help from a VB.Net discord channel I'm a part of and they suggested to do the same things that I've done and the procedure is still writing each new log entry at the end of the previous one in a continuous string. My writer is below. Am I missing something simple?
Edit: The code that worked is below in case anyone else comes along with the same issue. If you want to see the original code it's in the edit log.
Option Explicit On
Imports System.IO
Public Class WriteroLog
Public Shared Sub LogPrint(sMessage As String)
Dim AppPath As String = My.Application.Info.DirectoryPath
If File.Exists($"{AppPath}\Log.txt") = True Then
Try
Using objWriter As StreamWriter = File.AppendText($"{AppPath}\Log.Txt")
objWriter.WriteLine($"{Format(Now, "dd-MMM-yyyy HH:mm:ss")} – {sMessage}")
objWriter.Close()
End Using
Catch ex As Exception
MsgBox(ex)
Return
End Try
Else
Try
Using objWriter As StreamWriter = File.CreateText($"{AppPath}\Log.Txt")
objWriter.WriteLine($"{Format(Now, "dd-MMM-yyyy HH:mm:ss")} – {sMessage}")
objWriter.Close()
End Using
Catch ex As Exception
MsgBox(ex)
Return
End Try
End If
End Sub
End Class

The File.AppendText() method creates a new StreamWriter that is then used to append Text to the specified File.
Note, reading the Docs about this method, that you don't need to verify whether the File already exists: if it doesn't, the File is automatically created.
As a side note, when creating a Path, it's a good thing to use the Path.Combine method: it can prevent errors in the path definition and handles platform-specific formats.
Your code could be simplified as follows:
Public Shared Sub LogPrint(sMessage As String)
Dim filePath As String = Path.Combine(Application.StartupPath, "Log.Txt")
Try
Using writer As StreamWriter = File.AppendText(filePath)
writer.WriteLine($"{Date.Now.ToString("dd-MMM-yyyy HH:mm:ss")} – {sMessage}")
End Using
Catch ex As IOException
MsgBox(ex)
End Try
End Sub

The File.CreateText does not assign result to "objWrite", should be:
objWriter = File.CreateText($"{AppPath}\Log.Txt")
Not really sure if this is the root of your problem, but it is an issue.

In essences, your logic is re-opening or creating the stream "objWriter" for every call to this method. I would recommend you initialize "objWriter" to Nothing and only define if it is Nothing.
Set to Nothing as below.
Shared objWriter As IO.StreamWriter = Nothing
Then add check for Nothing in logic.

Related

Many instances of the same process writing to the same log file

I am kicking off a number of instances of the same process and the issue is that they all write to the same log file. I know it is not a good practice and was wondering what can I do to avoid possible issues. Here is the procedure I use to write to file:
Sub WriteToErrorLog(ByVal Msg As String)
Dim path As String
path = System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location)
Dim strFile As String = System.IO.Path.Combine(path, "Log_" & DateTime.Today.ToString("dd-MMM-yyyy") & ".txt")
Dim sw As StreamWriter
Dim fs As FileStream = Nothing
Try
If (Not File.Exists(strFile)) Then
fs = File.Create(strFile)
fs.Close()
End If
sw = File.AppendText(strFile)
sw.WriteLine(Msg & vbcrlf)
Catch ex As Exception
MsgBox("Error Creating Log File")
MsgBox(ex.Message & " - " & ex.StackTrace)
Finally
sw.Close()
End Try
End Sub
I would appreciate any suggestions/improvements. thanks!
As I have said in my comment, the scenario of multiple access to the same file resource should be handled carefully and probably the best solution is to use a well tested log library like Log4Net or NLog.
In any case you could improve your code in a couple of point
Sub WriteToErrorLog(ByVal Msg As String)
Dim path As String
path = System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location)
Dim strFile As String = System.IO.Path.Combine(path, "Log_" & DateTime.Today.ToString("dd-MMM-yyyy") & ".txt")
Dim retry as Integer = 3 ' this could be changed if you experience a lot of collisions.'
Dim sw As StreamWriter = Nothing
While retry > 0
Try
Using sw = File.AppendText(strFile)
sw.WriteLine(Msg & vbcrlf)
End Using
Exit While
Catch ex as Exception
retry -= 1
End Try
End While
' If retry has reached zero then we have exausted our tentatives and give up....'
if retry = 0 Then
MessageBox.Show("Error writing to Log File")
End if
End Sub
I have removed all the part that check if file exists and then create it. This is not necessary because as the documentation explains, File.Append is the same that calling StreamWriter(file, true) and this means that if the file doesn't exist it will be created.
Next, to try to handle possible collision with other process writing to the same file concurrently, I have added a retry loop that could get access to the log file just after another process finishes.
(this is really a poor-man solution but then it is better to use a well tested library)
It is important to enclose the opening and writing of the file inside a using statement that closes and disposes the Stream also in case of exceptions. This is mandatory to be sure to leave the file always closed for the other processes to work.

How to check if file (.wav) is opened and then play file

I am currently working on a console application to play a freshly created WAV RIFF file, and then delete it. Like I said, it is freshly created, so I need to make sure the file isn't being edited before I start playing it or it will be corrupted. After it plays, I delete it.
Currently, my code looks like this (using System.IO):
Sub Main()
Dim fileName As String
fileName = "C:\temp\Burst\Burst.wav"
While CheckFile(fileName)
End While
Try
My.Computer.Audio.Play(fileName, AudioPlayMode.WaitToComplete)
Catch ex As Exception
Console.WriteLine(ex.Message)
End Try
My.Computer.FileSystem.DeleteFile(fileName)
End Sub
Private Function CheckFile(ByVal filename As String) As Boolean
Try
System.IO.File.Open(filename, IO.FileMode.Open, IO.FileAccess.Read, IO.FileShare.None)
FileClose(1)
Return False
Catch ex As Exception
Return True
End Try
End Function
The function I am using to check if the file is opened was created by sealz. I found it here. Unfortunately, however, this function is causing an exception in that after it runs, the program cannot access the file because it is being used by another process. If I remove this function, the file can be opened, played and deleted.
The exception reads as follows:
An unhandled exception of type'System.IO.IOException' occurred in mscorlib.dll Additionalinformation: The process cannot access the file 'C:\temp\Burst\burst.wav' because it is being used by another process.
So the function that is supposed to help determine if the file is being used, is actually causing the file to be opened. It seems like it isn't closing. Is there anyway I can modify this current function to work properly for my application or are there any other ideas on how to tackle this. Thanks for your time.
-Josh
Here is your problem:
System.IO.File.Open(filename, IO.FileMode.Open, IO.FileAccess.Read, IO.FileShare.None)
FileClose(1)
Return False
A Using will help:
Using _fs as System.Io.FileStream = System.IO.File.Open(filename, IO.FileMode.Open, IO.FileAccess.Read, IO.FileShare.None)
End Using
Return False
File.Open Returns a Filestream, not an Integer needed for FileClose
As far as I get you are trying to check if file exists before playback using System.IO.File.Open however you may do it with File.Exists.
Method File.Exists from System.IO returns true if file exists on path and returns false the otherwise.
Also you are doing it wrong here,
While CheckFile(fileName)
End While
If file is found it will enter into an infinite loop without doing anything other than calling CheckFile repeatedly. If file is not found, it will get out of loop and attempt Audio.Play and FileSystem.DeleteFile and you end up getting a file not found exception.
Here is your code modified and working.
Imports System.IO
Module Module1
Sub Main()
Dim fileName As String
fileName = "C:\temp\Burst\Burst.wav"
While CheckFile(fileName)
Try
My.Computer.Audio.Play(fileName, AudioPlayMode.WaitToComplete)
'Delete statement here if you want file to be deleted after playback
Catch ex As Exception
Console.WriteLine(ex.Message)
End Try
End While
My.Computer.FileSystem.DeleteFile(fileName)
Console.ReadLine()
End Sub
Private Function CheckFile(ByVal filename As String) As Boolean
If (File.Exists(filename)) Then
Return True
Else
Return False
End If
End Function
End Module

VB.net Print to textbox from another thread

I have the following code and I am wanting to basically replicate the content of an ever changing text file and dump it into a text box display, I have the following code but it's not working at the moment.
If Not File.Exists(masterPath) Then
File.Create(masterPath).Dispose()
End If
Try
reader = New StreamReader(chatlog)
Catch
File.Create(chatlog)
bool = False
End Try
If bool Then
'Dim writer As New StreamWriter(masterPath)
Dim text As String
Do Until reader.EndOfStream
text = reader.ReadLine()
logMenu.AppendText(text & Environment.NewLine)
Loop
reader.Close()
File.Delete(chatlog)
File.Create(chatlog).Dispose()
End If
Thread.Sleep(1000)
Loop
Create a new method that uses Invoke as needed, like this:
Private Sub DisplayLogLine(ByVal text As String)
If logMenu.InvokeRequired Then
logMenu.Invoke(AddressOf DisplayLogLine, {text})
Return
End If
logMenu.AppendText(text & Environment.NewLine)
End Sub
Then call it with DisplayLogLine(reader.ReadLine()) in the loop.
Answer based on quick referencing Modifying the properties of controls from another thread in VB.net; it's been a while since I wrote multithreaded UI code in .NET.

How do I output to a text file without getting an IO exception?

I have this code.
Dim txtVern As String = String.Empty
Try
Using verSR As New StreamReader(appDataVersionLoc)
txtVern = verSR.ReadToEnd()
End Using
Catch ex As Exception
Dim verFile As System.IO.FileStream
verFile = System.IO.File.Create(appDataVersionLoc)
Dim wWFERes As DialogResult = MessageBox.Show("Version file missing/corrupt, created a new one.")
If My.Computer.FileSystem.FileExists(appDataVersionLoc) Then
My.Computer.FileSystem.WriteAllText(appDataVersionLoc, "0.0.0.0", True)
End If
End Try
But when it tries to execute My.Computer.FileSystem.WriteAllText(appDataVersionLoc, "0.0.0.0", True), I get this error:
The process cannot access the file 'C:\Users\Dubstaphone\AppData\Roaming\TheArena\version.txt' because it is being used by another process.
How to I avoid this? What I did was I made it create a text file called "version.txt" if it doesn't already exist. Now I'm trying to write "0.0.0.0" to it, but it's giving me that error. By the way, "appDataVersionLoc" equals GetFolderPath(SpecialFolder.ApplicationData) & "\TheArena\version.txt"
This variable works fine for everything else.
Thanks!
P.S.
I'm a complete noob.
System.IO.File.Create or My.Computer.FileSystem.WriteAllText may still hold a lock on the file. Use the System.IO.File WriteAllText and a Using statement.
If you use a Stream you should close/dispose of it. Better yet when dealing with files always use a Using statement.
Edit:
Example of creating a file
Using File.Create(path)
End Using
If File.Exists(appDataVersionLoc) Then
File.WriteAllText(appDataVersionLoc, "0.0.0.0")
End If
or
Dim appDataFile = File.Create(path)
appDataFile.Close

VB.NET Checking if a File is Open before proceeding with a Read/Write?

Is there a method to verify that a file is open? The only thing I can think of is the Try/Catch to see if i can catch the file-open exception but I figured that a method be available to return true/false if file is open.
Currently using System.IO and the following code under class named Wallet.
Private holdPath As String = "defaultLog.txt"
Private _file As New FileStream(holdPath, FileMode.OpenOrCreate, FileAccess.ReadWrite)
Private file As New StreamWriter(_file)
Public Function Check(ByVal CheckNumber As Integer, ByVal CheckAmount As Decimal) As Decimal
Try
file.WriteLine("testing")
file.Close()
Catch e As IOException
'Note sure if this is the proper way.
End Try
Return 0D
End Function
Any pointers will be appreciated! Thank you!!
Private Sub IsFileOpen(ByVal file As FileInfo)
Dim stream As FileStream = Nothing
Try
stream = file.Open(FileMode.Open, FileAccess.ReadWrite, FileShare.None)
stream.Close()
Catch ex As Exception
If TypeOf ex Is IOException AndAlso IsFileLocked(ex) Then
' do something here, either close the file if you have a handle, show a msgbox, retry or as a last resort terminate the process - which could cause corruption and lose data
End If
End Try
End Sub
Private Shared Function IsFileLocked(exception As Exception) As Boolean
Dim errorCode As Integer = Marshal.GetHRForException(exception) And ((1 << 16) - 1)
Return errorCode = 32 OrElse errorCode = 33
End Function
Call it like this:
Call IsFileOpen(new FileInfo(filePath))
There is really no point using a 'is file in use check' function since you will still need to have try catch to handle the case that the file fails to open. The file open can fail for many more reasons than it just being already open.
Also using a function to do a check is no guarantee of success. The 'is file in use check' might return false only for the file open to fail with a file already open error, because in time between the check and trying to open the file it was opened by someone else.
It looks like the two suggestions from this MSDN forum posting both involve trying to open the file.
The first one is similar to what you are doing now, and the second involves using a Windows API function (CreateFile) and checking for a invalid handle signifying the file is in use. In both cases they are relying on an error condition to determine if the file is open or not. In short, in my opinion the method you are using is correct since there is not a System.IO.File.IsOpen property.