Receiving WM_MOUSEMOVE from child controls on MFC CDialog - c++

I have my dialog derived from CDialog and I want to close it once user moves mouse cursor away from it. To do so, I've added OnMouseLeave handler which calls OnCancel(). As I understand, for WM_MOUSELEAVE events to be sent in time, TrackMouseEvent must be called inside OnMouseMove routine. So the whole code is as following:
void CDlgMain::OnMouseLeave()
{
CDialog::OnMouseLeave();
// Close dialog when cursor is going out of it
OnCancel();
}
void CDlgMain::OnMouseMove(UINT nFlags, CPoint point)
{
TRACKMOUSEEVENT tme;
tme.cbSize = sizeof(tme);
tme.hwndTrack = m_hWnd;
tme.dwFlags = TME_LEAVE;
tme.dwHoverTime = HOVER_DEFAULT;
TrackMouseEvent(&tme);
CDialog::OnMouseMove(nFlags, point);
}
It works fine, but the dialog is closed when user hovers some of its child controls (like buttons he wants to click on :) ). It is because child controls do not send WM_MOUSEMOVE to the parent dialog.
The only function I found to "propagate" WM_MOUSEMOVE messages from child controls is SetCapture(). And it does the job, but 1) user cannot click any button after that and 2) mouse icon changes to hourglasses. So this is not an option.
Any suggestions?
Update I placed TrackMouseEvent call to the PreTranslateMessage routine which is called correctly on any mouse move events (even hovering the child controls). The strange thing is WM_MOUSELEAVE is still generated when user hovers child control! Seems like TrackMouseEvent knows what control is hovered now. Any ideas how to fix this?

I would try CDialog::PreTranslateMessage() if this is a modal dialog. If you still cannot detect mouse movements inside the children, the only option left is SetWindowsHookEx + WH_MOUSE.

When 2 dialog get event, then rise child dialog's WM_MOUSELEAVE event by force.
see the code, below
void CDlgParent::OnMouseMove(UINT nFlags, CPoint point)
{
CWnd* cwnd = this->GetDlgItem(IDC_CHILDRENNAME);
::SendMessage(cwnd->m_hWnd, WM_MouseLeave());
CDialog::OnMouseMove(nFlags, point);
}
void CDlgMain::OnMouseMove(UINT nFlags, CPoint point)
{
TRACKMOUSEEVENT tme;
tme.cbSize = sizeof(tme);
tme.hwndTrack = m_hWnd;
tme.dwFlags = TME_LEAVE;
tme.dwHoverTime = HOVER_DEFAULT;
TrackMouseEvent(&tme);
::SetFocus(this->mhWnd);
CDialog::OnMouseMove(nFlags, point);
}
How do you think?

I think I understand the problem now. It's indeed a bit tricky. I think you need a timer to guarantee that the subsequent WM_MOUSEMOVE message is handled (you have to test this).
BOOL CTestDgDlg::PreTranslateMessage(MSG* pMsg)
{
if (pMsg->message == WM_MOUSEMOVE)
{
TCHAR buffer[255];
::GetWindowText(pMsg->hwnd, buffer, 255);
TRACE(_T("WM_MOUSEMOVE: %s\n"), buffer);
}
return CDialogEx::PreTranslateInput(pMsg);
}
Handle WM_MOUSELEAVE, wait for WM_MOUSEMOVE. Did it arrive? No -> dismiss dialog. Yes -> restart.

Thanks for all your help, guys. I wasn't able to make TrackMouseEvent properly, so I end up implementing solution with timer. On each tick I check position of mouse cursor to be inside my dialog area and ensure it is still foreground. That works perfect for me, though it is a little hack.
void CALLBACK EXPORT TimerProc(HWND hWnd, UINT nMsg, UINT nIDEvent, DWORD dwTime)
{
// This is a little hack, but suggested solution with TrackMouseEvent is quite
// unreliable to generate WM_MOUSELEAVE events
POINT pt;
RECT rect;
GetCursorPos(&pt);
GetWindowRect(hWnd, &rect);
HWND hFGW = GetForegroundWindow();
// Send leave message if cursor moves out of window rect or window
// stops being foreground
if (!PtInRect(&rect, pt) || hFGW != hWnd)
{
PostMessage(hWnd, WM_MOUSELEAVE, 0, 0);
}
}

Related

Problems with sending and getting messages from one class to another class. MFC program

Here is my code:
This function is called when i click the left button of the mouse and send te message:
#define WM_MYMESSAGE WM_USER+7
void CChildView::OnLButtonDown(UINT nFlags, CPoint point)
{
counter=0;
CWnd::OnLButtonDown(nFlags, point);
CRect wdRect;
GetClientRect(&wdRect);
HWND hwnd;
hwnd=::FindWindow(NULL,"Client");
if(wdRect.PtInRect(point))
{
counter++;
PostMessage(WM_MYMESSAGE,point.x,point.y);
}
}
in another file Mainfraim.cpp with the help of ON_MESSAGE(WM_MYMESSAGE, OnNameMsg) i send message to ONNameMsg function.This function opens the bmp file. The problem is that the function OnNameMsg does not respond to the message and this function does not work. What should i do to make this function respond on this message. Can you help me with this problem?? Here is the code.
BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd)
ON_WM_CREATE()
ON_WM_SETFOCUS()
ON_MESSAGE(WM_MYMESSAGE, OnNameMsg)
ON_COMMAND(ID_EDIT_LINE, OnEditLine)
END_MESSAGE_MAP()
afx_msg LRESULT CMainFrame::OnNameMsg(WPARAM wParam,LPARAM IParam)
{
MSG msg;
char FileName[500];
char FileTitle[100];
FileName[0]='\0';
GetMessage(&msg,NULL,WM_MOUSEFIRST,0);
CFileDialog file(TRUE);
file.m_ofn.lpstrFilter=TEXT("Bitmap picture files *.bmp\0*.bmp\0All Files *.*\0*.*\0\0");
file.m_ofn.lpstrFileTitle=FileTitle;
file.m_ofn.lpstrFile=FileName;
file.m_ofn.lpstrTitle="Open BMP File";
file.DoModal();
//if (FileName[0]=='\0')return;
SetWindowText(FileTitle);
HANDLE hdibCurrent1 = OpenDIB(FileName);
hbm=0;
hbm=BitmapFromDib(hdibCurrent1,0);
GetObject(hbm,sizeof(BITMAP),(LPSTR)&bm);
CRect wdRect;
GetClientRect(&wdRect);
ClientToScreen(&wdRect);
int j=wdRect.Height();
int i=wdRect.Width();
//SetWindowPos(NULL,wdRect.left,wdRect.top, i,j,NULL);
if(hbm) { CClientDC dc(this);
HDC hdc=::GetDC(m_hWnd);
HDC hdcBits=::CreateCompatibleDC(hdc);
SelectObject(hdcBits,hbm);
//CRect wdRect;
GetClientRect(&wdRect);
CBrush brush;
brush.CreateSolidBrush(RGB(0,0,0));
dc.FillRect(&wdRect,&brush);
BitBlt(hdc, 0, 0, bm.bmWidth,bm.bmHeight,hdcBits,0,0, SRCCOPY);
DeleteDC(hdcBits);
::ReleaseDC(m_hWnd,hdc);
}
return 1;
}
You are sending the message to CChildView, and not to the CMainFrame. Since you want to send message to main frame, and handling it there, you must send (PostMessage) to the window handle of main frame.
The PostMessage you are calling is method is from CWnd, which is not same as ::PostMessage API, and hence it is sending to this. You need a pointer of CMainFrame, and call through that pointer. Let's assume you get main frame's pointer into pMainFrame, and then you can call:
pMainFrame->PostMessage(WM_MYMESSAGE,point.x,point.y);

Reset cursor in WM_SETCURSOR handler properly

INTRODUCTION AND RELEVANT INFORMATION:
I have made an application that needs to change the look of a cursor into hand when mouse hovers above the static control, but resets it to a normal cursor otherwise.
My initial application was in full screen mode, but recently terms have changed and it must have a resizable window.
This means that my handler for WM_SETCURSOR must be rewritten to reflect newly introduced changes.
Cursors are loaded in WM_CREATE, and I have defined class cursor, like this:
// cursors
case WM_CREATE:
hCursorHand = LoadCursor( NULL, IDC_HAND );
hCursorArrow = LoadCursor( NULL, IDC_ARROW );
// other stuff
In my class:
WNDCLASSEX wc;
// ...
wc.hCursor = hCursorArrow;
//...
This is my old WM_CURSOR handler ( code is simplified for clarity purposes ):
case WM_SETCURSOR:
if( (HWND)wParam == GetDlgItem( hwnd, 4000 ) )
SetCursor(hCursorHand);
else
SetCursor(hCursorArrow);
return TRUE;
If cursor hovers above static control, then my handler changes it to hand, else sets it to default cursor ( arrow ).
Bellow is the picture I have sketched in Paint that displays the desired look of the cursor when it hovers above static control, it is on the client area, and when user resizes window.
If additional code snippets are required, ask and I will edit my post, but for now, they are omitted to keep the post short and concise.
I work on Windows XP, using MS Visual Studio C++ and pure Win32 API.
MY EFFORTS TO SOLVE PROBLEM:
Bellow are the code snippets that I have tried, but they all failed:
First snippet:
case WM_SETCURSOR:
if( (HWND)wParam == GetDlgItem( hwnd, 4000 ) )
{
SetCursor(hCursorHand);
return TRUE;
}
else
return DefWindowProc( hWnd, msg, lParam, wParam );
Second Snippet:
case WM_SETCURSOR:
if( (HWND)wParam == GetDlgItem( hwnd, 4000 ) )
{
SetCursor(hCursorHand);
return TRUE;
}
break; // based on MSDN example
Third snippet:
case WM_SETCURSOR:
if( (HWND)wParam == GetDlgItem( hwnd, 4000 ) )
{
SetCursor(hCursorHand);
return TRUE;
}
else
return FALSE;
These set cursor to hand no matter where it is.
If I leave my WM_SETCURSOR handler unchanged, the only problem I get is that instead of sizing arrows, I get regular arrow ( as the cursor’s look ) when I hover over the border, but window can be sized.
If I comment out my WM_SETCURSOR handler, sizing arrows and cursor arrow appear properly, but cursor doesn’t change into hand when hovers above static control ( which is logical, since there is no WM_SETCURSOR handler to change it ).
I have browsed through SO archive, looked on MSDN, CodeProject , DaniWeb, Cprogramming and CodeGuru, but had no success.
Looking through those, I have found an example where people compare low word of the lParam against hit test code.
Looking through MSDN I have found link for hit test values ( http://msdn.microsoft.com/en-us/library/windows/desktop/ms645618%28v=vs.85%29.aspx ) and I have found link for cursor types (http://msdn.microsoft.com/en-us/library/windows/desktop/ms648391%28v=vs.85%29.aspx ).
Currently I am reading them, because I think that I will have to load additional cursor resources, take several comparisons of hit test values, and then use those resources to set cursor look accordingly.
QUESTION:
I really would like my WM_SETCURSOR handler to look like this:
case WM_SETCURSOR:
if( (HWND)wParam == GetDlgItem( hwnd, 4000 ) )
{
SetCursor(hCursorHand);
return TRUE;
}
else
// reset cursor's look to default
so I ask the community to instruct me on how to do this.
If this isn’t possible, then I will consider using multiple if statements to check the hit test code, and set cursor’s look accordingly.
Of course, if there is better solution for my problem, please suggest it, I will consider it as well.
Thank you.
Regards.
In general, if you handle the WM_SETCURSOR message you must either
Call SetCursor() to set the cursor, and return TRUE, or
If the message came from a child window, return FALSE for default processing, or
If the message is from your own window, pass the message through to DefWindowProc()
I think the last two points aren't made quite clear by the MSDN docs.
The window under the mouse pointer gets the first WM_SETCURSOR message. If it handles it and returns at that point, nothing else happens. If however it calls DefWindowProc(), then DWP forwards the message to the window's parent to handle. If the parent chooses not to handle it, it can return FALSE and the DefWindowProc processing will continue.
But this only applies if the message came from a previous call to DWP. If the message originated with the window itself, rather than a child, returning TRUE or FALSE without setting the cursor means the cursor won't be set at all.
Another thing: although your question didn't specify, I'm assuming from your use of GetDlgItem() that your top-level window is a dialog. If that's true, you can't just return TRUE or FALSE for a message - you need to return the value using SetWindowLongPtr() and store the return value in DWLP_MSGRESULT. Returning FALSE from a dialog procedure indicates that you didn't handle the message at all - this is equivalent to passing a message through to DefWindowProc().
So I think the proper handling for your situation is, in your top-level window:
case WM_SETCURSOR:
if( (HWND)wParam == GetDlgItem( hwnd, 4000 ) )
{
SetCursor(hCursorHand);
SetWindowLongPtr(hwnd, DWLP_MSGRESULT, TRUE);
return TRUE;
}
return FALSE;
If your top-level window isn't in fact a dialog, you would do this:
case WM_SETCURSOR:
if( (HWND)wParam == GetDlgItem( hwnd, 4000 ) )
{
SetCursor(hCursorHand);
return TRUE;
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
Here's my first example, if cursor go to menubar, cursor changes to cursor hand:
HCURSOR cursorHand = LoadCursor(NULL, IDC_HAND);
case WM_SETCURSOR:
if(LOWORD(lParam) == HTMENU)
{
SetCursor(cursorHand);
}
break;
Here's my second example, if cursor goes to button, cursor changes to cursorHand:
HCURSOR cursorHand = LoadCursor(NULL, IDC_HAND);
case WM_SETCURSOR:
if(LOWORD(lParam) == buttonId)//declare you
{
SetCursor(cursorHand);
}
break;
Warning:menubar and button is not created! Create you and please, check my answer example.
The question as I understand it is, given a parent window with one more more child windows (one of which is a static control) how do you set the cursor to the hand when the cursor is over the static control, and the arrow when the cursor is over the client area of the parent window, and let default processing take place when the cursor is over a non-client area of the parent window.
To check things out, I wrote a simple program with a top-level window with a static control as a child window. My first attempt was the following:
1) Set the class cursor of the top-level window to LoadCursor(NULL, IDC_ARROW). This allows the default processing to set the cursor to the arrow when appropriate.
2) Keep track of the position of the mouse cursor by handling the WM_MOUSEMOVE message by calling my HandleWMMouseMove function as follows:
case WM_MOUSEMOVE:
HandleWMMouseMove(lParam);
break;
//ptCurrMousePos is a global variable of type POINT
static void HandleWMMouseMove(LPARAM mousepos)
{
ptCurrMousePos.x = (int)(short)(LOWORD(mousepos));
ptCurrMousePos.y = (int)(short)(HIWORD(mousepos));
}
3) and then all I had to do was handle the WM_SETCURSOR message by calling my HandleWMSetCursor function as follows:
case WM_SETCURSOR:
if (HandleWMSetCursor())
return TRUE;
break;
//hwndFrame is a handle to the top-level window.
//hwndStatic is a handle to the static control.
static BOOL HandleWMSetCursor(void)
{
if (ChildWindowFromPoint(hwndFrame, ptCurrMousePos)==hwndStatic) {
SetCursor(hCursorHand);
return TRUE;
}
return FALSE;
}
this worked fine but I couldn't understand how the code posted in the question was working even partially. So I asked the questioner whether the child window was really a static control. The answer was yes but it was created with the SS_NOTIFY style. So I created my child window with this style and my code stopped working, but the code posted with the question started to work. With the help of the Spy++ program distributed with Visual Studio I learned the following.
Static controls are normally transparent (not in the visual sense, but meaning that even when the mouse is over the transparent window, Windows will consider the mouse to be over the window underneath the transparent window).
When you create a static control with SS_NOTIFY the control is no longer transparent (i.e. it starts to receive an process mouse messages (like WM_MOUSEMOVE), so my code stopped working because it never received WM_MOUSE messages when the cursor was over the static control. On the other hand the code in the question did not work when the static control was created without SS_NOTIFY because without this style the static control was transparent, which meant that the WARAM in the WM_SETCURSOR message was never equal to the static control (in other words Windows never considered the mouse to be over the static control because it was transparent).
It is possible to combine the two approaches by changing the WM_SETCURSOR handler function to the following:
//hwndFrame is a handle to the top-level window.
//hwndStatic is a handle to the static control.
static BOOL HandleWMSetCursor(WPARAM wParam)
{
if (((HWND)wParam == hwndStatic) || (ChildWindowFromPoint(hwndFrame, ptCurrMousePos)==hwndStatic)) {
SetCursor(hCursorHand);
return TRUE;
}
return FALSE;
}

OnPaint is updated too often

I have a problem with the OnPaint method of CFrameWnd, and I cant seem to figure out what is happening. OnPaint is called approx every 10 ms, which causes the computer to freeze. Checked CPU usage and this app takes up 50%!
The application is a very simple MFC app, which is written in one file.
// Includes are done here...
class MFC_Tutorial_Window : public CFrameWnd
{
std::string data;
public:
MFC_Tutorial_Window()
{
this->data = "";
Create(NULL, "Data Win"); // Create window
}
void OnPaint()
{
CDC* pDC = GetDC();
CString s = CString(this->data.c_str());
RECT rc;
HWND hwnd = this->m_hWnd;
if(hwnd != NULL) {
::GetWindowRect(hwnd, &rc);
rc.top = rc.bottom/2;
if(pDC != NULL && pDC->m_hDC != NULL) {
pDC->DrawText(s, &rc, DT_CENTER);
}
}
}
void UpdateWithNewData(std::string up) {
this->data = up;
Invalidate();
}
DECLARE_MESSAGE_MAP()
};
BEGIN_MESSAGE_MAP(MFC_Tutorial_Window, CFrameWnd)
ON_WM_PAINT()
END_MESSAGE_MAP()
// App class
class MyApp :public CWinApp
{
MFC_Tutorial_Window *wnd;
BOOL InitInstance()
{
wnd = new MFC_Tutorial_Window();
m_pMainWnd = wnd;
m_pMainWnd->ShowWindow(3);
wnd->UpdateWithNewData("Hello world!");
return 1;
}
};
Does anyone know why OnPaint is spammed by the system? Have been staring at this code for ages and I just can't find it.
The CPaintDC destructor has to be called for the repainting flag to be reset. You need to call beginPaint(); and endPaint(); on your CDC which should actually be changed to a CPaintDC. More importantly, not calling endPaint(); will cause the context to be repainted no matter what.
A WM_PAINT message is generated whenever there are no other messages in the message queue and the window's update region (see InvalidateRect) is non-empty. When handling a WM_PAINT message an application signals that the update region has been repainted by calling EndPaint. Failing to call EndPaint will not mark the update region as handled, so the next time an application asks for a message, WM_PAINT is a valid candidate.
In MFC the functionality to call BeginPaint and EndPaint is encapsulated in the CPaintDC Class. The standard MFC message handler for WM_PAINT looks like this:
void OnPaint()
{
CPaintDC dc(this); // calls BeginPaint()
// Perform rendering operations on dc
// ...
} // CPaintDC::~CPaintDC() calls EndPaint()
More detailed information on using device contexts can be found at Device Contexts.

C++ window draggable by its menu bar

So I'm using Visual C++, and I have created a draggable, borderless window. Anyway, there is a toolbar along the top, and i want to be able to drag the window by that toolbar. I still want the toolbar to be functional, but i have no earthly idea how to be able to drag the window by it. This is my current window (see the toolbar on the top):
And this is my current code to make it draggable:
case WM_NCHITTEST: {
LRESULT hit = DefWindowProc(hWnd, message, wParam, lParam);
if(hit == HTCLIENT) hit = HTCAPTION;
return hit;
}
break;
You are on the right track with hooking WM_NCHITTEST. Now, you need to make change what constitutes a client hit versus a caption hit. If I understand your code right now, anywhere you click within the client area of the window (everything but the border) will allow you to drag the window elsewhere. This will make interacting with your application very difficult. Instead, you should be returning HTCAPTION only after you have determined that the hit was within the menubar area. Specifically, the area of the menubar that does not hold the File/Edit/Help buttons.
case WM_NCHITTEST: {
LRESULT hit = DefWindowProc(hWnd, message, wParam, lParam);
if (hit == HTCLIENT) { // The hit was somewhere in the client area. Don't know where yet.
// Perform your test for whether the hit was in the region you would like to intercept as a move and set hit only when it is.
// You will have to pay particular attention to whether the user is actually clicking on File/Edit/Help and take care not to intercept this case.
// hit = HTCAPTION;
}
return hit;
break;
}
Some things to keep in mind here:
This can be very confusing to a user that wants to minimize, close, or move your application. Menubars do not convey to the user that you can move the window by dragging them.
If you are concerned with vertical pixels you may consider doing what other applications on Windows are starting to do -- moving the menubar functionality to a single button that is drawn in the titlebar. (See recent versions of Firefox/Opera or Windows explorer in Windows 8 for some idea to move things to the titlebar.
In one of my applications I also wanted to make the window what I call "client area draggable". Unfortunately the mentioned solution (replacing HTCLIENT with HTCAPTION)
does have a serious flaws:
Double-clicking in the client area now shows the same behaviour as
double-clicking the caption (i.e. minimizing/maximizing the window)!
To solve this I did the following in my message handler (excerpt):
case WM_MOUSEMOVE:
// Move window if we are dragging it
if (mIsDragging) // variable: bool mIsDragging;
{
POINT mousePos = {GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)};
mIsDragging = (ClientToScreen(hWnd, &mousePos) &&
SetWindowPos(hWnd,
NULL,
mDragOrigin.left + mousePos.x - mDragPos.x,
mDragOrigin.top + mousePos.y - mDragPos.y,
0,
0,
SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE));
}
break;
case WM_LBUTTONDOWN:
// Check if we are dragging and safe current cursor position in case
if (wParam == MK_LBUTTON)
{
POINT mousePos = {GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)};
if (ClientToScreen(hWnd, &mousePos) &&
DragDetect(hWnd, mousePos))
{
// Check if the cursor is pointing to your new caption here!!!!
mIsDragging = true;
mDragPos = mousePos;
GetWindowRect(hWnd, &mDragOrigin);
SetCapture(hWnd);
}
}
break;
// Remove this case when ESC key handling not necessary
case WM_KEYDOWN:
// Restore original window position if ESC is pressed and dragging active
if (!mIsDragging || wParam != VK_ESCAPE)
{
break;
}
// ESC key AND dragging... we restore original position of window
// and fall through to WM_LBUTTONUP as if mouse button was released
// (i.o.w. NO break;)
SetWindowPos(hWnd, NULL, mDragOrigin.left, mDragOrigin.top, 0, 0,
SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
case WM_LBUTTONUP:
ReleaseCapture();
break;
case WM_CAPTURECHANGED:
mIsDragging = false;
break;
The (pseudo) code omits the return values (default: 0) and variable definitions but
should make the procedure clear anyway!? (If not drop me a line and I'll add more
or all code).
ps: I just found another comprehensive description which also explains the differences
of these two solutions: http://tinyurl.com/bqtyt3q

How to know when mouse leaves my window when WM_MOUSELEAVE sometimes doesn't work?

I'm having a problem with TrackMouseEvent and WM_MOUSELEAVE. I call TrackMouseEvent in my app when the mouse is over my window in the WM_SETCURSOR and WM_NCHITTEST handlers. The problem is that if I move the mouse very quickly out of my window, I don't get WM_MOUSELEAVE at all.
I'm fairly sure I'm using this correctly, because normal, slower movements will produce WM_MOUSELEAVE. It's only when the mouse is moving too fast that it doesn't get generated. The problem is, how am I supposed to detect this? My app isn't always in the foreground, so I'm not sure SetCapture will do what I need.
It could be that WM_NCMOUSELEAVE is what you need.
Edit: It's worth mentioning in my opinion that the docs imply that you must call TrackMouseEvent. However, I never did this and I still got the MOUSELEAVE message. Perhaps this call is now redundant and/or buggy?
My experience has been that TrackMouseEvent is not reliable. When I've needed reliable mouse leaves, I've used timers instead. (apologies for cutting this from an MFC project)
void OnNotifyMouseLeave()
{
// Mouse is gone
}
void OnMouseMove(UINT nFlags, CPoint point)
{
if ( m_uTimerId == 0 )
m_uTimerId = SetTimer( MOUSELEAVE, 250, NULL );
...
}
void OnTimer( UINT_PTR nIDEvent )
{
if ( nIDEvent == MOUSELEAVE )
{
POINT pt;
RECT rect;
GetCursorPos( &pt );
GetWindowRect( &rect );
if ( !PtInRect( &rect, pt ) )
{ OnNotifyMouseLeave();
if ( m_uTimerId != 0 )
{ KillTimer( m_uTimerId );
m_uTimerId = 0;
}
}
}
...
}
MouseLeave is finicky. SetCapture is what you need to use. Besides I don't think you can get mouse messages reliably if your app isn't in focus.