caraf_g
asked on
Stop 'n Start
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
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
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.
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.
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.
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.
Mutexes, semaphores et al. won't do you any good unless you're actually running your application with multiple threads. Are you doing this?
ASKER
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?
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?
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.
And, when you only load a form, without showing it ... it does not change focus.
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!
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!
By the way, watch the debug window when you run the program... ;-)
ASKER
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.
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.
ASKER
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
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
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!®©
No loops, No Doevents, No APIs...
Cheers!®©
ASKER
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
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
ASKER
"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.
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.
ASKER
<doh>
relapsed should be elapsed.
<blush>
relapsed should be elapsed.
<blush>
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!®©
Cheers!®©
ASKER
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.
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.
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.
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.
You already said you didn't want to use a modal form in the opening question.
The Sleep API puts the current app to sleep, is there something that puts another app to sleep ? (other exe to sleep)
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!®©
Cheers!®©
ASKER
"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.
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.
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?
ASKER
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.
ASKER
"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
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
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
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.
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.
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!®©
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!®©
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.
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
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
ASKER
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. ;-)
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. ;-)
ASKER
OR, of course. WOman! <g>
ASKER
Adjusted points to 150
caraf_g,
Did you actually get pjknibbs's solution to work?? When I try it, VB locks up tight...
Cheers!®©
Did you actually get pjknibbs's solution to work?? When I try it, VB locks up tight...
Cheers!®©
ASKER
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.
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.
ASKER
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.