StreamWriter and/or System.IO.File problems | | |
I'm having problems working with a streamwriter object. After closing the
streamwriter and setting it to Nothing, I try to delete the file it was
writing to, but I always get the following error message:
"The process cannot access the file "whatever" because it is being used
by another process."
I've even tried opening another file using the same streamwriter object
before deleting the original file, but it's no use. Something keeps a lock
on that original file, but I can't figure out what.
To reproduce this bug, create a new Windows Application project and create
the following class:
--- START ---
Imports System.IO
Public Class StreamWriterTest
#Region " Constructor/Destructor "
Public Sub New(ByVal filename As String)
' Remember the filename
_filename = filename
' Determine the path we should be storing the log file in
_folder =
System.Environment.ExpandEnvironmentVariables("%Wi nDir%") & "\Temp\"
' Open the log file
OpenLogFile()
End Sub
Protected Overrides Sub Finalize()
' Close the log file
CloseLogFile()
MyBase.Finalize()
End Sub
#End Region
#Region " Variables "
Private _filename As String = String.Empty ' log file name
Private _folder As String = String.Empty ' folder the log file is
in (always has a trailing \)
Private _logWriter As StreamWriter ' used to write to the
log file
#End Region
#Region " Properties "
Public ReadOnly Property Path() As String
Get
Return _folder & _filename
End Get
End Property
Public ReadOnly Property NewPath() As String
Get
Return _folder & _filename & "_"
End Get
End Property
#End Region
#Region " Methods "
Private Sub OpenLogFile()
Dim sqlLogFileSize As Long = 0
Try
' for debugging purposes
Console.WriteLine("*** Log File Opened : " & Me.Path)
' Create a new streamwriter object
_logWriter = New StreamWriter(Me.Path)
' Make the streamwriter automatically flush changes to the
file to the
' harddisk everytime we use the Write method
_logWriter.AutoFlush = True
' Write a header to the file
_logWriter.WriteLine("test")
Catch ex As Exception
Console.WriteLine("***ERROR : " & ex.Message)
End Try
End Sub
Private Sub CloseLogFile()
' DD Jul 23/04
'
' Closes the log file
Try
_logWriter.Close()
_logWriter = Nothing
GC.Collect()
Console.WriteLine("*** Log File Closed : " & Me.Path)
Try
If IO.File.Exists(Me.NewPath) Then
IO.File.Delete(Me.NewPath)
If Not IO.File.Exists(Me.NewPath) Then
'IO.File.Move(Me.Path, Me.NewPath)
IO.File.Copy(Me.Path, Me.NewPath)
IO.File.Delete(Me.Path)
Console.WriteLine("*** Log File Renamed : " &
Me.NewPath)
Else
Console.WriteLine("*** Log File NOT Renamed.
Another file with the same name already exists and could not be deleted.")
End If
Catch ex As Exception
Console.WriteLine("Error: Log File NOT Renamed: {0}",
ex.Message)
End Try
Catch ex As Exception
Console.WriteLine("Error closing log file: {0}", ex.Message)
End Try
End Sub
#End Region
End Class
--- END ---
Now place a button on Form1 and put the following code in it:
--- START ---
Randomize()
Dim x As StreamWriterTest
x = New StreamWriterTest("test1" & Format(Int(Rnd() * 99999999),
"00000000"))
x = Nothing
GC.Collect()
--- END ---
Looking at the Output Window, you'll see that the IO.File.Delete() raises
the exception. I am at my wits end trying to figure this out.
BTW, I don't think the bug occurs if you try to do the whole thing in, say,
the button's click event. It seems the fact that this code is the Finalize
method of a class has something to do with it.
- Don | | | | re: StreamWriter and/or System.IO.File problems
you are setting the varibale to nothing before the call to the
finalizer. additionally, you shouldn't do explicit GC unless
absolutely necessary.
this works fine...
Imports System.IO
Module Module1
Public Class StreamWriterTest
Public Sub New(ByVal filename As String)
' Remember the filename
_filename = filename
' Determine the path we should be storing the log file in
_folder = System.Environment.ExpandEnvironmentVariables("%Wi nDir%")
& "\Temp\"
' Open the log file
OpenLogFile()
End Sub
Protected Overrides Sub Finalize()
' Close the log file
CloseLogFile()
MyBase.Finalize()
End Sub
Private _filename As String = String.Empty ' log file name
Private _folder As String = String.Empty ' folder the log file is
in (always has a trailing \)
Private _logWriter As StreamWriter ' used to write to the log
file
Public ReadOnly Property Path() As String
Get
Return _folder & _filename
End Get
End Property
Public ReadOnly Property NewPath() As String
Get
Return _folder & _filename & "_"
End Get
End Property
Private Sub OpenLogFile()
Try
Debug.WriteLine("*** Log File Opened : " & Me.Path)
_logWriter = New StreamWriter(Me.Path)
_logWriter.AutoFlush = True
_logWriter.WriteLine("test")
Catch ex As Exception
Debug.WriteLine("***ERROR : " & ex.Message)
End Try
End Sub
Private Sub CloseLogFile()
Try
_logWriter.Close()
Debug.WriteLine("*** Log File Closed : " & Me.Path)
If IO.File.Exists(Me.NewPath) Then IO.File.Delete(Me.NewPath)
Catch ex As Exception
Debug.WriteLine("Error closing log file: {0}", ex.Message)
End Try
End Sub
End Class
Sub main()
Randomize()
Dim x As StreamWriterTest
x = New StreamWriterTest("test1" & Format(Int(Rnd() * 99999999),
"00000000"))
End Sub
End Module | | | | re: StreamWriter and/or System.IO.File problems
> you are setting the varibale to nothing before the call to the[color=blue]
> finalizer.[/color]
I don't see what the problem with this is. Setting it to Nothing should
make its Finalize method get called when garbage collection time comes
around. I've never had problems with this.
[color=blue]
> additionally, you shouldn't do explicit GC unless
> absolutely necessary.[/color]
That was just to highlight the bug. Taking that line out makes the bug
occur when you shut the program down. It behaves exactly the same way in
either case.
[color=blue]
> this works fine...[/color]
This only works because you removed these lines of code from
StreamWriterTest.CloseLogFile:
If Not IO.File.Exists(Me.NewPath) Then
IO.File.Copy(Me.Path, Me.NewPath)
IO.File.Delete(Me.Path)
Console.WriteLine("*** Log File Renamed : " & Me.NewPath)
Else
Console.WriteLine("*** Log File NOT Renamed. Another file with the
same name " & _
"already exists and could not be deleted.")
End If
If I comment those lines in my original class and leave the rest of the code
intact, it works, too. That's because the file created by the StreamWriter
is never touched after the StreamWriter is closed. If you'll notice in the
code you posted, the file being written to by the StreamWriter is not the
file that is deleted in CloseLogFile(). Me.NewPath gets deleted, not
Me.Path, which is the file we just created with the StreamWriter.
It seems that, after closing the StreamWriter, the file remains locked until
the application itself shuts down...and this happens only if the
StreamWriter is in another class and when it is closed in that class's
Finalize event.
- Don | | | | re: StreamWriter and/or System.IO.File problems
"Don" <unknown@oblivion.com> wrote in message
news:aIGpe.1602505$8l.1591155@pd7tw1no...
:
: I'm having problems working with a streamwriter object. After closing
: the streamwriter and setting it to Nothing, I try to delete the file
: it was writing to, but I always get the following error message:
:
: "The process cannot access the file "whatever" because it is being
: used by another process."
:
: I've even tried opening another file using the same streamwriter
: object before deleting the original file, but it's no use. Something
: keeps a lock on that original file, but I can't figure out what.
:
: To reproduce this bug, create a new Windows Application project and
: create : the following class:
<snip original code sample>
: Looking at the Output Window, you'll see that the IO.File.Delete()
: raises the exception. I am at my wits end trying to figure this out.
:
: BTW, I don't think the bug occurs if you try to do the whole thing in,
: say, the button's click event. It seems the fact that this code is
: the Finalize method of a class has something to do with it.
:
: - Don
I can't reproduce this. I copied your code into a console app with a few
changes. I removed your original comments and added new comments to flag
my changes (highlighted below). I only made two changes of any note:
* I added a new function 'ZapThisPuppyNow' to the class so I could
force a finalize event.
* And I added a test to the close log file function to ensure the
_logWriter object exists before I try to access it.
Apart from that, the core of your code is unchanged.
------------------------------------------
'[ADDED IMPORTS]
Imports Microsoft.VisualBasic
Imports System
Imports System.IO
'[ADDED SUB MAIN (REPLACES BUTTON EVENT CODE)]
public Class [class]
Public Shared Sub Main
Console.WriteLine("Enter Main" & vbCrLf)
Console.WriteLine("Creating Object" & vbCrLf)
Dim swt As New StreamWriterTest("swt.test")
Console.WriteLine("Finalizing Object" & vbCrLf)
swt.ZapThisPuppyNow
Console.WriteLine("Releasing Object" & vbCrLf)
swt = Nothing
Console.WriteLine("Exit Main" & vbCrLf)
End Sub
End Class
Public Class StreamWriterTest
'[ADD FUNCTION TO EXPLICITLY CALL FINALIZE]
Public Sub ZapThisPuppyNow()
Finalize
End Sub
'[ADD CONSOLE OUTPUT]
Public Sub New(ByVal filename As String)
Console.WriteLine("Constructing Class - " & filename & vbCrLf)
_filename = filename
_folder = "I:\tmp\"
Console.WriteLine("_filename = " & _filename & vbCrLf)
Console.WriteLine("_folder = " & _folder & vbCrLf)
OpenLogFile()
Console.WriteLine("End Constructing Class" & vbCrLf)
End Sub
'[ADD CONSOLE OUTPUT]
Protected Overrides Sub Finalize()
Console.WriteLine("Finalizing Class" & vbCrLf)
CloseLogFile()
Console.WriteLine("Finalizing MyBass Class" & vbCrLf)
MyBase.Finalize()
Console.WriteLine("End Finalizing Class" & vbCrLf)
End Sub
Private _filename As String = String.Empty
Private _folder As String = String.Empty
Private _logWriter As StreamWriter
Public ReadOnly Property Path() As String
Get
Return _folder & _filename
End Get
End Property
Public ReadOnly Property NewPath() As String
Get
Return _folder & _filename & "_"
End Get
End Property
'[ADD CONSOLE OUTPUT]
Private Sub OpenLogFile()
Console.WriteLine("Enter 'OpenLogFile'" & vbCrLf)
Dim sqlLogFileSize As Long = 0
Try
Console.WriteLine("*** Log File Opened : " & Me.Path)
_logWriter = New StreamWriter(Me.Path)
_logWriter.AutoFlush = True
_logWriter.WriteLine("test")
Catch ex As Exception
Console.WriteLine("***ERROR : " & ex.Message)
End Try
Console.WriteLine("Exit 'OpenLogFile'" & vbCrLf)
End Sub
'[ADD CONSOLE OUTPUT]
Private Sub CloseLogFile()
Console.WriteLine("Enter 'CloseLogFile'" & vbCrLf)
Try
'[ADDED TEST]
If _logWriter Is Nothing Then
Console.WriteLine("_logWriter Already Released")
Console.WriteLine("Exiting Sub" & vbCrLf)
Goto Exit_sub
End If
_logWriter.Close()
_logWriter = Nothing
GC.Collect()
Console.WriteLine("*** Log File Closed : " & Me.Path)
Try
'[CHANGE TO BLOCK IF/END IF]
If IO.File.Exists(Me.NewPath) Then
IO.File.Delete(Me.NewPath)
End If
If Not IO.File.Exists(Me.NewPath) Then
IO.File.Copy(Me.Path, Me.NewPath)
IO.File.Delete(Me.Path)
Console.WriteLine("*** Log File Renamed : " & Me.NewPath)
Else
Console.WriteLine("*** Log File NOT Renamed. Another " & _
"file with the same name already " & _
"exists and could not be deleted.")
End If
Catch ex As Exception
Console.WriteLine("Error: Log File NOT Renamed: {0}", _
ex.Message)
End Try
Catch ex As Exception
Console.WriteLine("Error closing log file: {0}", ex.Message)
End Try
'[ADDED LABEL IN ORDER TO CAPTURE EXIT EVENT]
Exit_Sub:
Console.WriteLine("Exit 'CloseLogFile'" & vbCrLf)
End Sub
End Class
------------------------------------------
I compiled as "vbc /out:test.exe test.vb"
When I ran 'Test.exe' from the command line as written above, I received
the following output:
===========================================
I:\tmp>test
Enter Main
Creating Object
Constructing Class - swt.test
_filename = swt.test
_folder = I:\tmp\
Enter 'OpenLogFile'
*** Log File Opened : I:\tmp\swt.test
Exit 'OpenLogFile'
End Constructing Class
Finalizing Object
Finalizing Class
Enter 'CloseLogFile'
*** Log File Closed : I:\tmp\swt.test
*** Log File Renamed : I:\tmp\swt.test_
Exit 'CloseLogFile'
Finalizing MyBass Class
End Finalizing Class
Releasing Object
Exit Main
Finalizing Class
Enter 'CloseLogFile'
_logWriter Already Released
Exiting Sub
Exit 'CloseLogFile'
Finalizing MyBass Class
End Finalizing Class
===========================================
Note that finalize is called twice - once manually by calling function
ZapThisPuppyNow and again when the 'Main' sub exits.
Here is the output when I run the code with the call to function
'ZapThisPuppyNow' commented out:
===========================================
Enter Main
Creating Object
Constructing Class - swt.test
_filename = swt.test
_folder = I:\tmp\
Enter 'OpenLogFile'
*** Log File Opened : I:\tmp\swt.test
Exit 'OpenLogFile'
End Constructing Class
Releasing Object
Exit Main
Finalizing Class
Enter 'CloseLogFile'
*** Log File Closed : I:\tmp\swt.test
*** Log File Renamed : I:\tmp\swt.test_
Exit 'CloseLogFile'
Finalizing MyBass Class
End Finalizing Class
===========================================
Ralf | | | | re: StreamWriter and/or System.IO.File problems
didn't catch your nuance in the original code, but this works without a
hitch
Private Sub CloseLogFile()
Try
_logWriter.Close()
Debug.WriteLine("*** Log File Closed : " & Me.Path)
If IO.File.Exists(Me.NewPath) Then IO.File.Delete(Me.NewPath)
If IO.File.Exists(Me.Path) Then IO.File.Delete(Me.Path)
Catch ex As Exception
Debug.WriteLine("Error closing log file: {0}", ex.Message)
End Try
End Sub
there was an issue specifically with the x=Nothing line when I tested
it the first time (a Null reference exception). I was NOT able to
replicate your specific error, but in hindsight suspect that you had it
open in a viewer to check its contents when the deletion was requested.
as for your original code, I have a great number of issues which I will
summarize as "Don't do it this way!"
1) Close the file as soon as you are done with it! If you need back
into it, re-open it.
2) Use Try...Catch...Finally.
3) Test to make sure that the file actualy opened, before closing it.
4) Combine _folder and _filename into one member variable and access it
directly
5) Don't use property calls within the class; you already own the
private member fields
6) If the output is for debug, use the Debug class and not the console | | | | re: StreamWriter and/or System.IO.File problems
"_AnonCoward" <abc@xyz.com> wrote in message
news:WsYpe.2956$ka.64677@twister.southeast.rr.com. ..[color=blue]
>
> "Don" <unknown@oblivion.com> wrote in message
> news:aIGpe.1602505$8l.1591155@pd7tw1no...
>
> I can't reproduce this.[/color]
My original code worked fine on your computer? That's odd....
[color=blue]
>I copied your code into a console app with a few
> changes. I removed your original comments and added new comments to flag
> my changes (highlighted below). I only made two changes of any note:
>
> * I added a new function 'ZapThisPuppyNow' to the class so I could
> force a finalize event.[/color]
Yes, I know that explicitly calling a method to close the file before
destroying the object will make everything work fine. Unfortunately it
defeats the purpose of what I wanted to do: have the opening and closing of
the file be automatic without having to make any special method calls to
either open or close the file. Unfortunately, it looks like there's not
other way around this bug. :(
- Don | | | | re: StreamWriter and/or System.IO.File problems
Thanks for the tips, but it looks like this is a lost cause. I guess I'll
just have to go to Plan B where I have to manually call methods to open and
close the log file. Too bad.
- Don |  | Similar Visual Basic .NET bytes | | | /bytes/about
We are a network of experts and professionals in IT and software development that help one another with answers to tough questions and share insights.
Get the best answers to your questions from over 226,471 network members.
|