Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations derfloh on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Log keypresses 1

Status
Not open for further replies.

LuckyLuke

Programmer
Mar 28, 2001
188
NL
Hi,

I want to log all keypresses, also when my application is minimized or not active, etc. I know how to create hotkeys, but I didn't find out how to log all keys, and making a hotkey of every key on the keyboard doesn't sound logic right ;) Anyway, I tried WM_KEYDOWN with subclassing, but my program doesn't return anything when I press a key then...

Thanks for your help!

LuCkY
 
Hi LuckyLuke,

Try thread711-33735 for the basics. Don't extend your hopes beyond today (nobody has *fully* answered this question at Tek-Tips, yet).

Hmmm.... I guess that some of us are still scratching our heads and puttering over the various shift-states.
VCA.gif

Alt255@Vorpalcom.Intranets.com
Don't sit down, it's time to dig another one.
 
Alt255,
I am still not convinced that the GetAsyncKeyState API is the way to go on this for reasons that I have previously posted and have reposted here . . .


you have put a looping function to check each key for a key press that is going to run forever in the background. The repeated looping within the timer callback is a waste of CPU time.
Also, The GetAsyncKeyState must be called for each and every key on the keyboard. It will determine if the key is being pressed or has been pressed since the last time the function was called. Normally, this would work fine, but consider this . . . someone pushes the "q" right AFTER your for-next loop checks for it. Then, some other program calls GetAsyncKeyState on the "q" key (effectively clearing its flag) before your loop has a chance to recheck it. When your looping code comes back to check the "q" key, it will not know that it has been pressed.


I think a better approach (and this is just my opinion :)) would be to monitor the OS message queue since the OS will automatically put a message in there each time any key is pressed . . . However, this method is not without its problems. This method works great when hooking into the message queue for a specific process, but when ever I try to hook into the queue for all processes, I get a GPF or similar errors on the callback. I'm still trying to get past that one . . .
- Jeff Marler B-)
 
Understood, Jeff. Wouldn't it be great if somebody found an efficient way to do this without crashing the system? :)

I can see where an excellent programmer, such as yourself, would be disgusted by the apparent sloth of a For/Next loop in a timer. On the other hand, I envision a mediocre programmer, such as myself, trying to create a routine that will run for years without a GPF or a noticable loss of performance.

I think there is an association here. There is a desperate need for code that can squeeze every drop of performance out of the Windows platform (this code is NOT written in Visual Basic). There is also a need for code designed to perform minor tasks every few milliseconds (this code is written by programmers such as LuckyLuke and myself).

You can't do what you are proposing with VB6. It might've worked with earlier versions and it might work again with VBnet. I just don't see a way to do it today without migrating to a different language.

I hope I'm wrong.


VCA.gif

Alt255@Vorpalcom.Intranets.com
Don't sit down, it's time to dig another one.
 
Hiya,

First of all, thanks for your (quick) responses. Second of all, thanks for the basics. I can't believe though, that there isn't a way to get combined keystrokes like SHIFT+A. After all you are in your example testing 255 codes, and if I'm not mistaking, these include the combined keystrokes aswell don't they? Anyway, isn't there a way such as subclassing, which isn't eating up your CPU, but still get all keystrokes?

LuCkY
 
LuckyLuke,
The subclassing method (w/o eating up CPU) would require the callback method to monitor messages in the message queue for the entire OS and not just a specific process . . . this is the piece that I am still trying to solve. I am convinced that there is a way to do it and that I just havn't found it yet :-(. If I figure it out, I will definitely post it in here. In the meantime, the API that Alt255 suggested is probably your best bet, but it too has is drawbacks (see my previous post). I think that this is just a case of "what can you do?". For the time being, you should go with Alt255's idea with the realization that you may, on occasion, miss some keystrokes and that some CPU time will be wasted. And the CPU time is not that big of a deal (Alt255 is correct when he stated that most apps designed for effiency are not written in VB - but that should not be an excuse for bad coding practices) . . . the bigger problem is that you might miss some of the keystrokes.

Alt255,

"Wouldn't it be great if somebody found an efficient way to do this without crashing the system"



Yes it would! And believe me . . . I'm working on it (any ideas?)! If I figure it out, I will definitely post it so every one can see it. By the way, on a personal note, I have read a lot of your posts, and even though I have not always agreed with you, I would not consider you to be a mediocre programmer by any stetch of the imagination. Just my 2 cents so to speak. :). - Jeff Marler B-)
 
LuckyLuke,

OK, if anyone is interested, I was able to get a global callback from the OS message queue to work! This was done without any looping or timer controls . . . however, it requires a small C++ DLL that I wrote since the callback function MUST be in a DLL in order to hook into the global message queue.
I know that this is a VB forum, but if you are still interested, and you don't mind some C++ code (very minimal) then I will post it in here.
I still have not found a way to efficiently do this in pure Visual Basic.

Alt255,

"Wouldn't it be great if somebody found an efficient way to do this without crashing the system"


Yes it would, and I have it now - without any loops! - Jeff Marler B-)
 
Got that already ;-) I wrote a dll in C++ too, it's small and quite easy actually. Its fast and not eating CPU. It logs any keystrokes, but mine doesn't log F1 to F12, Insert, Delete, PgUp, Home, End and PgDown... Does yours log them Jeff? I could pick them by settings some hotkeys to my program, but it would be great if it could be done with the same dll. And yes, I'm interested in the C++ code to see how you did it (Probably the same, but... ;-)) Anyway, Thanks a lot.

LuCkY
 
Okay. I found ways to do that with a couple of other languages. I HATE the "can't" word and I'm sorry I posted it. When VB fails I *always* rely on my backups.

Any thoughts on using VB6 to circumvent the MS language limitations? Post the DLL for evaluation and let's find a way to perform the same tasks with VB.
VCA.gif

Alt255@Vorpalcom.Intranets.com

all ignorance toboggans into know
and trudges up to ignorance again:
but winter's not forever, even snow
melts; and if spring should spoil the game,what then?

e.e. cummings
 
LuckyLuke,
Yes it logs all function keys (F1-F12), Pageup, page downs, etc. It will even log a Ctrl-Alt-Delete.
Let me organize my code a bit and put some thoughts together and then I will post it. I would like to see if it could be done in VB, but since the callback has to be in a DLL and VB can only make ActiveX DLLs (I don't think the call back can be in an ActiveX DLL . . . actually, it can, but it doesn't seem to behave the same way) I am not sure how it can be done (In Pure VB). - Jeff Marler B-)
 
ok . . . here is the C++ portion of the code. I have excluded the .DEF file.


#include "stdafx.h"


HANDLE m_hModule;
HWND m_hWnd;

BOOL APIENTRY DllMain( HANDLE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{


//Keep track of the DLL handle.

m_hModule = hModule;

return TRUE;

}


LRESULT CALLBACK KeyboardProc( int nCode, WPARAM wParam, LPARAM lParam)
{


/*
This Routine is call whenever a key is pressed in any
window. The following params are returned -

nCode:

wParam: The virtual keycode of the key that
generated the message.
lParam: Bit Mask as follows -

Bit 00-15 :: The Repeat Count.
Bit 16-23 :: The Scan Code.
Bit 24 :: Extended key. Set to 1 if the
key is an extended key;
otherwise, it is 0.
Bit 25-28 :: Reserved
Bit 29 :: The Context code. Set to 1 if
the ALT key is down;
otherwise, it is 0.
Bit 30 :: The Previous Key State. Set to
1 if the key was down before
the message is sent;
otherwise, it is 0 if the key
is up.
Bit 31 :: The transistion state. Set to
0 if the key is being pressed
and 1 if it is being released.

*/


MessageBox (m_hWnd ,"Key Pressed","KeyBoardHook", 1);
SendMessage (m_hWnd,nCode,wParam,lParam);

return 0;

}


LONG APIENTRY MySetWindowHook(HWND hWnd)
{

HHOOK hHook;
m_hWnd = hWnd;
hHook = SetWindowsHookEx(WH_KEYBOARD, (HOOKPROC)
KeyboardProc, (HINSTANCE)m_hModule, 0 );

return (LONG)hHook;

}




This is compiled into a dll and MySetWindowsHook is called from a VB application. After that, any keypress, in any window, will cause KeyBoardProc to fire.
Now I know that this is not complete (I will post the rest later - I have to go to work now), but it should give you the basic idea of what I am doing. This code still needs to be able to eventually release its hook and I need to implement a callback into the VB code so that the VB client knows about the keypress (still fine tuning the VB callback process).
If you are interested, you can implement the KeyboardProc callback in VB as well by placing the following code in a module . . .


Private Sub KeyboardProc(ByVal intCode As Integer, ByVal WPARAM As Long, ByVal LPARAM As Long)


'** Do something with the data


End Sub


but since it is not in a DLL, it will only monitor the current process. I have tried moving the mod to an ActiveX DLL (which I kept in memory by hanging onto a reference) and while it will receive messages about keystrokes in the current process, it does not know about keystrokes from other processes . . . to do that, I had to use the C++ code/DLL listed above.
Hope this helps you out with your problem! And, if you have any idea regarding how to implement the callback in pure VB let me know . . .
its not that I don't like using C++ (actually, I like to use it) . . . I am just curious to know if this could be done with PURE VB.

- Jeff Marler B-)
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top