【问题标题】:Windows setFocus, target window does not capture keyboard inputWindows setFocus,目标窗口不捕获键盘输入
【发布时间】:2017-11-23 10:56:07
【问题描述】:

我正在为 CMD 窗口(可能还有任何 shell)编写一个包装器,其目的是让 shell 实例始终在屏幕边框之外打开。将鼠标移动到屏幕边缘会导致窗口向下移动。 (窗口是最顶层的窗口)。 关键是让终端始终作为进程访问(不占用任务栏中的空间),并且在不使用时隐藏。

一个有用的功能是强制将焦点放在该窗口上,这样,一旦它开始在您的屏幕中移动,您就可以直接开始输入,而无需单击它以使其获得焦点。

我在 Visual Studio 中使用支持 sfml 的 c++ 编写所有这些代码(该程序本身有许多 sfml 图形窗口,并且该提示是唯一的非图形窗口)。与该包装器相关的代码中的 Sfml 仅用于获取鼠标坐标,格式为sf::Mouse::getPosition().x/y

当我在 Visual Studio 中运行程序时,无论是在调试模式还是在发布模式下,它都运行良好。我可以聚焦其他窗口,在那里做一些事情,一旦我将鼠标移动到使提示窗口在屏幕中移动的位置,如果我在不点击实际页面的情况下开始输入,提示实际上会开始捕获键盘按预期输入。

但是,如果我将程序作为独立的可执行文件运行,则不再实现此行为。看来提示确实获得了焦点,因为“打字光标”在那里,但窗口没有捕获实际的键盘输入,这很奇怪。

相关代码如下:

//create terminal window beg
STARTUPINFO prompt_startupinfo;
PROCESS_INFORMATION prompt_processinfo;
HWND prompt_window;

Win::spawn_prompt(&prompt_startupinfo, &prompt_processinfo, &prompt_window);
//always on bottom
SetWindowPos(prompt_window, HWND_TOP, 0, -PROMPT_HEIGHT + 2, Screen::get_width(), PROMPT_HEIGHT, SWP_SHOWWINDOW);
SetWindowLong(prompt_window, GWL_EXSTYLE, WS_EX_TOOLWINDOW);
SetWindowLong(prompt_window, GWL_STYLE, WS_POPUP | WS_VISIBLE);

//create terminal window end

bool outside = false;
bool exiting = false;
while (IsWindow(prompt_window))
    {
    //Console move beg
    int my = sf::Mouse::getPosition().y;
    int mx = sf::Mouse::getPosition().x;
    RECT rect;
    GetWindowRect(prompt_window, &rect);
    int wy = rect.bottom;
    if ((my <= wy + 1) and not exiting)
        {
        if ((not outside) or (mx < 32))
            {
            if (wy < PROMPT_HEIGHT)
                {
                wy += WINDOW_SPEED * 4;
                outside = false;
                if (wy > PROMPT_HEIGHT)
                    {
                    wy = PROMPT_HEIGHT;
                    }
                }
            SetForegroundWindow(prompt_window);
            }
        }
    else if (wy > 0)
        {
        wy -= WINDOW_SPEED * 4;
        exiting = true;
        if (wy <= 0)
            {
            wy = 0;
            outside = true;
            exiting = false;
            }
        }
    SetWindowPos(prompt_window, 0, 0, wy - PROMPT_HEIGHT, 0, 0, SWP_NOSIZE);
    //Console move end
    Sleep(1000 / 60);
    }

作为一个快速说明,当从 Visual Studio 中运行时,只需调用 SetForegroundWindow(prompt_window); 即可实现所需的行为,甚至不需要 SetFocus(prompt_window);


只是为了完成这里是 Win::spawn_prompt 函数:

HWND Win::find_main_window(unsigned long process_id)
    {
    handle_data data;
    data.process_id = process_id;
    data.best_handle = 0;
    EnumWindows(enum_windows_callback, (LPARAM)&data);
    return data.best_handle;
    }
BOOL CALLBACK Win::enum_windows_callback(HWND handle, LPARAM lParam)
    {
    handle_data& data = *(handle_data*)lParam;
    unsigned long process_id = 0;
    GetWindowThreadProcessId(handle, &process_id);
    if (data.process_id != process_id || !is_main_window(handle))
        {
        return TRUE;
        }
    data.best_handle = handle;
    return FALSE;
    }
BOOL Win::is_main_window(HWND handle)
    {
    return GetWindow(handle, GW_OWNER) == (HWND)0 && IsWindowVisible(handle);
    }
bool Win::spawn_prompt(STARTUPINFO* prompt_startupinfo, PROCESS_INFORMATION* prompt_processinfo, HWND* prompt_window)
    {
    // additional information
    STARTUPINFO si;
    PROCESS_INFORMATION pi;
    pi.hProcess;
    // set the size of the structures
    ZeroMemory(&si, sizeof(si));
    si.cb = sizeof(si);
    ZeroMemory(&pi, sizeof(pi));
    // start the program up
    CreateProcess(L"C:\\Windows\\System32\\cmd.exe",   // the path
        L"",        // Command line
        NULL,           // Process handle not inheritable
        NULL,           // Thread handle not inheritable
        FALSE,          // Set handle inheritance to FALSE
        0,              // No creation flags
        NULL,           // Use parent's environment block
        NULL,           // Use parent's starting directory 
        &si,            // Pointer to STARTUPINFO structure
        &pi             // Pointer to PROCESS_INFORMATION structure (removed extra parentheses)
    );
    Sleep(1000);
    HWND pw = Win::find_main_window(pi.dwProcessId);
    *prompt_startupinfo = si;
    *prompt_processinfo = pi;
    *prompt_window = pw;
    return false;
    }

【问题讨论】:

  • 也许您遇到了前台锁定,而SetForgroundWindow 无法在调试器下可靠地测试,因为被调试的进程得到了特殊处理。请参阅SetForegroundWindow 中的备注部分。检查SetForegroundWindow 结果。提示窗口的创建是从键盘开始的吗?
  • SetWindowLong(prompt_window, GWL_EXSTYLE, WS_EX_TOOLWINDOW); 替换为SetWindowLong(prompt_window, GWL_EXSTYLE, GetWindowLong(prompt_window, GWL_EXSTYLE)|WS_EX_TOOLWINDOW);。 `GWL_STYLE 也一样。
  • @DanielSęk 您的建议允许窗口保留其默认选项,因此窗口将保留其可拖动性、边框和其他所有内容,我不需要这些,因为它将是我的应用程序确定该窗口的移动。此外,这些更改甚至不能解决我的问题。一旦调用了 SetForegroundWindow 和/或 SetFocus,窗口仍然无法捕获键盘输入。

标签: c++ visual-studio winapi


【解决方案1】:

好的,我解决了这个问题。 由于只有当用户的鼠标位于某个位置时才应该触发“设置焦点”事件,所以我能想到的最直接的解决方法是模拟在该位置的单击。 感谢@Daniel Sęk 的提示, 我仍然希望有一个更干净、更少变通方法的解决方案,所以任何进一步的提示都将不胜感激。

编辑: 尽管窗口位于最上方,但有时当前聚焦的窗口仍位于 cmd 窗口上方,如果该聚焦窗口位于我模拟点击的坐标处,它会捕获该点击,从而使提到的解决方案并不总是可靠的。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-01-28
    • 2023-03-31
    • 2011-10-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多