Windows界面开发-VC开发在托盘中如何获取MOUSELEAVE消息?

Windows界面开发-VC开发在托盘中如何获取MOUSELEAVE消息?

夜无邪 发布于 2017-04-03 字数 0 浏览 1463 回复 2

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

归属感 2017-07-13 2 楼

对于一般窗口来说,使用 Windows界面开发-VC开发在托盘中如何获取MOUSELEAVE消息? 的方法就可以搞定,但因为 Windows shell 不支持 MOUSELEAVE 消息,所以需要自己来实现,安装 SHellNotify(托盘)窗口的消息钩子来实现。
另外一种是通过线程轮询来实现,思路很简单,如下:
在WM_MOUSEMOVE中保存鼠标的位置,然后启动线程比较当前的鼠标位置与你记录的鼠标位置进行比较,如果不同,就发送鼠标离开消息。

traypos.h

#ifndef TRAYPOS_H
#define TRAYPOS_H

class CTrayPos
{
private:
    POINT               m_ptMouse;
    
    
    HANDLE              m_hThread;
    HANDLE              m_hExitEvent;
    
    BOOL                m_bTrackMouse;

    CRITICAL_SECTION    m_cs;

    
public:
    CTrayPos();
    virtual ~CTrayPos();
    
    static UINT CALLBACK TrackMousePt(PVOID pvClass);
    VOID OnMouseMove();
    BOOL IsMouseHover();
    
protected:
    virtual VOID OnMouseHover() = 0;
    virtual VOID OnMouseLeave() = 0;
};

class CMsgTrayPos : public CTrayPos
{
private:
    HWND    m_hNotifyWnd;
    UINT    m_uID;
    UINT    m_uCallbackMsg;

public:
    CMsgTrayPos(HWND hwnd=NULL, UINT uID=0, UINT uCallbackMsg=0);
    ~CMsgTrayPos();

    VOID SetNotifyIconInfo(HWND hwnd, UINT uID, UINT uCallbackMsg);

protected:
    VOID OnMouseHover();
    VOID OnMouseLeave();
};

#endif

traypos.cpp

#include "stdafx.h"
#include <commctrl.h>
#include <process.h>

#include "traypos.h"
#include "resource.h"

CTrayPos::CTrayPos()
{
    UINT    uThreadId;

    m_bTrackMouse = FALSE;
    m_hExitEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
    m_hThread = (HANDLE) _beginthreadex(NULL, 0, CTrayPos::TrackMousePt, this, 0, &uThreadId);
    InitializeCriticalSection(&m_cs);
}

CTrayPos::~CTrayPos()
{
    if(m_hThread != NULL)
    {
        SetEvent(m_hExitEvent);
        if(WaitForSingleObject(m_hThread, 5000) == WAIT_TIMEOUT)
        {
            TerminateThread(m_hThread, 0);
        }

        CloseHandle(m_hThread);
        m_hThread = NULL;
    }

    if(m_hExitEvent != NULL)
    {
        CloseHandle(m_hExitEvent);
        m_hExitEvent = NULL;
    }

    DeleteCriticalSection(&m_cs);
}

UINT CALLBACK CTrayPos::TrackMousePt(PVOID pvClass)
{
    POINT       ptMouse;
    CTrayPos    *pTrayPos = (CTrayPos *) pvClass;

    while(WaitForSingleObject(pTrayPos->m_hExitEvent, 2000) == WAIT_TIMEOUT)
    {

        if(pTrayPos->m_bTrackMouse == TRUE)
        {
            GetCursorPos(&ptMouse);
            
            if(ptMouse.x != pTrayPos->m_ptMouse.x || ptMouse.y != pTrayPos->m_ptMouse.y)
            {
                pTrayPos->m_bTrackMouse = FALSE;
                pTrayPos->OnMouseLeave();
            }
        }
    }

    return 0;
}

VOID CTrayPos::OnMouseMove()
{
    EnterCriticalSection(&m_cs);

    GetCursorPos(&m_ptMouse);
    if(m_bTrackMouse == FALSE)
    {
        OnMouseHover();
        m_bTrackMouse = TRUE;
    }

    LeaveCriticalSection(&m_cs);
}

BOOL CTrayPos::IsMouseHover()
{
    return m_bTrackMouse;
}

//////////////////////////////////////////////////////////////////////////

CMsgTrayPos::CMsgTrayPos(HWND hwnd, UINT uID, UINT uCallbackMsg)
    : CTrayPos()
{
    SetNotifyIconInfo(hwnd, uID, uCallbackMsg);
}

CMsgTrayPos::~CMsgTrayPos()
{
}

VOID CMsgTrayPos::SetNotifyIconInfo(HWND hwnd, UINT uID, UINT uCallbackMsg)
{
    m_hNotifyWnd = hwnd;
    m_uID = uID;
    m_uCallbackMsg = uCallbackMsg;
}

VOID CMsgTrayPos::OnMouseHover()
{
    if(m_hNotifyWnd != NULL && IsWindow(m_hNotifyWnd))
        PostMessage(m_hNotifyWnd, m_uCallbackMsg, m_uID, WM_MOUSEHOVER);
}

VOID CMsgTrayPos::OnMouseLeave()
{
    if(m_hNotifyWnd != NULL && IsWindow(m_hNotifyWnd))
        PostMessage(m_hNotifyWnd, m_uCallbackMsg, m_uID, WM_MOUSELEAVE);
}
归属感 2017-05-02 1 楼

就要看你要获得的MOUSELEAVE是哪个窗口的。
1)如果要捕获的消息是shell弹出的tip窗口,那么由于这个窗口是explorer.exe进程创建的,现在并微软没有提供标准的接口来处理,因此可以用消息钩子来获得这个消息。
2)如果是你自己创建的窗口就很好办了,模拟_TrackMouseEvent 的实现即可以。在你的窗口中创建一个timer,隔一定时间检查鼠标是否是否在你关注的窗口之内就可以。