【发布时间】: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