locked
Help. Can't overwrite "File in use". RRS feed

  • Question

  • I have a program that checks for a Whitelist on start and (should) allow you to update it.

    In main Form_Load (this part works fine):

    'in a separate module:
    Public strOutput As StreamWriter
    Public strInput As StreamReader
    ...

    'In main Form_Load...
    strInput = FileIO.FileSystem.OpenTextFileReader(strCfgPath & "Whitelist.ini") intWL = -1 ' 0 is the header. Do intWL += 1 strWhitelist(intWL) = strInput.ReadLine ' Load array with WL entries. Loop Until strWhitelist(intWL) = "*** EOF ***" intWL -= 1 ' Total # of items in WL.


    But later (only thing before this line is a Comment):

    strOutput = FileIO.FileSystem.OpenTextFileWriter(strCfgPath & "Whitelist.ini", False)    ' Don't Append. Overwrite.

    ...gives me a "file in use" error:

    File In Use

    Must I close the file opened for input before I can replace it? And if so, how? There is no "FileIO.FileSystem.Close" function.

    TIA.


    Wednesday, September 2, 2020 5:24 PM

All replies

  • Hi

    Maybe this code will help.  Yout code 'seems' like it may be VB6 influenced?

    By the way,  yes,  it is a .Close that was required.  The best way to avoid leaving files open is to use a Using..End Using construct which does the .Close automatically.

    Dim strCfgPath As String = My.Computer.FileSystem.SpecialDirectories.Desktop
    Dim path As String = IO.Path.Combine(strCfgPath, "Whitelist.ini")
    Dim strWhitelist As List(Of String) = IO.File.ReadAllLines(path).ToList
    Dim TotalLines As Integer = strWhitelist.Count
    
    ' add some more items
    strWhitelist.Add("123")
    strWhitelist.Add("234")
    strWhitelist.Add("345")
    
    ' write back
    IO.File.WriteAllLines(path, strWhitelist)


    Regards Les, Livingston, Scotland



    • Edited by leshay Wednesday, September 2, 2020 5:49 PM
    Wednesday, September 2, 2020 5:47 PM
  • Thx for the reply, Les.

    I'm having some trouble implementing your code. I had removed my "Using" statements as they seemed to be unneeded. Clearly they were. I put them back and added ".Close" methods.

    But trouble resulted implementing your assignments. Your strCfgPath changes the source to my "OneDrive" cloud (Win10 backs up the Desktop to OD) whereas I use "C:\ProgramData" for my cfg & ini files:

    Public strCfgPath As String = Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData)

    .

    After many other changes then putting back my "ProgramData" reference, the assignment is now suddenly being ignored and I have no idea why. :(

    To make matters worse, the original problem never went away even after putting back the "Using" statements and adding ".close" methods.

    PS: My coding goes back to VB3, so my programming thinking still reflects that.


    Wednesday, September 2, 2020 7:38 PM
  • Hi

    The ProgramData folder is not a good choice, there are several other better folders to store user files. One I use most of the time is along with where ever the application executable is - thus always keeping the data with the application. See it in the example code below (note how easy it is to set up the path)

    Here is some more code.  This version (hope you can get it to work), uses 'generic' read and write code - the write takes the path and (in this case) a list(of string), and the read is a function that takes a path and returns a list(of string)

    There is a MessageBox if the file can't be found. In this example you should not get it as the data is written to the file first, but if the ReadData Function was called before any data had been written then the MessageBox would pop up.

    Option Strict On
    Option Explicit On
    Public Class Form1
    	' set path to use (including filename & ext)
    	Dim path As String = IO.Path.Combine(Application.StartupPath, "Data", "Special Data", "Whitelist.ini")
    	Dim lst As New List(Of String)
    
    	' just to get some random data
    	Dim rand As New Random
    	Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    
    		' if path doesn't exist then create it
    		If Not IO.Directory.Exists(IO.Directory.GetParent(path).FullName) Then
    			IO.Directory.CreateDirectory(IO.Directory.GetParent(path).FullName)
    		End If
    
    		' add some random data for testing
    		For i As Integer = 1 To 10
    			lst.Add(rand.Next(-99, 99).ToString)
    		Next
    		WriteData(path, lst)
    
    		Stop ' hover over the lst and see the random
    		' data being sent (overwritten) to the path.
    
    
    		' test read all data (lst will be reset to
    		' hold only data from the file)
    		lst = ReadData(path)
    		Stop ' hover again and see the list as read
    		' from the path
    	End Sub
    	Sub WriteData(_path As String, _list As List(Of String))
    		' True to append data, False to overwrite
    		Using sw As New IO.StreamWriter(_path, False)
    			For Each s As String In _list
    				sw.WriteLine(s)
    			Next
    		End Using
    	End Sub
    	Function ReadData(_path As String) As List(Of String)
    		Dim _list As New List(Of String)
    		Try
    			Using sr As New IO.StreamReader(_path)
    				Dim line As String = Nothing
    				Do
    					_list.Add(sr.ReadLine)
    				Loop Until sr.EndOfStream
    			End Using
    		Catch ex As IO.FileNotFoundException
    			MessageBox.Show("File not found ....")
    		End Try
    		Return _list
    	End Function
    End Class


    Regards Les, Livingston, Scotland

    Wednesday, September 2, 2020 8:41 PM
  • Thanks Les, but changing my path and altering the code to accommodate the new path creates more problems than it solves.

    I use "ProgramData" b/c it's hidden and I don't want the user accidentally editing/deleting the files. And using the protected "Program Files" folders often causes trouble when trying to write to it.

    I'll check out your revised code to see if it resolves my issues, but since it all depends on using a different target, probably not. :(


    Wednesday, September 2, 2020 9:19 PM
  • Hi

    OK, no problem.


    Regards Les, Livingston, Scotland

    Wednesday, September 2, 2020 9:22 PM
  • Hi

    OK, no problem.

    Attempting to preload my "Whitelist" array from a module gave me an error. And attempting to use "combine" to define the path to my ".ini" file from the same module is what caused the "ignored" error linked to above because that part of the path was "forgotten" once the code moved out of the module.

    So I'm pretty much back where I started except that I restored the "Using" blocks so that I could include ".close" methods, but that appears to make no difference. :(

    I inadvertently deleted the "Restricted" folder (by hand) that was producing the error and I'm not sure how to create a new one for testing purposes.

    Thursday, September 3, 2020 12:59 PM
  • Hi

    First of all, if you want to discuss/question an exception, saying 'gives me an error' just won't cut it - you need to provide the exception details.

    *

    Are you using Option Strict ON either at the top of your code or in the environment settings?

    Why are you using a Module?

    Your explanation of using a system folder for storing your application data is really asking for trouble.

    Defining a path is one thing, making it available throughout would need it to be declared Public and possibly fully qualified in use. Don't write off the Path.Combine method just because you are having trouble using it.

    As for the 'restricted' folder - just recreate it in the same way you did when you first created it.


    Regards Les, Livingston, Scotland

    Thursday, September 3, 2020 1:20 PM
  • Hi

    First of all, if you want to discuss/question an exception, saying 'gives me an error' just won't cut it -

    The error hasn't changed. Code is nearly back to its original state so the error is exactly the same as posted in my original question (same error. Same location.)

    I don't use "Option Strict On" as it typically creates a host of new problems that become a huge distraction spending hours changing code just to meet formatting standards that only make the program harder to edit/debug.

    The Path & Whitelist variables were declared "Public" in a separate module (see original post.) I do not know why the assignment(s) set there don't exist outside of the module.

    The "Restricted" folder that I was using for my test was created by another program (I do not know which), so I'm not sure why it was deemed "Restricted" to recreate it.

    Thx.


    Thursday, September 3, 2020 2:23 PM
  • Hi

    I have tried to run your code as posted.  There is NO issue with Public variables in the Module - they are fine.

    Option Strict On - we differ in opinion - I believe you are mistaken - using it HELPS when debugging/editing code, as well as helps prevent run time exceptions.

    Deleting something in a system folder when you don't know why it is there, or what created it is a really bad idea.  If you are lucky nothing will be apparent until sometime in the future when a program you know runs well suddenly doesnt - if you are unlucky, a system reinstal may be needed.

    I can tell from your comments that you don't much care for advice which causes you more work, or goes against your own ideas - OK, so good luck with that.

    I have tried to recreate your code as posted (Module as well) and added a few lines at bottom to add some extra data to the file.

    NOTE: see the NOTE NOTE NOTE NOTE lines - which may well be what you need to make your code work.

    Try thiis out as a stand alone example to see if it works for you.

    The Module - just as you posted

    Main Form code

    Option Strict On
    Option Explicit On
    Public Class Form1
    	Dim strCfgPath As String = My.Computer.FileSystem.SpecialDirectories.Desktop
    	Dim strWhitelist(111) As String
    	Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    		strInput = FileIO.FileSystem.OpenTextFileReader(strCfgPath & "\Whitelist.ini")
    		Dim intWL As Integer = -1   ' 0 is the header.
    		Do
    			intWL += 1
    			strWhitelist(intWL) = strInput.ReadLine    ' Load array with WL entries.
    		Loop Until strWhitelist(intWL) = "*** EOF ***"
    
    		' NOTE NOTE NOTE NOTE
    		strInput.Close
    		' NOTE NOTE NOTE NOTE
    
    		Dim cont As Integer = intWL
    
    		' add a couple more items
    		Do
    			strWhitelist(cont) = cont.ToString
    			cont += 1
    		Loop Until cont > 11
    		strWhitelist(cont) = "*** EOF ***"
    
    		strOutput = FileIO.FileSystem.OpenTextFileWriter(strCfgPath & "\Whitelist.ini", False)
    		Dim wr As Integer = 0
    		Do
    			strOutput.WriteLine(strWhitelist(wr))
    			wr += 1
    		Loop Until strWhitelist(wr) = "*** EOF ***"
    		strOutput.WriteLine(strWhitelist(wr))
    		strOutput.Close()
    
    		' at start I had the following in the file
    		' Header, 1, 2, 3, 4, 5, 6, *** EOF ***
    
    		' after adding/saving I have
    		' Header, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, *** EOF ***
    	End Sub
    End Class


    Regards Les, Livingston, Scotland

    Thursday, September 3, 2020 3:48 PM
  • Reboot your computer before trying this code. You'll have orphaned File Handles with evidently restrictive access/share permissions in system memory due to your failure to properly call the .Close() on your previous attempts.

    Imports System
    Imports System.Collections
    Imports System.Collections.Generic
    Imports System.IO
    
    Module ConsoleApp
    
        Public WhiteList As List(Of String)
    
        Sub readLines(ByVal filepath As String)
            WhiteList.Clear()
    
            Dim fs As FileStream
            fs = New FileStream(filepath, FileMode.OpenOrCreate, FileAccess.Read, FileShare.Read)
    
            Dim sr As StreamReader
            sr = New StreamReader(fs)
    
            Do Until sr.EndOfStream
                WhiteList.Add(sr.ReadLine)
            Loop
    
            sr.Close()
            sr.Dispose()
    
            fs.Close()
            fs.Dispose()
        End Sub
    
        Sub writeLines(ByVal filepath As String)
            If (File.Exists(filepath)) Then
                File.Delete(filepath)
            End If
    
            Dim fs As FileStream
            fs = New FileStream(filepath, FileMode.OpenOrCreate, FileAccess.Write, FileShare.Read)
    
            Dim sw As StreamWriter
            sw = New StreamWriter(fs)
    
            For Each line As String In WhiteList
                sw.WriteLine(line)
            Next
    
            sw.Close()
            sw.Dispose()
    
            fs.Close()
            fs.Dispose()
        End Sub
    
        Sub Main()
            '
            ' Getting the saveto/readfrom directory - this will evaluate to the directory the program is in 
            ' at runtime
            '
            Dim directory As String
            ' You could change the directory to a static string value if you like - just leave off 
            ' the trailing \
            directory = Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location)
            Dim file As String
            ' Obviously change the filename to anything at all as long as it's just the filename
            ' Just leave off the leading \
            file = "whitelist.txt"
            ' Getting the system directory separator - this will be injected automatically between directory 
            ' and filename
            Dim pathsep As String
            pathsep = Path.DirectorySeparatorChar.ToString()
    
            Dim FullWhiteListPath As String
            FullWhiteListPath = directory + pathsep + file
    
            WhiteList = New List(Of String)
    
            ' Add some whitelist entries
            WhiteList.Add("Some whitelist entry #0")
            WhiteList.Add("Some whitelist entry #1")
            WhiteList.Add("Some whitelist entry #2")
            WhiteList.Add("Some whitelist entry #3")
            WhiteList.Add("Some whitelist entry #4")
            WhiteList.Add("Some whitelist entry #5")
    
            ' Write the whitelist to disk
            ' You can breakpoint on this codeline to examine the contents of WhiteList before the save
            writeLines(FullWhiteListPath)
    
            '
            ' At this point open your white list file in a text editor
            '
    
            ' Now remove entry #4
            WhiteList.RemoveAt(4)
            ' Now remove entry #2
            WhiteList.RemoveAt(2)
    
            ' Now realize that was a mistake and reload the full whitelist from disk
            ' You can breakpoint on this codeline to examine the contents of WhiteList after removals 
            ' but before restoration from disk
            readLines(FullWhiteListPath)
    
            ' Now remove entry #5
            ' You can breakpoint on this codeline to examine the contents of WhiteList before the removal
            WhiteList.RemoveAt(5)
            ' Now remove entry #3
            WhiteList.RemoveAt(3)
    
            ' Now write the changes back to disk
            ' You can breakpoint on this codeline to examine the contents of WhiteList before the save
            writeLines(FullWhiteListPath)
    
            '
            ' You may have to re-open in the external text editor - some will auto-refresh 
            ' but most won't
            '
    
            Console.WriteLine("Press any key (except Enter) to exit")
            Console.ReadKey()
        End Sub
    
    End Module



    Thursday, September 3, 2020 4:01 PM
  • Why are you using a Module?


    Your explanation of using a system folder for storing your application data is really asking for trouble.

    Quick follow-up.

    I use a module for global variables so they are accessibly across multiple forms (and sometimes reuse the same module across similar apps I've written.)

    Storing data in a folder Windows has designated exclusively for storing ProgramData can hardly be considered "asking for trouble".

    Trying to reuse the program's own protected "Program Files" folder is far more likely to create problems.

    Thursday, September 3, 2020 9:53 PM
  • I have tried to run your code as posted.  There is NO issue with Public variables in the Module - they are fine.


    Deleting something in a system folder when you don't know why it is there, or what created it is a really bad idea.

    I can tell from your comments that you don't much care for advice which causes you more work, or goes against your own ideas - OK, so good luck with that.

    "Your Mileage May Vary" when it comes to Public Variables in a module. I don't know WHY the assignments are not retained outside of the module, I just know that's what happened (as the "ignored" screenshot shows.)

    On "Deleting something in a System Folder"... I didn't. The folder was in a folder of my own creation. My program allows users to define a "junk" folder with a time limit before items in it are permanently deleted.

    On my acceptance of advice, typically I'm looking for a fix or explanation of why an error is occurring. Re-writing entire swaths of code to achieve the same thing a completely different way is a lot of necessary redundant work that often is not supported by the rest of my code (all it takes is switching a few variables to break an entire program.)

    I'll check out your code to see if it can help me. Thx.


    Thursday, September 3, 2020 10:59 PM
  • Hi Mugsy_in_Houston,

    If your question has been answered then please click the "Mark as Answer" Link at the bottom of the correct post(s), so that it will help other members to find the solution quickly if they face a similar issue.

    Besides, if you need further assistance, please let us know.

    Best Regards,

    Xingyu Zhao

    MSDN Community Support
    Please remember to click "Mark as Answer" the responses that resolved your issue, and to click "Unmark as Answer" if not. This can be beneficial to other community members reading this thread. If you have any compliments or complaints to MSDN Support, feel free to contact MSDNFSF@microsoft.com.


    Friday, September 4, 2020 6:28 AM
  • I have tried to run your code as posted.  There is NO issue with Public variables in the Module - they are fine.


    Deleting something in a system folder when you don't know why it is there, or what created it is a really bad idea.

    I can tell from your comments that you don't much care for advice which causes you more work, or goes against your own ideas - OK, so good luck with that.

    "Your Mileage May Vary" when it comes to Public Variables in a module. I don't know WHY the assignments are not retained outside of the module, I just know that's what happened (as the "ignored" screenshot shows.)

    On "Deleting something in a System Folder"... I didn't. The folder was in a folder of my own creation. My program allows users to define a "junk" folder with a time limit before items in it are permanently deleted.

    On my acceptance of advice, typically I'm looking for a fix or explanation of why an error is occurring. Re-writing entire swaths of code to achieve the same thing a completely different way is a lot of necessary redundant work that often is not supported by the rest of my code (all it takes is switching a few variables to break an entire program.)

    I'll check out your code to see if it can help me. Thx.


    I had originally posted the explanation alone, then decided to replace it with my code snippet under the auspices of "The Code Is the Documentation."

    You were using kludgy and pre-built crap that comes with DotNET.  It all derives from System.IO.Stream and/or other System.IO.Stream derivatives.  You weren't closing system handles or disposing of resources consumed by your IO objects, so whatever FileShare and FileAccess locks each handle had assigned to it by your kludgy "path of least resistance" style coding weren't being released.

    My code snippet goes down to 1 derivation away from System.IO.Stream itself, showing how to use a FileStream to work with data on disk and how to use a StreamReader to work with data passed through the FileStream.  It's literally the same code you posted, except with the ability to actually assign your own FileAccess and FileShare permissions and of course with proper handle release and system resource cleanup when finished with each IO operation.

    So since your snarky attitude annoys me a lot, I'm going to tell you what I think of this whole situation: 

    Your first problem is that you bought a $40 DIY dresser at Wal-Mart.  Your second problem is that you expected sturdy construction.  Your third problem is that you came to a website and forum for professional carpenters who specialize in making high-quality hardwood furniture and, when you didn't understand their answers given in kind tones and word choices explaining what MDF is and why you shouldn't waste your time with Wal-Mart furniture, you pitched a titty fitty equivalent to throwing poo at them.  Luckily it's just a webforum so nobody caught the 3 strains of hep you picked up at that tattoo shop when you asked for a 13 but they drew a 31.


    Before you can learn anything new you have to learn that there's stuff you don't know.

    Friday, September 4, 2020 2:52 PM
  • So since your snarky attitude annoys me a lot, I'm going to tell you what I think of this whole situation:

    I wasn't aware "my attitude" had anything to do with the accuracy of your answer. I admit my programming style is "dated", but it's hardly "amateur" (as I noted, I having been using VB since VB3 for Win95.)

    What's frustrating is when someone comes looking for a simple answer, and instead has people trying to rewrite their entire project without consideration for how it would affect the remaining code (almost guaranteeing their solution will be useless.)

    If this program were for a client, I'd probably tighten up the code, but since the only person who will ever use it is myself (and maybe a friend or two), I couldn't care less that my code seems "Wal*Mart" to you.

    That being said, I will look into using a different method for the file IO than what I'm using as it seems to be the source of the problem.

    Friday, September 4, 2020 8:19 PM
  • What's frustrating is when someone comes looking for a simple answer, and instead has people trying to rewrite their entire project without consideration for how it would affect the remaining code (almost guaranteeing their solution will be useless.)

    Hi

    Emboldened text above - not so, just an exageration on your part to describe efforts to assist as such.  The idea is to show a way to achieve what is understood to be asked for, not a  re-writing at all, just to show possible method(s).  Examples are one way to show what moght be an acceptable alternative.  Not everyone here uses complete examples to illustrate their answer, but I usually do where possible. As I showed in an earlier post where I gave an example of a Function and a Sub that could be used throughout a Project. These would have been able to provide your read/write needs with little effort. (as is always the case, any example can fail in unforseen circumstances as little of no exception handling would be included)

    Anyway, I opted out of the thread because I was wasting my time with you.


    Regards Les, Livingston, Scotland

    Friday, September 4, 2020 11:18 PM