踩坑:程序运行时干扰外部输入

低功耗蓝牙项目,需要一块懂省电的板

思澈 SF32LB52 芯片,BLE 协议栈深度优化,上手即开发

省流:别用AttachThreadInput

起初如此实现获取窗口标题函数:

std::string getFocusedWindowTitle()
{
	DWORD tid = GetCurrentThreadId();
	DWORD foreTid = GetWindowThreadProcessId(GetForegroundWindow(), nullptr);
	AttachThreadInput(foreTid, tid, TRUE);   // 临时共享输入队列
	HWND hwnd = GetFocus();                  // 真正的焦点窗口
	AttachThreadInput(foreTid, tid, FALSE);

	if (!hwnd) return {};

	char buf[256]{};
	GetWindowTextA(hwnd, buf, sizeof(buf));
	return buf;
}

ai:

如果你在主循环(比如 Update 函数)或者某个定时器、后台线程中频繁调用这个函数,它完美地解释了你遇到的所有诡异问题(打字漏键、双击失效、卡在启动窗口依然干扰全局)。

这个代码片段中使用了 Windows API 中最臭名昭著、最危险的函数之一:AttachThreadInput

为什么 AttachThreadInput 会导致你的症状?

AttachThreadInput(foreTid, tid, TRUE) 的作用是:强行将你的游戏线程(tid)与当前系统的活动窗口线程(foreTid)的输入消息队列合并共享。

这意味着在这两句代码执行期间,你的游戏进程和桌面的进程(或记事本、浏览器进程)共用同一个键盘和鼠标状态

  1. 导致“鼠标双击没反应”的原因
    Windows 判断鼠标双击的原理是:在极短的时间内(通常500毫秒内),消息队列收到两次 WM_LBUTTONDOWN。
    如果你的游戏在这两次点击之间,或者点击发生时,调用了 AttachThreadInput 然后又立刻 FALSE 解除。这个“合并-拆分”队列的过程会强制重置输入队列的状态,或者导致其中一次点击的消息被短暂卡住。系统就无法正确计算两次点击的时间差,双击就永远变成了两次独立的单击。

  2. 导致“打字打不全(漏键)”的原因
    当你把键盘输入流绑在一起时,如果你的游戏线程当前正在处理一帧复杂的渲染(哪怕只有十几毫秒),或者正在加载资源,它就会反向阻塞那个前台线程的输入处理。你在外部打字时敲下的键,会在瞬间因为队列合并/拆分而丢失,或者因为你的游戏线程没及时 GetMessage 而被直接抛弃。

  3. 极易引发系统级死锁(Deadlock)
    微软官方文档明确警告过,滥用 AttachThreadInput 可能会导致两个应用互相死锁。即使没死锁,疯狂地 Attach/Detach 也会让 Windows 的输入子系统“精神分裂”。

解决方案:
方案 A:直接获取“最上层/前台窗口”的标题(99%的情况适用)

通常来说,只需要知道用户当前正在看哪个大窗口(比如“Chrome浏览器”、“桌面”)。直接用 GetForegroundWindow() 就可以了,完全安全,没有任何副作用

std::string getFocusedWindowTitle_Safe()
{
    // 直接获取前台窗口句柄,不需要 AttachThreadInput!
    HWND hwnd = GetForegroundWindow(); 

    if (!hwnd) return {};

    char buf[512]{};
    GetWindowTextA(hwnd, buf, sizeof(buf));
    return buf;
}
方案 B:确实需要获取“精确到子控件的焦点”(极少情况适用)

如果写的是输入法或者按键精灵,不仅需要知道用户在 Chrome 里,还必须知道光标焦点具体在哪个输入框里。
Windows 提供了专门用来安全查询其他线程焦点状态的 API:GetGUIThreadInfo

#include <windows.h>
#include <string>

std::string getExactFocusedWindowText_Safe()
{
    DWORD foreTid = GetWindowThreadProcessId(GetForegroundWindow(), nullptr);
    if (!foreTid) return {};

    GUITHREADINFO gti;
    gti.cbSize = sizeof(GUITHREADINFO);
    
    // 安全地查询目标线程的焦点,不干扰对方的输入队列!
    if (GetGUIThreadInfo(foreTid, &gti))
    {
        HWND hwndFocus = gti.hwndFocus; // 真正拥有输入焦点的子窗口
        if (hwndFocus)
        {
            char buf[512]{};
            GetWindowTextA(hwndFocus, buf, sizeof(buf));
            return buf;
        }
    }
    return {};
}

低功耗蓝牙项目,需要一块懂省电的板

思澈 SF32LB52 芯片,BLE 协议栈深度优化,上手即开发

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值