Baffling error in Background worker code
Variables:
Source and Destination are both bitmaps that are the same size
hgt and wid are the height and width of the images minus 1
Here's the background worker Code:
Code:
Private Sub BGWProcess_DoWork(sender As Object, e As DoWorkEventArgs) Handles BGWProcess.DoWork`
For Y = 0 To hgt
For X = 0 To wid
If BGWProcess.CancellationPending Then Exit Sub
Destination.SetPixel(X, Y, Source.GetPixel(X, Y)) 'Here is where it fails
Next
BGWProcess.ReportProgress(Y)
Next
End Sub
Private Sub BGWProcess_ProgressChanged(sender As Object, e As ProgressChangedEventArgs) Handles BGWProcess.ProgressChanged
TxtProgress.Text = e.ProgressPercentage.ToString
PictureBox1.Image = Destination
End Sub
The exception is "System.InvalidOperationException: Object is currently in use elsewhere."
at the line "Destination.SetPixel(X, Y, Source.GetPixel(X, Y))
There is nothing else going on that might be accessing any of the bitmaps or the PictureBox. Any ideas would be greatly appreciated.
Re: Baffling error in Background worker code
Presumably this is a threading problem. Somewhere in all that a bitmap is probably accessed by multiple threads simultaneously.
Re: Baffling error in Background worker code
I agree with Niya. Take a look and, if necessary, show us everywhere else that those fields and the objects they refer to are used. Is one or the other displayed in a PictureBox? I'm not sure whether that would be enough to cause an issue but, if they are, try not doing that and see if the error remains.
Re: Baffling error in Background worker code
I don’t understand why you’re doing a pixel by pixel clone of an image in a background thread?
Re: Baffling error in Background worker code
I have some memory that working with bitmaps across threads can cause trouble, but I forget the details about it. Still, Paul has a good point. You appear to be copying a detail from a larger image, and it appears to be a rectangular detail. There is likely a better way to accomplish the ultimate goal.
Re: Baffling error in Background worker code
There is only the one Background worker. Yes the picture box gets accessed when the form loads, bitmaps are declared and their contents are loaded from the resources file. The destination bitmap is displayed and once a choice is made, the source bitmap is set to the choice. All that is done before the Background Worker starts. Here is the code:
Code:
Imports System.ComponentModel
Public Class Form1
Private X, Y, W, H, HowWide, HowTall As Integer
Private Source, Destination, AN, RU, ADO, DOJ As Bitmap
Private Sub BtnReset_Click(sender As Object, e As EventArgs) Handles BtnReset.Click
Destination = My.Resources.Images.DOJ
PictureBox1.Image = Destination
End Sub
Private Sub BtnStart_Click(sender As Object, e As EventArgs) Handles BtnStart.Click
Destination = DOJ
If RBRudy.Checked Then Source = RU
If RBAnn.Checked Then Source = AN
If RBADO.Checked Then Source = ADO
BGWProcess.RunWorkerAsync()
End Sub
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
DOJ = My.Resources.Images.Don
Destination = DOJ
AN = My.Resources.Images.Ann
RU = My.Resources.Images.Rudy
ADO = My.Resources.Images.Adolf
PictureBox1.Image = Destination
HowWide = PictureBox1.Width - 1
HowTall = PictureBox1.Height - 1
End Sub
Private Sub BGWProcess_DoWork(sender As Object, e As DoWorkEventArgs) Handles BGWProcess.DoWork
For Y = 0 To HowTall 'process one row at a time for the effect
For X = 0 To HowWide
Destination.SetPixel(X, Y, Source.GetPixel(X, Y)) ' Generates System.InvalidOperationException: 'Object is currently in use elsewhere.""
Next
BGWProcess.ReportProgress(Y)
Next
End Sub
Private Sub BGWProcess_ProgressChanged(sender As Object, e As ProgressChangedEventArgs) Handles BGWProcess.ProgressChanged
TxtProgress.Text = e.ProgressPercentage.ToString
PictureBox1.Image = Destination
End Sub
End Class
Re: Baffling error in Background worker code
Your problem is almost definitely because you're trying to edit a My.Resources image
Re: Baffling error in Background worker code
Quote:
Originally Posted by
.paul.
Your problem is almost definitely because you're trying to edit a My.Resources image
No that's not it. All accessing an image resource does is create a Bitmap object on the fly. You're free to use it as you would any other image.
His problem is that the same image is being accessed by multiple threads. Create a project with an image resource and a button that executes the following code:-
Code:
Dim img = My.Resources.TestImage
Task.Run(Sub()
img.SetPixel(10, 10, Color.Red)
End Sub)
Task.Run(Sub()
img.SetPixel(10, 20, Color.Red)
End Sub)
Rapidly click that button multiple times and eventually, it will bomb out with the exact error OP is experiencing.
Re: Baffling error in Background worker code
Quote:
Originally Posted by
.paul.
Your problem is almost definitely because you're trying to edit a My.Resources image
I'm editing a copy of a copy of the my.Resources image. I can do that successfully in a timer tick.
DOJ = My.Resources.Images.Don
Destination = DOJ
I'm changing the bitmap "Destination", 2levels away from the resource image.
The bitmap "Source" is a copy of the corresponding resources file, 1 level away
Destination.SetPixel(X, Y, Source.GetPixel(X, Y))
Nothing tries to alter the resource images
Re: Baffling error in Background worker code
Quote:
Originally Posted by
Zomalaja
I'm editing a copy of a copy of the my.Resources image. I can do that successfully in a timer tick.
DOJ = My.Resources.Images.Don
Destination = DOJ
I'm changing the bitmap "Destination", 2levels away from the resource image.
It certainly doesn't look that way. Images are reference type objects so there's no copies of Images being made there. A resource is data compiled into your assembly. When you get a property of My.Resources, that data is extracted and an object created. Your first line of code does that and assigns that object to the DOJ variable. The next line doesn't copy anything. It just assigns that same object to the Destination variable. If you access those two variables on different threads at the same time, you're accessing the same object on different threads.
Re: Baffling error in Background worker code
To make a real copy...
Code:
Destination = DirectCast(DOJ.Clone, Bitmap)
Re: Baffling error in Background worker code
PictureBox is a UI element, and I guess the bitmap accessed here is part of it?
If so, I would not touch the bitmap from anything but the UI thread.
Re: Baffling error in Background worker code
I don't know what "part of it" means, sorry
the destination bitmap gets its pixels changed in the Background worker - It's supposed to, but it fails
The PictureBox Image is set to the destination bitmap in the report progress code where i thought a UI object could be changed
Re: Baffling error in Background worker code
The bitmap is assigned to a PictureBox so of course the UI thread is going to access it in order to render it. While it's doing this, the BGW is also trying to write to it on another thread.
If you alter the test code I posted earlier:-
Code:
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim img = My.Resources.TestImage
Task.Run(Sub()
img.SetPixel(10, 10, Color.Red)
End Sub)
Task.Run(Sub()
Dim k = img.GetPixel(10, 20)
End Sub)
End Sub
The change here is that now one thread is reading and one is writing and it still bombs out with "Object is currently in use".
Re: Baffling error in Background worker code
So, you need a different copy of the bitmap, not just a different copy of a reference to the bitmap, which is what you have now. Still, it seems likely that there is a better way to do this. Copying one pixel at a time seems inefficient. Those are some notoriously slow methods with some means to speed them up. If the reason for using the background thread was because the process was too slow, then there is probably a better solution that can be done in the foreground. After all, you seem to just obtaining rectangular subsets of a single image. What will change about that? It looks like you will always get a certain region if one radiobutton is checked, a different region for a second radiobutton, and so on. It feels like this is a problem that can be solved by throwing memory at it, such as keeping the subsets as separate bitmaps ahead of time and just selecting one on check changed.
Re: Baffling error in Background worker code
> The PictureBox Image is set to the destination bitmap
Which I believe makes the image a UI element.
UI elements can only be changed on the UI thread.
A BGW thread can be any thread (but also randomly) the UI thread, so it may randomly work.
If fex you tried changing the text of a textbox from a BGW you would get a more direct error message from.NET, but this uses GDI, so anything better than OutOfMemory is a win.
Fix? Do the changes to the image on the UI thread, or do the changes in a BGW on copies of the images, and then copy the images to Picturebox.