Solved

Stop 'n Start

Posted on 2000-03-03
34
147 Views
Last Modified: 2010-05-02
Has anyone got any idea on how to achieve an equivalent of the example
below, but:

1 without using a form
2 without using a tight "doevents" loop? No, even without ANY loops.

here's what I'm trying to do:

Imagine a simple scenario. You've got a form with a button on it, Command1
and a timer, Timer1

Private Sub Command1_Click()

1 - Some code is executed.

2 - The processing reaches the next instruction. And then it stops. But the
thread is not locked.....
>InstructionX. The processing will stop here and wait

4 - The processing continues here

End Sub

Private Sub Timer1_Timer()

3 - Something is done here so that the lock on instruction 2 is lifted

End Sub


A perfect example of this can be achieved when you *do* use a form as
follows

Private Sub Command1_Click()

1 - Some code is executed

2 - AnotherForm.Show vbmodal

4 - The processing continues here

End Sub
Private Sub Timer1_Timer()

3 - AnotherForm.Hide
'or
3 - Unload AnotherForm

End Sub


Problem is, I do not want to use a form, as this takes away focus from the
current form
I've meddled around with Mutexes, Critical Sections
(the program keeps crashing with these?) and Semaphores, but have not been
able to achieve the desired result. As far as I understand their use, in the
place of instruction 2 I would have to use WaitForSingleObject? But in the
case of using a Mutex, that seemed to do nothing - processing continued
immediately at instruction 4, and in the case of semaphores I eventually got
something to work, but that locked the whole thread so that instruction 3
cannot be executed.

Many thanks

Pino



0
Comment
Question by:caraf_g
  • 15
  • 9
  • 4
  • +4
34 Comments
 
LVL 10

Author Comment

by:caraf_g
ID: 2579608
PS - it obviously *must* be possible because, behind the scenes, that is exaclty what VB does when you execute

AForm.Show vbModal

Basically, what I need to know is, what is it that happens "behind the scenes", and how can I harness this technique.
0
 
LVL 14

Expert Comment

by:wsh2
ID: 2579760
Hello Caraf G !!! <smile>

How about an API solution.. <wink>.

Declare Sub Sleep Lib "Kernel32" _
(byval dwMilliseconds as long)

Add:

do until mbooTimerSwitchSelected _
    Sleep(2000)
loop

to your Command1_Click Sub.


0
 
LVL 4

Expert Comment

by:vindevogel
ID: 2579782
caraf_q, isn't this how an ordinary call works ;-))

Sub One
   Instruction1

   Call Two

   Instruction3
End Sub

Sub Two
   Instruction2
End Sub


But I see the problem ... you want to fire an event some time later, halt your program and have it continue afterwards ...

Nice problem, I'll think a weekend.


0
 
LVL 12

Expert Comment

by:pjknibbs
ID: 2579790
Mutexes, semaphores et al. won't do you any good unless you're actually running your application with multiple threads. Are you doing this?
0
 
LVL 10

Author Comment

by:caraf_g
ID: 2579843
Hello wsh2 <nudge, nudge>

The devil never sleeps <wink>

But seriously, sleep is not a solution. Sleep locks the whole thread:

Example

Command1_Click

Sleep -1 'forever, like snowwhite <g>

End Sub

Command2_Click

'Now, what can I do here to wake snow white up?

End Sub

What's more, Sleep locks the whole thread. I cannot even click on Command 2.


vindevogel:
This is the problem, You're talking about Sub1 and Sub2, where sub1 calls sub2. I'm not!

Sub1 is one command click. Sub2 is another. I want to stop in the middle of sub1, and in sub2 do something so that whatever has stopped in the middle of sub1 suddenly realises that it can continue on.


pjknibbs, No, everything's in the same thread. I'd come to that conclusion already :-(

Any ideas?
0
 
LVL 4

Expert Comment

by:vindevogel
ID: 2579919
And why don't you wanna use a eternal loop with doevents in it ?

And, when you only load a form, without showing it ... it does not change focus.

0
 
LVL 14

Expert Comment

by:mcrider
ID: 2579955
Ok, Here you go...

1) Create a new project.

2) Add a Timer control and 2 Command buttons.

3) Add the following code in the DECLARATIONS SECTION of the form, run the program and click Command1...

    Public Semaphore As Integer
    Private Sub Command1_Click()
        Select Case Semaphore
            Case 3
                Debug.Print vbCrLf + "You just clicked the button!"
                Debug.Print "I'm going to wait for 15 seconds"
                Debug.Print "you can still click anything you want..."
                Semaphore = 2
                Timer1.Enabled = True
            Case 2
                Debug.Print vbCrLf + "You can't click this button yet..."
                Debug.Print "I'm still waiting on a semaphore."
                Debug.Print "Go do something else..."
            Case 1
                Debug.Print vbCrLf + "15 seconds are up!"
                Debug.Print "Here is the rest of the code..."
                Semaphore = 3
        End Select
    End Sub
    Private Sub Command2_Click()
        Debug.Print vbCrLf + "Yahoo! Button 2!"
    End Sub
    Private Sub Form_Load()
        Timer1.Enabled = False
        Timer1.Interval = 15000
        Semaphore = 3
    End Sub
    Private Sub Timer1_Timer()
        Timer1.Enabled = False
        Semaphore = 1
        Command1_Click
    End Sub



Cheers!
0
 
LVL 14

Expert Comment

by:mcrider
ID: 2579961
By the way, watch the debug window when you run the program... ;-)
0
 
LVL 10

Author Comment

by:caraf_g
ID: 2580185
mcrider, you've misunderstood.

In your example, the code doesn't HALT anywhere in the command1_click module.

I want it to halt, and only continue when I say so.


vindevogel,

when I only load a form and don't show it, the code doesn't halt. I don't want to use an eternal loop with doevents, simply because

AForm.Show vbModal

doesn't use an eternal loop either (or does it?). So it can be done.
0
 
LVL 10

Author Comment

by:caraf_g
ID: 2580212
New example, to clarify further

Private mstrTheThingybob As String
Private mblnTimedOut As Boolean

Private Function GetNextThingybob() As String

1 - Some code is executed.

2 - The processing reaches the next instruction. And then it stops. But the
thread is not locked.....
>InstructionX. The processing will stop here and wait

4 - The processing continues here
If mblnTimedOut Then
    Err.Raise 50001, , "The request has timed out"
Else
    GetNextThingybob = mstrTheThingybob
End If

End Sub

Private Sub Timer1_Timer()

If it's possible Then
    mstrTheThingybob = somevalue
    Goto Release
ElseIf Timeout period has not yet relapsed
    Exit Sub
Else
    mblnTimedOut = True
    Release
Release:
3 - Something is done here so that the lock on instruction 2 is lifted

End Sub





Code that uses this, calling this from, e.g. an .exe, getNextThingybob could be e.g. in a DLL

'Do some code here
strSomeString = getNextThingybob
0
 
LVL 14

Expert Comment

by:mcrider
ID: 2580219
No, I understood... and if you run it, you'll see that it does the next best thing, accomplishing the same thing WITHOUT halting...  Everything you want executed before your halt point would be in "Case 3" and everything you want executed after your halt point would be in "Case 1"...

No loops, No Doevents, No APIs...


Cheers!®©
0
 
LVL 10

Author Comment

by:caraf_g
ID: 2580220
sorry, rushed job:

Private Sub Timer1_Timer()

If it's possible Then
    mstrTheThingybob = somevalue
    Goto Release
ElseIf Timeout period has not yet relapsed
    Exit Sub
Else
    mblnTimedOut = True
    Goto Release
End If

'Yeah, I know, it can't actually get here. But... you know.
Exit Sub

Release:
3 - Something is done here so that the lock on instruction 2 is lifted

End Sub







0
 
LVL 10

Author Comment

by:caraf_g
ID: 2580225
"accomplishing the same thing WITHOUT halting"

But it has to halt, otherwise, in the new example code the processing would return to the statement

strSomeString = getNextThingybob

Which I do not want to happen.
0
 
LVL 10

Author Comment

by:caraf_g
ID: 2580236
<doh>
relapsed should be elapsed.
<blush>
0
 
LVL 14

Expert Comment

by:mcrider
ID: 2580246
By the way, there is no way to physically halt execution on a line of code within a function or subroutine in a single thread application without using a loop with DoEvents...


Cheers!®©
0
 
LVL 10

Author Comment

by:caraf_g
ID: 2580312
mcrider, yes there is!

AForm.show vbModal

Does exactly what it says on the tin!

Except.... it shows a form.

But somewhere down deep in the dungeons, it does something. Probably calls some APIs or something. I'd like to know what that something is.

Perhaps it does loop with doevents. But I doubt it somehow.
0
 
LVL 14

Expert Comment

by:mcrider
ID: 2580318
You've done the very thing I *really* dislike... Changing requirements midstream...

First you said you wanted this to occur in a command button.  My code accomplishes this... Then you change the requirement to be a function...

If you're going to stick with a function, you can not do what you want in VB without using a "DoEvent" loop in the function.

The problem with it being in a function is a single thread will not dispatch without a doevents.
0
Highfive + Dolby Voice = No More Audio Complaints!

Poor audio quality is one of the top reasons people don’t use video conferencing. Get the crispest, clearest audio powered by Dolby Voice in every meeting. Highfive and Dolby Voice deliver the best video conferencing and audio experience for every meeting and every room.

 
LVL 14

Expert Comment

by:mcrider
ID: 2580329
You already said you didn't want to use a modal form in the opening question.
0
 
LVL 4

Expert Comment

by:vindevogel
ID: 2580361
The Sleep API puts the current app to sleep, is there something that puts another app to sleep ? (other exe to sleep)

0
 
LVL 14

Expert Comment

by:mcrider
ID: 2580425
By the way, the single thread dispatch limitation is also why you don't get writes on multiple sockets to work without calling the DoEvents after writing to a winsock control....

Cheers!®©
0
 
LVL 10

Author Comment

by:caraf_g
ID: 2580430
"You've done the very thing I *really* dislike... Changing requirements midstream"

Sorry... but I haven't changed the requirements, I'm just finding it very difficult to express exactly what it is I am trying to achieve.

I have however made it very clear from the start that I want the code to HALT at a specific instruction. Your code doesn't achieve this.
0
 
LVL 12

Expert Comment

by:pjknibbs
ID: 2580433
Actually, the "AForm.show vbModal" method almost certainly *does* use the equivalent of a DoEvents loop at its core, because there's no other way it can work in a single-threaded application. The documentation for the API function DialogBox(), which also waits for user input before returning, explicitly states it uses its own message loop to process events--if the Windows API can do it, why can't Visual BASIC?
0
 
LVL 10

Author Comment

by:caraf_g
ID: 2580439
vindevogel, the Sleep API won't do it. I want the application to halt at the instruction, not lock up completely. It should still be responsive to other events.
0
 
LVL 10

Author Comment

by:caraf_g
ID: 2580449
"almost certainly *does* use the equivalent of a DoEvents loop "

Yes, you're probably right. But in that case I'd like to learn what exactly that equivalent is, because it seems to behave a lot better than a doevents loop. The application seems steadier, and not as much processing time seems to get eaten by whatever it is it's doing.

That's the real motivation behind my attempt to get to the bottom of this. Processing time is important in this application.

Thanks

Pino
0
 
LVL 12

Accepted Solution

by:
pjknibbs earned 150 total points
ID: 2580479
The most likely equivalent would be the API version, which goes something like this in C:

MSG msg;

while(GetMessage(&msg, NULL, NULL, NULL))
{
    TranslateMessage(&msg);
    DispatchMessage(&msg);
}

One thing which *may* make this more efficient is that the GetMessage() API can be told to receive only messages for a particular window, and also a range of messages to retrieve (rather than all of them). You could thus make this loop only accept messages for the one window still requiring input, and ignore all the others.
0
 
LVL 3

Expert Comment

by:Joebob
ID: 2580481
Pardon me if I am restating another comment, but there were too many to read them all.  The problem that you are having with semaphores is that they are used to synchronize multiple threads, but VB doesn't truely do multithreading.

The only way to safely get a separate thread of execution in VB is to create an ActiveX EXE.  Since an EXE has it's own memory space and processor time allocation, it will continue to run will you main program is waiting on the MUTEX.

About the only way to solve this problem without creating a separate EXE is to use the loop approach or a form.  I agree that I would not use a form for possible focus issues, plus it takes up quite a bit of system resources which a loop doesn't.  I don't like the looping method either, because it ties up processor time by polling.

My suggestion.  If the timer code is very fast, use a loop.  If it takes a while, create a separate ActiveX EXE and use MUTEX semaphores.
0
 
LVL 14

Expert Comment

by:mcrider
ID: 2580495
Well since this function is INTERNAL to the VB interpreter, and not available to the VB program itself, I am assuming that VB is actually doing a Yield() or DirectedYield() call.

Here is the documentation on both from "C":

void Yield(void)
---------------------

The Yield function stops the current task and starts any waiting task.
Returns

This function does not return a value.

Comments

The Yield function should be used only when the application is guaranteed not to receive any messages.

Applications that contain windows should use a DispatchMessage, PeekMessage, or TranslateMessage loop rather than call the Yield function directly. The message-loop functions handle message synchronization properly and yield at the appropriate times.
See Also

DirectedYield, DispatchMessage, PeekMessage, TranslateMessage



void DirectedYield(htask)
------------------------------------
HTASK htask;


The DirectedYield function puts the current task to sleep and awakens the given task.

Parameter      Description

htask      Specifies the task to be executed.

Returns

This function does not return a value.

Comments

When relinquishing control to other applications (that is, when exiting hard mode), a Windows-based debugger should call DirectedYield, identifying the handle of the task being debugged. This ensures that the debugged application runs next and that messages received during debugging are processed by the appropriate windows.

The Windows scheduler executes a task only when there is an event waiting for it, such as a paint message, or a message posted in the message queue.
If an application uses DirectedYield for a task with no events scheduled, the task will not be executed. Instead, Windows searches the task queue. In some cases, however, you may want the application to force a specific task to be scheduled. The application can do this by calling the PostAppMessage function, specifying a WM_NULL message identifier. Then, when the application calls DirectedYield, the scheduler will run the task regardless of the task's event status.
DirectedYield starts the task identified by htask at the location where it left off. Typically, debuggers should use TaskSwitch instead of DirectedYield, because TaskSwitch can start a task at any address.

DirectedYield returns when the current task is reawakened. This occurs when the task identified by htask waits for messages or uses the Yield or DirectedYield function. Execution will continue as before the task switch.
DirectedYield is located in KRNL286.EXE and KRNL386.EXE and is available in Windows versions 3.0 and 3.1.
See Also

PostAppMessage, TaskSwitch, TaskGetCSIP, TaskSetCSIP, Yield



Cheers!®©
0
 
LVL 12

Expert Comment

by:pjknibbs
ID: 2580523
Actually, there's another reason why my earlier GetMessage() loop would be preferable to a DoEvents loop--GetMessage() actually puts the application to sleep until a message arrives, so you're not actually doing any processing unless something's happening which requires it. DoEvents() is more akin to a PeekMessage() loop, which makes your application use 100% of CPU time regardless of what's actually happening.
0
 
LVL 15

Expert Comment

by:ameba
ID: 2580589
Hi,
If you don't like doevents (or it doesn't exist, e.g. in VB for WinCE), but you want your app to respond to user input (mouse or keyboard), use this loop:

   Do
      If GetInputState() <> 0 then Exit Do
      Sleep 1 ' must be very short period, 500 msecs will probably mask some events
   Loop
0
 
LVL 10

Author Comment

by:caraf_g
ID: 2580635
Everybody....

Thanks very much for the overwhelming response.

However... the prize will go to pjknibbs. GetMessage is exactly what I was looking for. You da man. ;-)
0
 
LVL 10

Author Comment

by:caraf_g
ID: 2580639
OR, of course. WOman! <g>
0
 
LVL 10

Author Comment

by:caraf_g
ID: 2580643
Adjusted points to 150
0
 
LVL 14

Expert Comment

by:mcrider
ID: 2583220
caraf_g,

Did you actually get pjknibbs's solution to work?? When I try it, VB locks up tight...


Cheers!®©
0
 
LVL 10

Author Comment

by:caraf_g
ID: 2584737
Yes, I got it to work perfectly.

The way I did it was by loading a form with a timer on it (but, and that's important, NOT showing it).

Then I used GetMessage as follows (sorry for VB/C mix-match)

while(GetMessage(&msg, FormWithTimer.hwnd, 275&, NULL))
{
    'Do what I used to do in the _Timer procedure here
    'Decide whether to jump out of loop or not.
    TranslateMessage(&msg);
    DispatchMessage(&msg);
}

the nice thing about GetMessage is that you can choose which messages to process, which ones to ignore completely and which ones to pass on unaltered. It's got great potential, and I'm going to investigate it further.

Funny thing is, this is still a loop. But it's a much nicer loop. It only loops around when it is absolutely necessary. A DoEvents loop just loops aimlessly. I can accept this type of loop.
0

Featured Post

IT, Stop Being Called Into Every Meeting

Highfive is so simple that setting up every meeting room takes just minutes and every employee will be able to start or join a call from any room with ease. Never be called into a meeting just to get it started again. This is how video conferencing should work!

Join & Write a Comment

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…
Since upgrading to Office 2013 or higher installing the Smart Indenter addin will fail. This article will explain how to install it so it will work regardless of the Office version installed.
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…
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…

743 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