Safe copy file in visual basic - vb.net

The following code, was ripped out from a C# code. I wonder what is the importance of For.... To ...statement within the code?
Public Sub CopyFile(sourceFile As String, backupFile As String, overwrite As Boolean)
If String.IsNullOrEmpty(sourceFile) Then
Throw New ArgumentNullException("sourceFile")
End If
If String.IsNullOrEmpty(backupFile) Then
Throw New ArgumentNullException("backupFile")
End If
' According to MSDN Certain file attributes, Hidden and ReadOnly, can be combined. Other attributes, such as Normal, must be used alone.
If File.Exists(backupFile) Then
File.SetAttributes(backupFile, FileAttributes.Normal)
End If
' ????????
For index As Integer = 0 To 9
Try
File.Copy(sourceFile, backupFile, overwrite)
Exit Try
Catch ex As FileNotFoundException
Throw
Catch ex As Exception
If Not overwrite Then
Throw
End If
If index = 9 Then
Throw
End If
Thread.Sleep(1000)
End Try
Next
End Sub

Related

Log Writer not creating new line for each entry

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.

How to Determine Exception Subtype

I'm wondering if there's a standard way to determine the sub-type of an exception. For example, for the File.Copy() method, IOException indicates that the destination file exists OR a general I/O error occurred. There are other such cases. In my exception handler, how can I determine which it is? I'm checking the end of ex.Message for the string already exists., which works, but seems awfully kludgy and unreliable.
While it is possible to check File.Exists() on the destination file, confirm overwrite with the user if it exists and then perform File.Copy() this is not atomic, which is to say, between checking and copying, it is possible for the conditions to change, for example if some other process created or copied a file into the destination location.
EDIT:
I had already changed the code based on comments here, but I just rolled it back and will post it here, just to show what I was doing:
Try
File.Copy(SrcFile, DstFile, OverWrite)
Catch ex As DirectoryNotFoundException
MsgBox(ex.Message)
Catch ex As FileNotFoundException
MsgBox("File not found: " & ex.FileName)
Catch ex As UnauthorizedAccessException
MsgBox("You do not have write access to the destination.")
Catch ex As IOException
' IOException represents an existing destination file OR a general IO error.
If SubStr(ex.Message, -15) = "already exists." Then
OverwriteCheck = MsgBox(
"Overwrite " & IO.Path.GetFileName(SrcFile) & " in destination directory?",
MsgBoxStyle.YesNo
)
If OverwriteCheck = DialogResult.Yes Then
Try
File.Copy(SrcFile, DstFile, OverWrite)
Catch iex As Exception
MsgBox("Unable to copy " & SrcFile & ":" & vbNewLine & iex.Message)
End Try
End If
Else
Throw ex
End If
Catch ex As ArgumentException
' The user left a blank line in the text box. Just skip it.
End Try
Here is an option using FileStreams to get more granular information about your exception
Sub Main()
Try
copyTo("C:\t\output3.txt", "C:\t\output1.txt", True)
Catch ex As Exception
Console.WriteLine(ex.Message)
End Try
Console.ReadLine()
End Sub
Private Sub copyTo(source As String, destination As String, Optional overwrite As Boolean = False)
' raises FileNotFoundException if source doesn't exist
Using fsSource As New FileStream(source, FileMode.Open, FileAccess.Read, FileShare.None)
If Not overwrite AndAlso File.Exists(destination) Then
' Raises exception when destination file exists and not overwrite
Throw New Exception(
String.Format("Destination file '{0}' exists and overwrite is false.", destination))
Else
Using fsDestination As New FileStream(destination, FileMode.OpenOrCreate, FileAccess.Write, FileShare.None)
fsSource.CopyTo(fsDestination)
End Using
End If
End Using
End Sub
This is a rudimentary example, but you can see how you can differentiate different exception cases, while having atomicity between checking file existence and copying.
I believe you looking for this pattern:
Try
IO.File.Copy("source", "Dest", True)
Catch exUnAuth As System.UnauthorizedAccessException
Catch exArg As System.ArgumentException
Catch exNotFound As IO.FileNotFoundException
Catch exGeneral As System.Exception
End Try
Place the list of specific exceptions first in the sequence. The last exception tested for should be the least derived.
You should read through the documentation: How to use structured exception handling in Visual Basic .NET or in Visual Basic 2005. Yes this is an old reference, but that is an indication how long this has been part of the language.

VB.net TableAdapter.Update not updating database

I'm adding a new row to an Access 2010 database, and attempting to update the new row using TableAdapter.Update(). The code does not throw an exception, and it does not make a successful update to the database either. I've checked the original DB for changes, the one in the project folder, and the copy that is made in bin and there are no changes to any of them. Here is my code and any feedback would be greatly appreciated.
Private Sub insertRow(job As String, part As String, castDate As Date, inspectDate As Date, NCIR As Integer)
Dim row As As_Cast_DBDataSet.As_CastRow
Dim adapter As New As_Cast_DBDataSetTableAdapters.As_CastTableAdapter
row = Me.DBDataSet.As_Cast.NewAs_CastRow()
adapter = Me.As_CastTableAdapter
row.Job_Number = job
row.Part_Number = part
row.Casting_Date = castDate
row.Inspection_Date = inspectDate
row.NCIR = NCIR
Try
Me.DBDataSet.As_Cast.Rows.Add(row)
Catch ex As Exception
MessageBox.Show(ex.Message)
End Try
Try
adapter.Update(Me.DBDataSet.As_Cast)
Catch ex As Exception
MessageBox.Show(ex.Message)
End Try
End Sub

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

Building A True Error Handler

I am trying to build an error handler for my desktop application. The code Is in the class ZipCM.ErrorManager listed below.
What I am finding is that the outputted file is not giving me the correct info for the StackTrace.
Here is how I am trying to use it:
Try
'... Some stuff here!
Catch ex As Exception
Dim objErr As New ZipCM.ErrorManager
objErr.Except = ex
objErr.Stack = New System.Diagnostics.StackTrace(True)
objErr.Location = "Form: SelectSite (btn_SelectSite_Click)"
objErr.ParseError()
objErr = Nothing
End Try
Here is the class:
Imports System.IO
Namespace ZipCM
Public Class ErrorManager
Public Except As Exception
Public Location As String
Public Stack As System.Diagnostics.StackTrace
Public Sub ParseError()
Dim objFile As New StreamWriter(Common.BasePath & "error_" & FormatDateTime(DateTime.Today, DateFormat.ShortDate).ToString().Replace("\", "").Replace("/", "") & ".log", True)
With objFile
.WriteLine("-------------------------------------------------")
.WriteLine("-------------------------------------------------")
.WriteLine("An Error Occured At: " & DateTime.Now)
.WriteLine("-------------------------------------------------")
.WriteLine("LOCATION:")
.WriteLine(Location)
.WriteLine("-------------------------------------------------")
.WriteLine("FILENAME:")
.WriteLine(Stack.GetFrame(0).GetFileName())
.WriteLine("-------------------------------------------------")
.WriteLine("LINE NUMBER:")
.WriteLine(Stack.GetFrame(0).GetFileLineNumber())
.WriteLine("-------------------------------------------------")
.WriteLine("SOURCE:")
.WriteLine(Except.Source)
.WriteLine("-------------------------------------------------")
.WriteLine("MESSAGE:")
.WriteLine(Except.Message)
.WriteLine("-------------------------------------------------")
.WriteLine("DATA:")
.WriteLine(Except.Data.ToString())
End With
objFile.Close()
objFile = Nothing
End Sub
End Class
End Namespace
What is happenning is the .GetFileLineNumber() is getting the line number from objErr.Stack = New System.Diagnostics.StackTrace(True) inside my Try..Catch block. In fact, it's the exact line number that is on.
Any thoughts of what is going on here, and how I can catch the real line number the error is occuring on?
Edit: Changed the code to account for the Exception.StackTrace being a string rather than a real StackTrace
You're creating a new StackTrace, so then it will be for the line you're declaring it on, if you want the line number of the original exception, use the stack trace in Exception.StackTrace.
I think you're being a little confused, I can't see why you create the new StackTrace at all?
Edit: Added more bits to the answer here since easier to see the syntax than in a comment
Currently you have the line
objErr.Stack = New System.Diagnostics.StackTrace(True)
Which means that you're creating a whole new stacktrace, starting when you're creating it.
Instead change that line to:
objErr.Stack = New System.Diagnostics.StackTrace(ex, True)
Which will have the stacktrace from when the error actually happened.
Edit: Added complete sample:
Private Sub a1()
Try
a2()
Catch ex As Exception
Dim st As New StackTrace(True)
Debug.WriteLine(String.Format("ST after exception, will give line number for where st was created. Line No: {0}", st.GetFrame(0).GetFileLineNumber()))
st = New StackTrace(ex, True)
Debug.WriteLine(String.Format("ST after exception using exception info, will give line number for where exception was created. Line No: {0}", st.GetFrame(0).GetFileLineNumber()))
End Try
End Sub
Private Sub a2()
Dim st As New StackTrace(True)
Debug.WriteLine(String.Format("ST before exception, will give line number for where st was created. Line No: {0}", st.GetFrame(0).GetFileLineNumber()))
Dim b As Integer = 0
Dim a As Integer = 1 / b
End Sub
SOLVED: You should change the wrong instruction, of the original code:
.WriteLine(Stack.GetFrame(0).GetFileLineNumber())
with this new one:
.WriteLine(Stack.GetFrame(Stack.FrameCount - 1).GetFileLineNumber)
and you will see, it will return the exact Line_Number of code
where the run time error occurs!!
You do not need to include a Stack property for your ErrorManager, because you have access to the stack trace through the exception.
From experience, I would create a Shared Sub Write(ex As Exception, location As String) method on the ErrorManager, and call in your Catch statement as:
ZipCM.ErrorManager.Write(ex, "Form: SelectSite (btn_SelectSite_Click)")
This change results in cleaner code, reduces the need to write lots of code in each Catch statement, and allows you to change the implementation of Write without having to revisit/rework/refactor each Catch statement.
For instance, you can change the Write method to also call Debug.WriteLine(ex) so you can see which exceptions you are handling during debugging without having to open the file. Moreover, you may want to include WriteNotify method that displays a message box of the exception then calls the Write method to log the exception.