Suppress keystrokes to dialog - winapi

I have added a keyboard hook for a dialog that I don't control (it runs its own DialogProc Function in a library with closed source). I'm able to capture the key strokes, but the key stroke is always sent the dialog. This causes error beeps as well as some weird behavior.
Installing the hook:
hKeyHook=SetWindowsHookEx(WH_KEYBOARD, KeyHookProc, hInst, GetCurrentThreadId());
The hook function:
LRESULT CALLBACK KeyHookProc(int code, WPARAM wParam, LPARAM lParam)
{
if(!(lParam & 0x80000000))
{
if(wParam == VK_mykey)
{
// Do my stuff
return 0; // Don't continue the hook chain
}
}
// Continue with next hook
return CallNextHookEx(hKeyHook, code, wParam, lParam);
}
Releasing the hook:
UnhookWindowsHookEx(hKeyHook);
Is there a way to stop the key stroke from being sent to the control that has focus in the dialog?

You could subclass the control in question by replacing its GWLP_WNDPROC (See the remarks section of this page) value. The basic concept is that you filter out the WM_KEY* messages, then pass the rest to the original WndProc.

This
if(!(lParam & 0x80000000))
is true when the key is pressed. When it's released you do nothing.
KeyHookProc is called when the keys are held down or when they are released.

Hmm. First point are you sure that you wish to use your own process thread id here. Is the dialog definitely under your process thread?

Related

Disabling the Find/Find Next options in a FindReplace dialog

When using the ::ReplaceText() Win32 api function, is it possible to disable or hide the find/find next buttons, leaving only the Replace?
If not I will need to roll my own dialog.
The ReplaceText() dialog allows you to hide the direction, Match case, and Match Whole Word boxes using the various FR_HIDE... flags, but it does not have any flags for hiding the Find Next and Replace buttons. So you will have to do it manually.
There are two ways to do this:
You can create a custom dialog resource that contains just the UI fields you want to display, and then you can enable the FR_ENABLETEMPLATE or FR_ENABLETEMPLATEHANDLE flag and provide the dialog as a template in the lpTemplateName or hInstance field, respectively.
You can enable the FR_ENABLEHOOK flag and provide a lpfnHook callback that disables/hides the existing buttons in the default dialog when processing the WM_INITDIALOG message.
These techniques are described in the MSDN documentation:
Customizing the Find or Replace Dialog Box
I would opt for #2, as it is easy to implement in code, eg:
UINT_PTR CALLBACK FRHookProc(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)
{
if (uiMsg == WM_INITDIALOG)
{
HWND hBtn = GetDlgItem(hdlg, 1); // The "Find Next" button is ID 1
if (hBtn)
ShowWindow(hBtn, SW_HIDE);
}
return 0;
}
FINDREPLACE fr = {sizeof(fr), 0};
...
fr.Flags = FR_ENABLEHOOK;
fr.lpfnHook = &FRHookProc;
...
HWND hDlg = ReplaceText(&fr);

"Caps Lock is on"-tooltip doesn't go away (Citrix? Codejock?)

I'm working with an MFC application. In some of our forms, we have password fields - ordinary CEdit controls with the ES_PASSWORD style set. When focus is moved to a password field, Windows displays a tooltip-warning if the user has Caps Lock on. (See screendump here) Which is OK - the password is case sensitive, so it's good that the user gets informed he should turn Caps Lock off.
But sometimes the warning doesn't go away. Even after the CEdit control (and the whole view window) has been deleted/destroyed. The "tooltip balloon" remains on screen, on top of everything else. (Hides new controls.) The only way to get rid of it is to close the application and restart it.
The problem doesn't appear so often, but is very annoying for the user when it does. As far as we know, this has only happend to users running our application under Citrix - not on a standalone PC. Our application is using Codejock's XTP, but the tooltip is localized so it seems to be generated by Windows.
(1) Does anybody know what can caus e this? We haven't been able to find anything about it online! Could it be a Citrix problem? Or are we wrong about this beeing a Windows thing - could this be a Codejock bug?
(2) Is there a way to fix this? (CEdit::HideBalloonTip/EM_HIDEBALLOONTIP does not help.)
(3) If there isn't a solution to the problem, can we disable the "Caps Lock is on"-tooltip completely for the CEdit control? Better not to show the tooltip at all, than having this problem.
To suppress the Balloon tip you can filter the EM_SHOWBALLOONTIP message as described here:
WNDPROC g_wpEdit;
LRESULT CALLBACK NoBalloonWndProc(HWND hwnd, UINT uMsg,
WPARAM wParam, LPARAM lParam)
{
switch (uMsg) {
case EM_SHOWBALLOONTIP: return FALSE;
}
return CallWindowProc(g_wpEdit, hwnd, uMsg, wParam, lParam);
}
BOOL
OnCreate(HWND hwnd, LPCREATESTRUCT lpcs)
{
g_hwndChild = CreateWindow(TEXT("edit"), NULL,
ES_PASSWORD | WS_CHILD | WS_VISIBLE, 0, 0,
0, 0, hwnd, NULL, g_hinst, 0);
if (!g_hwndChild) return FALSE;
g_wpEdit = SubclassWindow(g_hwndChild, NoBalloonWndProc);
return TRUE;
}

How to redirect mouse wheel message to other windows?

I'm developing a Word addin for MS Word on Windows, and this addin has as 'advanced task pane' showing and docking on the left side of the Word document window (it's treeview(outline) showing a list of Word documents for fast editing multiple documents in a project).
My question is, the Word document window responds to mouse wheel message only when it's focused, but I want to be able to scroll the document with mouse wheel whenever the mouse cursor is hovering on it even the Word document window doesn't have a input focus.
Any hints in this particular case? Thank you!
Not quite sure it will work, but I'd try the following:
Implement a global low-level mouse hook using the SetWindowsHookEx function.
In the hook procedure, which should be called on mouse wheel scroll events, check if the window under mouse cursor is the Word document window. If so, set a shared flag indicating the needed scroll action.
Don't send WM_VSCROLL directly from the hook procedure! This procedure has to be really fast and simple.
Now, in your add-in's message loop check the flag and if it is set, send WM_VSCROLL to the Word document window.
Perhaps you could make use of the SetCapture(hWnd) Windows API function. This will cause all mouse events to go to your hWnd instead of whatever hWnd might normally expect to receive them. If you capture when the mouse enters the Word document window, and ReleaseCapture() when the mouse leaves or Word gains focus, it should work alright.
Disclaimer: I've used mouse capturing in C# before, but I've never done it in C++. I don't know if it behaves exactly the same way.
Try the following , this might help you.
1) Handle WM_MOUSEHOVER message.
2) In the handler , use SendMessage, with WM_VSCROLL as the message parameter .
Using Spy++ I saw that the window that gets the messages is of the class _Wwg (At least 2003 is) and it is responding to the WM_MOUSEWHEEL message. So you would send that window a WM_MOUSEWHELL message when you want it to scroll.
I've got the C++ code snipped below from a comment in https://msdn.microsoft.com/en-us/library/windows/desktop/ms645617(v=vs.85).aspx
And I used it (and variations on it) successfully.
The user who wrote it claims it was inspired by a ms recommendation on a Windows Vista best practices guide, to forward the mouse wheel event to whatever window is hovered by the mouse cursor. The virtue of his/her implementation is that it's completely unintrusive, you just drop it and it set the hooks, referencing your main thread. It avoids forwarding the event to windows belonging to other processes, but perhaps that could actually be a good thing.
namespace {
LRESULT CALLBACK mouseInputHook(int nCode, WPARAM wParam, LPARAM lParam) {
//"if nCode is less than zero, the hook procedure must pass the message to the CallNextHookEx function
//without further processing and should return the value returned by CallNextHookEx"
if (nCode >= 0) {
MSG& msgInfo = *reinterpret_cast<MSG*>(lParam);
if (msgInfo.message == WM_MOUSEWHEEL ||
msgInfo.message == WM_MOUSEHWHEEL) {
POINT pt = {};
pt.x = ((int)(short)LOWORD(msgInfo.lParam)); //yes, there's also msgInfo.pt, but let's not take chances
pt.y = ((int)(short)HIWORD(msgInfo.lParam)); //
//visible child window directly under cursor; attention: not necessarily from our process!
//http://blogs.msdn.com/b/oldnewthing/archive/2010/12/30/10110077.aspx
if (HWND hWin = ::WindowFromPoint(pt))
if (msgInfo.hwnd != hWin && ::GetCapture() == nullptr) {
DWORD winProcessId = 0;
::GetWindowThreadProcessId(//no-fail!
hWin, //_In_ HWND hWnd,
&winProcessId); //_Out_opt_ LPDWORD lpdwProcessId
if (winProcessId == ::GetCurrentProcessId()) //no-fail!
msgInfo.hwnd = hWin; //it would be a bug to set handle from another process here
}
}
}
return ::CallNextHookEx(nullptr, nCode, wParam, lParam);
}
struct Dummy {
Dummy() {
hHook = ::SetWindowsHookEx(WH_GETMESSAGE, //__in int idHook,
mouseInputHook, //__in HOOKPROC lpfn,
nullptr, //__in HINSTANCE hMod,
::GetCurrentThreadId()); //__in DWORD dwThreadId
assert(hHook);
}
~Dummy() {
if (hHook)
::UnhookWindowsHookEx(hHook);
}
private:
HHOOK hHook;
} dummy;
}

Win32: My Application freezes while the user resizes the window

I write a win32 application. I implemented the message loop myself like this:
bool programcontinue = true;
while(programcontinue)
{
while (PeekMessage(&Msg, NULL, 0, 0, PM_REMOVE))
{
TranslateMessage(&Msg);
DispatchMessage(&Msg);
}
IdleProcess();
}
There is a resizable window in my application. Usually, IdleProcess() gets called several times per second. When the user grabs a corner or an edge of the resizable window, IdleProcess() doesn't get called anymore until the user releases the mouse button.
What happens here?
I tried exchanging the inner while with an if, but that doesn't change the behaviour. It seems like when resizing starts, the handler for that message does not return until the resizing is done?
Is there a way to change this and call IdleProcess() during resizing a few times per second?
Thanks
Marc
EDIT:
What I mean by replacing the inner while with if is:
bool programcontinue = true;
while(programcontinue)
{
if (PeekMessage(&Msg, NULL, 0, 0, PM_REMOVE)) // <<<<
{
TranslateMessage(&Msg);
DispatchMessage(&Msg);
}
IdleProcess();
}
My window Proc is a bit lengthy, but I get the same behavior with a small test app. This is identical to the wndproc the VS Project Wizard creates:
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
int wmId, wmEvent;
PAINTSTRUCT ps;
HDC hdc;
switch (message)
{
case WM_COMMAND:
wmId = LOWORD(wParam);
wmEvent = HIWORD(wParam);
// Parse the menu selections:
switch (wmId)
{
case IDM_ABOUT:
DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
break;
case IDM_EXIT:
DestroyWindow(hWnd);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
break;
case WM_PAINT:
hdc = BeginPaint(hWnd, &ps);
// TODO: Add any drawing code here...
EndPaint(hWnd, &ps);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
There are a number of modal operations that happen on windows. Win32 Modal operations refer to functions that put an application into a "mode" by starting their own event processing loop until the mode finishes. Common application modes include drag and drop operations, move/size operations, anytime a dialog pops up that needs input before the application can continue.
So what is happening is: Your message loop is NOT being run.
Your window recieved a WM_LBUTTONDOWN message that you passed to DefWindowProc. DefWindowProc determined that the user was trying to size or move the window interactively and entered a sizing/moving modal function. This function is in a message processing loop watching for mouse messages so that It can intercept them to provide the interactive sizing experience, and will only exit when the sizing operation completes - typically by the user releasing the held button, or by pressing escape.
You get notified of this - DefWindowProc sends a WM_ENTERSIZEMOVE and WM_EXITSIZEMOVE messages as it enters and exits the modal event processing loop.
To continue to generate "idle" messages, typically create a timer (SetTimer) before calling a modal function - or when getting a message that DefWindowProc is entering a modal function - the modal loop will continue to dispatch WM_TIMER messages... and call the idle proc from the timer message handler. Destroy the timer when the modal function returns.
When DefWindowProc handles WM_SYSCOMMAND with either SC_MOVE or SC_SIZE in the wParam, it enters a loop until the user stops it by releasing the mouse button, or pressing either enter or escape. It does this because it allows the program to render both the client area (where your widgets or game or whatever is drawn) and the borders and caption area by handling WM_PAINT and WM_NCPAINT messages (you should still receive these events in your Window Procedure).
It works fine for normal Windows apps, which do most of their processing inside of their Window Procedure as a result of receiving messages. It only effects programs which do processing outside of the Window Procedure, such as games (which are usually fullscreen and not affected anyway).
However, there is a way around it: handle WM_SYSCOMMAND yourself, resize or move yourself. This requires a good deal of effort, but may prove to be worth it. Alternatively, you could use setjmp/longjmp to escape from the Window Procedure when WM_SIZING is sent, or Windows Fibers along the same lines; these are hackish solutions though.
I solved it (using the first method) this past weekend, if you're interested I have released the code to the public domain on sourceforge. Just make sure to read the README, especially the caveat section. Here it is: https://sourceforge.net/projects/win32loopl/
You can still receive the WM_PAINT message, you just gotta tell the WinAPI that you want it (seen in NeHe OpenGL tutorials):
windowClass.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC; // Redraws The Window For Any Movement / Resizing
It will still block your while/PeekMessage-loop though! WinAPI just calls your WndProc directly.
During resize Windows sends quite a few messages to your program. I have not proved this, but the behavior you describe is familiar. I'd suggest to call your function IdleProcess() also within the while(...) loop for certain events such as WM_SIZING which your application will receive frequently during window resizing:
bool programcontinue = true;
while(programcontinue)
{
while (PeekMessage(&Msg, NULL, 0, 0, PM_REMOVE))
{
TranslateMessage(&Msg);
DispatchMessage(&Msg);
if(Msg.message == WM_SIZING)
IdleProcess();
}
IdleProcess();
}
Be aware though that this assumes, that IdleProcess() does not create or consume any events. If thats the case, things get much more complicated.

Why can my property sheet, shown from a system tray icon, lock up the taskbar?

Note: code samples have been simplified, but the overall structure remains intact.
I am working on a Win32 application whose main interface is a system tray icon. I create a dummy window, using HWND_MESSAGE as its parent, to receive the icon's messages:
WNDCLASSEX wndClass;
wndClass.lpfnWndProc = &iconWindowProc;
// ...
iconWindowHandle = CreateWindow(wndClass.lpszClassName, _T(""), 0, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, HWND_MESSAGE, NULL, GetModuleHandle(NULL), 0);
Then the icon is created, referring to this message-only window:
NOTIFYICONDATA iconData;
iconData.hWnd = iconWindowHandle;
iconData.uCallbackMessage = TRAYICON_MESSAGE;
// ...
Shell_NotifyIcon(NIM_ADD, &iconData)
When the tray icon is double-clicked, I create and show a property sheet (from comctl32.dll):
LRESULT CALLBACK iconWindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
switch (uMsg) {
case TRAYICON_MESSAGE:
switch (lParam) { // that contains the "real" message
case WM_LBUTTONDBLCLK:
showPropertySheet();
return 0;
// ...
}
break;
// ...
}
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
The property sheet has no parent window. The PropertySheet function is called from the window procedure of the message-only window. The PSH_MODELESS flag is not set; thus, PropertySheet only returns after the property sheet window is closed again:
void showPropertySheet() {
PROPSHEETPAGE pages[NUM_PAGES];
pages[0].pfnDlgProc = &firstPageDialogProc;
// ...
PROPSHEETHEADER header;
header.hwndParent = NULL;
header.dwFlags = PSH_PROPSHEETPAGE | PSH_USECALLBACK;
header.ppsp = pages;
// ...
PropertySheet(&header);
}
Now all this works just fine, until I set a breakpoint inside the dialog procedure of one of the property sheet's pages:
BOOL CALLBACK firstPageDialogProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
return FALSE; // breakpoint here
}
When the program stops on the breakpoint, the entire taskbar locks up!
The call stack is quite useless; it shows that the dialog procedure is called from somewhere inside comctl32.dll, via some calls inside user32.dll. No window procedure of my own is in between.
Making the property sheet modeless doesn't seem to help. Also, I'd rather not do this because it makes the code more complex.
As long as my dialog procedure returns quickly enough, this shouldn't be a problem. But it seems so weird that a longer operation inside the dialog procedure would not only lock up the dialog itself, but the entire shell. I can imagine that the message-only window procedure has the power to cause this behaviour, since it's more closely related to the tray icon... but this function is not shown on the call stack.
Am I doing something fundamentally wrong? Can anyone shed some light on this issue?
Actually, it's rather obvious, and the confusion must have been due to a lack of coffee.
The taskbar probably uses SendMessage to send the message to my application, which causes it to block until the message is handled. SendMessageTimeout is apparently not used.
I still think it's strange that no function of my own shows up on the call stack. Surely, such a message must flow through my message loop in order to be processed? Maybe the warning that "stack frames below this line may be incomplete or missing" was actually right, then.

Resources