?
Solved

VB6: How to abort a procedure that's taking too long

Posted on 2009-05-06
6
Medium Priority
?
828 Views
Last Modified: 2012-05-06
Dear Experts,

I am using a subroutine to shrink and antialias images. It works well in general, but on very large images it takes too long and in Windows Vista, this has caused freezing of the system.

I would like recommendations on the best way to abort a subroutine if it is taking longer than a specified number of seconds (I have experimented with using a timer and DoEvents command but so far I'm not satisfied).

Also, if anyone can recommend a very fast method of resizing with antialiasing, it would be very helpful.

Thank you!
0
Comment
Question by:ttobin333
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 3
  • 2
6 Comments
 
LVL 16

Accepted Solution

by:
HooKooDooKu earned 1000 total points
ID: 24318780
Well, fundimentally speaking, once VB starts a subroutine, the only way to get the application to do anything other than run the subroutine to completion is to insert DoEvents.

But that's just the 1st part of the problem.  That's how to allow windows to respond to messages such as minimize the window, move the window, or process a timer event.

What you need in addition to DoEvents is an event that sets a flag that the subroutine tests for and aborts the subroutine when the flag is set.

As an example, here's the code I use within a Form that has a subroutine that needs to be terminated if the user clicks on an abort button.  The main keys are:
1. Flags to allow attempts to close the form to react like the abort button was clicked instead.
2. AbortProcess function that includes 'DoEvents' and returns the current Abort status
3. Abort Command Button that simply sets the Abort Flag (after user confirms abort)
4. Periodically calling the AbortProcess function during the long subroutine.

Option Explicit
 
Private m_Abort As Boolean  'Flag that Abort Button has been Clicked and Confirmed
'How should the X System Button be Treated
Private Enum enumXButton
    X_NA        'Has No Effect
    X_ABORT     'Like an Abort Button
    X_CLOSE     'Like a Cancel Button
End Enum
Dim m_X As enumXButton
 
'Has the Abort Button been Clicked (and confirmed)?
Public Function AbortProcess() As Boolean
    DoEvents
    AbortProcess = m_Abort
End Function
Private Sub cmdAbort_Click()
    Dim Reply As VbMsgBoxResult
    Reply = MsgBox("Abort Process?", vbQuestion + vbYesNo + vbDefaultButton2)
    If Reply = vbYes Then
        m_Abort = True
    End If
End Sub
Private Sub Form_Load()
    m_X = X_CLOSE
End Sub
Private Sub Form_QueryUnload(Cancel As Integer, UnloadMode As Integer)
    Select Case m_X
        Case X_NA:      Cancel = 1
        Case X_ABORT:   Call cmdAbort_Click
                        Cancel = 1
        Case X_CLOSE:   'NOP - Allow Shutdown
    End Select
End Sub
 
'Now to code the really long subroutine
Private Sub SomeReallyLongProcess()
    'Initialize the Abort Flags
    m_Abort = False
    m_X = X_ABORT   'Makes any attempt to close the form react as if the Abort Button was clicked
 
    'Other intitilizing code...
 
    'The processing loop 
    Dim Index as Long
    For Index = 1 to 100000
        'Test for Abort - This single command can be sprinkled through out the processing loop
        if AbortProcess then Goto CloseAndExitSub
 
        'The rest of your processing logic goes here...
    Next I
 
CloseAndExitSub:
    m_X = X_CLOSE    'Reset how form reacts to a Close Command
    'Other common clean up code here...
End Sub

Open in new window

0
 
LVL 16

Expert Comment

by:HooKooDooKu
ID: 24318834
Additional though!

If all you are interested in doing is abort the subroutine if it runs too long, at the start of the subroutine, simply record the current system Date/Time.  Periodically, test how much time has elapsed, and abort the subroutine if too much time has passed.  (Note, I might have the order of the two dates in DateDiff backwards).
Private Sub SomeLongProcess()
    Dim StartTime as Date
 
    StartTime = Now
 
    Dim I as Long
    For I = 1 to 100000
        'Abort if Process has run longer than 10 seconds
        if DateDiff("s",StartTime,Now) > 10 Then Goto CloseAndExitSub
 
        'The rest of the process here
    Next I
 
CloseAndExitSub:
    'Cleanup code
End Sub

Open in new window

0
 
LVL 10

Assisted Solution

by:peetm
peetm earned 1000 total points
ID: 24325736
You don't need to use DoEvents - and you probably shouldn't, as you can easily check your message queue directly.  Certain messages are buffered in the process' message queue, and you simply access that to see what's waiting - like a critical key press.

I've attached a small routine from one of my old projects that sets a Global called Abort to true if the Esc key is hit/released.

Cheers
Public Function AbortCheck() As Boolean
 
    Dim Msg As Msg
 
    ' The improvement over using GetAsyncKeyState [IsKeyDown below] is that the check for the Esc key in
    ' GetAsyncKeyState will only work if the key is down when we call the function. By using the message
    ' queue, it doesn't matter when the key is pressed.
 
    If Not Screen.ActiveForm Is Nothing Then
    
        ' If we've a WM_KEYUP message in our queue: go and remove it.  NOTE - we ignore anything else.
        '
        Do While PeekMessage(Msg, Screen.ActiveForm.hWnd, WM_KEYUP, WM_KEYUP, PM_REMOVE)
        
            ' If the virtual keycode of the WM_KEYUP is VK_ESCAPE.
            '
            If Msg.wParam = VK_ESCAPE Then
            
                ' Flush the message queue of all keyboard messages.
                '
                Do While PeekMessage(Msg, Screen.ActiveForm.hWnd, WM_KEYFIRST, WM_KEYLAST, PM_REMOVE)
                
                    ' Nothing.
                    
                Loop
    
                Abort = True
                
                AbortCheck = Abort
            
            End If
        
        Loop
    
    End If
 
End Function

Open in new window

0
Industry Leaders: We Want Your Opinion!

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

 
LVL 16

Expert Comment

by:HooKooDooKu
ID: 24326572
@peetm
I'm curious as to why you say that you probably shouldn't use DoEvents?

From what I see, it looks like your AbortCheck() is sort of like a special purpose DoEvents.  But rather than allowing the events to happen, it just ignores them as it looks for a specific event.  If so, that would seem to make an application look stuck because it looks like it's not responding to events (like Minimize the Window, Move the Window, refresh the window if something has been drawn on top of it).  But with DoEvents, the application will continue to be responsive to windows messages, allow for the testing of an abort condition, and continue processing the subroutine.
0
 
LVL 10

Expert Comment

by:peetm
ID: 24326865
>>I'm curious as to why you say that you probably shouldn't use DoEvents?


Simply because one cannot 'filter' what messages will be flushed, and sent to your app whenever it's called - this may lead to all manner of state changes that can really 'hurt'.

Yes, polling the message queue is like a filtering DoEvents - just what's needed!

>>If so, that would seem to make an application look stuck because it looks like it's not responding to events (like Minimize the Window, Move the Window, refresh the window if something has been drawn on top of it)

Not at all, in that I've used the same technique to allow moving/resizing/re-painting - all with absolute 'control', and without calling DoEvents.
0
 

Author Closing Comment

by:ttobin333
ID: 31578578
Thanks guys, both methods will be very useful in my toolbox!
0

Featured Post

Industry Leaders: We Want Your Opinion!

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

There are many ways to remove duplicate entries in an SQL or Access database. Most make you temporarily insert an ID field, make a temp table and copy data back and forth, and/or are slow. Here is an easy way in VB6 using ADO to remove duplicate row…
You can of course define an array to hold data that is of a particular type like an array of Strings to hold customer names or an array of Doubles to hold customer sales, but what do you do if you want to coordinate that data? This article describes…
Get people started with the process of using Access VBA to control Outlook using automation, Microsoft Access can control other applications. An example is the ability to programmatically talk to Microsoft Outlook. Using automation, an Access applic…
Get people started with the utilization of class modules. Class modules can be a powerful tool in Microsoft Access. They allow you to create self-contained objects that encapsulate functionality. They can easily hide the complexity of a process from…
Suggested Courses

770 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question