首页 > 解决方案 > 拖动窗口时,WindowFromPoint 不会触发 WM_NCHITTEST

问题描述

除了当前正在拖动的窗口之外,我正在尝试按照答案中提供的说明找到某个点下方的窗口。

WindowFromPoint我尝试的问题是,在拖动操作期间调用时,WM_NCHITTEST显然没有触发消息。所以整个事情都不起作用,我无法获得被拖动的窗口下方的窗口。

这是一个最小的可重现示例。如果您运行此程序并通过标题栏拖动其中一个窗口,您会注意到“OnNcHitTest”不会显示在调试控制台中,即使WM_WINDOWPOSCHANGED正在处理和WindowFromPoint调用它也是如此。

#include <windows.h>
#include <atlbase.h>
#include <atlwin.h>
#include <atltypes.h>

using namespace ATL;

class CMyWindow :
    public CWindowImpl<CMyWindow, CWindow, CWinTraits<WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME, WS_EX_TOOLWINDOW>>
{
public:
    CMyWindow() : m_fDragging(FALSE)
    {
    }

BEGIN_MSG_MAP(CMyWindow)
    MESSAGE_HANDLER(WM_WINDOWPOSCHANGED, OnWindowPosChanged)
    MESSAGE_HANDLER(WM_NCHITTEST, OnNcHitTest)
    MESSAGE_HANDLER(WM_EXITSIZEMOVE, OnExitSizeMove)
    MESSAGE_HANDLER(WM_SYSCOMMAND, OnSysCommand)
    COMMAND_HANDLER(IDCANCEL, BN_CLICKED, OnClickedCancel)
END_MSG_MAP()

private:
    LRESULT OnSysCommand(UINT, WPARAM wParam, LPARAM, BOOL& bHandled)
    {
        bHandled = FALSE;

        if ((wParam & 0xFFF0) == SC_MOVE)
        {
            m_fDragging = TRUE;
            ATLTRACE(L"\nDrag begin\n");
        }
            
        return 0;
    }

    LRESULT OnExitSizeMove(UINT, WPARAM, LPARAM, BOOL& bHandled)
    {
        bHandled = FALSE;
        
        m_fDragging = FALSE;
        ATLTRACE(L"\nDrag end\n");
        
        return 0;
    }

    LRESULT OnNcHitTest(UINT, WPARAM, LPARAM, BOOL& bHandled)
    {
        ATLTRACE(L"\nOnNcHitTest\n");
        
        if (m_fDragging)
            return HTTRANSPARENT;

        bHandled = FALSE;
        return 0;
    }

    LRESULT OnWindowPosChanged(UINT, WPARAM, LPARAM, BOOL& bHandled)
    {
        ATLTRACE(L"\nOnWindowPosChanged\n");
        
        bHandled = FALSE;

        if (m_fDragging)
        {
            CPoint pt;
            if (GetCursorPos(&pt))
            {
                ATLTRACE(L"\nCalling WindowFromPoint\n");
                HWND hwndFromPoint = WindowFromPoint(pt);
            }
        }

        return 0;
    }

    LRESULT OnClickedCancel(WORD, WORD, HWND, BOOL&)
    {
        DestroyWindow();
        return 0;
    }

    void OnFinalMessage(HWND)
    {
        delete this;
    }

    BOOL m_fDragging;
};

INT APIENTRY wWinMain(HINSTANCE, HINSTANCE, LPWSTR, INT)
{
    for (UINT i = 0; i < 3; ++i)
    {
        CMyWindow* pwnd = new CMyWindow();
        if (pwnd)
        {
            CRect rc(0, 0, 500, 500);
            if (pwnd->Create(NULL, &rc))
            {
                pwnd->CenterWindow(NULL);
                pwnd->ShowWindow(SW_SHOW);
            }
        }
    }

    MSG msg;
    while (GetMessage(&msg, NULL, 0, 0))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    return (INT)msg.wParam;
}

谁能解释我做错了什么?这是否与移动/大小模态循环或其他什么有关?我认为这不是问题,因为只要WM_WINDOWPOSCHANGED被触发WindowFromPoint并被调用,那么我认为WM_NCHITTEST会被发送。但也许我错了。

或者问题是否与鼠标位于非客户区域有关?

谢谢你的帮助。

标签: c++winapidrag-and-drop

解决方案


我进行了更改OnNcHitTest以允许拖动客户区中的任何点:

LRESULT OnNcHitTest(UINT, WPARAM, LPARAM, BOOL& bHandled)
{
    ATLTRACE(L"\nOnNcHitTest\n");

    if (m_fDragging)
        return HTTRANSPARENT;
    else
        return HTCAPTION;

    bHandled = FALSE;
    return 0;
}

现在,如果您拖动标题以外的任何内容,OnNcHitTest则在拖动过程中调用。你会得到不同的 HWND 句柄。不知道为什么会有区别。

你到底想达到什么目的?自定义拖放?


推荐阅读