PixelFormat.Format32bppArgb causing exception
After the 55th time my code is run, I get the following exception:
Quote:
An unhandled exception of type 'System.ArgumentException' occurred in System.Drawing.dll
Additional information: Parameter is not valid.
The line that triggers this is:
vbnet Code:
capture = New System.Drawing.Bitmap(area.Width, area.Height, System.Drawing.Imaging.PixelFormat.Format32bppArgb)
I basically have a timer running in the background on one of my forms that takes a screenshot of the screen. After the 55th tick of the timer, this line of code causes an exception. This happens no matter the speed of the timer, or anything else I do in the program.
Relevant code:
vbnet Code:
Private Sub tmrRefresh_Tick(sender As Object, e As EventArgs) Handles tmrRefresh.Tick
tmrRefresh.Interval = getRedrawSpeed()
Me.BackgroundImage = resizeImage(takePicture(), Me.Width, Me.Height)
End Sub
Private Function takePicture() As System.Drawing.Bitmap
Dim area As Rectangle
Dim capture As System.Drawing.Bitmap
Dim graph As Graphics
If getRelativeSetting(relativeSettings.relativeHeight) Then
area.Height = ((getGrabHeight() / 100) * Screen.PrimaryScreen.Bounds.Height)
Else
area.Height = getGrabHeight()
End If
If getRelativeSetting(relativeSettings.relativeWidth) Then
area.Width = ((getGrabWidth() / 100) * Screen.PrimaryScreen.Bounds.Width)
Else
area.Width = getGrabWidth()
End If
If getRelativeSetting(relativeSettings.relativeX) Then
area.X = ((getXPosition() / 100) * Screen.PrimaryScreen.Bounds.Width)
Else
area.X = getXPosition()
End If
If getRelativeSetting(relativeSettings.relativeY) Then
area.Y = ((getYPosition() / 100) * Screen.PrimaryScreen.Bounds.Height)
Else
area.Y = getYPosition()
End If
capture = New System.Drawing.Bitmap(area.Width, area.Height, System.Drawing.Imaging.PixelFormat.Format32bppArgb)
graph = Graphics.FromImage(capture)
graph.CopyFromScreen(area.X, area.Y, 0, 0, area.Size, CopyPixelOperation.SourceCopy)
Return capture
End Function
Private Function resizeImage(ByVal theImage As System.Drawing.Bitmap, newXPixels As Integer, newYPixels As Integer) As System.Drawing.Bitmap
Dim returnImage As New System.Drawing.Bitmap(newXPixels, newYPixels)
Dim objResizer As Graphics = Graphics.FromImage(returnImage)
objResizer.DrawImage(theImage, 0, 0, returnImage.Width + 1, returnImage.Height + 1)
Return returnImage
End Function
Re: PixelFormat.Format32bppArgb causing exception
You say on the 55th tick of the Timer. Does this mean it works 54 times and fails on the 55th ?
Re: PixelFormat.Format32bppArgb causing exception
Yep, I had a counter variable incrementing (That was also updating some random textbox on another form) right before the code line. It would get to 55, then the exception would come up asking if I want to break or continue. Have retested multiple times.
Edit:
Actually, increasing the dimension of the screen capture makes it happen a lot sooner.
Re: PixelFormat.Format32bppArgb causing exception
In that case, I'd say that the exception message is pretty misleading. If it worked the first 54 times, then the arguments are not wrong. Had they been wrong on the 55th time, they would have been just as wrong for the first 54 times, as well. The exception to this would be if there was something that was different about the arguments on the 55th iteration, but you'd probably have some idea what that is.
My guess is that you are running into either a memory or resource limitation that is manifesting in a weird way. They can do that, because when either memory or resources get low it can result in failures that don't appear to be what they are. It's just a guess, though, based on the fact that creating pictures is something that can gobble up both resources and memory at a fantastic rate. If the resources aren't released, you may eventually run out, which would manifest by something crashing after N iterations. That's pretty much exactly what you are seeing, though the number of iterations is pretty low.
Re: PixelFormat.Format32bppArgb causing exception
Try changing the Tick event handler to look like this:-
vbnet Code:
'
Private Sub tmrRefresh_Tick(ByVal sender As Object, ByVal e As EventArgs) Handles tmrRefresh.Tick
Dim b = Me.BackgroundImage
tmrRefresh.Interval = getRedrawSpeed()
Me.BackgroundImage = resizeImage(takePicture(), Me.Width, Me.Height)
b.Dispose()
End Sub
I have no idea how many times a second you're capturing the screen but if its fast as I suspect, you may be taxing the GDI for its limited resources. The above code has been altered to dispose of the background image when its no longer needed thus freeing GDI handles. I'm just speculating of course. Try it and tell me if you're still getting the exception.
Re: PixelFormat.Format32bppArgb causing exception
I tried this code:
vbnet Code:
Private Sub tmrRefresh_Tick(sender As Object, e As EventArgs) Handles tmrRefresh.Tick
tmrRefresh.Interval = getRedrawSpeed()
resizeImage(takePicture(), Me.Width, Me.Height)
End Sub
I took out the form's background update completely, but the same error still occurs
I got a null pointer exception when it tried to run b.Dispose() and no difference when putting Me.BackgroundImage.Dispose() after the Me.BackgroundImage=resizeImage().
By default it's running at 250ms, but I've increased it up to 500ms, and 1000ms, but still get the same result on the 55th tick.
Is there any way to force clear the GDI memory/resources on each tick?
I think you may be right, as running this makes it happen much faster
vbnet Code:
Private Sub tmrRefresh_Tick(sender As Object, e As EventArgs) Handles tmrRefresh.Tick
tmrRefresh.Interval = getRedrawSpeed()
resizeImage(takePicture(), Me.Width, Me.Height)
resizeImage(takePicture(), Me.Width, Me.Height)
resizeImage(takePicture(), Me.Width, Me.Height)
resizeImage(takePicture(), Me.Width, Me.Height)
resizeImage(takePicture(), Me.Width, Me.Height)
resizeImage(takePicture(), Me.Width, Me.Height)
resizeImage(takePicture(), Me.Width, Me.Height)
resizeImage(takePicture(), Me.Width, Me.Height)
resizeImage(takePicture(), Me.Width, Me.Height)
resizeImage(takePicture(), Me.Width, Me.Height)
End Sub
Re: PixelFormat.Format32bppArgb causing exception
What do you do with the bitmaps that result? It seems to me that those would normally be HUGE, so if they are sitting in memory, they may be choking off your RAM in short order.
You might also try explicitly disposing the Graphics object you are creating in each call, but I really don't think that should help. I don't know the internals of GDI, so it is possible that something gets retained as long as the the Graphics object persists, and it may persist until disposed by the GC even though it is a local variable, but that seems kind of unlikely.
Re: PixelFormat.Format32bppArgb causing exception
Quote:
Originally Posted by
Shaggy Hiker
What do you do with the bitmaps that result? It seems to me that those would normally be HUGE, so if they are sitting in memory, they may be choking off your RAM in short order.
You might also try explicitly disposing the Graphics object you are creating in each call, but I really don't think that should help. I don't know the internals of GDI, so it is possible that something gets retained as long as the the Graphics object persists, and it may persist until disposed by the GC even though it is a local variable, but that seems kind of unlikely.
Yep, messing around with it now.
If I change:
To
vbnet Code:
takePicture = capture
capture.Dispose()
In the takePicture() function, I immediately get this error inside the resizeImage() function on the line:
vbnet Code:
objResizer.DrawImage(theImage, 0, 0, returnImage.Width + 1, returnImage.Height + 1)
This error is strange because I'm passing it ByVal. This also happens when disposing the graphics object.
I've tried changing it to ByRef before removing the return.
Going to make these private variables for the entire form, instead of the functions, and see if that fixes it.
Re: PixelFormat.Format32bppArgb causing exception
I made them so that they were global variables (global as in for this form) and placed this code at the top of each of the functions:
vbnet Code:
Try
returnImage.Dispose()
Catch ex As Exception
End Try
So that it would clean the old images out, but the error still comes up. I'm out of ideas after this.
I even disabled the timer, and made it do:
vbnet Code:
Me.BackgroundImage = resizeImage(takePicture(), Me.Width, Me.Height)
On a form click. Sure enough, on the 55th click this error came up, regardless if I clicked fast, or slow.
Re: PixelFormat.Format32bppArgb causing exception
Changing ByVal or ByRef will make no difference in any way shape or form to this method. If you pass a variable ByVal, you are passing the contents of the variable. If you pass the variable ByRef, then you are passing a reference to the variable. However, in this case you have a variable that is, itself, a reference type. This means that the variable already holds nothing more than a refernence to the actual object. So, the variable holds the address in memory of the actual object. Passing ByVal copies this address (not the object, just the address), whereas passing ByRef makes a reference to the address, so it passes the address of the variable holding the address. In both cases, the actual object is sitting somewhere out in memory and you are just messing with copies of the address of that memory.
Similarly, the snippet where you set:
TakePicture = capture
won't do anything either, because all that is in capture is the address of the memory where the image resides. All you are doing with that line is copying that address to the semi-hidden TakePicture variable. The image isn't affected by that, you have just copied the address from one place to another. As long as that bitmap is in memory, and as long as there is at least one variable that is still holding it's address, it still lives and will continue to live even if the garbage collector runs.
Still, this doesn't seem like a memory issue explicitly, to me. You do appear to be creating a huge demand for RAM, but with a modern OS, running out of RAM takes a while. If you were running at a resolution of 2000x1000, then each image would be 2000x1000x4 bytes, or about 16MB (plus some overhead). One hundred of those would gobble up a pretty good amount of RAM (over 1.6 GB), so your problem at 55 images would mean less than 1GB as a conservative estimate, which most modern systems should be able to handle.
On the other hand, the OS has limits on several graphical resources which are not explicitly tied to RAM. That may be where the problem lies.
Re: PixelFormat.Format32bppArgb causing exception
Is there any way to clear said memory? I am not taking a full screen image, but my resolution is at 2560x1440. Based off what you've said something must be allocating memory without disposing of old objects despite by best efforts, until the OS says no more, which is when it fails. It's probably passing a false or 0 as a parameter somewhere, which is causing the invalid argument?
Re: PixelFormat.Format32bppArgb causing exception
As long as you are holding the address of those images anywhere in the program, there is no way to recover that memory. If you grab an image and immediately commit it to the HD, you could then get rid of that image. However, if you were doing that, you might never need more than one bitmap to begin with, since you'd really just be changing the bits in the one bitmap. Whether or not that's realistic I can't say. I'm far from the graphics expert on this site. However, the real key for you is: How many images do you need to have available to work with (in RAM, not on the HD) for the program to do what you need? If you can get that number down to 1 in RAM and the rest on the HD, then that would be ideal.
Re: PixelFormat.Format32bppArgb causing exception
I only need one to be available in RAM. I'm basically trying to create a program that captures a section of the screen and then zooms it to a larger (or smaller) picture as a form background (the form being resizable of course). I'm not sure how to clear previous images from memory though, even with dispose and what not.
Re: PixelFormat.Format32bppArgb causing exception
Put a Label and a Timer on a Form and try this code:-
vbnet Code:
Public Class Form1
Private g_cnt As Integer
Private Function ResizeBitmap(ByVal bmp As Bitmap, ByVal newSize As Size) As Bitmap
Return New Bitmap(bmp, newSize)
End Function
Private Function GetScreenCapture(ByVal rect As Rectangle) As Bitmap
Dim bmp As New Bitmap(rect.Width, rect.Height)
Dim g As Graphics = Graphics.FromImage(bmp)
g.CopyFromScreen(rect.X, rect.Y, 0, 0, rect.Size)
Return bmp
End Function
Private Sub Timer1_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Timer1.Tick
Dim cap = GetScreenCapture(New Rectangle(0, 0, 400, 400))
cap = ResizeBitmap(cap, Me.Size)
Me.BackgroundImage = cap
g_cnt += 1
Label1.Text = g_cnt.ToString
End Sub
End Class
I set the Timer to 255. That code does a partial screen capture every time the Timer fires and it works fine for me with no errors. Why don't you try it out and see if it works for you as well.
Re: PixelFormat.Format32bppArgb causing exception
Quote:
Originally Posted by
Niya
Put a
Label and a
Timer on a
Form and try this code:-
vbnet Code:
Public Class Form1
Private g_cnt As Integer
Private Function ResizeBitmap(ByVal bmp As Bitmap, ByVal newSize As Size) As Bitmap
Return New Bitmap(bmp, newSize)
End Function
Private Function GetScreenCapture(ByVal rect As Rectangle) As Bitmap
Dim bmp As New Bitmap(rect.Width, rect.Height)
Dim g As Graphics = Graphics.FromImage(bmp)
g.CopyFromScreen(rect.X, rect.Y, 0, 0, rect.Size)
Return bmp
End Function
Private Sub Timer1_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Timer1.Tick
Dim cap = GetScreenCapture(New Rectangle(0, 0, 400, 400))
cap = ResizeBitmap(cap, Me.Size)
Me.BackgroundImage = cap
g_cnt += 1
Label1.Text = g_cnt.ToString
End Sub
End Class
I set the
Timer to 255. That code does a partial screen capture every time the
Timer fires and it works fine for me with no errors. Why don't you try it out and see if it works for you as well.
I tested out this code, but it crashed when the label reached 250. I ran it again to make sure, and it worked fine for a long time (Was passing something like 800)!!! The third time I ran it, it crashed at 270.
I decided to watch the application's memory usage in task manager. It keeps climbing up, and crashes when it gets to just under 2GBs (1, 811, 596K). I don't know why it's taking so much memory up, where is the memory leak? I always assumed all variables and objects are destroyed once the function finishes. Only what was return is kept on the stack? One time when I was watching it, it must have done some garbage collection, because the memory usage jumped back down to something like 10MBs when it was approaching the 2GBs mark, and then started climbing again. It didn't do this a second time however.
Re: PixelFormat.Format32bppArgb causing exception
Interesting. Now try this one out:-
vbnet Code:
Public Class Form1
Private g_cnt As Integer
Private Function ResizeBitmap(ByVal bmp As Bitmap, ByVal newSize As Size) As Bitmap
Return New Bitmap(bmp, newSize)
End Function
Private Function GetScreenCapture(ByVal rect As Rectangle) As Bitmap
Dim bmp As New Bitmap(rect.Width, rect.Height)
Using g As Graphics = Graphics.FromImage(bmp)
g.CopyFromScreen(rect.X, rect.Y, 0, 0, rect.Size)
End Using
Return bmp
End Function
Private Sub Timer1_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Timer1.Tick
Dim cap = GetScreenCapture(New Rectangle(0, 0, 400, 400))
Dim rezCap As Bitmap = ResizeBitmap(cap, Me.Size)
Dim prevB As Bitmap = Me.BackgroundImage
cap.Dispose()
Me.BackgroundImage = rezCap
If prevB IsNot Nothing Then
prevB.Dispose()
End If
g_cnt += 1
Label1.Text = g_cnt.ToString
End Sub
End Class
I dispose of the various object properly so I wouldn't expect any problems.
Re: PixelFormat.Format32bppArgb causing exception
Just tested my last version and it went to 2170 and counting.
Re: PixelFormat.Format32bppArgb causing exception
By the way, there isn't necessarily a memory leak. While you dispose of objects, that doesn't immediately free the memory. .NET manages memory pretty efficiently, but one of the implications of that is that unused memory isn't immediately released. The objects are flagged, but left untouched until the garbage collector runs to free up the memory. It isn't efficient to immediately free the memory, because that takes time that may not be needed at all. Therefore, as long as there is plenty of memory, used memory isn't cleaned up. Once memory gets low, then the garbage collector cleans up those items that are no longer reachable. One aspect of this that sometimes surprises people is that the size of memory usage by an app can appear to keep on growing for a very long time. The reason is that it is generally cheaper to get new memory from the OS than to reorganize what you already have, so the app is liable to just keep acquiring new blocks of memory until the OS tells it to do otherwise.
Re: PixelFormat.Format32bppArgb causing exception
In this case its not so much memory as it is GDI objects. To use a more broad stroke, unmanaged objects. Thing is, if this type of application was creating objects that only used managed objects then it wouldn't be having this problem because the CLR is aware of exactly how much memory its using so it knows when to order the GC to free them. However, when you're using Bitmaps and Graphics objects things get sticky. These objects hold pointers and handles to OS resources that the GC is not aware of. A Bitmap object for example holds a handle to something called an hBitmap. This is unmanaged. The GC can only account for the memory used by the managed elements of the Bitmap object. Things like internal fields and references to managed objects. It cannot know if the hBitmaps are taking too much space. So what happens is that the GC can think it has enough memory when in reality it doesn't because it doesn't account for the extra weight of unmanaged allocations so your application can very well run out of memory. This is the exact and sole purpose of the IDisposable interface. You're expected to use it where applicable when you're finished with objects that implement it. IDisposable gives you the ability to free these unmanaged elements immediately and the GC can handle the managed portions of the object as normal.