Windows setFocus,目标窗口不捕获键盘输入

时间:2017-11-23 10:56:08

标签: c++ visual-studio winapi

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

一个有用的功能是强制关注该窗口,这样,一旦它开始在屏幕上移动,您可以直接开始输入而不点击它以使其聚焦。

我正在使用sfml支持在visual studio中用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;
    }

1 个答案:

答案 0 :(得分:0)

好的,我解决了这个问题。 由于只有当用户的鼠标位于特定位置时才会触发“设置焦点”事件,因此我能想到的最直接的解决方法是模拟该位置的点击。 感谢@DanielSęk的提示, 我仍然感谢更清洁,更少解决方法,所以任何进一步的提示都将受到赞赏。

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