There is no other way, because in order to check it has to have orders to check something..Timer is the only way buddy (unless you only want to fire the thing once..)
---
Actually..you could use a do loop and add some doevents inside and such..but it will use 50% computer usage
If it's your app, then it could write a file, kind of like a flag, and then delete the file when the app closes. If it's another program, maybe you could use a batch file that does the same thing.
You said that timers are inconsistent, slow, horrible, but you haven't said why. And also I hardly think that any of those reasons matter in a situation like this. But if it is possible to do it through a hook then I would not touch any form of loop or timer.
By installing a system-wide WHSHELL hook you can get HSHELL_WINDOWCREATED notifications whenever a new window is created. Unfortunately this has to be done in C.
Oh wait! there is that fancy method of yours that allows us to create win32 dlls in VB. Why don't you whip up a little demo for this monkey?
Oh wait! there is that fancy method of yours that allows us to create win32 dlls in VB. Why don't you whip up a little demo for this monkey?
OK, I will, but to run it, you will need to mess with your VB files a little, and to make it, I will need to know how on earth to install a WHSHELL hook, and what on earth to do with it I will search for information but since I have no idea how one works I can't promise anything
OK, here is the code.
Make sure that the ShellProce function is in a standard module and that
you export the other two subs.
If this works it will open a world of programming, good luck.
VB Code:
Option Explicit
'This code is meant to be compiled into a win32 dll
Private Declare Function SetWindowsHookEx Lib "user32" Alias "SetWindowsHookExA" ( _
ByVal idHook As Long, _
ByVal lpfn As Long, _
ByVal hmod As Long, _
ByVal dwThreadId As Long _
) As Long
Private Declare Function UnhookWindowsHookEx Lib "user32" ( _
ByVal hHook As Long _
) As Long
Private Declare Function CallNextHookEx Lib "user32" ( _
ByVal hHook As Long, _
ByVal ncode As Long, _
ByVal wParam As Long, _
lParam As Any _
) As Long
Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" ( _
Destination As Any, _
Source As Any, _
ByVal Length As Long _
)
Public Const WH_SHELL = 10
Public Const HSHELL_WINDOWCREATED = 1
Public Const HSHELL_WINDOWDESTROYED = 2
Public Const HSHELL_ACTIVATESHELLWINDOW = 3
Public Const HSHELL_APPCOMMAND = 12 '???
' #if(WINVER >= 0x0400) NT 4.0 and above
Public Const HSHELL_WINDOWACTIVATED = 4
Public Const HSHELL_GETMINRECT = 5
Public Const HSHELL_REDRAW = 6
Public Const HSHELL_TASKMAN = 7
Public Const HSHELL_LANGUAGE = 8
' #if(_WIN32_WINNT >= 0x0500)
Public Const HSHELL_ACCESSIBILITYSTATE = 11
Public Const ACCESS_STICKYKEYS = &H1
Public Const ACCESS_FILTERKEYS = &H2
Public Const ACCESS_MOUSEKEYS = &H3
Public Type RECT
Left As Long
Top As Long
Right As Long
Bottom As Long
End Type
Private hHook As Long
Public IsHooked As Boolean
Public Sub SetShellHook()
If IsHooked Then
MsgBox "Don't hook SHELL twice or you will be unable to unhook it."
You said that timers are inconsistent, slow, horrible, but you haven't said why. And also I hardly think that any of those reasons matter in a situation like this. But if it is possible to do it through a hook then I would not touch any form of loop or timer.
I have yet to find out the real reasons why. They are probably slow because they are objects, simulating a loop and executing the code within it ever so many intervals. How it maintains low CPU usage, I can't tell ya. I have no idea what code MS put in there to do this. But if I knew, then I would know why they are so slow, inconsistant, inaccurate, and such.
When I get home, I'm gonna do an experiment with multiple timers to see why they make it even worse. When I am done, Im gonna upload it.
It's possible to make your own timer functions with GetTickcount and the Sleep API and a Date variable.
Just calling Sleep with a value of one millsec, from inside the delay loop, reduces the CPU use to almost nill.
The Problem is that it's damned hard to kill your program if the user exits while in one of those loops.
A VB Timer doesn't have that problem.
That's because you aren't suppose to be using the Sleep API to slow down loops. It halts your program and all of it's events for a number of milliseconds, even after using DoEvents. It's no wonder why you have a hard time closing your app. This project I uploaded is a managed loop locked at 60 FPS.
The GetTickCount API is only 10 ms accurate, while the QueryPerformanceCounter and QueryPerformanceFrequency API's are 1 ms accurate.
Ah ha. That's why Timers get worse and worse the more you use. Let's say you have a Listbox and 4 timers, with the first 3 set at an interval of 1, and the 4th one set at an interval of 1000. Using this code:
VB Code:
Option Explicit
Private Sub Timer1_Timer()
List1.AddItem "1"
List1.AddItem "1"
List1.AddItem "1"
End Sub
Private Sub Timer2_Timer()
List1.AddItem "2"
List1.AddItem "2"
List1.AddItem "2"
End Sub
Private Sub Timer3_Timer()
List1.AddItem "3"
List1.AddItem "3"
List1.AddItem "3"
End Sub
Private Sub Timer4_Timer()
Timer1.Enabled = False
Timer2.Enabled = False
Timer3.Enabled = False
Timer4.Enabled = False
End Sub
You will see that Timer1's code will fire off first, then Timer2, then Timer3, then back to Timer1, 2, 3, etc. But sometimes it'll end up skipping every now and then, so sometimes you end up with 1, 3, 1, 2, 3, 1, 2, 1, 2, 3, etc. I had it add 3 items each into the listbox because I wanted to see if the timers actually ran at the same time to interupt each other. Turns out that it has to execute ALL of the code one timer at a time before executing code in the next timer. So the other timers have to wait. It was an interesting learning experience there.
Ah ha. That's why Timers get worse and worse the more you use.
That's why I mentioned the Sleep API
And I just have Sleep set to 1 ms, not many things will hit it in that time.
The problem with killing the app doesn't come from the Sleep API.
It happens when the user tries to exit the program and triggers an event.
The forms unload event will run, but then the code will return to the loop and then to the sub that called the loop.
That normally prevents the app from closing.
I'll have to check into QueryPerformanceCounter and QueryPerformanceFrequency, sounds good.
BTW, I looked at another of your posts with your Managed Loop.zip
You mentioned that it's not setup to handle DoEvents.
So what do you do when you DO need to handle user events during a delay?
It does use DoEvents, although I recommend an alternative to it since it's one of the worse functions ever written for VB. And read post #18 which stated this:
Originally Posted by Jacob Roman
That's because you aren't suppose to be using the Sleep API to slow down loops. It halts your program and all of it's events for a number of milliseconds, even after using DoEvents. It's no wonder why you have a hard time closing your app.
So don't use the Sleep API at all
And if you noticed in my code, I had my loop setup like this:
Well, judging by your title of 'DirectX God', I'm guessing your delay loops are for games where time/respnce is critical.
But I'm more likely to use them to halt a program and wait for a user to answer a question or hit a 'continue' button.
In which case I see nothing wrong with using Sleep.
And the 'Stops your app' aspect is exactly what lowers the CPU use.
Last edited by longwolf; Jul 13th, 2005 at 10:36 PM.
Let's see if you are right. You're using XP I assume, is that correct? I'm using ME so I can't do this. Make a sample app. Put the Sleep API in the General declarations and in the Form_Activate event, make it Sleep 100000. While the app is frozen, check how much CPU is being used. Is it low or high?
Let's see if you are right. You're using XP I assume, is that correct? I'm using ME so I can't do this. Make a sample app. Put the Sleep API in the General declarations and in the Form_Activate event, make it Sleep 100000. While the app is frozen, check how much CPU is being used. Is it low or high?
I run everything from win95/win98 - WinNt4-WinXp.
(Several comps with several partions)
My main comp is XP.
Your post raises a lot of Q's.
Why can't you do it on ME?
Why would you want a delay loop in the Form_Activate event?
For grins and giggles I tried it.
The CPU useage was between 0-4%
Most of that was going to IE and taskmanager.
And the 'Stops your app' aspect is exactly what lowers the CPU use
Don't worry about what Taskmanager is telling you the CPU usage is.
If you have a loop with a DoEvents statement in it you'll see 100% CPU usage. All that means is that the CPU is spending time in your loop rather than the idle process. Your DoEvents allows other threads to grab CPU time when they need it.
Putting a sleep statement in your loop only puts it back into the idle process for the sleep time. Besides, putting Sleep 10 in your loop limits your loop times to > 10ms increments.
OK, here is the code.
Make sure that the ShellProce function is in a standard module and that
you export the other two subs.
If this works it will open a world of programming, good luck.
What do I do once I've compiled it? Don't I have to register the hook or something?
Last edited by penagate; Jul 15th, 2005 at 09:38 AM.
So, you should now have two subs you can call from VB in the dll. so from your VB app
VB Code:
Option Explicit
Private Declare Sub SetShellHook Lib "MyDll.dll" ()
Private Declare Sub RemoveShellHook Lib "MyDll.dll" ()
Private Sub Form_Load()
SetShellHook
End Sub
Private Sub Form_Unload(Cancel As Integer)
RemoveShellHook
End Sub
will set the hook
First you have to decided what to do when the message is received. I doubt if you could do something as simple as popup a MsgBox, but you could use sendmessage to send mouse clicks or keypresses back to your VB app.
What's the point of making it a standard DLL anyway? If the code is that simple then it could just be run from an EXE like a regular callback. Obviously I have missed something.
What's the point of making it a standard DLL anyway?
It's because we are setting up a system-wide hook (as opposed to a thread-specific hook).
System-wide hooks can intercept messages dispatched to all threads in the system, and the code for their filter functions must (in most cases) be placed inside a Win32 dll. This is because when a thread needs to call the filter function it needs to map the function into its own address space and this can only be done using a Win32 dll.
So we are really injecting our code into every running thread that can receive shell messages.
moeur I changed SetShellHook into a function that returns the hHook value. But when I call it (It is supposed to display a messagebox with the result) the MsgBox doesn't show up
When you previously used this method to create dlls, did you try to invoke a messagebox from inside the dll?
No
Originally Posted by moeur
Anyway, make SetShellHook a function that returns a Long and retrun the value that way.
That's what I did. The MsgBox call was in the test EXE project. That's why I'm so confused why it didn't work. I also tried stripping all the code except the CallNextHookEx call from ShellProc. Still no luck.
The RemoveShellHook call returns zero and the MsgBox displays. The other one doesn't. I also tried removing the parameter from SetShellHook (like you had it). That didn't produce a MsgBox either.
What is the code inside the dll SetHookFunction, any changes?
Also, you will not be able to make a Notify callback from the dll, so don't worry about passing AddressOf HookNotify. The reason is that when the filter function is called by the message system, it is called in another process which has another address space and so pointers to functions in the VB address space won't make sense.
Also, you will not be able to make a Notify callback from the dll, so don't worry about passing AddressOf HookNotify. The reason is that when the filter function is called by the message system, it is called in another process which has another address space and so pointers to functions in the VB address space won't make sense.
Bugger didn't think of that. What are we supposed to do with the hook then once it is set up anyway?
Last edited by penagate; Jul 15th, 2005 at 12:42 PM.
I found the issue, it was App.hInstance. Changing that to zero made the function work and the Msgbox was displayed correctly (But of course the hook wasn't set).
Because App is part of the VB runtime we can't use that. So we need another way of finding the instance handle, unless we can somehow link to the VB runtime through code.