Windows Practice(八)_MFC

简单的Spy++实现

从Spy++运行的效果来看,我们不难推测出,它是根据鼠标移动的位置来进行窗口的查找。那么是什么API呢?那就需要靠万能的谷歌和百度了。

源码实现:
解决方案结构如下:
这里写图片描述
运行效果如下图:
这里写图片描述

主要代码如下:

// Spy++DemoDlg.cpp : 实现文件
//

#include "stdafx.h"
#include "Spy++Demo.h"
#include "Spy++DemoDlg.h"
#include "afxdialogex.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#endif


// CSpyDemoDlg 对话框



CSpyDemoDlg::CSpyDemoDlg(CWnd* pParent /*=NULL*/)
    : CDialogEx(IDD_SPYDEMO_DIALOG, pParent), m_hCursor(nullptr), m_bCapture(FALSE)
{
    m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}

void CSpyDemoDlg::DoDataExchange(CDataExchange* pDX)
{
    CDialogEx::DoDataExchange(pDX);
    DDX_Control(pDX, IDC_STATIC_PIC_TARGET, m_picTarget);
}

BEGIN_MESSAGE_MAP(CSpyDemoDlg, CDialogEx)
    ON_WM_PAINT()
    ON_WM_QUERYDRAGICON()
    ON_WM_LBUTTONDOWN()
    ON_WM_LBUTTONUP()
    ON_WM_MOUSEMOVE()
END_MESSAGE_MAP()


// CSpyDemoDlg 消息处理程序

BOOL CSpyDemoDlg::OnInitDialog()
{
    CDialogEx::OnInitDialog();

    // 设置此对话框的图标。  当应用程序主窗口不是对话框时,框架将自动
    //  执行此操作
    SetIcon(m_hIcon, TRUE);         // 设置大图标
    SetIcon(m_hIcon, FALSE);        // 设置小图标

    // TODO: 在此添加额外的初始化代码
    m_picTarget.GetWindowRect(&m_rectPicTarget);
    ScreenToClient(&m_rectPicTarget);

    m_hCursor = AfxGetApp()->LoadCursorW(IDC_CURSOR_TARGET);


    static_cast<CButton *>(GetDlgItem(IDC_RADIO_PROPERTY))->SetCheck(TRUE);

    GetWindowRect(m_rectWindow);


    m_iconTargetBlank = AfxGetApp()->LoadIcon(IDI_ICON_BLANK_TARGET);
    m_iconTarget = AfxGetApp()->LoadIcon(IDI_ICON_TARGET);

    return TRUE;  // 除非将焦点设置到控件,否则返回 TRUE
}

// 如果向对话框添加最小化按钮,则需要下面的代码
//  来绘制该图标。  对于使用文档/视图模型的 MFC 应用程序,
//  这将由框架自动完成。

void CSpyDemoDlg::OnPaint()
{
    if (IsIconic())
    {
        CPaintDC dc(this); // 用于绘制的设备上下文

        SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);

        // 使图标在工作区矩形中居中
        int cxIcon = GetSystemMetrics(SM_CXICON);
        int cyIcon = GetSystemMetrics(SM_CYICON);
        CRect rect;
        GetClientRect(&rect);
        int x = (rect.Width() - cxIcon + 1) / 2;
        int y = (rect.Height() - cyIcon + 1) / 2;

        // 绘制图标
        dc.DrawIcon(x, y, m_hIcon);
    }
    else
    {
        CDialogEx::OnPaint();
    }
}

//当用户拖动最小化窗口时系统调用此函数取得光标
//显示。
HCURSOR CSpyDemoDlg::OnQueryDragIcon()
{
    return static_cast<HCURSOR>(m_hIcon);
}

void CSpyDemoDlg::OnLButtonDown(UINT nFlags, CPoint point)
{
    if (m_rectPicTarget.PtInRect(point))
    {
        SetCursor(m_hCursor);
        ::SetCapture(m_hWnd);
        m_bCapture = TRUE;

        m_picTarget.SetIcon(m_iconTargetBlank);

        GetWindowRect(&m_rectWindow);
    }

    CDialogEx::OnLButtonDown(nFlags, point);
}

void CSpyDemoDlg::OnLButtonUp(UINT nFlags, CPoint point)
{
    if (m_bCapture)
    {
        ReleaseCapture();
        m_picTarget.SetIcon(m_iconTarget);
        m_bCapture = FALSE;
    }

    CDialogEx::OnLButtonUp(nFlags, point);
}

void CSpyDemoDlg::OnMouseMove(UINT nFlags, CPoint point)
{
    if (m_bCapture)
    {
        // 画框
        // 使得我们的程序也支持dpi缩放
        SetProcessDPIAware();
        // 获取鼠标指向窗口的信息
        // 窗口句柄
        POINT ptMouse = { 0 };
        if (GetCursorPos(&ptMouse))
        {
            HWND hwnd = ::WindowFromPoint(ptMouse);
            if (hwnd)
            {
                if (!m_rectWindow.PtInRect(ptMouse))
                {
                    RECT rectClient = { 0 };
                    if (::GetWindowRect(hwnd, &rectClient))
                    {
                        HDC hdc = ::GetDC(::GetDesktopWindow());
                        if (hdc)
                        {
                            ::InvalidateRect(m_lastDrawHwnd, &m_lastRect, TRUE);

                            HPEN hpen = CreatePen(PS_SOLID, 5, RGB(255, 0, 0));
                            HPEN oldPen = (HPEN)SelectObject(hdc, hpen);
                            HBRUSH hOldBrush = (HBRUSH)SelectObject(hdc, GetStockObject(HOLLOW_BRUSH));

                            Rectangle(hdc, rectClient.left, rectClient.top, rectClient.right, rectClient.bottom);

                            SelectObject(hdc, hOldBrush);
                            SelectObject(hdc, oldPen);
                            DeleteObject(hpen);
                            ::ReleaseDC(hwnd, hdc);

                            m_lastRect = rectClient;
                            m_lastDrawHwnd = hwnd;
                        }

                        CString strHwnd;
                        strHwnd.Format(TEXT("%08X"), reinterpret_cast<unsigned int>(hwnd));
                        SetDlgItemTextW(IDC_EDIT_HANDLE, strHwnd);

                        TCHAR pTitle[MAXBYTE] = { 0 };
                        ::GetWindowTextW(hwnd, pTitle, MAXBYTE);
                        SetDlgItemTextW(IDC_STATIC_TITLE, pTitle);

                        TCHAR pClass[MAXBYTE] = { 0 };
                        ::GetClassNameW(hwnd, pClass, MAXBYTE);
                        SetDlgItemTextW(IDC_STATIC_CLASS, pClass);

                        CString strStyle;
                        strStyle.Format(TEXT("%08X"), ::GetWindowLong(hwnd, GWL_STYLE));
                        SetDlgItemTextW(IDC_STATIC_STYLE, strStyle);

                        CString strRect;
                        RECT rect = { 0 };
                        ::GetWindowRect(hwnd, &rect);
                        strRect.Format(TEXT("(%d,%d)-(%d,%d) %dx%d"), rect.left, rect.top, rect.right, rect.bottom, (rect.right - rect.left), (rect.bottom - rect.top));
                        SetDlgItemTextW(IDC_STATIC_RECT, strRect);
                    }
                }
            }
        }
    }

    CDialogEx::OnMouseMove(nFlags, point);
}
// 实现
protected:
    HICON m_hIcon;

    // 生成的消息映射函数
    virtual BOOL OnInitDialog();
    afx_msg void OnPaint();
    afx_msg HCURSOR OnQueryDragIcon();
    DECLARE_MESSAGE_MAP()
public:
    afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
    CStatic m_picTarget;
    CRect m_rectPicTarget;
    CRect m_rectWindow;
    RECT m_lastRect;
    HCURSOR m_hCursor;
    BOOL m_bCapture;
    HWND m_lastDrawHwnd;
    HICON m_iconTargetBlank;
    HICON m_iconTarget;
    afx_msg void OnLButtonUp(UINT nFlags, CPoint point);
    afx_msg void OnMouseMove(UINT nFlags, CPoint point);

    POINT m_pPrev;

至于用的图标,读者可以自行去网上搜索一下,有很多。

注意的点

  • 只有桌面才会不停地刷新,它的刷新时机不受我们控制,而我们自己写的程序界面的刷新是受我们控制的。
  • dpi缩放的支持,使用SetProcessDPIAware()函数就开启了dpi的缩放功能。但是这个函数只有在Vista之后才被支持。GetDeviceCaps()函数可以帮我们获取dpi缩放值。
  • picture控件的使用,可以显示一个图片,本程序中显示的是一个target图标。鼠标在按下的时候,还可以更换成另一个图标。
  • 消息路由,我们想在鼠标在picture控件上响应鼠标按键的消息时,但是picture控件没有鼠标按键响应的消息处理,那么只能将此操作传递给父类(对话框)来处理。此时的picture的Notify属性应该设置为False,表示它的消息全部由父类来处理,这样就把消息路由到父类对话框中了。我们知道对话框肯定有鼠标响应的函数,我们就可以添加处理逻辑了。还需要注意的是,在按下鼠标的时候,需要判断是否是picture所在的区域,是的话才响应鼠标按键消息,否则不要响应。在判断窗口位置的时候,还需要注意坐标系的转换,这里就涉及到了三套坐标系,桌面坐标系、对话框坐标系以及picture坐标系,需要使用ScreenToClient(&m_rectPicTarget)函数进行坐标转换。
  • 鼠标图标资源的加载需要注意,SetCursor(m_hCursor);
  • 鼠标消息的捕获需要使用::SetCapture(m_hWnd)函数设置,但是用完之后还需要用ReleaseCapture()函数释放。在我们程序中,鼠标按下时需要捕获,在鼠标弹起时需要释放。
  • 矩形的擦除,如果我们使用的是桌面DC,那么就可以在我们程序窗口的外部画矩形,因为它画的是桌面,但是这个矩形的擦除(也就是刷新)是不受我们程序控制的,所以就会出现画的矩形久久不会消去。这样就不像一般的截图软件那样给窗口画矩形的效果了。
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值