-
Feb 21st, 2020, 12:26 PM
#1
Thread Starter
Junior Member
[RESOLVED] Deletion of current image impossible as it is being used by another process
Hello,
I have a WindowsForm in VB.NET in which I display either a single image or a slide show of images.
When I delete an image which is in a slideshow it works fine, because I first select the next image and then delete the old image.
Whenever I have a solitary image however there is a problem, because I cannot delete the image as long it is displayed by the PictureBox.
Therefore I first load another image (from My.Resources) and then Refresh(), dispose of the oldImage, set the oldImage to Nothing and still get the error
System.IO.IOException: 'The process cannot access the file 'D:\OneDrive\Afbeeldingen\Schermafbeeldingen\2016-10-03 (0) - Copy_R.png' because it is being used by another process.'
Any other suggestions?
Code:
Private Sub DoDelete()
StrOrgFile = PictureBox1.ImageLocation
If StrOrgFile = "" Then Exit Sub
Dim oldImage As Image = PictureBox1.Image
Dim Response As Long = MsgBox("Are you sure you want to delete this image?", 32 + 4, "Delete?")
If Response = vbYes Then
BlnDelete = True
StrStartInFolder = GetTextFromFile(SettingsPath, 0, StrErr)
' In case a single picture is displayed, StrStartInFolder = "",
' else it is in a FolderPath and another image is loaded before the oldImage is deleted
If StrStartInFolder = "" Or StrStartInFolder = "0" Then
'Display different image in PictureBox1
PictureBox1.Image = My.Resources.startpage_l
Refresh()
'New image is displayed so I suppose I can delete the old one
oldImage.Dispose()
oldImage = Nothing
System.IO.File.Delete(StrOrgFile)
'Kill(StrOrgFile)
'SafeFileDelete(StrOrgFile)
'System.IO.IOException 'The process cannot access the file _
'D:\OneDrive\Afbeeldingen\Schermafbeeldingen\2016-10-03 (0) - Copy_R.png' _
'because it Is being used by another process.'
Exit Sub
Else
Grtz Bart
-
Feb 21st, 2020, 01:02 PM
#2
Re: Deletion of current image impossible as it is being used by another process
.Net locks the file so that it has the option of using the file as a backing store for the image if it needs to release memory. It can then reload the image from the disk as needed. Getting it to release the file is somewhat challenging.
Generally, if I needed to do that, I would only load the image from the file temporarily, then draw a copy of it to a bitmap, release the file, and use the bitmap in the picturebox. Something along the lines of this test example.
Code:
Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
Dim b1 As Image = Image.FromFile("c:\c\pumpgages.png")
Dim b2 As New Bitmap(b1.Width, b1.Height)
Using g As Graphics = Graphics.FromImage(b2)
g.DrawImage(b1, 0, 0)
End Using
PictureBox1.Image = b2
b1.Dispose()
End Sub
"Anyone can do any amount of work, provided it isn't the work he is supposed to be doing at that moment" Robert Benchley, 1930
-
Feb 21st, 2020, 02:42 PM
#3
Thread Starter
Junior Member
Re: Deletion of current image impossible as it is being used by another process
Originally Posted by passel
.Net locks the file so that it has the option of using the file as a backing store for the image if it needs to release memory. It can then reload the image from the disk as needed. Getting it to release the file is somewhat challenging.
Generally, if I needed to do that, I would only load the image from the file temporarily, then draw a copy of it to a bitmap, release the file, and use the bitmap in the picturebox. Something along the lines of this test example.
Code:
Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
Dim b1 As Image = Image.FromFile("c:\c\pumpgages.png")
Dim b2 As New Bitmap(b1.Width, b1.Height)
Using g As Graphics = Graphics.FromImage(b2)
g.DrawImage(b1, 0, 0)
End Using
PictureBox1.Image = b2
b1.Dispose()
End Sub
ThnX Passel for your suggestion.
I tried it but still have the same error when I delete... something else seems to hold up the original file.
This is the code:
Code:
Private Sub LoadImage(StrImgPath As String)
Dim b1 As Image = Image.FromFile(StrImgPath)
Dim b2 As New Bitmap(b1.Width, b1.Height)
b2.GetPropertyItem()
Using g As Graphics = Graphics.FromImage(b2)
g.DrawImage(b1, 0, 0)
End Using
PictureBox1.ImageLocation = ""
PictureBox1.Image = b2
Refresh()
b1.Dispose()
End Sub
Private Sub DoDelete()
StrOrgFile = PictureBox1.ImageLocation
If StrOrgFile = "" Then Exit Sub
LoadImage(StrOrgFile)
Dim Response As Long = MsgBox("Are you sure you want to delete this image?", 32 + 4, "Delete?")
If Response = vbYes Then
BlnDelete = True
StrStartInFolder = GetTextFromFile(SettingsPath, 0, StrErr)
' In case a single picture is displayed, StrStartInFolder = "",
' else it is in a FolderPath and another image is loaded before the oldImage is deleted
If StrStartInFolder = "" Or StrStartInFolder = "0" Then
'Display different image in PictureBox1
PictureBox1.Image = My.Resources.startpage_l
Refresh()
System.IO.File.Delete(StrOrgFile)
'SafeFileDelete(StrOrgFile)
Exit Sub
Else
Dim di As New DirectoryInfo(StrStartInFolder)
ArrFi = di.GetFiles("*")
If q = 0 Then
GetFileUp()
ElseIf q = -1 Then
GetFileDown()
Else
MsgBox("No q")
End If
End If
Else
BlnDelete = False
End If
BlnDelete = False
Exit Sub
noDelete:
MsgBox("You can not delete this image, try deleting it in a 'Browse folder' view.")
BlnDelete = False
End Sub
-
Feb 21st, 2020, 03:30 PM
#4
Re: Deletion of current image impossible as it is being used by another process
It's amazing how tenacious System.Drawing is in keeping its grubby grip on the image. After some experimentation, however, I found a more concise way to do it:
Code:
Dim img As Image = Image.FromFile(filename)
PictureBox1.Image = New Bitmap(img)
img.Dispose()
'and now this works!
IO.File.Delete(filename)
But it doesn't work if you comment out the Dispose statement (or Using ... End Using). I wonder why...?
BB
-
Feb 21st, 2020, 03:44 PM
#5
Thread Starter
Junior Member
Re: Deletion of current image impossible as it is being used by another process
Originally Posted by boops boops
It's amazing how tenacious System.Drawing is in keeping its grubby grip on the image. After some experimentation, however, I found a more concise way to do it:
Code:
Dim img As Image = Image.FromFile(filename)
PictureBox1.Image = New Bitmap(img)
img.Dispose()
'and now this works!
IO.File.Delete(filename)
But it doesn't work if you comment out the Dispose statement (or Using ... End Using). I wonder why...?
BB
Hi BB,
Regretably not yet:
-
Feb 21st, 2020, 04:33 PM
#6
Re: Deletion of current image impossible as it is being used by another process
Hi BartH,
I hadn't looked at your code because my reply was meant as a footnote to Passel's recommendation. I wasn't offering you a method you can simply paste into your present code.
Passel's code in the LoadImage sub is fine; my suggested method is just more compact. It leaves you free to delete the file in the DoDelete sub or wherever you like. But in DoDelete you are now unnecessarily reloading the image, not once but twice! Probably that is what is causing the problem. You could just delete the first 6 lines in DoDelete (post #6). Or, better, just keep a line of code to check that the file exists before you delete it.
BB
Last edited by boops boops; Feb 21st, 2020 at 04:51 PM.
Reason: EDIT: I edited this post heavily, please read the latest version!
-
Feb 21st, 2020, 05:14 PM
#7
Thread Starter
Junior Member
Re: Deletion of current image impossible as it is being used by another process
Originally Posted by boops boops
Hi BartH,
I hadn't looked at your code because my reply was meant as a footnote to Passel's recommendation. I wasn't offering you a method you can simply paste into your present code.
Passel's code in the LoadImage sub is fine; my suggested method is just more compact. It leaves you free to delete the file in the DoDelete sub or wherever you like. But in DoDelete you are now unnecessarily reloading the image, not once but twice! Probably that is what is causing the problem. You could just delete the first 6 lines in DoDelete (post #6). Or, better, just keep a line of code to check that the file exists before you delete it.
BB
Thanks for clarifying BB,
I did test various variations and will look again tomorrow and set up an isolated test case.
I am curious: how could I test if the file is used by another process, apart from trying to delete it and find that doesn't work...
Grtz Bart
-
Feb 21st, 2020, 05:17 PM
#8
Re: Deletion of current image impossible as it is being used by another process
I don't want to take this off at a tangent but I thought the usual error message when an image file was locked was something like "A generic error occurred in GDI+" - unless this has changed in later versions of VS. I wonder if the use of Onedrive here is significant?
This is my method of opening an image so the file is not locked:
Code:
' Open new image using FileStream so file is not locked
Using fs As FileStream = New FileStream(filename, FileMode.Open)
Dim bm As Bitmap = CType(Image.FromStream(fs), Bitmap)
PictureBox1.Image = bm
End Using
-
Feb 21st, 2020, 05:23 PM
#9
Re: Deletion of current image impossible as it is being used by another process
Code:
Private Sub DoDelete()
StrOrgFile = PictureBox1.ImageLocation
The above line should be meaningless with what I intended.
You must still be assigning the file to the picturebox somewhere.
There should be no ImageLocation as I intended that you assign a bitmap to the picturebox and draw in it. The picturebox should have no association with a file.
If you need to associate the file path of the image stored in the picturebox, you can either track that separately somehow, or assigin it to the .Tag property of the picturebox, where it is just associated data, not a reference to the image as far as the picturebox is concerned.
Last edited by passel; Feb 21st, 2020 at 05:28 PM.
"Anyone can do any amount of work, provided it isn't the work he is supposed to be doing at that moment" Robert Benchley, 1930
-
Feb 21st, 2020, 06:39 PM
#10
Re: Deletion of current image impossible as it is being used by another process
Originally Posted by paulg4ije
I don't want to take this off at a tangent but I thought the usual error message when an image file was locked was something like "A generic error occurred in GDI+" - unless this has changed in later versions of VS. I wonder if the use of Onedrive here is significant?
This is my method of opening an image so the file is not locked:
Code:
' Open new image using FileStream so file is not locked
Using fs As FileStream = New FileStream(filename, FileMode.Open)
Dim bm As Bitmap = CType(Image.FromStream(fs), Bitmap)
PictureBox1.Image = bm
End Using
The pattern is becoming clear. Our 'methods' are really similar to Passel's:
1. Get the image from a file (Passel's b1, your FileStream, my Image.FromFile).
2. Copy the image from step 1 (Passel's b2, your Image.FromStream, my New Bitmap(image)
3. Dispose of the first image (b1.Dispose, End Using, my img.Dispose). This breaks the link with the file.
Actually, I normally prefer Using..End Using blocks to Dispose statements, because they emphasize the temporary lifespan of the object.
As for the error messages in System.Drawing, it wouldn't surprise me if they have upgraded the messages in later Framework versions. From useless garbage to unhelpful gibberish.
BB
-
Feb 21st, 2020, 07:55 PM
#11
Re: Deletion of current image impossible as it is being used by another process
Why are you using ImageLocation in such a weird way? Just set the ImageLocation to the path of the file. That's it, that's all. Doing so does not lock the file so you can do whatever you want to that file.
-
Feb 21st, 2020, 08:54 PM
#12
Thread Starter
Junior Member
Re: Deletion of current image impossible as it is being used by another process
Hi jmcilhinney,
That was what I started off with, but it resulted in not being able to delete the displayed image (even after the PictureBox was fed a new image).
The current status is that it all works as desired with loading the images with
Code:
Using fs As FileStream = New FileStream(filename, FileMode.Open)
Dim bm As Bitmap = CType(Image.FromStream(fs), Bitmap)
PictureBox1.Image = bm
End Using
in ordert o be able to delete it with
Code:
Dim img As Image = Image.FromFile(StrOrgFile)
img.Dispose()
Refresh()
File.Delete(StrOrgFile)
-
Feb 21st, 2020, 09:17 PM
#13
Re: Deletion of current image impossible as it is being used by another process
Originally Posted by BartH_NL
That was what I started off with, but it resulted in not being able to delete the displayed image (even after the PictureBox was fed a new image).
You're trying to fix the wrong problem. If you explain what the actual issue was when using ImageLocation as it was intended then I'm confident that we can provide a solution for that.
-
Feb 21st, 2020, 10:43 PM
#14
Re: Deletion of current image impossible as it is being used by another process
I haven't worked with the ImageLocation property before, but it works as jmcilhinney says.
If you use the Load method, that will update ImageLocation but will also lock the file.
If you just set ImageLocation directly, it must load a copy of the image, and you can then do what you want with the file, even including deleting the file.
Deleting the file will not affect the copy that is showing in the picturebox, but of course you can't reload it later, if you've deleted the file.
So, a quick test that just sets the ImageLocation to two different file paths, or sets it to a null string seems to work fine for displaying or not displaying an image in the picturebox.
Code:
Public Class Form1
Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
PictureBox1.ImageLocation = "c:\c\pumpgages1.png"
End Sub
Private Sub Button2_Click(sender As System.Object, e As System.EventArgs) Handles Button2.Click
PictureBox1.ImageLocation = "c:\c\wrappedCase1.png"
End Sub
Private Sub Button3_Click(sender As System.Object, e As System.EventArgs) Handles Button3.Click
PictureBox1.ImageLocation = ""
' IO.File.Delete("c:\c\pumpgages1.png")
End Sub
End Class
Likewise, uncommenting the File.Delete line (and commented out setting ImageLocation to ""), tested the fact that I could hit button1, loading the image, and then button3,deleting the source file of the image, without affecting the displayed image (and proving the file was not locked). I also manually renamed the files while they were displayed, which is what I originally did to see that they were locked before, and I had no issues renaming the file after it was loaded by setting ImageLocation.
"Anyone can do any amount of work, provided it isn't the work he is supposed to be doing at that moment" Robert Benchley, 1930
-
Feb 22nd, 2020, 11:00 AM
#15
Thread Starter
Junior Member
Re: Deletion of current image impossible as it is being used by another process
ThanX a lot, Passel and jmcilhinney,
I was already going to test it all over today, but the last post of Passel describes the solution perfectly.
I set up this: (make sure you adjust the image locations - use copies as the images are going to be deleted)
Code:
Public Class Form3
Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
On Error GoTo ErrMsg
PictureBox1.Load("C:\T\IMG_1.JPG")
If MsgBox("Delete image?", 4) = vbYes Then
'PictureBox1.Image.Dispose() - not applicable here
PictureBox1.ImageLocation = ""
IO.File.Delete("C:\T\IMG_1.JPG")
End If
Exit Sub
ErrMsg:
MsgBox(Err.Description, 16, "Err: " & Err.Number)
End Sub
Private Sub Button2_Click(sender As System.Object, e As System.EventArgs) Handles Button2.Click
On Error GoTo ErrMsg
PictureBox1.Image = Image.FromFile("C:\T\IMG_2.JPG")
If MsgBox("Delete image?", 4) = vbYes Then
PictureBox1.Image.Dispose()
PictureBox1.Image = Nothing
IO.File.Delete("C:\T\IMG_2.JPG")
End If
Exit Sub
ErrMsg:
MsgBox(Err.Description, 16, "Err: " & Err.Number)
End Sub
Private Sub Button3_Click(sender As System.Object, e As System.EventArgs) Handles Button3.Click
On Error GoTo ErrMsg
PictureBox1.ImageLocation = "C:\T\IMG_3.JPG"
If MsgBox("Delete image?", 4) = vbYes Then
'PictureBox1.Image.Dispose() - not applicable here
PictureBox1.ImageLocation = ""
IO.File.Delete("C:\T\IMG_3.JPG")
End If
Exit Sub
ErrMsg:
MsgBox(Err.Description, 16, "Err: " & Err.Number)
End Sub
End Class
Conclusions:
PictureBox.Load loads the file from fullname but also locks the file, if file is missing - error, delete - error because locked
PictureBox.Image locks and accepts the file as Image object, if file is missing - error, delete - if Image.Dispose() is used no problem (else error because locked)
PictureBox.ImageLocation does not lock and takes file fullname as String, if file is missing - no error (displays missing file icon), delete no problem
Last edited by BartH_NL; Feb 22nd, 2020 at 06:35 PM.
-
Feb 26th, 2020, 03:45 PM
#16
Thread Starter
Junior Member
Re: Deletion of current image impossible as it is being used by another process
Additional comment:
I noticed that when I add the images to the PictureBox with CType(Image.FromStream(fs), Bitmap), in time the resources get all taken up, so the application runs into troubles when the 4GB treshold is reached.
Code:
Using fs As FileStream = New FileStream(OpenFileDialog1.FileName, FileMode.Open)
Dim bm As Bitmap = CType(Image.FromStream(fs), Bitmap)
PictureBox1.Tag = OpenFileDialog1.FileName
PictureBox1.Image = bm
BlnIsStart = False
End Using
I solved this by abandoning the consept of using a dummy image so the original image can be disposed of (when needed) and using ImageLocation instead.
Apparently the copied images keep taking up memory.
-
Feb 27th, 2020, 12:51 AM
#17
Re: Deletion of current image impossible as it is being used by another process
In order to not have to test to see if there is an image in the picturebox before I dispose of it, at startup I create a small empty bitmap and assign it to the picturebox.
Then each time I updated the picturebox from a memory stream, I disposed the existing image. I was displaying 100's of Gigabytes worth of images over the course of a single day, and I know the applications ran for weeks or months without issue.
I think if you added a line to the code you posted, it probably would have fixed the issue.
Code:
Using fs As FileStream = New FileStream(OpenFileDialog1.FileName, FileMode.Open)
Dim bm As Bitmap = CType(Image.FromStream(fs), Bitmap)
PictureBox1.Tag = OpenFileDialog1.FileName
PictureBox1.Image.Dispose() 'dispose of the old image before assigning a new image
PictureBox1.Image = bm
BlnIsStart = False
End Using
You didn't realize while the local reference to the bitmap (bm) is temporary, you copy that reference to the Picturebox1.Image, so the bitmap isn't temporary, it is reference by the picturebox. And then you clobber that reference the next time without cleaning up the resources, so resource leak...
p.s. I guess in an earlier case I didn't preset the picturebox with an empty bitmap, so just tested each time, but I think that is less optimal. This is the actual code that was in that program.
Code:
With PictureBox1
If .Image IsNot Nothing Then .Image.Dispose() 'If we have an image in the picturebox, dispose of it first
.Image = New Bitmap(mstream) 'Create a new image from the memorystream provided by the TCP receiver
End With
Last edited by passel; Feb 27th, 2020 at 01:03 AM.
"Anyone can do any amount of work, provided it isn't the work he is supposed to be doing at that moment" Robert Benchley, 1930
-
Feb 27th, 2020, 01:23 AM
#18
Re: Deletion of current image impossible as it is being used by another process
Originally Posted by passel
In order to not have to test to see if there is an image in the picturebox before I dispose of it, at startup I create a small empty bitmap and assign it to the picturebox.
Then each time I updated the picturebox from a memory stream, I disposed the existing image.
Or, if you're using a VB version that supports them, use a null-conditional operator, i.e.
vb.net Code:
PictureBox1.Image?.Dispose()
-
Feb 29th, 2020, 05:37 AM
#19
Thread Starter
Junior Member
Re: [RESOLVED] Deletion of current image impossible as it is being used by another pr
Hello Passel and jmcilhinney,
I took a little while before I had the chance to look into your comments, thanx for pointing this out to me.
@Passel:
I understand what you mean - I can smack my head as I have been using .Image.Dispose() before, just hadn't thought of it now I needed it 8-[
@jmcilhinney:
That's interesting - next level - using null-conditional operators makes much cleaner code!
Tags for this Thread
Posting Permissions
- You may not post new threads
- You may not post replies
- You may not post attachments
- You may not edit your posts
-
Forum Rules
|
Click Here to Expand Forum to Full Width
|