In my C++ MFC application I have an ActiveX control on a form. At some point I create and show a new dialog. I don't want the user to be able to click the ActiveX control while this second dialog is up so I tried creating it as a child dialog. However the ActiveX control always appears above the child dialog in Z order. I have tried sending message on the create to change the Z order but nothing worked.
I've tried using Windows Hooks to intercept the mouse click using the following code:
GetWindowsHookEx(WH_MOUSE_LL, (HOOKPROC)CDWFDLG::ClickProc, GetModuleHandle(NULL), 0)
LRESULT CALLBACK CDWFDLG::ClickProc(int ncode, WPARAM wparam, LPARAM lparam)
{
if(wparam == WM_LBUTTONDOWN)
{
Beep(110, 30);
return TRUE;
}
return CallNextHookEx(0, ncode, wparam, lparam);
}
This blocks all left mouse clicks which is what I want. However it does this on everything, not just on my application. I've tried setting the thread Id using
GetCurrentThreadId()
and
GetWindowThreadProcessId(this->m_hWnd, &threadId )
However neither of these worked. What should I use to just get the hook to run on my application? Once this is working I was planning on using the coordinates of the click to check whether is was on the new dialog and handle it from there.
Thanks
GetWindowsHookEx(WH_MOUSE_LL, (HOOKPROC)CDWFDLG::ClickProc, GetModuleHandle(NULL), 0)
Means you are hooking globally, all mouse clicks performed.
What you want is to hook WH_MOUSE, with the option GetCurrentThreadId() instead of 0, this will yield the results you want.
Although I couldn't fix the problem using Window Hooks, I think I've fixed it using the dialog properties. I've set the parent dialog's Control Parent to True and left everything else in the child dialog's properties to default (Control is false and and Style is Popup etc).
Now when I call the dialog through DoModal() it has focus and doesn't allow clicks on the ActiveX control.
Thanks
Related
I'm using the Win32 API and I want to check if a minimized window is flashing, ie showing the window tab with orange, blinking color, through C code.
I checked the API and I only found FlashWindow/Ex, which is causing the flashing; however I want to test it against a specific window.
I also found SystemParametersInfo(SPI_GETFOREGROUNDLOCKTIMEOUT,..), which may be related, but also global, I suppose.
I'm using Windows 7
There is no Win32 API function to query if a given window is currently being flashed with FlashWindow/Ex().
However, SetWindowsHookEx() has a WH_SHELL hook, which reports HSHELL_REDRAW notifications when a window title is redrawn:
nCode [in]
Type: int
The hook code. 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. This parameter can be one of the following values.
...
HSHELL_REDRAW
6
The title of a window in the task bar has been redrawn.
wParam [in]
Type: WPARAM
This parameter depends on the value of the nCode parameter, as shown in the following table.
...
HSHELL_REDRAW
A handle to the redrawn window.
lParam [in]
Type: LPARAM
This parameter depends on the value of the nCode parameter, as shown in the following table.
...
HSHELL_REDRAW
The value is TRUE if the window is flashing, or FALSE otherwise.
So, you can use GetWindowThreadProcessId() to get the thread ID of the target window, and then pass that to the dwThreadId parameter of SetWindowsHookEx() to start monitoring all windows in the same thread as the target window, and then you can filter for HSHELL_REDRAW notifications to detect if the target window is flashing or not.
NOTE: I AM TRYING TO SOLVE THE MOUSE ISSUE, NOT THE KEYBOARD PROBLEM, WHICH IS ALREADY SOLVED
So I am creating a Visual Studio 2015 extension, working on the Options pages.
I am using WPF, so I use ElementHost to host a UserControl. At first it wasn't receiving keyboard events, so I implemented the solution at:
WPF TextBox not accepting Input when in ElementHost in Window Forms
A quick run down of the solution:
A) on the UserControl's Loaded event, I do:
var s = HwndSource.FromVisual(this) as HwndSource;
s?.AddHook(ChildHwndSourceHook);
B) In ChildHwndSourceHook(), I do something like:
static IntPtr ChildHwndSourceHook(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
if (msg == WM_GETDLGCODE)
{
handled = true;
return new IntPtr(DLGC_WANTCHARS | DLGC_WANTARROWS | DLGC_HASSETSEL | DLGC_WANTTAB);
}
return IntPtr.Zero;
}
HOWEVER, now mouse over events seem to be being ignored, as the cursor doesn't change when moving it over textboxes or grid splitters, not even on new Windows I create. Very occasionally, though, the mouse events do work, though, and continue to work until I move to another page or close the dialog. That's the weirdest part.
I've tried everything and have scoured Google all day, but I am no closer to understanding why clicks work fine, but mouse over events don't seem to be registered.
I did try REMOVING the message handler, then opening a Window, but it seems once the handler is added, removing it won't fix anything.
Does anyone know how I can get mouse over events to work on my controls? Thanks so much!
I've found success using the method the creator Viasfora used. You can see the code on the GitHub repo here. In particular, look at the TextObfuscationDialog and how it is hosted. I believe there might be something wrong with the VS extension documentation, because I ran into the same problem you did.
EDIT:
I've created a sample project to show this method does work (I'm using it in 2 of my own extensions right now). Hopefully this working code might make it easier for you to implement it in your own project.
You can download the source code from my OneDrive here.
The UIElementDialogPage on MSDN says:
Provides seamless hosting of Windows Presentation Foundation (WPF)
content inside a native dialog running an IsDialogMessage-style
message loop. This class enables tabbing into and out of the WPF child
window handle (HWND), and enables keyboard navigation within the WPF
child HWND.
So while an ElementHost will not function correctly within the message loop of a normal/WinForms DialogPage, the UIElementDialogPage will. There are a number of classes that have UIElement* or similar prefix - they are to help migrate the legacy code of VS from Windows Forms to WPF.
A notification is sent by a control to its parent. When I subclass a control using SetWindowSubclass, how can I handle the notifications? I don't want to handle them in the parent's window proc. Is there some thing I can do in subclass proc?
If I subclass a Edit control, how to handle EN_CHANGE notification in the subclass?
Update
This is the subclass proc:
LRESULT CALLBACK MyEditWindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData)
{
switch (uMsg)
{
default:
return DefSubclassProc(hWnd, uMsg, wParam, lParam);
}
}
I use
SetWindowSubclass(GetDlgItem(hWnd, ID_MYEDIT), MyEditWindowProc, 0, 0);
to subclass the Edit control.
But which message should I handle? Certainly not WM_NOTIFY because it's handled by the parent.
If I subclass a Edit control, how to handle EN_CHANGE notification in the subclass?
Short answer: you can't. If you subclass an edit, you only get to see the messages sent to it; you don't get to listen to or intercept the messages already sent by it (although you can add additional outgoing messages).
If you do need to do this, however, an alternative technique might be to create an intermediate window that wraps the edit, so that the original parent dialog has your wrapper as the child, and your wrapper has the edit as the child. Now your wrapper is positioned to intercept and filter messages going in either direction.
It will have to take care to manually forward all relevant messages, and handle resizing and other housekeeping issues; you get a bunch of that 'for free' with subclassing, but have to deal with it explicitly when wrapping. Also, since mouse/keyboard input will still go to the inner control; if you need to listen in on that, then subclassing will be necessary in addition to wrapping.
As far as I know, there is no straightforward solution to this using just the Win32 API. Subclassing lets you process messages sent to a control, not sent by the control. Win32 notifications are sent by a control directly to the parent and I don't think you can change this behaviour.
MFC does something similar to what you want through a feature called message reflection -- the message is still sent to the parent window, but the parent looks for "reflection" handlers in the child window and manually calls these handlers.
You can write something similar to MFC reflection yourself, but it's going to take some effort, so if there's an easier alternative, you should go with that.
EN_CHANGE and the like are always sent to the parent window. You can't handle them in the sub-classed control because they aren't sent to the control, they're sent to the parent.
If you're looking for a way to do a self-contained sub-class of an edit control without having to add code to the parent's window procedure, and you want to handle those notification messages, the only way would be to sub-class the parent window as well.
I made a custom Splitter control in pure Windows API. It's made of 4 controls: the main container, the splitter and the 2 panes.
Now I needed to hook into the windows procedure in order to find out when one of its child controls was moving or resizing, so I used SetWindowsHookEx. I get the WM_SIZE messages in my hook procedure just fine, but no WM_MOVE messages are ever caught from my Splitter's child windows.
I tried adding a child window to a Groupbox (which I know isn't the way they're supposed to be used) just to see if the WM_MOVE messages were caught by the hook procedure, and they were.
So what am I missing here? What do I need to add to my Splitter window procedure so those WM_MOVEs get sent? Or was my error somewhere else?
PS: SetWindowPos does work on those child windows, it's just not catching WM_MOVE.
EDIT: As requested, here is the full code of the Splitter window class: http://pastebin.com/Lgvb0Vfv
Here is the part of the code that matters:
LRESULT WINAPI AnchorProc(int nCode, WPARAM wParam, LPARAM lParam) {
CWPRETSTRUCT* theMessage = (CWPRETSTRUCT*)lParam;
if (theMessage->message == WM_MOVE) printf ("!");
}
Sometime after the main window's WM_CREATE:
SetWindowsHookEx(WH_CALLWNDPROCRET,AnchorProc,NULL,GetCurrentThreadId());
// groupbox
HWND gb = CreateWindowEx(0,"button",NULL,BS_GROUPBOX|WS_CHILD,0,0,200,200,hwndMain,0,hInst,NULL);
HWND but = CreateWindowEx(0,"button",NULL,BS_PUSHBUTTON|WS_CHILD,0,0,40,40,gb,0,hInst,NULL);
// custom control
HWND split = CreateWindowEx(0,"FSplitterClass",NULL,WS_CHILD,200,0,200,200,hwndMain,0,hInst,NULL);
HWND pane1 = (HWND)SendMessage(split,WM_SPGETPANE,0,0);
HWND but1 = CreateWindowEx(0,"button",NULL,BS_PUSHBUTTON|WS_CHILD,0,0,40,40,pane1,0,hInst,NULL);
SetWindowPos(but, NULL, 1,1,0,0,SWP_NOSIZE|SWP_NOZORDER); // triggers WM_MOVE
SetWindowPos(but1, NULL, 1,1,0,0,SWP_NOSIZE|SWP_NOZORDER); // doesn't
A windows hook is overkill here. Subclassing is much more efficient.
WM_MOVE is generated only if the window procedure passes the WM_WINDOWPOSCHANGED message to DefWindowProc. If you cannot guarantee that, then you are not guaranteed a WM_MOVE message. Listen for WM_WINDOWPOSCHANGED.
I have been reading the MSDN documentation on subclassing and I have been successful in handling events in a subclass
My issue is with passing messages back to the original WndProc.
As an example, if I have a window, with a sub-classed groupbox control and a button as a child of that groupbox, I want to handle the button event in the original window procedure, not the subclassed groupbox procedure.
Essentially, I want an empty subclass procedure:
LRESULT FAR PASCAL SubClassFunc(HWND hwnd,
UINT uMsg,
WPARAM wParam,
LPARAM lParam)
{
return CallWindowProc(oldProc, hwnd, uMsg, wParam, lParam);
}
Where oldProc is:
FARPROC oldProc = (FARPROC)SetClassLong(group_box, GCL_WDPROC, (DWORD)SubCLassFunc);
And where the window and groupbox and button are:
HWND window = CreateWindowEx(
WS_EX_WINDOWEDGE,
appname,
TEXT("Subclass Test"),
WS_VISIBLE |WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
300,
400,
NULL,
NULL,
hInstance,
0);
HWND group_box = CreateWindowEx(
0,
TEXT("BUTTON"),
TEXT("Group Box"),
WS_CHILD | WS_VISIBLE | BS_GROUPBOX,
8,
8,
275,
350,
window,
NULL,
hInstance,
0);
HWND push_button = CreateWindowEx(
0,
TEXT("BUTTON"),
TEXT("Push Button"),
WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | BS_VCENTER,
50,
100,
100,
25,
group_box,
(HMENU)PUSH_BUTTON,
hInstance,
0);
I can handle the button events in the SubClassFunc, but what I want to do is pass them back to the window WndProc. It seems that CallWindowProc isn't doing this, or I may be totally wrong in how CallWindowProc works.
The button notifications are sent to the button's parent, which is the group box. Because you've subclassed the group box, your SubClassFunc receives these messages, which then passes them to the group box's original window procedure using CallWindowProc.
If you want the button notifications to go to the parent window (i.e, window in your code), you could either set the button's parent to window instead of group_box, or use PostMessage from within SubClassFunc to post the message (WM_COMMAND or WM_NOTIFY as appropriate) to window.
Also, I see that you're using SetClassLong to set the window procedure. What this does is replace the window procedure for the entire BUTTON class, but only for windows that are subsequently created. Any BUTTON windows created before calling SetClassLong will not be subclassed. You may want to consider using SetWindowLong instead, to subclass individual windows rather than the entire class.
Edit: The group box's original window procedure doesn't send WM_COMMAND messages to its parent. This is explained in Charles Petzold's Programming Windows book:
The group box, which has the BS_GROUPBOX style, is an oddity in the button class. It neither processes mouse or keyboard input, nor sends WM_COMMAND messages to its parent.
You should find that button notifications don't get through to window even if you don't subclass the group box.
I hope this helps!
I suspect if you remove the subclass all together the button events will still not reach the original window procedure as you expect.
Since you have an subclass procedure doing nothing more than calling CallWindowProc the window is effectively not subclasses.
My suggestion would be to use the Spy++ tool to see which window is getting the button event messages.
One of the more difficult aspects of Win32 programming is determining which window gets which message and Spy++ is invaluable when it comes to figuring out this information.