Solved

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

Posted on 2009-05-06
6
810 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
  • 3
  • 2
6 Comments
 
LVL 16

Accepted Solution

by:
HooKooDooKu earned 250 total points
Comment Utility
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
Comment Utility
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 250 total points
Comment Utility
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
How to improve team productivity

Quip adds documents, spreadsheets, and tasklists to your Slack experience
- Elevate ideas to Quip docs
- Share Quip docs in Slack
- Get notified of changes to your docs
- Available on iOS/Android/Desktop/Web
- Online/Offline

 
LVL 16

Expert Comment

by:HooKooDooKu
Comment Utility
@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
Comment Utility
>>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
Comment Utility
Thanks guys, both methods will be very useful in my toolbox!
0

Featured Post

Maximize Your Threat Intelligence Reporting

Reporting is one of the most important and least talked about aspects of a world-class threat intelligence program. Here’s how to do it right.

Join & Write a Comment

Introduction In a recent article (http://www.experts-exchange.com/A_7811-A-Better-Concatenate-Function.html) for the Excel community, I showed an improved version of the Excel Concatenate() function.  While writing that article I realized that no o…
When designing a form there are several BorderStyles to choose from, all of which can be classified as either 'Fixed' or 'Sizable' and I'd guess that 'Fixed Single' or one of the other fixed types is the most popular choice. I assume it's the most p…
As developers, we are not limited to the functions provided by the VBA language. In addition, we can call the functions that are part of the Windows operating system. These functions are part of the Windows API (Application Programming Interface). U…
Show developers how to use a criteria form to limit the data that appears on an Access report. It is a common requirement that users can specify the criteria for a report at runtime. The easiest way to accomplish this is using a criteria form that a…

728 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

Need Help in Real-Time?

Connect with top rated Experts

12 Experts available now in Live!

Get 1:1 Help Now