digitalmars.D.learn - WinAPI LowLevel Keyboard Hooks
- DLimited (25/25) Jul 19 2012 Hello everyone,
- David (2/4) Jul 19 2012 The W-Function is the Unicode version and the A is the ANSI version.
- DLimited (86/86) Jul 19 2012 But what are the differences of loading the Unicode version vs.
- dnewbie (9/95) Jul 19 2012 I guess you have to 'export' the function:
- DLimited (4/12) Jul 19 2012 Thanks, I changed that. Also, I changed LoadLibraryW( ) to
- DLimited (97/112) Jul 19 2012 Acutally, that was only the half-truth. The .dll seems to get
- dnewbie (3/3) Jul 19 2012 You don't see the "WHOA" message?
- DLimited (3/6) Jul 19 2012 No, I don't get any message after key-presses. I changed int
- dnewbie (4/10) Jul 19 2012 For some reason, it doesn't work with 'Thread.sleep'
- DLimited (8/19) Jul 19 2012 It doesn't work for me. I can 1 Message Box from the Code in
- dnewbie (6/27) Jul 19 2012 Did you add
- DLimited (4/33) Jul 19 2012 Yes, I did. Are the newlines important?
- dnewbie (4/7) Jul 19 2012 Yes, I get 2 "WHOA" messages. One from the WM-KEYDOWN and the
- DLimited (7/15) Jul 19 2012 THANK YOU for your help! It works now!
- Mike Parker (8/24) Jul 19 2012 The runtime is initialzed automatically in an executable because it has
- Mike Parker (4/6) Jul 19 2012 You should always be using the Unicode version of Win32 functions in new...
- torhu (4/10) Jul 21 2012 If you don't want make your own program, you can go here instead:
Hello everyone, I had this great idea of writing a Program that intercepts all keyboard presses and modifies them in certain cases. I want to use it as some kind of global makro program to run in the background and for example allow me to easily post unicode smileys. This is where the probelms begin. If I understood the WinAPI doc correctly, I need to install a LowLevel Keyboard Hook using SetWindowsHookEx(). Unfortunately there are two versions of this function, SetWindowsHookExW and SetWindowsHookExA. What's the difference? The function to get called is passed as a parameter, but apparently it needs to be located in a .dll if using global hooks. So I need to first load my .dll file using LoadLibraryA( ) or LoadLibraryW( ), locate my function using GetProcAddress( ) and then set the hook using SetWindowsHookExW (or *-A)). Unfortunately for me, even the LoadLibrary function fails, returning "Module could not be found". Now I'm 99% sure my .dll is crap because this is the first time I ever wrote one, but it DOES have a DllMain(), initializes the D Runtime and also has the other function I want to install the hook for. I'm hoping some bright minds here could help me out because google didn't, and I'm out of my depth. If you want me to, I'll post the source code, but I didn't want this post to get too big, which still did, but anyway.
Jul 19 2012
Unfortunately there are two versions of this function, SetWindowsHookExW and SetWindowsHookExA. What's the difference?The W-Function is the Unicode version and the A is the ANSI version. Showing the code of your DLL might help.
Jul 19 2012
But what are the differences of loading the Unicode version vs. the ANSI version? I called the Unicode one because I figured that would be the sensible choice, since Unicode is the default for D (if I remember correctly). I have no clue what the actual effects of calling the wrong version would be. Anyway, here's the of my .dll: < ------ Code begin ------ > import std.c.windows.windows; import core.sys.windows.dll; import core.runtime; extern (C) void gc_init(); extern (C) void gc_term(); extern (C) void _minit(); extern (C) void _moduleCtor(); extern (C) void _moduleDtor(); extern (Windows) struct KBDLLHOOKSTRUCT { DWORD vkCode; DWORD scanCode; DWORD flags; DWORD time; ULONG_PTR dwExtraInfo; }; extern (Windows) LRESULT CallNextHookEx( int function() hhk, int nCode, WPARAM wParam, LPARAM lParam ); __gshared HINSTANCE g_hInst; extern (Windows) BOOL DllMain(HINSTANCE hInstance, ULONG ulReason, LPVOID pvReserved) { return true; switch (ulReason) { case DLL_PROCESS_ATTACH: g_hInst = hInstance; Runtime.initialize; //dll_process_attach( hInstance, true ); break; case DLL_PROCESS_DETACH: dll_process_detach( hInstance, true ); break; case DLL_THREAD_ATTACH: dll_thread_attach( true, true ); break; case DLL_THREAD_DETACH: dll_thread_detach( true, true ); break; default: return true; } return true; } extern (Windows) LRESULT LowLevelKeyboardProc(int code, WPARAM wParam, LPARAM lParam) { KBDLLHOOKSTRUCT* details = cast(KBDLLHOOKSTRUCT*) lParam; MessageBoxA(null, cast(char *)"WHOA", "Error", MB_OK | MB_ICONEXCLAMATION); if(code == 0 && wParam == WM_KEYDOWN) { if(details.vkCode == 0x41) { return 1; } } return CallNextHookEx(null, code, wParam, lParam); } < ------ Code End ------ > Lots of copy&paste was used. I injected some senseless code to try and check if a specific function ever gets called, though I now realise the DllLoad itself is what fails. Haven't cleaned it back up yet, though. The .def file contains the following: (including newlines) < ------ .DEF BEGIN ------ > LIBRARY "keydll.dll" EXETYPE NT SUBSYSTEM WINDOWS CODE PRELOAD DATA PRELOAD < ------ .DEF END ------ > I compiled the dll using: dmd -ofkeydll.dll -L/IMPLIB keydll.d keydll.def No linker/compiler errors.
Jul 19 2012
On Thursday, 19 July 2012 at 15:49:48 UTC, DLimited wrote:But what are the differences of loading the Unicode version vs. the ANSI version? I called the Unicode one because I figured that would be the sensible choice, since Unicode is the default for D (if I remember correctly). I have no clue what the actual effects of calling the wrong version would be. Anyway, here's the of my .dll: < ------ Code begin ------ > import std.c.windows.windows; import core.sys.windows.dll; import core.runtime; extern (C) void gc_init(); extern (C) void gc_term(); extern (C) void _minit(); extern (C) void _moduleCtor(); extern (C) void _moduleDtor(); extern (Windows) struct KBDLLHOOKSTRUCT { DWORD vkCode; DWORD scanCode; DWORD flags; DWORD time; ULONG_PTR dwExtraInfo; }; extern (Windows) LRESULT CallNextHookEx( int function() hhk, int nCode, WPARAM wParam, LPARAM lParam ); __gshared HINSTANCE g_hInst; extern (Windows) BOOL DllMain(HINSTANCE hInstance, ULONG ulReason, LPVOID pvReserved) { return true; switch (ulReason) { case DLL_PROCESS_ATTACH: g_hInst = hInstance; Runtime.initialize; //dll_process_attach( hInstance, true ); break; case DLL_PROCESS_DETACH: dll_process_detach( hInstance, true ); break; case DLL_THREAD_ATTACH: dll_thread_attach( true, true ); break; case DLL_THREAD_DETACH: dll_thread_detach( true, true ); break; default: return true; } return true; } extern (Windows) LRESULT LowLevelKeyboardProc(int code, WPARAM wParam, LPARAM lParam) { KBDLLHOOKSTRUCT* details = cast(KBDLLHOOKSTRUCT*) lParam; MessageBoxA(null, cast(char *)"WHOA", "Error", MB_OK | MB_ICONEXCLAMATION); if(code == 0 && wParam == WM_KEYDOWN) { if(details.vkCode == 0x41) { return 1; } } return CallNextHookEx(null, code, wParam, lParam); } < ------ Code End ------ > Lots of copy&paste was used. I injected some senseless code to try and check if a specific function ever gets called, though I now realise the DllLoad itself is what fails. Haven't cleaned it back up yet, though. The .def file contains the following: (including newlines) < ------ .DEF BEGIN ------ > LIBRARY "keydll.dll" EXETYPE NT SUBSYSTEM WINDOWS CODE PRELOAD DATA PRELOAD < ------ .DEF END ------ > I compiled the dll using: dmd -ofkeydll.dll -L/IMPLIB keydll.d keydll.def No linker/compiler errors.I guess you have to 'export' the function: extern (Windows) export LRESULT LowLevelKeyboardProc(int code, WPARAM wParam, LPARAM lParam) and include EXPORTS LowLevelKeyboardProc in the .DEF file
Jul 19 2012
I guess you have to 'export' the function: extern (Windows) export LRESULT LowLevelKeyboardProc(int code, WPARAM wParam, LPARAM lParam) and include EXPORTS LowLevelKeyboardProc in the .DEF fileThanks, I changed that. Also, I changed LoadLibraryW( ) to LoadLibraryA( ) in the main program and now it works (kinda). I feel stupid now, although I still don't get why it wouldn't work with LoadLibraryW.
Jul 19 2012
On Thursday, 19 July 2012 at 16:38:19 UTC, DLimited wrote:Acutally, that was only the half-truth. The .dll seems to get loaded correctly ( GetLastError returns 0), but my keyboard-presses aren't captured at all. My system seems to freeze up for ~5sec, after which everything resumes. Any keyboard input seems to get buffered and is processed by my terminal after my program closes. Also I'm unsure about types because the often-used HHOOK is not defined with my imports, so I'm left guessing what it is. I used the type int function() instead (my best guess). Here's the code: < ------- CODE BEGIN --------- > import std.c.windows.windows; import std.stdio; import core.thread; extern (C) void gc_init(); extern (C) void gc_term(); extern (C) void _minit(); extern (C) void _moduleCtor(); extern (C) void _moduleDtor(); extern (C) void _moduleUnitTests(); extern (Windows) int function() SetWindowsHookExA( int idHook, HOOKPROC lpfn, HINSTANCE hMod, DWORD dwThreadId ); extern (Windows) struct KBDLLHOOKSTRUCT { DWORD vkCode; DWORD scanCode; DWORD flags; DWORD time; ULONG_PTR dwExtraInfo; }; extern (Windows) LRESULT CallNextHookEx( int function() hhk, int nCode, WPARAM wParam, LPARAM lParam ); extern (Windows) bool UnhookWindowsHookEx( int function() hhk ); extern (Windows) HMODULE LoadLibraryA( LPCTSTR lpFileName ); extern (Windows) int WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { int result; gc_init(); // initialize garbage collector _minit(); // initialize module constructor table try { _moduleCtor(); // call module constructors //_moduleUnitTests(); // run unit tests (optional) result = myWinMain(hInstance, hPrevInstance, lpCmdLine, nCmdShow); _moduleDtor(); // call module destructors } catch (Exception o) // catch any uncaught exceptions { MessageBoxA(null, cast(char *)o.toString(), "Error", MB_OK | MB_ICONEXCLAMATION); result = 0; // failed } gc_term(); // run finalizers; terminate garbage collector return result; } int myWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { HOOKPROC hkprcSysMsg; HINSTANCE hinstDLL; extern (Windows) int function() hhookSysMsg; hinstDLL = LoadLibraryA(cast(LPCTSTR)"correct_absolute_path\\keydll.dll"); writeln(GetLastError()); //returns 0 hkprcSysMsg = cast(HOOKPROC)GetProcAddress(hinstDLL, "LowLevelKeyboardProc"); writeln(GetLastError()); //return 0 hhookSysMsg = SetWindowsHookExA( 13, hkprcSysMsg, hinstDLL, 0); writeln(GetLastError()); // returns 0 aswell Thread.sleep( dur!("seconds")(10) ); UnhookWindowsHookEx( hhookSysMsg ); return 0; }I guess you have to 'export' the function: extern (Windows) export LRESULT LowLevelKeyboardProc(int code, WPARAM wParam, LPARAM lParam) and include EXPORTS LowLevelKeyboardProc in the .DEF fileThanks, I changed that. Also, I changed LoadLibraryW( ) to LoadLibraryA( ) in the main program and now it works (kinda). I feel stupid now, although I still don't get why it wouldn't work with LoadLibraryW.
Jul 19 2012
You don't see the "WHOA" message? Try this alias HANDLE HHOOK;
Jul 19 2012
On Thursday, 19 July 2012 at 17:35:29 UTC, dnewbie wrote:You don't see the "WHOA" message? Try this alias HANDLE HHOOK;No, I don't get any message after key-presses. I changed int function() to HANDLE, sadly it still doesn't work.
Jul 19 2012
On Thursday, 19 July 2012 at 17:48:06 UTC, DLimited wrote:On Thursday, 19 July 2012 at 17:35:29 UTC, dnewbie wrote:For some reason, it doesn't work with 'Thread.sleep' This works: http://dpaste.dzfl.pl/1e6e5960You don't see the "WHOA" message? Try this alias HANDLE HHOOK;No, I don't get any message after key-presses. I changed int function() to HANDLE, sadly it still doesn't work.
Jul 19 2012
On Thursday, 19 July 2012 at 18:40:15 UTC, dnewbie wrote:On Thursday, 19 July 2012 at 17:48:06 UTC, DLimited wrote:It doesn't work for me. I can 1 Message Box from the Code in MyWinMain, but none for the Keystrokes. I registered a hook for Keyboard input, and that code is supposed to produce a message box aswell. The function for that is called LowLevelKeyboardProc and located in above-mentioned .dll file. The registering of the hook seems to pass, but the function never actually gets called.On Thursday, 19 July 2012 at 17:35:29 UTC, dnewbie wrote:For some reason, it doesn't work with 'Thread.sleep' This works: http://dpaste.dzfl.pl/1e6e5960You don't see the "WHOA" message? Try this alias HANDLE HHOOK;No, I don't get any message after key-presses. I changed int function() to HANDLE, sadly it still doesn't work.
Jul 19 2012
On Thursday, 19 July 2012 at 18:56:15 UTC, DLimited wrote:On Thursday, 19 July 2012 at 18:40:15 UTC, dnewbie wrote:Did you add EXPORTS LowLevelKeyboardProc to the .DEF file? It works here.On Thursday, 19 July 2012 at 17:48:06 UTC, DLimited wrote:It doesn't work for me. I can 1 Message Box from the Code in MyWinMain, but none for the Keystrokes. I registered a hook for Keyboard input, and that code is supposed to produce a message box aswell. The function for that is called LowLevelKeyboardProc and located in above-mentioned .dll file. The registering of the hook seems to pass, but the function never actually gets called.On Thursday, 19 July 2012 at 17:35:29 UTC, dnewbie wrote:For some reason, it doesn't work with 'Thread.sleep' This works: http://dpaste.dzfl.pl/1e6e5960You don't see the "WHOA" message? Try this alias HANDLE HHOOK;No, I don't get any message after key-presses. I changed int function() to HANDLE, sadly it still doesn't work.
Jul 19 2012
On Thursday, 19 July 2012 at 19:43:45 UTC, dnewbie wrote:On Thursday, 19 July 2012 at 18:56:15 UTC, DLimited wrote:Yes, I did. Are the newlines important? And you really get a MessageBox per keystroke? I start as admin, disabled my AV but still, no success.On Thursday, 19 July 2012 at 18:40:15 UTC, dnewbie wrote:Did you add EXPORTS LowLevelKeyboardProc to the .DEF file? It works here.On Thursday, 19 July 2012 at 17:48:06 UTC, DLimited wrote:It doesn't work for me. I can 1 Message Box from the Code in MyWinMain, but none for the Keystrokes. I registered a hook for Keyboard input, and that code is supposed to produce a message box aswell. The function for that is called LowLevelKeyboardProc and located in above-mentioned .dll file. The registering of the hook seems to pass, but the function never actually gets called.On Thursday, 19 July 2012 at 17:35:29 UTC, dnewbie wrote:For some reason, it doesn't work with 'Thread.sleep' This works: http://dpaste.dzfl.pl/1e6e5960You don't see the "WHOA" message? Try this alias HANDLE HHOOK;No, I don't get any message after key-presses. I changed int function() to HANDLE, sadly it still doesn't work.
Jul 19 2012
On Thursday, 19 July 2012 at 19:51:31 UTC, DLimited wrote:Yes, I did. Are the newlines important? And you really get a MessageBox per keystroke? I start as admin, disabled my AV but still, no success.Yes, I get 2 "WHOA" messages. One from the WM-KEYDOWN and the other from WM-KEYUP. Sorry I don't know what is wrong.
Jul 19 2012
On Thursday, 19 July 2012 at 20:06:55 UTC, dnewbie wrote:On Thursday, 19 July 2012 at 19:51:31 UTC, DLimited wrote:THANK YOU for your help! It works now! I didn't call Runtime.initialize; in my exported function because I thought the runtime would already be initialized in the DllMain - seems like that is not the case. Wouldn't have thought of it if I hadn't had your sample code, though! Thanks a ton!Yes, I did. Are the newlines important? And you really get a MessageBox per keystroke? I start as admin, disabled my AV but still, no success.Yes, I get 2 "WHOA" messages. One from the WM-KEYDOWN and the other from WM-KEYUP. Sorry I don't know what is wrong.
Jul 19 2012
On 7/20/2012 5:17 AM, DLimited wrote:On Thursday, 19 July 2012 at 20:06:55 UTC, dnewbie wrote:The runtime is initialzed automatically in an executable because it has its own C 'main' entry point. That gets called first, the runtime does all its initialization, then it calls your D main function. In a DLL, the entry point is DllMain, not 'main'. So the runtime is not initialized at startup and you have to do it manually. The same holds true if you create an application with a WinMain entry point instead of main.On Thursday, 19 July 2012 at 19:51:31 UTC, DLimited wrote:THANK YOU for your help! It works now! I didn't call Runtime.initialize; in my exported function because I thought the runtime would already be initialized in the DllMain - seems like that is not the case. Wouldn't have thought of it if I hadn't had your sample code, though! Thanks a ton!Yes, I did. Are the newlines important? And you really get a MessageBox per keystroke? I start as admin, disabled my AV but still, no success.Yes, I get 2 "WHOA" messages. One from the WM-KEYDOWN and the other from WM-KEYUP. Sorry I don't know what is wrong.
Jul 19 2012
On 7/20/2012 12:49 AM, DLimited wrote:But what are the differences of loading the Unicode version vs. the ANSI version?You should always be using the Unicode version of Win32 functions in new applications. AFAIK, the ANSI versions call the Unicode versions internally, but do a conversion on the string args before doing so.
Jul 19 2012
On 19.07.2012 13:45, DLimited wrote:Hello everyone, I had this great idea of writing a Program that intercepts all keyboard presses and modifies them in certain cases. I want to use it as some kind of global makro program to run in the background and for example allow me to easily post unicode smileys.If you don't want make your own program, you can go here instead: http://www.autohotkey.com But you probably knew about it.
Jul 21 2012