如何使用 Win32 API 阻止透明 window 的鼠标输入?
How to block mouse input from transparent window with Win32 API?
我在一个不属于我正在创建的程序的进程中有一个主程序 window。我正在使用 Windows Hook
将 DLL 注入此进程,目的是将子 window 添加到此主 window.
我的最终目标是创建一个 WS_EX_LAYERED
window 允许我创建内部彩色边框但允许中心部分透明并允许鼠标点击通过。这部分工作完美。
WNDCLASS wndClass = {};
wndClass.style = CS_HREDRAW | CS_VREDRAW;
wndClass.lpfnWndProc = OverlayProc;
wndClass.hInstance = g_TargetInstance;
wndClass.hbrBackground = (HBRUSH)CreateSolidBrush(RGB(0, 255, 255));
wndClass.lpszClassName = "OVERLAY";
RegisterClass(&wndClass);
g_Window = CreateWindowEx(WS_EX_LAYERED | WS_EX_TRANSPARENT, "OVERLAY", nullptr,
WS_CHILDWINDOW, rect.left, rect.top, rect.right+1, rect.bottom+1, data->hwnd, nullptr, g_TargetInstance, nullptr);
SetLayeredWindowAttributes(g_Window, RGB(0, 255, 255), 0, LWA_COLORKEY);
ShowWindow(g_Window, SW_SHOW);
UpdateWindow(g_Window);
第二部分是我想有条件地阻止对父级的所有鼠标输入 window。我无法使用 WS_EX_LAYERED
window 的透明部分执行此操作,因此我尝试创建第二个透明 STATIC
控件作为主要 window 的子控件,但这并没有t 也阻止鼠标输入。
我还通过调用 PostMessage
、传递 WM_LBUTTONDOWN
和 WM_LBUTTONUP
向父级 window 发送模拟鼠标点击。我如何通过透明 window?
阻止对父级 window 的所有鼠标输入
这似乎无法通过在同级控件上绘制的简单透明 window 来实现。我最终做的是使用 SetWindowHookEx
添加一个 WH_GETMESSAGE
挂钩到我用来替换主 window 的 WndProc 函数并拦截鼠标消息的进程中。我在 wParam
参数中用特定值标记我的模拟鼠标消息,这样 proc 现在将被模拟并删除该值,并将其传递给父级 window.
如果它没有在点击消息中检测到我的 "tag" 值,它将吞下鼠标消息而不将其传递给原始 WndProc 函数。
注入的 WndProc 替换
LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData)
{
switch (uMsg)
{
case WM_LBUTTONDOWN:
wParam -= 11141008;
if (wParam != MK_LBUTTON && !g_Paused)
return 0;
break;
case WM_LBUTTONUP:
wParam -= 11141008;
if (wParam != 0 && !g_Paused)
return 0;
break;
case WM_MOUSEHOVER:
case WM_MOUSEMOVE:
if (!g_Paused)
return 0;
}
return DefSubclassProc(hWnd, uMsg, wParam, lParam);
}
Windows 钩子函数的片段
//...
switch (data->message)
{
case (WM_USER + 1):
{
g_Paused = FALSE;
//...
SetWindowSubclass(data->hwnd, WndProc, 1, 0);
break;
}
case (WM_USER + 2):
{
RemoveWindowSubclass(data->hwnd, WndProc, 1);
//...
break;
}
}
//...
window 钩子函数中的代码用于子类化主进程 window 并注入我自己的 WndProc 函数,该函数反过来按我想要的方式处理鼠标输入。
这是用于 "simulate" 鼠标点击的代码,无需在 window 中进行物理点击。请注意添加到 wParam
的值,以将此点击标识为模拟点击而非由用户生成。
void Window::LeftClick(DWORD x, DWORD y, DWORD delayMillis)
{
LPARAM lparam = MAKELPARAM(x, y);
lock_guard<mutex> lock(this->m_ClickMutex);
PostMessage(this->m_Window, WM_LBUTTONDOWN, 11141008 + MK_LBUTTON, lparam);
this_thread::sleep_for(std::chrono::milliseconds(delayMillis));
PostMessage(this->m_Window, WM_LBUTTONUP, 11141008, lparam);
}
另外,针对评论中嘲笑我选择simulated
这个词的人以及对使用PostMessage
模拟键盘输入的批评,这是我的键盘输入测试方法这(为了我的目的)完美且非常可靠地工作
void GameWindow::KeyPress(UINT vkCode) const
{
UINT scanCode = MapVirtualKey(vkCode, MAPVK_VK_TO_VSC);
LPARAM lparam1 = MAKELPARAM(1, scanCode);
LPARAM lparam2 = MAKELPARAM(1, 0xC000 | scanCode);
PostMessage(this->m_Window, WM_KEYDOWN, vkCode, lparam1);
this_thread::sleep_for(chrono::milliseconds(25));
PostMessage(this->m_Window, WM_KEYUP, vkCode, lparam2);
}
我在一个不属于我正在创建的程序的进程中有一个主程序 window。我正在使用 Windows Hook
将 DLL 注入此进程,目的是将子 window 添加到此主 window.
我的最终目标是创建一个 WS_EX_LAYERED
window 允许我创建内部彩色边框但允许中心部分透明并允许鼠标点击通过。这部分工作完美。
WNDCLASS wndClass = {};
wndClass.style = CS_HREDRAW | CS_VREDRAW;
wndClass.lpfnWndProc = OverlayProc;
wndClass.hInstance = g_TargetInstance;
wndClass.hbrBackground = (HBRUSH)CreateSolidBrush(RGB(0, 255, 255));
wndClass.lpszClassName = "OVERLAY";
RegisterClass(&wndClass);
g_Window = CreateWindowEx(WS_EX_LAYERED | WS_EX_TRANSPARENT, "OVERLAY", nullptr,
WS_CHILDWINDOW, rect.left, rect.top, rect.right+1, rect.bottom+1, data->hwnd, nullptr, g_TargetInstance, nullptr);
SetLayeredWindowAttributes(g_Window, RGB(0, 255, 255), 0, LWA_COLORKEY);
ShowWindow(g_Window, SW_SHOW);
UpdateWindow(g_Window);
第二部分是我想有条件地阻止对父级的所有鼠标输入 window。我无法使用 WS_EX_LAYERED
window 的透明部分执行此操作,因此我尝试创建第二个透明 STATIC
控件作为主要 window 的子控件,但这并没有t 也阻止鼠标输入。
我还通过调用 PostMessage
、传递 WM_LBUTTONDOWN
和 WM_LBUTTONUP
向父级 window 发送模拟鼠标点击。我如何通过透明 window?
这似乎无法通过在同级控件上绘制的简单透明 window 来实现。我最终做的是使用 SetWindowHookEx
添加一个 WH_GETMESSAGE
挂钩到我用来替换主 window 的 WndProc 函数并拦截鼠标消息的进程中。我在 wParam
参数中用特定值标记我的模拟鼠标消息,这样 proc 现在将被模拟并删除该值,并将其传递给父级 window.
如果它没有在点击消息中检测到我的 "tag" 值,它将吞下鼠标消息而不将其传递给原始 WndProc 函数。
注入的 WndProc 替换
LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData)
{
switch (uMsg)
{
case WM_LBUTTONDOWN:
wParam -= 11141008;
if (wParam != MK_LBUTTON && !g_Paused)
return 0;
break;
case WM_LBUTTONUP:
wParam -= 11141008;
if (wParam != 0 && !g_Paused)
return 0;
break;
case WM_MOUSEHOVER:
case WM_MOUSEMOVE:
if (!g_Paused)
return 0;
}
return DefSubclassProc(hWnd, uMsg, wParam, lParam);
}
Windows 钩子函数的片段
//...
switch (data->message)
{
case (WM_USER + 1):
{
g_Paused = FALSE;
//...
SetWindowSubclass(data->hwnd, WndProc, 1, 0);
break;
}
case (WM_USER + 2):
{
RemoveWindowSubclass(data->hwnd, WndProc, 1);
//...
break;
}
}
//...
window 钩子函数中的代码用于子类化主进程 window 并注入我自己的 WndProc 函数,该函数反过来按我想要的方式处理鼠标输入。
这是用于 "simulate" 鼠标点击的代码,无需在 window 中进行物理点击。请注意添加到 wParam
的值,以将此点击标识为模拟点击而非由用户生成。
void Window::LeftClick(DWORD x, DWORD y, DWORD delayMillis)
{
LPARAM lparam = MAKELPARAM(x, y);
lock_guard<mutex> lock(this->m_ClickMutex);
PostMessage(this->m_Window, WM_LBUTTONDOWN, 11141008 + MK_LBUTTON, lparam);
this_thread::sleep_for(std::chrono::milliseconds(delayMillis));
PostMessage(this->m_Window, WM_LBUTTONUP, 11141008, lparam);
}
另外,针对评论中嘲笑我选择simulated
这个词的人以及对使用PostMessage
模拟键盘输入的批评,这是我的键盘输入测试方法这(为了我的目的)完美且非常可靠地工作
void GameWindow::KeyPress(UINT vkCode) const
{
UINT scanCode = MapVirtualKey(vkCode, MAPVK_VK_TO_VSC);
LPARAM lparam1 = MAKELPARAM(1, scanCode);
LPARAM lparam2 = MAKELPARAM(1, 0xC000 | scanCode);
PostMessage(this->m_Window, WM_KEYDOWN, vkCode, lparam1);
this_thread::sleep_for(chrono::milliseconds(25));
PostMessage(this->m_Window, WM_KEYUP, vkCode, lparam2);
}