Sign in to follow this  
Followers 0

Keyboard Hook

37 posts in this topic

Posted

I know this is a long post, but please take a look at what I've done and let me know your thoughts...

I created a hook DLL for AutoIt v3. The release version of the DLL is currently just 25K and is much, much more flexible than the current hotkey support. Right now I just hook the keyboard, but it would be very easy to add a hook for the mouse. I was thinking of adding the mouse hook anyway, just for completeness. With extra work you could add event journalling and playback. It is a shared DLL, so many copies of AutoIt can all use a single system hook.

I sent an email off to the HiddenSoft support email a few days ago to see if they wanted to include my code into the AutoIt base, but I haven't heard anything back yet. My email may have been mis-classified as spam and overlooked. (I've had that happen with another site recently.)

By the way, you can have many global hooks, not just one. The biggest consideration is that the hook code needs to be very fast/efficient because it is getting control for every keystroke.

I created two new AutoIt functions, HookKeySet and HookVirtKeySet. The functions are patterned after HotKeySet, but look a bit uglier because they need to support more functionality/flexibility. The HookVirtKeySet takes a virtual key code and a scan code. The scan code is only used when the VK is 255. I intended it to be used for special keys. For instance, I have a Logitech keyboard with about 20 special internet and multimedia keys than all report as VK=255.

The DLL is automatically loaded when AutoIt is started, but the keyboard hook is not established until and unless you execute a hook function in your script. When AutoIt is ending it will automatically remove all hooks that are still in place. When the last AutoIt calls the unhook method, the DLL will remove the system hook.

With my support, you can:

1. Have up to 10 copies of AutoIt hook every key

2. Have up to 64 regular keys hooked, with up to 4 hooks per key

3. Have up to 50 hooks of special (vk=255) keys

(All of the above can be changed by updating constants and recompiling.)

4. When hooking every key you can:

a) Get the key irregardless of the current modifier keys

B) Get the key when at least one of a set of modifier keys is pressed

c) Get the key only when a specific set of modifier keys is pressed

5. When hooking a single key you have the same 3 combinations ( as above) in regards to modifier keys. It is also possible to hook a regular key (like "a"-"z") without any modifier keys (I don't think hot keys handle this), or even to hook a single modifier key (i.e. have a user function get control when the ctrl key is pressed).

6. On a hook-by-hook basis you can decide whether you want the hooked key eaten by the hook or passed on to the application that has the input queue. My expectation is that you want to hook keys to perform an action when a key combination is pressed, so the default is to eat the key. The option to pass the key on fits more with system recording or monitoring.

7. I added new AutoIt macros to provide information about the last hooked key event. This is necessary because one user function may need to handle multiple keys. You can get:

a) virtual key code

B) key name

c) scan code

d) modifier keys pressed at the time

The support should work on Win98, and possibly even on Win95 (I don't have docs that far back). However, it works best on Win2K and newer because those versions have a new type of hook (KeyboardLL) available.

On Win98 & ME, if you create both a hook and a hotkey for a given key combination then the hotkey wins and the hook event will never happen. On Win98 & ME you can also have problems when the active application gets behind in it's processing of the input queue. This happens because the modifier keys are checked when the key is processed instead of when the key is pressed. It seems to be a necessary evil, because key states are not added to the virtual keyboard until after the hooks run, which forces me to go straight to the lower-level key status.

So far I've done almost all my testing on WinXP, but I have run a couple of times on Win98SE. I don't have any of the intermediate versions available for testing, but it should work just fine.

So... What do you think? Is this useful to anyone? Did I miss anything, other than my current lack of mouse hooks? Are mouse hooks more useful or less useful than key hooks?

-Mepper

Share this post


Link to post
Share on other sites



Posted

There is a way to have the hook in your .exe, however I'm not sure if doing it can cause problems. Microsoft definitely doesn't recommend it, they don't even mention it. That's why I built a separate DLL.

If I built a version with the hook integrated into the main .exe, do you have any way to verify that it doesn't cause any problems on any version of Windows?

Share this post


Link to post
Share on other sites

Posted

I've been doing a little thinking...

I believe that one of the implications of trying to move the hook into the .exe would be that each instance of AutoIt would have to use a separate global hook instead of having a single global hook established by the DLL. This would have a bit more impact on the system, but you'd probably never notice it if your PC is faster than 500MHz.

There would also be a problem if AutoIt terminated without undoing the global hook. I'm not sure what would happen on WinXP, but I'll bet that it would cause some older versions of Windows to crash.

-Mepper

Share this post


Link to post
Share on other sites

Posted

How do you go about creating a global hook without a DLL? Where did you find the information on how to do it?

Share this post


Link to post
Share on other sites

Posted

I've seen claims in a number of places that it can be done. I saw actual code and description in one place, which unfortunately I forgot to bookmark. It worked by passing the handle to the .exe for the HINSTANCE parameter on the call to set the hook. I suspect that the true mechanism that allows it to work is that the hook callback has a DLL-style entry point even though it is in an .exe and the system loads the whole .exe into every address space just like it would for a DLL. You definitely wouldn't want to do it if your .exe had any real size to it. Given that it is an .exe and not a .dll you may have problems with shared segments, if the linker treats the two any differnetly. I would have to do some experiments to find out. Using a .dll is definitely the saner and safer approach.

So... What exactly is so bad about shipping a DLL with AutoIt? I can see the problem if the DLL has to go in a system directory, but I can't see the problem when the DLL can be kept in the local directory. AutoIt already ships with one DLL.

Right now my code uses a statically linked DLL, just for my convenience. It could easily be changed to dynamically load the DLL so that the DLL wouldn't be required unless the hook functions were actually used.

Share this post


Link to post
Share on other sites

Posted

I thought I saw a post where Jon said he didn't really want to include keyboard or mouse hooks because it might cause AntivirusSoftware to begin flagging AutoIt scripts as malicious.....

But having full hook functionality would be useful

Share this post


Link to post
Share on other sites

Posted

As I stated in another thread. External DLL's create a dependancy on that DLL. Yes, AutoIt does include a DLL, but it's a Microsoft DLL they pretty much forced you to use. It's not Jon's choice to include it, I'm sure, but it's necessary because of an inconsitency with the process stuff on various versions of Windows. That being said, Microsoft is to blame for that DLL, however, a hook-callback DLL or any other DLL (Excluding future potential for a plugin API, of course) creates a dependancy by the author of the software, which Jon doesn't want to do.

That is the reason for not including a DLL in general, and then there is what CyberSlug said.

As far as global hooks in EXE's, most likely, the code does indeed load an EXE in the manner you have described. However, as you have already stated, it's much saner to load a DLL. I searched CodeProject and Google and neither turned up any results on global hooks in DLL's. If CodeProject doesn't have anything, I would interpret that as meaning it's not a very useful (or safe) method to use. It sounds more like a dirty hack to me.

Share this post


Link to post
Share on other sites

Posted

not that this is news....

but I am liking this...A LOT

Share this post


Link to post
Share on other sites

Posted

...mmnm... What is a keyboard hook? :D:huh2:

Share this post


Link to post
Share on other sites

Posted

Maybe this can be accomplished as a kind of plugin? You could FileInstall the hook.dll in any script that needed to use it.

Perhaps even as a proper plugin ((I seem to recall some mention of plugins recently))?

Share this post


Link to post
Share on other sites

Posted

What is a keyboard hook?

Share this post


Link to post
Share on other sites

Posted

I wanted to add my two cents about this thread. One of the advantages of AutoIt is that it has no dlls (except for the Microsoft required one). This is a great advantage because some companies do not permit any system modifications. Also, some users do not have authority to install and register dlls. When you are told that thing you installed is causing a problem and you can safely say it doesnt use any dlls or change any standard dlls, they usually go looking for others to blame. So, a utility that is portable between machines, interacts with the Windows platform, is self contained, can be made into an exe, has more than a message box interface (VBS) and can handle many scripting challenges is why I like AutoIt. Maybe there is a way to have specialized plugins that keep the base tool intact.

Thanks

Share this post


Link to post
Share on other sites

Posted

It is not a problem, if you keep the dll in the program folder or merge it in the compiled executable with fileinstall.

Share this post


Link to post
Share on other sites

Posted

Look at the sticky at the top of this forum. See the entry called "plug-ins"? More than likely that will end up being some sort of API so that DLL's adding new script functions can be loaded. If that ends up being the case, then you can write or find somebody to write a keyboard hook for you. But I highly doubt if you will ever see a keyboard hook in the official distribution of AutoIt. I doubt you will even see a DLL of any sort (With the exception of PSAPI.dll) ever in an official distribution. I was working on such an API and it worked fine, but I found a flaw in it which compromised one of the original goals of a plugin architecture in the first place, so it's been put to the side indefinitely.

ezzetabi, a keyboard hook traps all keyboard events going to the system and let's you have first dibs at handling them. This can be anything from a low-level hotkey filter to blocking keyboard input altogether (By throwing the event away instead of passing it on to it's target), to logging all the events and creating a key-logger for password theft or whatever.

Share this post


Link to post
Share on other sites

Posted

Condoman, the way I handled it you do not need administrative authority to install the DLL. The DLL is not a change to a system DLL. The DLL is never registered. Because the DLL is never registered, it is located via the standard search path. In particular, it is found by being in the same directory as the AutoIt exe. If you follow standard good practices by installing AutoIt into its own directory then there is no way the DLL can affect any other programs even if the DLL name happened to match the name of a DLL from a different package.

The way I built it, there is no strong dependency on having the DLL. If you do not wish to use the keyboard hook functionality then you can run my customized version of AutoIt just fine without the DLL. When you make your first keyboard hook call from a script then the DLL will be loaded dynamically. If the DLL is not found then the hook function fails gracefully. For anyone that does not understand these matters, if you try to run a program and it fails saying that a DLL cannot be found then the program was either statically linked to the DLL or else the programmer made a concious decision that the program should not run without the DLL.

The keyboard hook itself is treated similarly. If you do not use any keyboad hook functionality then the keyboard hook is never established. It is only when you make your first keyboard hook call from a script that the keyboard hook is established. At the end the keyboard hook is removed. In other words, barring a catestrophic failure with no chance to clean up, the keyboard hook only exists while AutoIt is running and only exists if it is actively in use.

Share this post


Link to post
Share on other sites

Posted

I just finished adding support for a mouse hook. That drove my DLL size up to 28K. I handled it similarly to the keyboard hook. It even handles combinations like <ctrl><left mouse double click> and <alt><mouse wheel backward>. Next week I'll pick up the code again and add support for 5-button mice.

I also fixed the code so that it wasn't sensitive to the current working directory. It now specifically looks in the directory containing AutoIt.exe instead of using the system search path.

Share this post


Link to post
Share on other sites

Posted

is having the dll in the current directory the only option?

can you add a dll directory to the Opt list? that way I could FileInstall the dll and have it extracted to the TEMP folder.

Share this post


Link to post
Share on other sites

Posted

Matt, I didn't look hard, but the AutoIt installer code is not part of the source code package. (Besides, I really didn't want to mess with the installer.) So... I don't do anything at install time and there is nothing in the registry, etc., for my DLL.

I use GetModuleFileName() to find the directory containing the AutoIt.exe that is executing. I then use the LOAD_WITH_ALTERED_SEARCH_PATH flag with LoadLibraryEx() to specify the AutoIt.exe directory as the first place to look for my DLL.

The DLL really doesn't have to be in the same directory as the exe. It is just the most convenient place for me. The DLL directory search order for LoadLibraryEx is:

1. The directory specified by the lpFileName path. (In other words, the directory that the AutoIt.exe is in.)

2. The current directory.

3. The system directory. Use the GetSystemDirectory function to get the path of this directory.

4. The 16-bit system directory. There is no function that obtains the path of this directory, but it is searched. (Windows Me/98/95: This directory does not exist.)

5. The Windows directory. Use the GetWindowsDirectory function to get the path of this directory.

6. The directories that are listed in the PATH environment variable.

(The above information was extracted from the Windows Platform SDK documentation.)

If you wanted to put the DLL in any location other than #1 or #2 then it could easily be seen by other applications. It would also be easy to either accidentally use an old version of the DLL after upgrading or else leave it hanging around as garbage after uninstalling. You are allowed to do it and it would work; I just think it is a bad idea.

Requiring it be in the current directory might be bad also. What if you had multiple directories containing scripts or logs and you wanted to run AutoIt with the working directory set to the appropriate script or log directory? You would have to copy the DLL into each script directory, which is really ugly. Since a script can itself change the current directory it could get very confusing.

Share this post


Link to post
Share on other sites

Posted

Could you tell me where I could find this DLL and some Autoit script example?

I am a new user to Autoit and very interested in this DLL.

Thank you...

Share this post


Link to post
Share on other sites
Sign in to follow this  
Followers 0