none
Can't overwrite a file. "File in use"... by MY program. How do I fix? RRS feed

  • Question

  • I'm trying to solve a very old and infrequent mystery bug that keeps popping up.

    I've written an "Icon Manager". It opens several dozen icons on screen after which you can then copy or overwrite with new ones.

    99.9% of the time, everything works fine. But on rare occasion, I get the following error:

    The process cannot access the file 'E:\Program Files\Test\Icon80.png'
    because it is being used by another process.


    That "other process" is MY program (confirmed.) "Icon80.png" is the file to be overwritten and varies. Even after a reboot, I still get this error when I try to overwrite the file.

    I suspect the file is somehow still seen as "open" by my program preventing changes to it, but I tried "FileClose()" and it didn't help. I don't know of another way to sever a link to an open file.

    My code to load the icon:

    For Each strFile As String In IO.Directory.GetFiles(strSrcPath, "Icon*.png")
       intCnt = 0
       Dim img = Image.FromFile(strFilename)
       ImageList2.Images.Add(intCnt.ToString, img)
       img.Dispose()
       intCnt += 1
    Next

    ...then later, ImageList2 is assigned to a ListView.


    My code to overwrite the icon:

    intCB += 1  ' Position of Check Box
    Try
       FileIO.FileSystem.CopyFile(strSrcPath & "Icon" & Format(intCB, "00") & ".png", strDestPath & strCategory & Format(intCB, "00") & ".png", True)
       'Also tried: My.Computer.FileSystem.CopyFile(...
    Catch ex As Exception
       strErrorMsg = ex.Message
    End Try

    Like I said, most of the time this works just fine, but every once in a while I get an error and I have no idea why.

    I'm able to reproduce the error at the moment, but unlikely to in the days to follow. Any help is appreciated. TIA.



    Saturday, September 23, 2017 7:24 PM

All replies

  • This has always worked for me:

        ''' <summary>
        ''' A method which will return an image from an image file.
        ''' </summary>
        ''' <param name="imgFilePath">The full file path of the image file.</param>
        ''' <returns></returns>
        ''' <remarks>Since this is passed through a stream, the reference to 
        ''' the original image file will be lost and the result is that the 
        ''' original image file will not be locked once this method exits.</remarks>
        Private Function _
            GetImageFromFileStream(ByVal imgFilePath As String) As Image
    
            Dim retVal As Image = Nothing
    
            If Not String.IsNullOrWhiteSpace(imgFilePath) Then
                Dim fi As New IO.FileInfo(imgFilePath)
    
                If fi.Exists Then
                    Using fs As New IO.FileStream(fi.FullName, IO.FileMode.Open, IO.FileAccess.Read)
                        retVal = Image.FromStream(fs)
                    End Using
                End If
            End If
    
            Return retVal
    
        End Function


    "A problem well stated is a problem half solved.” - Charles F. Kettering

    • Proposed as answer by tommytwotrain Monday, September 25, 2017 12:49 PM
    Saturday, September 23, 2017 8:14 PM

  • My code to load the icon:

    For Each strFile As String In IO.Directory.GetFiles(strSrcPath, "Icon*.png")
       intCnt = 0
       Dim img = Image.FromFile(strFilename)
       ImageList2.Images.Add(intCnt.ToString, img)
       img.Dispose()
       intCnt += 1
    Next


    Is that really the code you're using? If so, please clarify:

    What is strFilename and where/how is it defined?

    Where in the loop you posted is strFile used?

    - Wayne

    Saturday, September 23, 2017 8:56 PM
  • Is that really the code you're using? If so, please clarify:

    What is strFilename and where/how is it defined?

    Where in the loop you posted is strFile used?


    Thx for the reply.

    I edited out some extraneous code in between and may have omitted something important. If so, I apologize.

    "strFilename" is obtained from a File Dialog elsewhere in the program.  I never actually use "strFile" for anything. I just use it to create a loop for the exact number of times as there are files matching "Icon*.png",

    I hope this clarifies things some. Thx.


    Saturday, September 23, 2017 9:09 PM
  • This has always worked for me:

        Private Function _
            GetImageFromFileStream(ByVal imgFilePath As String) As Image
    


    I'll look into this. There is an issue though that I'm also reading other types of files besides images (like a description in a .txt file) within the same loop, but so far, only overwriting the image has been a problem (but that may be only b/c the program never gets any farther to know.)

    I'll try your method out and report back in the next day or two. Thx.

    Saturday, September 23, 2017 9:16 PM
  • This has always worked for me:

        Private Function _
            GetImageFromFileStream(ByVal imgFilePath As String) As Image
    


    I'll look into this. There is an issue though that I'm also reading other types of files besides images (like a description in a .txt file) within the same loop, but so far, only overwriting the image has been a problem (but that may be only b/c the program never gets any farther to know.)

    I'll try your method out and report back in the next day or two. Thx.

    Notwithstanding the type of file, the "issue" is with what you're using to "read" it.

    For example, a streamreader to open and parse through a text file.

    If it has a .Dispose method (ergo, it implements IDisposable), then it's not an option to dispose it; it's mandatory.

    Putting it it inside a Using block will do that for you...


    "A problem well stated is a problem half solved.” - Charles F. Kettering

    Saturday, September 23, 2017 9:22 PM
  • Hello,

    The following is not a solution (because I'm using a PictureBox, you would need to adapt to the imagelist which should be easy) but an idea in that I load the image same as you but then clone it, use the clone and then dispose of the image loaded from disk. 

    Module ImageLoader
        ''' <summary>
        ''' Load an image into a PictureBox which allows the original image to be
        ''' freed, not locked by the application.
        ''' </summary>
        ''' <param name="Path">Path and filename to load into the picturebox</param>
        ''' <param name="ImageControl">Picturebox control to display the image</param>
        ''' <remarks>
        ''' .NET locks an image which disallows you to modify or delete it.
        ''' </remarks>
        Public Sub LoadImageClone(ByVal Path As String, ByVal ImageControl As PictureBox)
            Dim imageClone As Bitmap ' the clone to be loaded to a PictureBox
            Dim imageOriginal As System.Drawing.Image = System.Drawing.Image.FromFile(Path)
    
            imageClone = New Bitmap(imageOriginal.Width, imageOriginal.Height) '  create clone, initially empty, same size
    
            Dim gr As Graphics = Graphics.FromImage(imageClone) ' get object representing(clone) 's currently empty drawing surface
            gr.SmoothingMode = Drawing2D.SmoothingMode.None
            gr.InterpolationMode = Drawing2D.InterpolationMode.NearestNeighbor
            gr.PixelOffsetMode = Drawing2D.PixelOffsetMode.HighSpeed
            gr.DrawImage(imageOriginal, 0, 0, imageOriginal.Width, imageOriginal.Height) 'copy original image onto this surface
            gr.Dispose()
            imageOriginal.Dispose()
    
            ImageControl.Image = imageClone '   assign the clone to picture box
        End Sub
    End Module
    


    Please remember to mark the replies as answers if they help and unmark them if they provide no help, this will help others who are looking for solutions to the same or similar problem. Contact via my Twitter (Karen Payne) or Facebook (Karen Payne) via my MSDN profile but will not answer coding question on either.
    VB Forums - moderator
    profile for Karen Payne on Stack Exchange, a network of free, community-driven Q&A sites

    • Proposed as answer by tommytwotrain Monday, September 25, 2017 12:49 PM
    Sunday, September 24, 2017 10:20 AM
    Moderator
  • The following is not a solution (because I'm using a PictureBox, you would need to adapt to the imagelist which should be easy)


    I ended up opting for a "quick & dirty" solution. :D

    Since my program was recognizing the last opened image as still open when I try to overwrite it, I simply made sure the "last" file was no longer my LAST file.

    At the end of my load routine, I simply Copy the last read image file to "C:\Users\Public\Junk.png" and then immediately delete it. Now the "last" file read was something else. :P

    I know it's terribly poor coding, but it works w/o changing a line of existing code (with the risk of inserting new bugs.) Can't do better than that.

    Sunday, September 24, 2017 8:51 PM
  • Well I thought Razerz would say it but don't forget to dispose of your temporary images too Karen although in this case it may not matter.

    :)

    Maybe:

     using imageClone As Bitmap ' the clone to be loaded to a PictureBox

           ImageControl.Image = imageClone.clone   

     end using

    Monday, September 25, 2017 12:53 PM
  • Well I thought Razerz would say it but don't forget to dispose of your temporary images too Karen although in this case it may not matter.

    :)

    Maybe:

     using imageClone As Bitmap ' the clone to be loaded to a PictureBox

           ImageControl.Image = imageClone.clone   

     end using

    There seems to be some misunderstanding about Image.Clone: It's a shallow clone only, not a deep clone.

    This is a good discussion about it in StackOverflow:

    https://stackoverflow.com/questions/12709360/whats-the-difference-between-bitmap-clone-and-new-bitmapbitmap

    It's worth reading all of the responses but in particular, take note to the comment by Hans (NoBugz) which is near the top.


    "A problem well stated is a problem half solved.” - Charles F. Kettering

    Monday, September 25, 2017 1:56 PM
  • Well I thought Razerz would say it but don't forget to dispose of your temporary images too Karen although in this case it may not matter.

    :)

    Maybe:

     using imageClone As Bitmap ' the clone to be loaded to a PictureBox

           ImageControl.Image = imageClone.clone   

     end using

    There seems to be some misunderstanding about Image.Clone: It's a shallow clone only, not a deep clone.

    This is a good discussion about it in StackOverflow:

    https://stackoverflow.com/questions/12709360/whats-the-difference-between-bitmap-clone-and-new-bitmapbitmap

    It's worth reading all of the responses but in particular, take note to the comment by Hans (NoBugz) which is near the top.


    "A problem well stated is a problem half solved.” - Charles F. Kettering


    Yes I guess.

    I am not sure what you are saying Frank?

    Where is the confusion?

    Monday, September 25, 2017 2:54 PM


  • Yes I guess.

    I am not sure what you are saying Frank?

    Where is the confusion?

    Using a clone is just a way of a quick copy - it may as well be original bitmap.

    If it's truly cloned, like a deep clone through serialization, it's another object entirely. A shallow clone still refers to the original reference in memory.


    "A problem well stated is a problem half solved.” - Charles F. Kettering

    Monday, September 25, 2017 2:59 PM


  • Yes I guess.

    I am not sure what you are saying Frank?

    Where is the confusion?

    Using a clone is just a way of a quick copy - it may as well be original bitmap.

    If it's truly cloned, like a deep clone through serialization, it's another object entirely. A shallow clone still refers to the original reference in memory.


    "A problem well stated is a problem half solved.” - Charles F. Kettering

    Ok.

    So? Is there something specific you are saying about the example I show?

    Monday, September 25, 2017 3:04 PM


  • Ok.

    So? Is there something specific you are saying about the example I show?

    Show it in a code block because as you have it, it wouldn't even compile so I'm sure I'm misunderstanding.

    "A problem well stated is a problem half solved.” - Charles F. Kettering

    Monday, September 25, 2017 3:13 PM


  • Ok.

    So? Is there something specific you are saying about the example I show?

    Show it in a code block because as you have it, it wouldn't even compile so I'm sure I'm misunderstanding.

    "A problem well stated is a problem half solved.” - Charles F. Kettering

    Well I am just being sure I understand now. Here is an untested code block just going from memory.

     Public Sub LoadImageClone(ByVal Path As String, ByVal ImageControl As PictureBox)
            Dim imageOriginal As System.Drawing.Image = System.Drawing.Image.FromFile(Path)
    
            Using imageClone As New Bitmap(imageOriginal.Width, imageOriginal.Height) ' the clone to be loaded to a PictureBox
                'Dim imageClone As Bitmap ' the clone to be loaded to a PictureBox
    
                'imageClone = New Bitmap(imageOriginal.Width, imageOriginal.Height) '  create clone, initially empty, same size
    
                Dim gr As Graphics = Graphics.FromImage(imageClone) ' get object representing(clone) 's currently empty drawing surface
                gr.SmoothingMode = Drawing2D.SmoothingMode.None
                gr.InterpolationMode = Drawing2D.InterpolationMode.NearestNeighbor
                gr.PixelOffsetMode = Drawing2D.PixelOffsetMode.HighSpeed
                gr.DrawImage(imageOriginal, 0, 0, imageOriginal.Width, imageOriginal.Height) 'copy original image onto this surface
                gr.Dispose()
    
                imageOriginal.Dispose()
    
                ImageControl.Image = CType(imageClone.Clone, Image)  '   assign the clone to picture box
            End Using
    
        End Sub


    From memory, if you dispose of a temp bitmap declared in the routine then you will lose the image upon exiting the routine and there is nothing in the picturebox.

    If you clone it you dont lose the image in the picturebox.

    Since Karen makes a copy of the original by drawing the original on a new bitmap and then imageOriginal.Dispose() the clone in the picturebox gets disposed next time around the loop.

    The file reference is lost when the original image is drawn on the new bitmap and then disposed.

    The clone of the temp has no ref to the file that has been disposed with the original. The next time throught the loop the clone is the original and gets disposed.

    Of course the temp image will be disposed next time around if it is not disposed in the routine Karen originaloly shows it. But our sub routine does not know that? I think its best to dispose the temp right away even if its just making a clone copy. The file is free either way because it was drawn on a new bitmap. Thats a deep copy I would say?

    Sometimes if you are doing large images, ie 1mb or more, at a high rate you can get out of memory probs if the garbage is dumped slower than the animation is generating temp images.

    But again the sub routine does not know that. So to be object oriented we should clean up the temp images where they are made.

    That's all I am sayin.

    :)

    PS: Maybe the dispose should come last after the picturebox image is replaced ie


                ImageControl
    .Image = CType(imageClone.Clone,  Image) 

                imageOriginal.Dispose()


    Monday, September 25, 2017 3:35 PM


  • Ok.

    So? Is there something specific you are saying about the example I show?

    Show it in a code block because as you have it, it wouldn't even compile so I'm sure I'm misunderstanding.

    "A problem well stated is a problem half solved.” - Charles F. Kettering

    Well I am just being sure I understand now. Here is an untested code block just going from memory.

     Public Sub LoadImageClone(ByVal Path As String, ByVal ImageControl As PictureBox)
            Dim imageOriginal As System.Drawing.Image = System.Drawing.Image.FromFile(Path)
    
            Using imageClone As New Bitmap(imageOriginal.Width, imageOriginal.Height) ' the clone to be loaded to a PictureBox
                'Dim imageClone As Bitmap ' the clone to be loaded to a PictureBox
    
                'imageClone = New Bitmap(imageOriginal.Width, imageOriginal.Height) '  create clone, initially empty, same size
    
                Dim gr As Graphics = Graphics.FromImage(imageClone) ' get object representing(clone) 's currently empty drawing surface
                gr.SmoothingMode = Drawing2D.SmoothingMode.None
                gr.InterpolationMode = Drawing2D.InterpolationMode.NearestNeighbor
                gr.PixelOffsetMode = Drawing2D.PixelOffsetMode.HighSpeed
                gr.DrawImage(imageOriginal, 0, 0, imageOriginal.Width, imageOriginal.Height) 'copy original image onto this surface
                gr.Dispose()
    
                imageOriginal.Dispose()
    
                ImageControl.Image = CType(imageClone.Clone, Image)  '   assign the clone to picture box
            End Using
    
        End Sub

    From memory, if you dispose of a temp bitmap declared in the routine then you will lose the image upon exiting the routine and there is nothing in the picturebox.

    If you clone it you dont lose the image in the picturebox.

    Since Karen makes a copy of the original by drawing the original on a new bitmap and then imageOriginal.Dispose() the clone in the picturebox gets disposed next time around the loop.

    The file reference is lost when the original image is drawn on the new bitmap and then disposed.

    The clone of the temp has no ref to the file that has been disposed with the original. The next time throught the loop the clone is the original and gets disposed.

    Of course the temp image will be disposed next time around if it is not disposed in the routine Karen originaloly shows it. But our sub routine does not know that? I think its best to dispose the temp right away even if its just making a clone copy. The file is free either way because it was drawn on a new bitmap. Thats a deep copy I would say?

    Sometimes if you are doing large images, ie 1mb or more, at a high rate you can get out of memory probs if the garbage is dumped slower than the animation is generating temp images.

    But again the sub routine does not know that. So to be object oriented we should clean up the temp images where they are made.

    That's all I am sayin.

    :)

    Sure, I can see how that would work, and so will this:

    Option Strict On
    Option Explicit On
    Option Infer Off
    
    Public Class Form1
        Private Sub _
            Form1_Load(sender As System.Object, _
                       e As System.EventArgs) _
                       Handles MyBase.Load
    
            Dim desktop As String = _
                Environment.GetFolderPath(Environment.SpecialFolder.Desktop)
    
            Dim filePath As String = _
                IO.Path.Combine(desktop, "IMGP0580.JPG")
    
    
            With PictureBox1
                .SizeMode = PictureBoxSizeMode.Zoom
                .Image = GetImageFromFileStream(filePath)
            End With
    
            IO.File.Delete(filePath)
    
        End Sub
    
        Private Function _
            GetImageFromFileStream(ByVal imgFilePath As String) As Image
    
            Dim retVal As Image = Nothing
    
            If Not String.IsNullOrWhiteSpace(imgFilePath) Then
                Dim fi As New IO.FileInfo(imgFilePath)
    
                If fi.Exists Then
                    Using fs As New IO.FileStream(fi.FullName, IO.FileMode.Open, IO.FileAccess.Read)
                        retVal = Image.FromStream(fs)
                    End Using
                End If
            End If
    
            Return retVal
    
        End Function
    End Class

    It works on the same basis that serialization creates a deep clone: It's just a stream of bytes.

    Nothing more or less.


    "A problem well stated is a problem half solved.” - Charles F. Kettering

    Monday, September 25, 2017 3:40 PM


  • Sure, I can see how that would work, and so will this:

    
    

    It works on the same basis that serialization creates a deep clone: It's just a stream of bytes.

    Nothing more or less.


    "A problem well stated is a problem half solved.” - Charles F. Kettering

    Yup. That's why I marked FileStream as an answer.

    I should have just kept quiet. It was more a joke about Razerz than anything else.

    :)

    Monday, September 25, 2017 3:55 PM
  • Hi,

    Been busy, here is sample code that shows a modified method that I showed prior, didn't have time to optimize yet the bottom line is without external usage of the images I remove them all and show them.

    Read comments to fully understand what I'm doing.

    Public Module ImageLoader
        Public Function ClonedImage(ByVal pPath As String) As Bitmap
            Dim imageClone As Bitmap
            Dim imageOriginal As Image = Image.FromFile(pPath)
    
            imageClone = New Bitmap(imageOriginal.Width, imageOriginal.Height)
    
            Dim gr As Graphics = Graphics.FromImage(imageClone)
            gr.SmoothingMode = Drawing2D.SmoothingMode.None
            gr.InterpolationMode = Drawing2D.InterpolationMode.NearestNeighbor
            gr.PixelOffsetMode = Drawing2D.PixelOffsetMode.HighSpeed
            gr.DrawImage(imageOriginal, 0, 0, imageOriginal.Width, imageOriginal.Height)
            gr.Dispose()
            imageOriginal.Dispose()
            Return imageClone
        End Function
    End Module
    

    Form (this code could be in it's own class with a little effort)

    Public Class Form1
        Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
            Dim fileList As New List(Of String)
            ' original images stored one level below Bin\Debug so we can re-run
            Dim imagePath As String = IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Images")
            ' equates to Bin\Debug in this case
            Dim targetPath As String = AppDomain.CurrentDomain.BaseDirectory
    
            ' get all images
            Dim files As String() = IO.Directory.GetFiles(imagePath)
    
            Dim originalFileName As String = ""
            Dim appFileName As String = ""
    
            ' 1. copy to Bin\Debug folder
            ' 2. Add to image list
            ' 3. add to list for removal
            For Each file As String In files
                originalFileName = IO.Path.Combine(targetPath, IO.Path.GetFileName(file))
                appFileName = IO.Path.Combine(targetPath, IO.Path.GetFileName(file))
    
                If Not IO.File.Exists(originalFileName) Then
                    IO.File.Copy(file, appFileName)
                End If
                ImageList1.Images.Add(ClonedImage(appFileName))
                fileList.Add(appFileName)
            Next
    
            ' remove copies
            fileList.ForEach(Sub(file) IO.File.Delete(file))
    
            ' use the images
            For Each image As Image In ImageList1.Images
                Dim pb As New PictureBox With {.Image = image, .Parent = FlowLayoutPanel1}
            Next
        End Sub
    End Class
    

    Pictures are small as I did not configure them for size


    Please remember to mark the replies as answers if they help and unmark them if they provide no help, this will help others who are looking for solutions to the same or similar problem. Contact via my Twitter (Karen Payne) or Facebook (Karen Payne) via my MSDN profile but will not answer coding question on either.
    VB Forums - moderator
    profile for Karen Payne on Stack Exchange, a network of free, community-driven Q&A sites

    Monday, September 25, 2017 5:02 PM
    Moderator