EN_UPDATE works unreliably? - c++

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.

Related

MFC CToolBar update mechanism bug?

In Visual Studio, MFC CToolBar buttons are updated by ON_UPDATE_COMMAND_UI which is sent during idle state. I suppose this mechanism is buggy if I change enable/disable state of the button in ON_UPDATE_COMMAND_UI handler.
Specifically:
Suppose the button is now in enabled state. At a certain time, the code 'wants' the button to be disabled (and of-course should not be clicked by user), but the button will be really disabled at next idle period, as in following figure:
In red area in the figure, the code state, in my opinion, is unstable and the developer must handle this unstable state by checking the button state manually. I have no idea if I have missed something or have some misunderstanding of this mechanism?
PS: The procedure for Menu is OK. It always calls ON_UPDATE_COMMAND_UI handler and check the button state before ON_COMMAND handler.
My question is how to make the ON_UPDATE_COMMAND_UI handler be called before ON_COMMAND handler just like Menus do?
After debugging and tracing, I finally found a possible solution. Key codes are listed here to help others with the same problem. Override OnCommand as follows:
BOOL CMainDlg::OnCommand(WPARAM wParam, LPARAM lParam)
{
// Disable WM_COMMAND reflection for ToolBar control
// makes the ON_UPDATE_COMMAND_UI handler be called
// right before ON_COMMAND handler.
if ((HWND)lParam == m_wndToolBar.GetSafeHwnd())
lParam = NULL;
return CDialog::OnCommand(wParam, lParam);
}
The side effect is WM_COMMAND reflection is disabled for ToolBar, but it would be OK in most cases.
As I just ran into this so I thought I'd add my solution. I have a button to paste records into a database and so it is clear for the client, I only wanted the button enabled if there is valid data on the clip board. Here is what it looks like:
My App in the back and notepad++ in front with records selected. When I 'ctrl C' the text in notepad++ the 'I' on my tool bar becomes active even though my app is idle. My app is part of the clipboard chain and gets notified. This is the WM_DRAWCLIPBOARD handler.
LRESULT CMainFrame::OnDrawClipboard( WPARAM wparam, LPARAM lparam )
{
if( hWndClipboardChain )
::SendMessage( hWndClipboardChain, WM_DRAWCLIPBOARD, wparam, lparam );
if( wparam )
PostMessage( ID_CLIPBOARD_HASCHANGED, 0, 0 );
return TRUE;
}
From there I post to my app not getting in the way of the WM_DRAWCLIPBOARD message, and there:
LRESULT CMainFrame::OnCheckClipboard( WPARAM wparam, LPARAM lparam )
{
std::string data( GetClipboardStr( ) );
std::string::size_type end_cnt= data.find( "\r\n" );
if( end_cnt == std::string::npos )
bClipboardHasValidRecords= false;
else
{
auto header_end= data.begin( ) + end_cnt;
csv_vect_t header;
split( header, str_it_range_t( data.begin( ), header_end ), boost::is_any_of("\t") );
bClipboardHasValidRecords= header.size( ) == RARECORD_SIZE;
}
return TRUE;
}
The main thread of my app is waken up by the messages and the 'I' will turn on and off without making the app an active window. And it just happens without any extra code.

MFC Have enter key act as "apply" button

In a certain dialog I would like when the user presses the enter key for it to act as an "apply" button. So far I have at least been able to make the dialog not close upon pressing enter by overriding CWnd::PreTranslateMessage, so currently it just does nothing and I'm not sure how to send apply command from there.
Every dialog should have one and only one button with the BS_DEFPUSHBUTTON style, which indicates to the dialog that this is the button to activate with the Enter key. Usually this is the OK button, but you can make it the Apply button if you want to.
As Mark pointed out above the dialog manager already has all the logic built in to handle the Enter key by invoking the command associated with the default button. You can statically assign the BS_DEFPUSHBUTTON style or handle the DM_GETDEFID message.
The former is trivially easy and the latter is fairly simple to implement. Make sure you set the Default Button property to False for all buttons on your dialog. Now add a message handler for the DM_GETDEFID message. There is no dedicated macro for this message so you have to use the generic handler:
BEGIN_MESSAGE_MAP(CMyDialog, CDialogEx)
...
ON_MESSAGE(DM_GETDEFID, OnGetDefId)
END_MESSAGE_MAP()
The message handler is equally simple and uses the default message handler signature:
LRESULT CMyDialog::OnGetDefId(WPARAM wParam, LPARAM lParam)
{
return MAKELRESULT(ID_APPLY, DC_HASDEFID);
}
The message handler must return a value whose high-order word contains DC_HASDEFID and the low-order word contains the control ID.
If you navigate over the controls of the dialog you will see that the Apply button has the typical default button visual cue while focus is not on another command button. Pressing Enter while a non-button control has the input focus invokes the default button's command handler. No additional code required.
If your intent is to handle the Enter key without dismissing the dialog, you may be going about it incorrectly. Please take a look at this MSDN article. While using PreTranslateMessage should work, it is not the best way to handle these types of events.
You'll need to handle the OnKeyDown message, and handle the VK_RETURN character inside that function.
void MyCtrl::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
{
if(nChar == VK_RETURN)
{
// Do Stuff
return;
}
CWnd::OnKeyDown(nChar, nRepCnt, nFlags);
}
Another way to overwrite the message.
BOOL CMyDialog::PreTranslateMessage(MSG* pMsg)
{
if (pMsg->message == WM_KEYDOWN)
{
switch (pMsg->wParam)
{
case VK_RETURN:
{
UINT nID = ::GetDlgCtrlID(pMsg->hwnd);
if (nID == ID_APPLY)
{
//DO YOUR STUFF HERE
}
}
break;
default:
break;
}
}
return CDialog::PreTranslateMessage(pMsg);
}
Also, you don't need to use PreTranslateMessage if you are using ::OnKeyDown

Low level keyboard hook: differentiate between key codes

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);

C++ WM_KEYDOWN different intervals

I am currently trying to get KeyBoard Input from the WM_KEYDOWN and WM_CHAR message for my own InputBox.
This is the code that I am using for basic input, which works fine for characters:
if(msg.message == WM_KEYDOWN)
{
keyHandled = false;
//handle other keys here, e.g. VK_LEFT
}
else if(msg.message == WM_CHAR && !keyHandled)
{
keyHandled = true;
gui->UpdateInput(msg.wParam);
}
If the key that is being pressed is also a key that triggers the WM_CHAR message, the interval is as in usual input boxes.
However, if it is a key like VK_LEFT, it keeps receiving the WM_KEYDOWN message without any delay.
Is there any way that I can receive all keys with the same interval or do I have to implement a timer that handles the delay between the messages? I have also had a look at the WM_KEYDOWN message on msdn, but I could not find anything related to the intervals.
Windows has its own delays for sending Keyboard events from Keyboard input and this isn't something you can simply change. As you know, holding down a key will result in a first message, a delay, and then a series of more messages with a quicker interval. You can get around this by requesting states rather than relying on the messages directly. This is called Unbuffered input and is state oriented. To store your states, you can do the following:
bool keys[256];
When you are checking windows events, you can update the key states accordingly like this:
//In your WinProc function most likely
if (msg.message == WM_KEYDOWN)
{
keys[msg.wParam] = true;
}
if (msg.message == WM_KEYUP)
{
keys[msg.wParam] = false;
}
Then whenever you'd like, you can request the state of a specific key through the following function:
bool getKeyPressed(char keycode)
{
return keys[keycode];
}
For example:
//This could be in some random update function and called
//whenever you need the information.
if (getKeyPressed(VK_UP))
{
//Do something here...
}
The above function can be used wherever you'd like, therefore the frequency at which you update is completely up to you at that point. As mentioned before, this is Unbuffered input and is State oriented, whereas Buffered Input is Event oriented.
I think that this internal delay can not be modified easily.
One approach could be writing your own general key handler that keeps track of the states of all keys. For example, a list containg the keycodes of all pressed keys. On WM_KEYDOWN you add the keycode to the list and on WM_KEYUP you remove it. Then create something like a timer that simply notifies you in the desired delay times and calls your key handling function for each element of the list.

Activate windows under mouse through mouse hook

Basically i make mousestruct in the hook
MOUSEHOOKSTRUCT* str;
Then make it from lparam,
LRESULT CALLBACK MouseProc( int nCode, WPARAM wParam, LPARAM lParam )
{
str = (MOUSEHOOKSTRUCT *) lParam;
...
Then I catch mousemovements
case WM_MOUSEMOVE:
wParm = AU3_WM_MOUSEMOVE;
fromp = WindowFromPoint(str->pt);
Then validate and try not to send to many messages...
if (fromp != currentwindow)
{
currentwindow= fromp;
PostMessage(m_hHwndMouse, wParm,(WPARAM)( (MOUSEHOOKSTRUCT*) lParam )->hwnd, LPARAM(fromp));
}
break;
This sends the mousemove message along with hwnd to my autoit app which inspects hwnd and if that hwnd is not active it activates it.
Func mouse_func($hWndGUI, $MsgID, $wParam, $lParam)
Select
Case $MsgID = $WM_AUTOITMOUSEMOVE
If GUICtrlRead($activateundermouse) = 1 And $sitting = 0 Then
;Local $starttime = _Timer_Init()
If StringInStr(WinGetTitle($lParam), "ID=") Then
If Not WinActive($lParam) Then
;ConsoleWrite("HOVERING NEW, Activate It: " & WinGetTitle($lParam) & #LF)
WinActivate($lParam)
EndIf
;ConsoleWrite("diff is > " & _Timer_Diff($starttime) & #LF)
EndIf
EndIf
This is how I am activating window that is hovered by the mouse but the problem is that rarely autoit wont read the message that should signal new window being hovered(or the dll with hook didnt send it, I dont know)
Also if the window is overlapping another window and both of them are valid windows that should be activate once hovered I get flickering as autoit is constantly trying to activate the current window and the overlapped one, in a loop
Is there something that perhaps I missed or could be doing wrong here?
It's easiest just to use the facility built into Windows. For example on Windows 7 it looks like this:
This capability is present in older versions of Windows too but is not exposed in such a simple interface. Instead you have to set it with a PowerToy or through SystemParametersInfo.
As Raymond Chen explains, this is a user preference which should not be changed without the user's consent.
You can use the blocking SendMessage instead depending on surrounding implementation. PostMessage will send to the window message queue but may not have the priority you expect as it returns without waiting for the processing of the message.
Also check out SetCapture and ReleaseCapture to change which window is capturing mouse events based on a hit test in mouse move as an alternative to forwarding. Only one window at a time can capture mouse events using these functions so that will solve your flicker issue of the windows forwarding messages to each other most likely.