让鼠标在Windows C++中穿梭

10
我正在开发一个Win32 C++应用程序,在这个应用程序中,我希望忽略鼠标事件,让它们穿透到我的窗口下面的窗口。基本上,我的下面的窗口将处理鼠标事件。我宁愿不使用SendMessage将鼠标消息发送到我的下面的窗口,也不使用SetCapture。是否有一种方法可以忽略鼠标事件,并让它们通过Windows API或样式来传递?请注意,我的窗口不是透明的。
提前感谢您的帮助。

2
看看这里的第一个答案。它建议使用WS_EX_TRANSPARENT(窗口不透明,但鼠标事件会穿过)。如果你负担不起,那么其他可能的解决方案就更加复杂,包括窗口子类化和手动将鼠标事件从上层窗口转发到下层窗口。 - Stan
谢谢回复。我尝试了WS_EX_TRANSPARENT,但由于某些原因它没有起作用,它没有将鼠标事件传递到下面的窗口。 - JoderCoder
4个回答

10

我在尝试创建一个音乐播放器时发现了这个问题,以及其他问题。该播放器可以在屏幕上覆盖图形显示,而不影响任何其他交互,包括拖动窗口等。

我已经尝试了WM_NCHITTEST方法和简单地向我的窗口添加WS_EX_TRANSPARENT,但这两种方法都似乎捕获了鼠标点击事件,这是我不想要的。

然而,纯粹出于巧合,我成功地找到了一组标志,可以传递给SetWindowLong(..., GWL_EXSTYLE, ...),看起来能够解决问题,代码如下:

LONG cur_style = GetWindowLong(hwnd, GWL_EXSTYLE);
SetWindowLong(hwnd, GWL_EXSTYLE, cur_style | WS_EX_TRANSPARENT | WS_EX_LAYERED);

看起来这种行为在此文档 这里 有说明:

分层窗口的点击测试基于窗口的形状和透明度。这意味着窗口中颜色键掉或者alpha值为零的区域将鼠标消息传递。但是,如果分层窗口具有WS_EX_TRANSPARENT扩展窗口样式,则分层窗口的形状将被忽略,并且鼠标事件将传递到分层窗口下面的其他窗口。

扩展窗口样式文档也非常有用。对于像我的应用程序一样不希望与之交互的窗口,WS_EX_NOACTIVATE也可能很有用,因为它可以防止某些用户交互操作。

出于纪念的目的,我将注明我使用的代码以确保我的窗口始终位于顶部:

SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);

2
我会尝试处理WM_NCHITTEST并返回HTNOWHERE
我认为使用WS_EX_TRANSPARENT的方法会有其他副作用,只有在基础窗口由同一线程拥有时才有用。从问题中,不清楚基础窗口是属于同一个应用程序还是任何旧的应用程序。

感谢您的回复。我正在开发一个应用程序,用于远程桌面连接(RDC)环境,您可以远程连接到另一台Windows 7机器。我有一个窗口,它是RDC的子窗口 - 组合窗口 - RDC窗口是完全另一个我无法控制的进程。因此,当鼠标移动到我的窗口上时,单击,双击,我只想忽略它并将其传递给下面的窗口。下面的窗口是RDC窗口,它将将其传递到远程会话中的正确窗口。我尝试了WM_NCHITTEST和WS_EX_TRANSPARENT,但都没有起作用。 - JoderCoder

1

我已经使用两个不同的第三方RDC解决方案进行测试。每个解决方案可能以不同的方式创建自己的窗口,具有不同的样式等。如果我在WindowProc中执行以下操作:

    case WM_MOUSEMOVE:
    {   
        std::cout << "WM_MOUSEMOVE" << std::endl;
        VideoWindowWin32* window = reinterpret_cast<VideoWindowWin32*> (GetWindowLongPtr (hWnd, GWL_USERDATA));
        if (window)
        {               
            HWND rParent = GetParent(window->window);
            SetCapture(window->parent);
            //SendMessage(window->parent, uMsg, wParam, lParam);
        }
    }
    break;

其中一个可以正常工作,但另一个却不能。

如果您有任何建议,我将不胜感激。


1
尝试使用MS Spy来确定Windows层次结构、样式和消息流。 - Stan

0
根据Adrian McCarthy的基础上进行了修改,对我来说实际有效(但只有在父窗口拥有子窗口时才有效,否则窗口会再次捕获鼠标):
case WM_NCHITTEST: return HTTRANSPARENT;

HTNOWHERE 导致窗口的 LoadCursor() 不再显示。

这些值似乎是可能的:

#ifndef NONCMESSAGES

/*
 * WM_NCHITTEST and MOUSEHOOKSTRUCT Mouse Position Codes
 */
#define HTERROR             (-2)
#define HTTRANSPARENT       (-1)
#define HTNOWHERE           0
#define HTCLIENT            1
#define HTCAPTION           2
#define HTSYSMENU           3
#define HTGROWBOX           4
#define HTSIZE              HTGROWBOX
#define HTMENU              5
#define HTHSCROLL           6
#define HTVSCROLL           7
#define HTMINBUTTON         8
#define HTMAXBUTTON         9
#define HTLEFT              10
#define HTRIGHT             11
#define HTTOP               12
#define HTTOPLEFT           13
#define HTTOPRIGHT          14
#define HTBOTTOM            15
#define HTBOTTOMLEFT        16
#define HTBOTTOMRIGHT       17
#define HTBORDER            18
#define HTREDUCE            HTMINBUTTON
#define HTZOOM              HTMAXBUTTON
#define HTSIZEFIRST         HTLEFT
#define HTSIZELAST          HTBOTTOMRIGHT
#if(WINVER >= 0x0400)
#define HTOBJECT            19
#define HTCLOSE             20
#define HTHELP              21
#endif /* WINVER >= 0x0400 */


/*
 * SendMessageTimeout values
 */
#define SMTO_NORMAL         0x0000
#define SMTO_BLOCK          0x0001
#define SMTO_ABORTIFHUNG    0x0002
#if(WINVER >= 0x0500)
#define SMTO_NOTIMEOUTIFNOTHUNG 0x0008
#endif /* WINVER >= 0x0500 */
#if(WINVER >= 0x0600)
#define SMTO_ERRORONEXIT    0x0020
#endif /* WINVER >= 0x0600 */
#if(WINVER >= 0x0602)
#endif /* WINVER >= 0x0602 */

#endif /* !NONCMESSAGES */

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接