I need some help with my program. I want to rewrite the data on my .txt file but an error occurs:
The process cannot access the file 'C:\Users\AARVIII\Documents\Visual Studio 2010\Projects\PROJECT\WindowsApplication3\bin\Debug\ORDERS\aa.txt' because it is being used by another process.
Here is the code:
Sub WRITEDATA()
Dim write As New System.IO.StreamWriter("ORDERS\" & TBFNAME.Text + "" + TBLNAME.Text & ".txt", False)
write.WriteLine(TBFNAME.Text)
write.WriteLine(TBLNAME.Text)
write.WriteLine(TBEADD.Text)
write.WriteLine(TBEADD2.Text)
write.WriteLine(TBADDRESS.Text)
write.WriteLine(TBCONTACT.Text)
write.close()
End Sub
I used a StreamReader to get the data which had already been put in that text file. Please help me figure out how to kill that process so that I can rewrite my data.
It is very possible that your app (on another thread?) is the culprit. First, to make sure you release the resource, make sure to wrap your code in a using block:
Using Dim write As New System.IO.StreamWriter("ORDERS\" & TBFNAME.Text + "" + TBLNAME.Text & ".txt", False)
write.WriteLine(TBFNAME.Text)
write.WriteLine(TBLNAME.Text)
write.WriteLine(TBEADD.Text)
write.WriteLine(TBEADD2.Text)
write.WriteLine(TBADDRESS.Text)
write.WriteLine(TBCONTACT.Text)
End Using
Additionally, you may want to see this thread: .NET Asynchronous stream read/write
Related
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
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.
I have a requirement of programmatically compiling a solution. I cannot directly give the path of MSBuild because it differs between 2013 and earlier versions.
I am sharing my code below -
Using exeprocess As New System.Diagnostics.Process
exeprocess.StartInfo.FileName = "cmd"
exeprocess.StartInfo.CreateNoWindow = True
exeprocess.StartInfo.UseShellExecute = False
exeprocess.StartInfo.RedirectStandardInput = True
exeprocess.StartInfo.RedirectStandardOutput = True
exeprocess.Start()
Dim sw As StreamWriter = exeprocess.StandardInput
Dim sr As StreamReader = exeprocess.StandardOutput
sw.WriteLine("PUSHD C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\")
sw.WriteLine("call vcvarsall.bat")
sw.WriteLine("#MSBuild /t:Rebuild" & " /flp1:logfile=" & """" & logFilePath & """;errorsonly" & " " & """" & solutionPath & """")
sr.ReadLine()
While Not sr.EndOfStream()
sr.ReadLine()
End While
End Using
My requirement is to wait until the compilation is over.
The issue is that it hangs at the line "While Not sr.EndOfStream()".
I am unable to understand the reason for the issue. Not sure if this is the right way of ensuring that the compilation is over.
Any help is highly appreciated.
I can't tell what your specific problem is, but a few recommendations:
Don't bother writing to input stream, just generate a simple .bat text file and launch it.
You can use Microsoft.Build.Utilities.ToolLocationHelper to get any of the paths.
You can use Microsoft.Build.Execution.BuildManager to build programmatically.
I am creating a VB app which will "move" xls reports from a directory to a ReportSafe app. I am also working in an existing VB app which does just that, so I am using it for reference.
It isn't as simple as moving files from one directory to another, because ReportSafe requires an lpr command to tell it (ReportSafe) which file to pick up.
Here is what I have so far:
Imports System.IO
Module Module1
Sub Main()
''Declarations
Dim Files As ArrayList = New ArrayList()
Dim FileName As String
''Write All Files in *directory* to ReportSafe
Files.Clear()
Files.AddRange(Directory.GetFiles(*directory*))
For Each FileName In Files
Dim RPname As String
Dim RealName As String
RPname = FileName.ToString
RealName = "/"
RealName = RealName & RPname.Remove(0, 34)
Dim a As New Process
a.StartInfo.FileName = "C:\Windows\system32\lpr.exe"
a.StartInfo.Arguments = "-S*ServerName* -Plp -J" & Chr(34) & RealName & Chr(34) & " " & Chr(34) & RPname & Chr(34)
a.StartInfo.UseShellExecute = False
Next
End Sub
End Module
The whole lpr command/arguments are throwing me for a loop. I'm not sure if my question is specific to ReportSafe, and if that's the case, I may be out of luck here. I have pulled this code from the already existing app which moves reports to ReportSafe, and adjusted for my own use, but no luck so far.
FYI, I had to turn on LPR Monitor services to obtain to the lpr.exe
Questions:
What are the proper arguments to pass through to this lpr command?
Is there a problem with the logic that is causing the issue?
I continued to tinker and look at my reference code and discovered some flaws in logic:
For one, the report name I was passing did not include the complete file path.
Another thing is that I never started the process with a.Start(). Rookie mistakes for sure... haha
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!