Several Bugs

MTSkull

Centurion
Joined
Mar 25, 2003
Messages
151
Location
Boulder, Colorado
I wrote a simple screen saver app that cycles through all the pictures in a folder. I have 1 question and 2 bugs that I need help with.

Visual Basic:
Public bFirstMove As Boolean
Public Pics() As String
Public position As Int16

Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) ;
Handles MyBase.Load

        Me.CenterToScreen()

        picBox.Height = Me.Height
        picBox.Width = Me.Width

        bFirstMove = True

        Pics = Directory.GetFiles("C:\Fractals")
        position = 0
        picBox.Image = Drawing.Bitmap.FromFile(Pics(position))

End Sub

Private Sub Form1_KeyUp(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyEventArgs) ;
Handles MyBase.KeyUp

        Me.Close()

End Sub

Private Sub picBox_MouseMove(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) ;
Handles picBox.MouseMove

        If Not bFirstMove Then
            Me.Close()
        Else
            bFirstMove = False
        End If

End Sub

Private Sub Timer1_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) ;
Handles Timer1.Tick

        position += 1
        If position = UBound(Pics) Then position = 0

        picBox.Image = Drawing.Bitmap.FromFile(Pics(position))

End Sub

1. How do I pass the capture of the mouse move to the underlying form? Since the pic box is sized to = the form I have to put the mouse move event in the picBox control. This probably does not matter but I am curious how it is done.

2. If I hit run (F5) from inside the .net developer, it always picks up a mouse move event and shuts down the program. That is the reason for the bFirstMove form global. Is there a way to flush the mouse move buffer?

3. I compiled this and tested it out and it works as a screen saver (changed .exe to .scr). After about a 1/2 hour of continuous running I get a "System out of Memory" fault that crashes the program. Do I need to set the picBox.image to nothing to flush memory or something?


Thanks
Brian
 
1) Look at the [api]SetCapture[/api] API.

2) Not that I'm aware of (though I don't totally understand why you would need this... if you just ignore the event it will go away on its own).

3) Not sure why that is happening... Perhaps you should try loading all the images into an array of Image objects at load, and simply cycling through those, rather than continuously loading the images from the HDD.


Also, instead of
Visual Basic:
UBound(Pics)
you should use the .NET CLR way
Visual Basic:
Pics.GetUpperBound(0)
 
In short, the answers to your questions are:
1. As far as I know, you have to use something outside of the .NET framework to do so.
2. I do not believe there is a way to do this. Windows sends mouse messages whether you want them or not.
3. Use the Bitmap's Dispose method before it goes out of scope or is set to a new Bitmap.


At length, the answers to your questions are:
The following should give you something else to think about:

Remove the PictureBox
Set Form1.FormBorderStyle to None
Try this:

Private Pics() As String
Private position As Int16
Private bmpCurrent, bmpOld As Bitmap

Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
Me.CenterToScreen()

Pics = System.IO.Directory.GetFiles("C:\Fractals")
position = 0

bmpCurrent = Bitmap.FromFile(Pics(position))
End Sub

Private Sub Form1_KeyUp(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyEventArgs) Handles MyBase.KeyUp
Me.Close()
End Sub

Private Sub Form1_MouseMove(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles MyBase.MouseMove
Static bCaughtMove As Boolean

If bCaughtMove Then
Me.Close()
Else
bCaughtMove = True
End If
End Sub

Private Sub Form1_Paint(ByVal sender As Object, ByVal e As System.Windows.Forms.PaintEventArgs) Handles MyBase.Paint
e.Graphics.DrawImage(bmpCurrent, 0, 0)
End Sub

Private Sub Timer1_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Timer1.Tick
position += 1
If position = Pics.GetUpperBound(0) Then position = 0

bmpOld = bmpCurrent
bmpCurrent = Bitmap.FromFile(Pics(position))
bmpOld.Dispose()

Me.Refresh()
End Sub


This is what I did:

I changed the global variables to Private instead of Public. Just a good habit.

1. I moved all of the Painting/Pictures directly to the form. No need to worry about mouse events on the PictureBox anymore.

2. Put the MouseChecking variable into the Mouse Event Handler. It's a bit cleaner that way. As far as I know there is no way to flush it.

3. Now the bitmap is drawn during the Form's Paint event. When updating the picture what I did was set bmpOld to whichever bitmap was currently being used. I then set bmpCurrent to the new pic about to be displayed. I then call Dispose on bmpOld. This is why you had a memory leak.

Any Class that implements the IDisposable Interface (quickest way to check is to just see if it has a Dispose Method) does not clean itself up automatically. You have to call the Dispose method to get it to clean up internal resources (in this case an HBITMAP Windows API Structure).

The reason I didn't simply call bmpCurrent.Dispose and then set it to the new one is because that might cause problems if the form's Paint event went off while it was loading the new one.

4. Sorry if I was a bit lengthy, but I like to espouse good coding practices whenever possible. The more people who code in a clean, structured (possibly Object Oriented) manner, the more code there will be around for others to see and understand.
 
Back
Top