Solved

DoEvent or Loop

Posted on 2011-02-22
29
621 Views
Last Modified: 2012-05-11
Hello Experts,

First of all, i would like to add this before posting my question...
I remember three years ago here on EE, Visual Basic 6 used to be visible
everytime i used to ask any question about this language, but now all i see
is something pointhing to Visual Basic Classic in which i only see things like
VB Controls, and few more, etc. but nothing mentioning Visual Basic 6...
is this okay? or i am lost? thanks.

Now here is my question,
I would like to add an event to my project instead of delay like i am doing now.
this is what i am working, i have a project that lunch, lets say notepad.exe as an example,
what my project does is rename the caption of that program window.

The only problem i see is this, the program window that my project luch, it takes some
time to luch, some times it takes 3 seconds, some other times it take even 5 seconds...
so what i have in my project is the sleep event, and i would like to change with DoEvent
instead of sleeping, cuz the DoEvent is going to wait for that process to appear and then
rename or do what ever other command i add into my project.

Now am i right about this? if so, how could i acomplish this DoEvent to my project?
the way i am doing it now is on the Load event.

Private Sub Form_Load()
Shell "C:\WINDOWS\notepad.exe"
Sleep 3

'The rest of my code goes here
End Sub

Thanks in advance




0
Comment
Question by:C0ding
  • 12
  • 8
  • 7
  • +2
29 Comments
 
LVL 5

Expert Comment

by:karthika_cts
Comment Utility
Have you tried like this?

Private Sub Form_Load()
DoEvents: DoEvents: DoEvents
Shell "C:\WINDOWS\notepad.exe"
DoEvents: DoEvents: DoEvents

'The rest of my code goes here
MsgBox "Notepad"

End Sub
0
 

Author Comment

by:C0ding
Comment Utility

Okay, but lets say the program being executed from my project takes 6 second,
8 seconds, 10 seconds, 2 seconds, or what ever time, this tip will help for this?

0
 

Author Comment

by:C0ding
Comment Utility

All i want in my project is that if the program being executed takes what ever x-time,
my project will be willing to wait for it until the program lunch so my project send the event.
0
 
LVL 16

Assisted Solution

by:HooKooDooKu
HooKooDooKu earned 100 total points
Comment Utility
DoEvents just allows your application to process any pending Windows messages.

For example, say you have an application that take a file an makes a backwards copy of it.  Because the user might give you a 1GB file (that's going to take some time to process) so you provide the user a progress bar to show how much of the file has been processed, and you even provide an ABORT button.  On the Abort button's Click event, you simply have the abort button set a global flag:
Private Sub ABORT_Click()
  g_UserClickedAbort = TRUE
End Sub
In your file processing loop, you've even included a line of code that basically says...
  if g_UserClickedAbort Then Exit Sub  'Aborts the rest of this process

So your application starts running, and the user decides to click Abort.  But nothing happens.  The button does not seem to respond to any click events.  It never appears "pressed" like if you user holds the mouse button down over the button.

The problem is that each time the user clicks on the ABORT button, all that does is place a "The Abort Button was Pressed" message in your applications Message Que.  The subroutine "ABORT_Clicked" doesn't get called until your application checks the message que and processes the messages there.  But your application is busy running the file, and since VB6 is NOT multi-threaded, it can't force your subroutine to halt so that it can process the message que and execute the "ABORT_Clicked" event.

This is where DoEvents comes into play.  When you call DoEvents, it causes your application to process the message que.  If there are no messages in the message que, the call to DoEvents returns immediately.  If there are messages in the que, DoEvents stays in a look responding to all the messages waiting in the que.  When there are no more messages to process, the subroutine returns and your code resumes executing.

So to make the program respond the way you intented, rather than a simple check in your processing loop to see if your flag has been set, you have to include a call to DoEvents...

do while( MoreFileToProces )
  DoEvents 'If the user has clicked the ABORT button, the ABORT_Click event will now fire
  if g_UserClickedAbort then ExitDo
  ...code to continue processing file goes here

The Sleep() API call is NOT a substitute for calling DoEvents.  You can think of a call to Sleep() as being a function that sits in an infinite loop doing nothing but checking the time, and when the time for the Sleep() function to returns occurs, the function returns.  The Sleep() function is MUCH more processer utilization friendly than that, but if your program is in a processing loop that includes a call to Sleep(), that just pauses your program for a fixed amount of time.  

BTW, the way a VB6 application normally works is that it is just one big DoEvents loop.  The program just loops for ever looking to see if anything is in the message que, and when there is, it processing the message, firing off events (such as MyButton_Click() ) as messages are encountered.  But once an event has fired (i.e. a call to something like MyButton_Click() ), the processing of messages halts until the event's subroutine has finished executing.  

So the real purpose of DoEvents is to allow your VB application to continue to respond to user input while it is in the middle of running a subroutine.


As for your problem at hand, the question is how do you know when the called application has finished loading.  The Shell function basically sends a message to Windows telling it to start some program.  Once Windows has created the new process from the Shell command, it returns from Shell (because the new "process" now has its own thread to run in.  The Shell command returns a process handle for the process that just started, but from what I've heard, it's not a simple task to take that handle and convert it into a window handle that you might be able to query.  The only thing I know for SURE that you can do with that process handle is determine if the process is still running.

For example, I wrote for myself a VB subroutine called "WaitShell".  It basically just calls Shell, then sits in an infinite loop testing the returned process handle.  Then the process handle indicates the process has terminated, then I exit WaitShell.
0
 
LVL 14

Expert Comment

by:VBClassicGuy
Comment Utility
Just implement and "Shell and Wait" routine. I have mine in a separate module named Lib_Shell.bas. Call the routine, and it won't return until the shelled process is complete. Here is the code:

Option Explicit

Private Const SYNCHRONIZE = &H100000
Private Const WAIT_TIMEOUT = &H102

Private Declare Function OpenProcess Lib "kernel32" (ByVal dwDesiredAccess As Long, ByVal bInheritHandle As Long, ByVal dwProcessId As Long) As Long
Private Declare Function WaitForSingleObject Lib "kernel32" (ByVal hHandle As Long, ByVal dwMilliseconds As Long) As Long
Private Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)
Private Declare Function CloseHandle Lib "kernel32" (ByVal hObject As Long) As Long

Sub ShellAndWait(ByVal cmd As String)
   Dim lPid As Long
   Dim lHnd As Long

   lPid = Shell(cmd, vbNormalFocus)
   If lPid <> 0 Then
      lHnd = OpenProcess(SYNCHRONIZE, 0, lPid)
      If lHnd <> 0 Then
         Do
            DoEvents 'keep application responsive
            Sleep 50 'keep CPU usage from ramping to 100%
         Loop Until WaitForSingleObject(lHnd, 0) <> WAIT_TIMEOUT
         CloseHandle (lHnd)
      End If
   End If
   
End Sub


0
 
LVL 14

Expert Comment

by:VBClassicGuy
Comment Utility
And I know what you mean about finding the VB6 area. It used to be OK, with mainly VB6 questions in the VB Classic zone, but there was a long discussion on EE about where to place VBA questions. The result was they piled them into VB Classic, and I can hardly find any VB6 questions to answer any more. And since that's what I specialize in and get most of my points from, I may have to drop out of EE if the VB6 questions start getting too sparse or hard to find.
0
 
LVL 85

Expert Comment

by:Mike Tomlinson
Comment Utility
*Do you want to know when Notepad has EXITED, or simply when its main WINDOW is available?

For the former, use code like VBClassicGuy suggests.

For the latter, use code like this to determine when the main window is open and "ready":
Private Const INFINITE = -1& 
Private Const PROCESS_ALL_ACCESS = &H1F0FFF

Private Declare Function OpenProcess Lib "kernel32" (ByVal dwDesiredAccess As Long, ByVal bInheritHandle As Long, ByVal dwProcessId As Long) As Long
Private Declare Function WaitForInputIdle Lib "user32" (ByVal hProcess As Long, ByVal dwMilliseconds As Long) As Long 
 
...  
    Dim FileName As String
    Dim params As String
    FileName = "C:\program.exe"
    params = "some params here"

    Dim pid As Long
    Dim lngProcess As Long
    pid = Shell(Chr(34) & FileName & Chr(34) & " " & params, vbNormalFocus)
    lngProcess = OpenProcess(PROCESS_ALL_ACCESS, 0&, CLng(pid))
    If lngProcess <> 0 Then
        WaitForInputIdle lngProcess, INFINITE

        ' ... program is ready ...

    End If

Open in new window

0
 
LVL 14

Expert Comment

by:VBClassicGuy
Comment Utility
That's great code for knowing when an external app is ready, Idle_Mind, thanks for posting that. One question, if the external app never opens (the path/filename was wrong or something), how would you "timeout" and get out of the routine?
0
 
LVL 85

Expert Comment

by:Mike Tomlinson
Comment Utility
See WaitForInputIdle():
http://msdn.microsoft.com/en-us/library/ms687022(VS.85).aspx

Instead of passing in INFINITE, you can give it a timeout value in milliseconds:

    "The time-out interval, in milliseconds. If dwMilliseconds is INFINITE, the function does not return until the process is idle."

You can check the return value to see if it is ready (0), timed out (WAIT_TIMEOUT), or an error occurred (WAIT_FAILED):
0
 
LVL 14

Expert Comment

by:VBClassicGuy
Comment Utility
Thanks for the info. Again, great routine!
0
 

Author Comment

by:C0ding
Comment Utility

Thank you HooKooDooKu for all your explanation, i appreciate it.

Hi VBClassicGuy, thanks for sharing your example code,
Now let me tell you a bit of what i have, i have a simple Form1 which is my project,
Then all i wanted to do, is to call an unknown.exe program which in this case i used
notepad.exe as an example, the idea of my project is to rename the caption of that
program which i already have the code all setup for that event proposes.

Now what i really need is to replace my Sleep event for something better that can
wait for that program to be loaded which in some times my timing fail to catch his
event, so thats why i decide to change Sleep for something better like DoEvent or
something that can allow my application to do the right timing until is ready to send
his task in other words the renaming event code to that program caption.

How can i accomplish this the right way using the example you provided it here?
Ok i get the point of the Lib_Shell.bas ...then how can i call the routine?

Thanks in advance

0
 
LVL 85

Expert Comment

by:Mike Tomlinson
Comment Utility
In my post back here:
http://www.experts-exchange.com/Programming/Languages/Visual_Basic/Q_26841079.html#34961071

Insert your code to change the caption at line #20 where it says " ... program is ready ...".
0
 
LVL 14

Expert Comment

by:VBClassicGuy
Comment Utility
Yeah, you don't want my code, as it will tel you when notepad is exited. Use Idle_mind's code to tell you when notepad is loaded and ready to accept input.
0
 

Author Comment

by:C0ding
Comment Utility

Hi Idle,

Sorry to ask the same question over and over,
so if thats the code to be use and the right one...
how can i accomplish that code? i mean how do i call it?

I tried but i do get an error message:

Run-time error '76':

Path not found

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 85

Expert Comment

by:Mike Tomlinson
Comment Utility
I simply gave you a snippet demonstrating its use...

...we'd have to see YOUR code that attempts to use it for any chance at giving meaningful help.
0
 

Author Comment

by:C0ding
Comment Utility

Ok sorry, i fix it...

Private Const INFINITE = -1&
Private Const PROCESS_ALL_ACCESS = &H1F0FFF

Private Declare Function OpenProcess Lib "kernel32" (ByVal dwDesiredAccess As Long, ByVal bInheritHandle As Long, ByVal dwProcessId As Long) As Long
Private Declare Function WaitForInputIdle Lib "user32" (ByVal hProcess As Long, ByVal dwMilliseconds As Long) As Long
 



Private Sub Command1_Click()
Dim FileName As String
Dim params As String
FileName = "C:\WINDOWS\notepad.exe"
params = "some params here"

Dim pid As Long
Dim lngProcess As Long
pid = Shell(Chr(34) & FileName & Chr(34) & " " & params, vbNormalFocus)
lngProcess = OpenProcess(PROCESS_ALL_ACCESS, 0&, CLng(pid))
If lngProcess <> 0 Then
WaitForInputIdle lngProcess, INFINITE
 '... program is ready ...
End If
End Sub

Open in new window


Ok, now where it says " '... program is ready ..."
there is where my code will have to be set right?

0
 
LVL 85

Expert Comment

by:Mike Tomlinson
Comment Utility
Yes...but you need to change line #14 so that "params" is either blank or contains a valid filename to open.
0
 

Author Comment

by:C0ding
Comment Utility

Ok, in this case if our example is notepad, could it be "notepad.exe" ? or just "notepad"?
other thing going on with my code, is that, when i was using the sleep even in a timer...
i was doing this cuz right after my project change the caption of this program, the program
itself change back to its original name, then i got to get arround this by adding it to a timer.
0
 
LVL 85

Expert Comment

by:Mike Tomlinson
Comment Utility
If notepad is in the path variable it will be found.

What is the actual program you are trying to change?

If it changes its own caption then either it is detecting the change and putting it back or it is updating its own caption (probably using a Timer itself).
0
 

Author Comment

by:C0ding
Comment Utility

Yes, but it does it only once, then after that with the help of the timer if it find that
name again the timer change it once again, and that it, after that my program simple
close, unload, end by his own. and the program name, is an emulator for n64 games.
my intentions is to add the name of each game i am playing, intead of the name of the
program, which i already did, ive been doing it for years cuz i like it this way.

0
 
LVL 14

Expert Comment

by:VBClassicGuy
Comment Utility
IMHO, if you're changing the caption of a program that always changes it back, you shouldn't be doing it anyway. It is kinda a waste of time and CPU usage to constantly be changing a caption on a "foreign" program using a timer in your code.
0
 

Author Comment

by:C0ding
Comment Utility

No, actually nop, it does it only once, after that it stays like that.
then my program close. which is no more waste of time or cpu.
0
 

Author Comment

by:C0ding
Comment Utility

Thanks Idle, i be right back.
Now i will be testing this code to see what i do get.
0
 
LVL 14

Expert Comment

by:VBClassicGuy
Comment Utility
Oh, OK. Good luck!
0
 

Author Comment

by:C0ding
Comment Utility

I have two problems now...

1.) I call the execute file in this following matter:
Shell "Folder\Program.exe C:\Folder\GameDir\Game.z64", vbNormalFocus
Then with the new parameters giving by idle, i have to exclude the ", vbNormalFocus"
otherwise i get errors.

2.) Then if i lieve it like this:
FileName = "Folder\Program.exe C:\Folder\GameDir\Game.z64"
i get another error which is this one:

Run-time error '5':

Invalid procedure call or argument


The error point to this line of code:
pid = Shell(Chr(34) & FileName & Chr(34) & " " & params, vbNormalFocus)

0
 

Author Comment

by:C0ding
Comment Utility

Could somebody give me the DoEvent instead of the code from idle?
I see now why of his nick name, but thanks anyway Idle_Mind
0
 
LVL 85

Accepted Solution

by:
Mike Tomlinson earned 400 total points
Comment Utility
You need to separate the filename and the params:

    FileName = "Folder\Program.exe"
    params = "C:\Folder\GameDir\Game.z64"

So that in this line:

    pid = Shell(Chr(34) & FileName & Chr(34) & " " & params, vbNormalFocus)

The quotes get put around the filename only and the params end up after the quoted filename separated by a space.
0
 

Author Comment

by:C0ding
Comment Utility

Thanks Idle, i finally got it working




0
 
LVL 85

Expert Comment

by:Mike Tomlinson
Comment Utility
Yay!  =)
0

Featured Post

Find Ransomware Secrets With All-Source Analysis

Ransomware has become a major concern for organizations; its prevalence has grown due to past successes achieved by threat actors. While each ransomware variant is different, we’ve seen some common tactics and trends used among the authors of the malware.

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…
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 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…

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

11 Experts available now in Live!

Get 1:1 Help Now