I'm trying to get a mouse click event in Qt, this mouse click must be global, and must be received even if the mouse isn't over my app window, I saw in these links:
Receive WM_COPYDATA messages in a Qt app
http://developer.qt.nokia.com/forums/viewthread/8103
That I can get a message from Windows reimplementing the WinEvent. I have tried this and when debuging this event is never raised. Have I missed something?
here's a sample of my code:
bool WindowsUtil::winEvent( MSG * message, long * result ) {
if (message->message == WM_LBUTTONDOWN) {//never got here at all
*result = 0;
return true;
}
// give the event to qt
return false;
}
Finally got it.
I used SetWindowsHookEx on the initialization with WH_MOUSE_LL as a paramenter for low level messages.
So, at the end, no Qt code was needed for getting mouse events.
Related
I am currently writing a wrapper for an existing application that has its own GUI. I don't have access to original application's source code (unfortunately). The program that I am writing is in C++ and I am making use of WinAPI. I am manipulating target application by simulating button-clocks, ticking checkboxes etc.
The problem I am facing at the moment is following:
I need to make a selection in droplist implemented as WinAPI ComboBox. I am doing it by using macro ComboBox_SetCurSel. The selection in the droplist changes correctly. However in the original application there is a read-only textbox that changes the value depending on the selection in combobox. And this one does not change when I execute ComboBox_SetCurSel.
The assumption I made is that CBN_SELENDOK and/or CBN_SELCHANGE are sent when selecting an entry in ComboBox manually and this is the bit I am not doing when setting the selection with ComboBox_SetCurSel macro.
However due to lack of experience I cannot figure out how to resolve the problem. Who is normally listening for CBN_SELENDOK and CBN_SELCHANGE. Is it main application window, parent element of the combobox or main application thread? How do I find out.
Is there a macro that would do the whole thing? Like changing the selected item in ComboBox and sending all necessary notifications? Is there some smart workaround?
Any help on the subject, or any additional questions that would help to make situation more clear are welcome.
UPDATE: thanks for comment by Jonathan Potter. I am now attempting to send messages explicitly. Here is the part of the code where I am doing it:
int res = ComboBox_SetCurSel(this->handle, index);
if (res == CB_ERR)
{
return false;
}
PostMessage(GetParent(this->handle),WM_COMMAND, MAKEWPARAM(0,CBN_SELENDOK),0);
PostMessage(GetParent(this->handle),WM_COMMAND, MAKEWPARAM(0,CBN_SELCHANGE),0);
Note this->handle is just a handle to ComboBox itself as I have packed it into the structure for convenience. GetParent(this->handle) Should get immediate parent of ComboBox
Still no result. Does the order of messages matter? Also how do I obtain the identifier that needs to go into LOWORD of WPARAM sent along with WM_COMMAND?
ANSWER:
Thanks to AlwaysLearningNewStuff I have found and an answer. I have been sending messages with 0 as LPARAM. Apparently a handle to ComboBox itself neets to be sent as LPARAM in order for solution to work. This would take me ages to figure it out.
#AlwaysLearningNewStuff, you should have posted this as an answer, not a comment.
Also the bit about using GetDlgCtrlID() to get ControlID of the ComboBox is very useful. This makes code more reliable.
Thank you, everyone who participated.
Here is my final code:
if (this->handle == NULL)
{
return false;
}
int res = ComboBox_SetCurSel(this->handle, index);
if (res == CB_ERR)
{
return false;
}
PostMessage(GetParent(this->handle), WM_COMMAND, MAKEWPARAM(GetDlgCtrlID( this->handle ),CBN_SELENDOK),
(LPARAM)(this->handle));
PostMessage(GetParent(this->handle), WM_COMMAND, MAKEWPARAM(GetDlgCtrlID( this->handle ),CBN_SELCHANGE),
(LPARAM)(this->handle));
return true;
You are correct that CBN_SELCHANGE is not sent when using ComboBox_SetCurSel(), and the documentation says as much:
The CBN_SELCHANGE notification code is not sent when the current selection is set using the CB_SETCURSEL message.
So you have to send the notifications manually. However, you are missing key elements in your messages - the ComboBox's Control ID and HWND. The parent window uses those to identify which child control is sending messages to it so it can then act accordingly.
Try this instead:
int res = ComboBox_SetCurSel(this->handle, index);
if (res == CB_ERR)
{
return false;
}
HWND hParent = GetParent(this->handle);
int iCtrlId = GetDlgCtrlID(this->handle);
if (GetWindowLong(this->handle, GWL_STYLE) & CBS_SIMPLE)
PostMessage(hParent, WM_COMMAND, MAKEWPARAM(iCtrlId,CBN_SELENDOK), LPARAM(this->handle));
PostMessage(hParent, WM_COMMAND, MAKEWPARAM(iCtrlId,CBN_SELCHANGE), LPARAM(this->handle));
I'm trying to restrict the mouse pointer (with a custom cursor) to the client area of the window for my app which works initially but runs into problems when I switch away from the app and back. I'm using ClipCursor() to restrict the cursor and using a couple of different cursors which are situation dependant so I'm using SetCursor() instead of setting the window's class cursor.
I've got the cursor reinstating itself correctly when Alt-Tabbing away from the app and back (which I want to allow) by checking for the WM_SETCURSOR message in my WndProc:
case WM_SETCURSOR:
{
if (mIsMouseGrabOn) // a bool that indicates if the mouse should be restricted to the cient area or not
SetCursor(gmInstance->m_cursorTargetGreen);
else
SetCursor(gmInstance->m_cursorTargetRed);
return 0; // prevent DefWndProc from resetting it
}
but on switching back the cursor is no longer clipped to the client area of my app's window. I've tried checking for WM_ACTIVATEAPP (as shown below) with no luck and tried checking for WM_ACTIVATE with the same code and the same result.
case WM_ACTIVATEAPP:
{
if(wParam == TRUE) // When we are activated
{
if (mIsMouseGrabOn)
GrabMouse(); // function to determine client area and call ClipCusor with the results
else
ClipCusor(NULL); // make the sure cursor is freed
}
else if(wParam == FALSE) // When we are deactivated
{
ClipCursor(NULL);
}
//return 0; // Let DefWndProc handle anything else
}
With the above code in place, the cursor changes correctly but doesn't get captured regardless of the state of mIsMouseGrabOn.
How do I get my cursor to be bound correctly when switching away from the app and back? What message(s) should I be watching for instead of WM_ACTIVATEAPP?
You can watch lost focus and got focus messages
1) WA_ACTIVE or WA_CLICKACTIVE - window got focus
2) WA_INACTIVE - window lost focus
and you should watch if your app run in fullscreen mode:
3) WM_DISPLAYCHANGE - when Windows changing desktop resolution
and resize message
WM_SIZE - if wparam equals SIZE_MINIMIZED,SIZE_MAXIMIZED or SIZE_RESTORED you can clip or unclip cursor
WM_SETFOCUS is sent after the Window receives keyboard focus, which allows ClipCursor to work.
So I thought this would be pretty simple, but I forgot it's MFC. Instead of registering a notification listener for data model changes that would possibly require a GUI update on each individual control I figure why not register it once and then send a message to all the open dock panes and allow them to update their controls as needed on their own terms for efficiency.
My callback function for handling the notification from the server looks something like this:
void CMainFrame::ChangeCallback(uint32_t nNewVersion, const std::vector<uint32_t>& anChangedObjectTypes)
{
CObList panes;
GetDockingManager()->GetPaneList(panes); // assert failure
if (!panes.IsEmpty())
{
POSITION pos = panes.GetHeadPosition();
while (pos)
{
CDockablePane* pPane = dynamic_cast<CDockablePane*>(panes.GetNext(pos));
if (pPane)
pPane->PostMessage(DM_REFRESH, nNewVersion);
}
}
}
The error I am getting is an assertion failure on line 926 of wincore.cpp
CHandleMap* pMap = afxMapHWND();
ASSERT(pMap != NULL); // right here
There is a comment below this saying this can happen if you pass controls across threads however this is a single threaded MFC application and this is all being done from the main frame.
Does anyone know what else can cause this?
If there is another way to go about sending a message to all the open CDockablePane derived windows in MFC that works as well ...
Here's the obvious workaround that I didn't want to have to do but after hours of debugging and no response here I guess this is a viable answer:
I added std::vector<CDockPane*> m_dockList; to the members of CMainFrame
Now after each call to AddPane in various places that can create and open new dock panes I make a subsequent call to push_back and then I override CDockablePane::OnClose like so:
CMainFrame* pMainFrame = reinterpret_cast<CMainFrame*>(AfxGetMainWnd());
if (pMainFrame)
{
std::vector<CDockPane*>::const_iterator found(
std::find(pMainFrame->DockList()->begin(), pMainFrame->DockList()->end(), this));
if (found != pMainFrame->DockList()->end())
pMainFrame->DockList()->erase(found);
}
CDockablePane::OnClose();
Now this list will only contain pointers to open dock panes which allows me to handle the event notification in my callback and simply do a for loop and PostMessage to each.
Why does Windows SendMessage() always return ZERO, even the message delivery is success? Is there anyway to check the message delivery failure with SendMessage() ?
EDIT
Forgot to mention that I'm using SendMessage() inside a c++ DLL
LRESULT result = ::SendMessage(hwndOtherWindow,WM_COPYDATA, NULL/*(WPARAM)this->GetSafeHwnd()*/,(LPARAM)&structCDS);
"result" is always zero :(, but message delivers to other window successfully
EDIT
BOOL CDlg::OnCopyData(CWnd* pWnd, COPYDATASTRUCT* pCopyDataStruct)
{
return /*CDialog::OnCopyData(pWnd, pCopyDataStruct)*/ true; //true is the trick
}
A zero return from SendMessage for WM_COPYDATA means the target application didn't process the message (FALSE = 0).
The message might deliver successfully, but if the target application doesn't handle the message properly (ie, wrong return value, or passing it to the default window procedure) then your SendMessage call will appear to come back with the wrong result.
It might be worth your time to see what the target application's handling of the WM_COPYDATA message is, if possible.
I have a dialog with a rich-text control. The dialog wants to intercept right-click events on the control; in some cases the dialog should do its own functionality and block the message reaching the control, in other cases it should let the control receive the message.
So I have:
ON_NOTIFY(EN_MSGFILTER, IDC_RICHTEXT, OnRichTextMsgfilter)
void CMyDialog::OnRichTextMsgfilter(NMHDR *pNMHDR, LRESULT *pResult)
{
MSGFILTER *pMsgFilter = reinterpret_cast<MSGFILTER *>(pNMHDR);
*pResult = 0;
if (pMsgFilter->msg == WM_RBUTTONUP)
{
if(...)
*pResult=1;
}
}
I step through the code and pResult is set when it should be, but the control still gets the message. Looking at MSDN it says:
If the control should process the
event, the message returns a zero
value. If the control should ignore
the event, the message returns a
nonzero value.
But the defined message handler signature has no return... I am assuming that's what *pResult is for. Is that not true? If so how do I achieve this?
So i've tried to reproduce this behavior in a simple dialog based app and i really can't -- however, i'm not sure what it is that intercepting the right button message is trying to solve.
That said the following code completely blocks the Left button mouse clicks in my testing (If this returns TRUE the control does not respond to left clicks - however focus will get set to the control on the initial click down and that is more a window manager issue than the control itself)
void CTestDlg::OnMsgfilterRichedit1(NMHDR* pNMHDR, LRESULT* pResult)
{
MSGFILTER *pMsgFilter = reinterpret_cast<MSGFILTER *>(pNMHDR);
if (pMsgFilter->msg == WM_LBUTTONUP || pMsgFilter->msg == WM_LBUTTONDOWN)
{
*pResult = TRUE;
return;
}
*pResult = FALSE;
}
If i change *pResult = TRUE to *pResult = FALSE then the left clicks start working again.
It could be that you want to catch and filter out the WM_RBUTTONDOWN rather than WM_RBUTTONUP to do what you intend, but since i'm unsure what functionality you are trying to filter out i can't say for sure.
The WM_RBUTTONDOWN is stil getting through...