i'm developing Autocad/Bricscad-Dialogs in MFC C++. Know i detected a bigger problem. There is a dialog which sets metadata for 'special' drawing objects. I update the data of every 'special' drawing object with this dialog (in a loop). So if you have ten 'special' drawing objects, the same dialog will open ten times (successively). Now i have the problem that the user sometimes make a double click on the "OK"-Button. But if this double click is fast enough, the "OK"-Button of the next instance of this dialog will clicked automatically. I tried a lot (for example disabling the button if it was clicked) but nothing solved my problem. Maybe someone of you have a good idea.
Best regards,
Simon
When you open a new dialog you can flush the message queue of mouse click messages before going into your normal message loop, e.g.:
MSG msg;
while (PeekMessage(&msg, hWndDlg, WM_LBUTTONDOWN, WM_LBUTTONDOWN, PM_REMOVE));
I try to extend the answer of Jonathan Potter.
When you open a new dialog and OnInitDIalog is called, just remove the mouse messages from the queue and wait for 1/10 of a second.
MSG msg;
DWORD dwStart = ::GetTickCount():
while (PeekMessage(&msg, hWndDlg, WM_LBUTTONDOWN, WM_LBUTTONDOWN, PM_REMOVE)!=0 ||
(::GetTickCount() - dwStart) < 100))
;
The trick with the PeekMessage will work, the problem is that you need to run the loop as long as a "double click" will take. If the clicks have a distance of 1/10th of a second you need to remove all mouse clicks for this period of time.
And also OnInitDialog is the correct position. You may extend this flush to all mouse messages WM_MOUSEFIRST/WM_MOUSELAST... to get all clicks.
The delay of 1/10 second when launching the next dialog isn't expensive or annoying.
Related
I am creating program in C++ (Windows 7 ), that controls one specific window by reading its screen and sending back mouse signals (only left-clicks). I am using WinAPI obviously. Problem is with the mouse signals. My target is to send mouse events independently on actual cursor position. (i.e. it can run on "the background" and the window does not have to be visible).
I tried the obvious solution using SendMessage (or PostMessage):
PostMessage(hwnd, WM_LBUTTONDOWN, MK_LBUTTON, MAKELPARAM(x, y));
this_thread::sleep_for (std::chrono::milliseconds(100));
PostMessage(hwnd, WM_LBUTTONUP , MK_LBUTTON, MAKELPARAM(x, y));
I think the commands work fine but there is some problem with how the application process the click events. It seems it does not take into account the parameters x,y and instead when WM_LBUTTONUP is called, it asks OS where the cursor is and make the click on that location. So in the end the click occurs always on the location of cursor (if it is inside the window).
I also tried to send WM_MOUSEMOVE event before WM_LBUTTONUP but it didn't help.
This behavior is really strange, and I fully blame the application not WinAPI. Any ideas how to solve this? Can I maybe somehow trick the window so it thinks that cursor is elsewhere?
If this is the only thing you need then use SendInput with the MOUSEINPUT structure.
If you want to understand why then read on. What you are doing does not work because mouse messages are special. They are not regular messages that arrive and wait for you in the message queue. They are synthesized on demand when you call GetMessage and therefore they get their data from a secret, hidden place . In fact, generally speaking input messages are treated differently than posted messages. Here is some reading material.
I currently have a small game which runs in a win32 window. I just noticed that when I hold the top of the window (the bar which has the closing button) it freezes my application. I would like to disable this as it manages to completely destroy my application (timers continue to count).
It seems that with even the most minimalistic settings for creation of the window it still has this feature. How can I disable this? I currently have:
HWND hWnd = CreateWindowW( L"Game",L"Game",
0x00000000L | 0x00080000L,
wr.left,
wr.top,
wr.right-wr.left,
wr.bottom-wr.top,
NULL,
NULL,
wc.hInstance,
NULL );
I read that my thread is ignored while dragging, if I am forced into using 2 threads could someone please provide a small example of usage?
Or should I stop the timers? (what message should I catch, and would it even be catched?)
Update
I am using instances of my time class to handle timings which looks something like:
Timer::Timer() {
__int64 frequency;
QueryPerformanceFrequency( (LARGE_INTEGER*)&frequency );
invFreqMilli = 1.0f / (float)((double)frequency / 1000.0);
StartWatch();
}
void Timer::StartWatch() {
startCount = 0;
currentCount = 0;
watchStopped = false;
QueryPerformanceCounter( (LARGE_INTEGER*)&startCount );
}
My Win32 message loop contains: mousemove, keyup and keydown.
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/
Since the title bar to the user that he/she can move the window, you could remove that title bar and borders altogether. See "opening a window that has no title bar with win32" for an example.
When the game launches or is paused, you could show your own UI elements to allow the user to move the game window in these specific situations but only then.
You can check for the size/move loop using the WM_ENTERSIZEMOVE and WM_EXITSIZEMOVE messages.
I'm working on an application to detect a pop-up dialog and then
automatically dismiss it. I'm writing this as a C++/Win32 app. The
dialog box is generated by IE 7 and I can detect the window, but
several methods to get the OK button to "click" have failed.
Doing searches for other people's solutions, sending these messages to
the button handle seems to have worked in a lot of situations:
PostMessage( handle, WM_LBUTTONDOWN, 0, 0 );
PostMessage( handle, WM_LBUTTONUP, 0, 0 );
PostMessage( handle, BM_SETSTATE, 1, 0 );
It has no effect on the button state in my tests though.
I can send tab characters to the main window and see that the OK
button gets focus, but then sending return characters does nothing.
To learn more about this I used Spy++ to get information about the
window hierarchy and what messages are delievered when I manually
click the OK button.
Looking at the message log and reading about WM_MOUSEACTIVATE seamed
to offer a solution. The log info shows that 0002166C was the button
window. So in my code I tried this:
GetClassNameA( handle, str, str_size );
if( strcmp( str, "Internet Explorer_Server" ) != 0 )
return TRUE; // Not the window we're interested in.
// Send a message to activate the button window and have it process a mouse click.
PostMessage( handle, WM_MOUSEACTIVATE, (WPARAM) dialog_handle, MAKELPARAM( HTCLIENT, WM_LBUTTONDOWN );
Based on the window hierarchy and message log, I think the window with
the class name "Internet Explorer_Server" is the button. Maybe I'm
wrong, because it does seem like an odd class name for a button...
Below is a link to the window hierarchy image, message log when I
manually click the OK button. Last is the code that's executed on a 1
second timer ticket, looking for the window.
Any insight and help is appreciated!
Image of the window hierarchy, source, window messages, and test dialog source are available here:
https://sites.google.com/site/matthewmillersmiscellanea/Home/
Ideally, you should create a DLL which exports a Global CBT Window Hook. This would allow you to get early notification when a dialog is going to be created. This would avoid the need to drain resources by constantly polling.
Once you've detected that a dialog is about to be created, you have two options:
1) Prevent the dialog creation.
I don't recommend this, it causes all sorts of problems with code that was fully expecting a valid HWND to be returned by CreateDialog();
2) Asynchronously control the dialog.
We achieved this by using PostMessage with a Registered user message and picking it up by hooking the WNDPROC. When you get this message, then you have to decide how to kill the dialog that you're in.
There are multiple ways to exit the dialog:
a) Simulate pressing OK, Cancel, Abort, No buttons using WM_COMMAND(BN_CLICKED) (as Chris comments). You can use GetDlgItem(), look for the WindowText and make your choice. However, this doesn't work for non-US-English. There may be some distance in leveraging the Accessibility API here though.
b) Simulate closing the dialog with PostMessage(WM_CLOSE, m_hWnd). This doesn't always work as expected - some dialogs have no [X] close button and their client code is expecting a specific button to be pressed instead.
c) Simulate user input using the SendInput() API. This worked around dialogs that had anti-popup-killer code in them :)
Our final solution was a rule+heuristic-based approach that had a configuration file which we could tweak when the app/IE dialogs changed their ID's, class names or parent class names.
To close continually a specific popup given that you know the window class name and window caption
#define UNICODE
#include <windows.h>
#pragma comment(lib, "user32")
int main (int nn, char ** aa)
{
while (true) {
HWND iHandle = FindWindow (L"theWindowClassName", L"theWindowCaption");
if (iHandle > 0) SendMessage(iHandle, WM_SYSCOMMAND, SC_CLOSE, 0);
Sleep (200); // check 5 times per second
}
return 0;
}
if one is not known or too generic (e.g. "Dialog") you can omit it by passing a null
HWND iHandle = FindWindow (L"theWindowClassName", 0);
or
HWND iHandle = FindWindow (0, L"theWindowCaption");
of course this will close all windows with the given names.
i am using OnLButtonUp and OnLButtonDblClk in my application but whenever i double click OnLButtonDblClk and OnLButtonUp both called but i wanted only OnLButtonDblClk to be called not OnLButtonUp . How to do this?
The problem is that whenever the user double clicks there are four messages sent:
WM_LBUTTONDOWN
WM_LBUTTONUP
WM_LBUTTONDBLCLK
WM_LBUTTONUP
This only happens if the user clicks the second time within the "double click time" (use ::GetDoubleClickTime() to get it).
So what you can do is set a timer (with a timeout value equal to the double click time) when the user first clicks and if the second click comes before the timer goes off, you have a double click and you can disregard the button up message.
If the timer goes off, you call your button-up handler.
This technique has the drawback that it delays a bit the response to "Button Up" or single click, depending how you do it, but there's no easy way to discard just the Button Up messages when there's a double click.
EDIT:
If you just want to discard the second WM_LBUTTONUP, you can use a flag. You set it when you receive WM_LBUTTONDBLCLK. Then in the handler for WM_LBUTTONUP you do nothing if it's set (and then you clear it, of course).
OnLButtonUp(UINT nFlags, CPoint point)
{
Sleep(::GetDoubleClickTime());
MSG msg;
if( ::PeekMessage(&msg, NULL, WM_LBUTTONDBLCLK, WM_LBUTTONDBLCLK, PM_NOREMOVE) )
return; // LEFT CONTROL TO DBL CLICK ;-)
// NORMAL OnLButtonUp
}
I've got a window that I handle WM_NCLBUTTONUP messages, in order to handle clicks on custom buttons in the caption bar. This works great when the window is maximised, but when it's not, the WM_NCLBUTTONUP message never arrives! I do get a WM_NCLBUTTONDOWN message though. Strangely WM_NCLBUTTONUP does arrive if I click on the right of the menu bar, but anywhere along the caption bar / window frame, the message never arrives.
After a while of debugging I discovered that if I set a breakpoint on CMainFrame::OnNcLButtonDown(), clicked the caption bar, but keep the mouse button held down, let the debugger break in the function, hit F5 to continue debugging, then release the mouse button - magically WM_NCLBUTTONUP is sent!!
My question is two-fold, (1) what the hell is going on? (2) how do I get around this "problem".
I also note that there are several other people on the internet who have the same issue (a quick Google reveals lots of other people with the same issue, but no solution).
Edit
Thanks for the first two replies, I've tried calling ReleaseCapture in NCLButtonDown, but it has no effect (in fact, it returns NULL, indicating a capture is not in place). I can only assume that the base class (def window proc) functionality may set a capture. I shall investigate on Monday...
I've had this same problem. The issue is indeed that a left button click on the window caption starts a drag, and thus mouse capture, which prevents WM_NCLBUTTONUP from arriving.
The solution is to override WM_NCHITTEST:
LRESULT CALLBACK WndProc(HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam)
{
switch (nMsg)
{
...
case WM_NCHITTEST:
Point p(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam);
ScreenToClient(p);
if (myButtonRect.Contains(p))
{
return HTBORDER;
}
break;
}
return DefWindowProc(hWnd, nMsg, wParam, lParam);
}
So essentially you inform Windows that the area occupied by your button is not part of the window caption, but a non-specific part of the non-client area (HTBORDER).
Footnote: If you have called SetCapture() and not yet called ReleaseCapture() when you expect the WM_NCLBUTTONDOWN message to come in, it won't arrive even with the above change. This can be irritating since it's normal to capture the mouse during interaction with such custom buttons so that you can cancel the click/highlight if the mouse leaves the window. However, as an alternative to using capture, you might consider SetTimer()/KillTimer() with a short (eg. 100 ms) interval, which won't cause WM_NCLBUTTONUP messages to vanish.
A wild guess - some code is capturing the mouse, probably to facilitate the window move when you grab the title. That would explain also why breaking in the debugger would cause the message to show up - the debugger interaction is clearing the mouse capture.
I would suggest you run Spy++ on that window and it's children and try to figure out who gets the button up message.
As to how to fix it - can't help you there without looking at the actual code. You'll have to figure out who the culprit is and look at their code.
To add to Franci Penov's answer, a click on the title bar is interpreted as the start of a drag to reposition the window. The window is capturing the mouse so it can perform the drag. Since a maximized window can't be dragged, the capture is skipped and the message routes normally.
include ReleaseCapture() in WM_NCLBUTTONDOWN {code block}