I'm trying to limit the access of the users of my program to the keyboard. To do that I've defined a low level keyboard hook:
LRESULT CALLBACK lowLevelKeyboardProc(int key, WPARAM wParam, LPARAM lParam)
{
KBDLLHOOKSTRUCT* pkbhs = (KBDLLHOOKSTRUCT*)lParam;
switch (key)
{
case HC_ACTION:
....
and hooked it:
m_hHook = SetWindowsHookEx(WH_KEYBOARD_LL, (HOOKPROC)lowLevelKeyboardProc, 0, 0);
I need the users to be able to use only the alphanumeric chars, ~, #, # ... only the chars that can be in a password (printable chars).
What would be the easiest way to differentiate between those chars and all the others using the low level keyboard hook parameters: int key, WPARAM wParam, LPARAM lParam ?
The program is written in c++ and compiled in VC2010.
All help is appreciated!
Before actually answering your question, I have a couple of questions for you:
Why would you want to do this globally, using a global low-level keyboard hook?
Although sometimes they are the only way to solve a problem, using a global hook like this is generally strongly discouraged for many reasons. There are many better ways of preventing a user from entering invalid or unacceptable data. For example, I would just disable the keys you don't want for a particular control or set of controls (e.g. all textboxes). That way, the user can still use keyboard shortcuts and other non-alphanumeric keys to interact with your application, which is critical for accessibility reasons. Remember that a global hook will affect all of the other threads running on the machine, not just your app—that is probably not what you want.
Even if you do decide to use a global hook, are you sure that you really need to disable all of the non-alphanumeric keys? What about Backspace and Delete? Shouldn't the user be able to delete things? And what about Tab? Enter? And what about modifier keys, like Shift, Ctrl, and Alt: are those allowed?
Please seriously reconsider whether you really need a low-level hook before continuing forward with this design. If you need help on devising another solution, please ask a new question, describing what you want to accomplish (e.g., I want to prevent users from entering any non-alphanumeric characters in a textbox control; here is the code I use to create my textbox…).
But if you insist on ignoring my advice, the solution is rather simple: investigate the members of the KBDLLHOOKSTRUCT structure that is passed to the hook procedure. The vkCode member gives you the virtual key code of the key that was pressed. Chances are, that is all the information you need. But just in case it's not, the hardware scan code of the key is also provided in the scanCode member.
Unfortunately, the code that you currently have is wrong. The first parameter to the hook callback procedure is indeed an int, but it is not a key code. Rather, it is a code that signals how the hook procedure should process the message. Use it like this:
LRESULT CALLBACK LowLevelKeyboardProc(int nCode, WPARAM wParam, LPARAM lParam)
{
// If nCode is greater than or equal to HC_ACTION, process the message.
if (nCode >= HC_ACTION)
{
KBDLLHOOKSTRUCT* pkbhs = reinterpret_cast<KBDLLHOOKSTRUCT*>(lParam);
// Handle the keys as you wish here.
//
// Remember that pkbhs->vkCode gives you the virtual key code
// of the key that was pressed.
//
// To prevent a particular key from being processed, you should
// return a non-zero value (e.g. 1) immediately.
}
// Pass the message on.
return CallNextHookEx(m_hHook, nCode, wParam, lParam);
}
And when you install the hook, there is absolutely no need to cast the function pointer. Pointless casts like this just hide potential compile-time errors, leading to a crash at runtime. Write it simply:
m_hHook = SetWindowsHookEx(WH_KEYBOARD_LL, lowLevelKeyboardProc, 0, 0);
Related
I need to intercept key pressings and other user actions in my RichEdit box. I found it too complex to intercept user input in WM_KEYDOWN or WM_CHAR since some of the key pressings fire WM_CHAR, some of them not and also it has some other problems.
So I decided to listen EN_UPDATE messages, cuz it is said that this event fires on every change and just before RichEdit control starts to redraw itself ( https://learn.microsoft.com/en-us/windows/desktop/controls/en-update ). Well it sounds like trustworthy mechanism which allows to intercept all the changes.
But I found that not every WM_KEYDOWN causes firing of EN_UPDATE. I rapidly pressed many buttons (usual "char" buttons like "d", "f" and so on, no special keys), and found that when I inputed 100 chars, and WM_KEYDOWN also fired 100 times, but EN_UPDATE fired only 96 times (the number of activations of EN_UPDATE varies, sometimes it equals to the number of keypressings, don't know what it depends on). The number of WM_KEYDOWN and the number of characters entered are always equal of course.
Here's the code:
BOOL CEditorView::OnCommand( WPARAM wParam, LPARAM lParam )
{
static long count = 0;
if( HIWORD( wParam) == EN_UPDATE )
{
if( (HWND)lParam == g_hwnd_RE )
{
if( g_allowProcessing )
{
count++;
}
}
}
return CDockablePane::OnCommand( wParam, lParam );
}
///// and WM_KEYDOWN
case WM_KEYDOWN:
{
g_testCount++;
return DefSubclassProc( hwnd, msg, wp, lp );
break;
}
Am I doing something wrong or is it just specific working style of EN_UPDATE? Like may be it accumulates changes when user input is too fast.
EN_UPDATE is sent when the control is about to redraw itself. This does not imply that the control has to update after each WM_KEYDOWN, the control might have some caching mechanism to update delaying when keys are down within a little margin, for example.
This is not standard per control, you may have a control that updates on each keypress, even if there is 1 ms delay between keys, and you may have a control that updates each second, no matter how many keys it gets. It depends on the control.
Therefore, if you really want to get notified of every key, you have to use WM_KEYDOWN. EN_UPDATE is usually used to take the current state of the control and update another control.
I'm a beginner with the WinAPI and trying to modify code from someone else, making a pair of checkboxes exclusive which were non-exclusive previously.
My initial try was to uncheck the other immediately if one was checked, but this obviously works in one direction only. If IDC_2 is checked and I try to check IDC_1 it triggers the first 'if' statement again and it fails.
This is the code I have:
static WDL_DLGRET dlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
if (IsDlgButtonChecked(hwndDlg, IDC_1)) {
// do something
// try to make exclusive
CheckDlgButton(hwndDlg, IDC_2, BST_UNCHECKED);
}
if (IsDlgButtonChecked(hwndDlg, IDC_2)) {
// do something
// try to make exclusive
CheckDlgButton(hwndDlg, IDC_1, BST_UNCHECKED);
}
}
I know about radio buttons, but in this case the choices can also be non mutually exclusive. It's possible to have non of the two boxes checked (but not both at once) so I think radio buttons wouldn't be the right choice here, or ?
You have two solutions that immediately present themselves
Radio buttons but add a third "none" option
react to check-box activation (being ticked) and clear out your other check-boxes.
Take option 1. Principle of least suprise. It is a bad idea to subvert common (well known and functionally accepted) user interfaces.
You could keep the checkboxes if this is a setup that later validates the input, e.g. in a dialog or form that you then submit, but you will need to add a description along the lines of:
select up to one option below
and then perform validation, refusing to accept until a maximum of one are selected.
Just take option 1.
I am implementing logic to handle global mouse clicks and I stuck at moment where I need to get data from LowLevelMouseProc. Currently, I am setting global variable at point when I am creating instance of MyClass and I able to access it from LowLevelMouseProc, but I suppose that isn't right approach.
Setting hook:
g_myClass = this;
SetWindowsHookEx(WH_MOUSE_LL, MyClass::MouseHookProc, LoadLibraryA("user32.dll"), 0);
LowLevelMouseProc:
LRESULT CALLBACK MyClass::MouseHookProc(int nCode, WPARAM wParam, LPARAM lParam) {
MOUSEHOOKSTRUCT * pMouseStruct = (MOUSEHOOKSTRUCT *)lParam;
g_myClass.onClick(wParam, pMouseStruct->pt.x, pMouseStruct->pt.y); //isn't good
}
The low level mouse hook offers no mechanism by which you can retrieve an instance pointer. Using a global variable is often the best option.
If you cannot make your program work that way you will need to use a thunk. That technique has been discussed in many places and a web search will provide more detail. For instance a cursory search on my part yielded this: http://zabkat.com/blog/hook-callback-thunk-x64.htm
I am looking to create a program where I can set the maximum maximize size (as in the size the window maximises to when you hit the maximise button) and maximize position (X/Y coordinated for the maximised window) for all of the windows that are running. This is so that I can have my Rainmeter visible at all times on my secondary monitor without having to manually resize every window fit inside of it.
I have managed to do this for a simple program I wrote using MINMAXSIZE from the WinAPI. This method seems to work perfectly for my little program, but there is very little documentation on it beside 1 or 2 articles on the internet. I was wondering if this would be the best way to set the maximum maximise size, or if there is another way to do this.
They way I planned to implement this into all of the applications was going to be either DLL Injection or Hooks (neither of which I have any experience with), so I was also wondering your guys' thoughts on these methods.
I know there are a few applications out there that already do this, but I thought this could be a learning experience, and as well, all of the applications I tried do not work very well (although this could be the case with all of them due to the way Windows functions).
If any of you are still unsure about what I am talking about, MaxMax does exactly what I want (although it doesn't work so well, as I stated in my previous paragraph).
Thank you all in advance.
What you're probably looking for is the work area setting, that you can set/retrieve with the SystemParametersInfo function, called with the flags SPI_SETWORKAREA/SPI_GETWORKAREA.
What you want to do is use a global windows hook to handle WM_GETMINMAXINFO. As you may be aware, this is the message that is:
Sent to a window when the size or position of the window is about to
change. An application can use this message to override the window's
default maximized size and position, or its default minimum or maximum
tracking size.
The best way to use this to override the default maximum is to fill in the MINMAXINFO structure like so:
case WM_GETMINMAXINFO: {
DefWindowProc(hWnd, message, wParam, lParam);
MINMAXINFO* mmi = (MINMAXINFO*)lParam;
mmi->ptMaxTrackSize.x = 100;
mmi->ptMaxTrackSize.y = 100;
return 0;
}
This will allow the default values to be assigned to the sizes you don't care about (min x/y) in this case, leaving you to fiddle with the max values as you please. Your windows hook should be done with SetWindowsHookEx() and should look something like this:
SetWindowsHookEx(WH_CALLWNDPROC, hook_procedure, instance_handle, 0);
hMod (instance_handle) should only be set depending on the circumstances (check the docs for this). The dwThreadId mandates a global hook. Your CallWndProc might looks something like this:
__declspec(dllexport) LRESULT WINAPI CallWndProc(int nCode, WPARAM wParam, LPARAM lParam) {
CWPSTRUCT* cwp = (CWPSTRUCT*)lParam;
if(WM_GETMINMAXINFO == cwp->message) {
DefWindowProc(hWnd, message, wParam, lParam);
MINMAXINFO* mmi = (MINMAXINFO*)lParam;
mmi->ptMaxTrackSize.x = 100;
mmi->ptMaxTrackSize.y = 100;
return 0;
}
return CallNextHookEx(next_hook, nCode, wParam, lParam);
}
Unfortunately something you are going to have to deal with is that the only windows that will be hooked are the ones that had were existing when you made your call to SetWindowsHookEx(). I'm not aware of a clean way of getting past this, short of looping a call to SetWindowsHookEx() (ergh!).
You could potentially do this with DLL injection and effectively subclass every window with EnumWindows, EnumChildWindow and SetWindowLongPtr/SetWindowSubclass. But why go to all that trouble when you could just use SetWindowsHookEx? :)
To alter the x/y, you might have to add an override for WM_SYSCOMMAND and check for SC_MAXIMIZE then use SetWindowPos/MoveWindow to position it properly (if you don't want it on the default 0, 0).
I want to subclass RichEdit in my program (here is c++ code: http://dumpz.org/46182/). _native_log is a hwnd of richedit. At first all works fine and LogWindow::wndProc callback called normal, but if i set some text in RichEdit or click on them LogWindow::wndProc stops work (there no any further calls of it). Is there any thoughts what's i do wrong?
void LogWindow::replaceNativeLog(HWND native_log_handle) {
_native_log = native_log_handle;
SendMessage(_native_log, EM_GETOLEINTERFACE, 0, (LPARAM) &_rich_edit_ole);
_old_wnd_proc = (WNDPROC) SetWindowLongPtr(_native_log, GWLP_WNDPROC, (LONG) &wndProc);
}
LRESULT LogWindow::wndProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam) {
switch (Msg) {
case EM_STREAMIN:
break;
case WM_SETTEXT:
break;
};
return CallWindowProc(_old_wnd_proc, _native_log, Msg, wParam, lParam);
}
Starting with Common Controls version 6 the procedure of subclassing windows has been revised to eliminate the issues with previous versions. In particular it is no longer a problem if a control is subclassed more than once.
A comparison between subclassing pre-v6 Common Controls and the v6 way of doing things can be found at "Subclassing Controls". Instead of calling SetWindowLongPtr to replace the window procedure there is SetWindowSubclass which in addition to replacing the window procedure does all the internal bookkeeping. A consequence of the redesign is that you do not have to store a pointer to the previous window procedure either; if you need to call into the original window procedure there is DefSubclassProc at your disposal.
This of course will only help if all competing clients trying to subclass the a control all agree on using the v6 style subclassing.
Finally, I found the problem. I actually develop a plugin for Miranda IM, and there was another function trying to subclass richedit i want. So there is a kind of conflict between my and that functions. Thanks all for trying to help.