使用ReadConsoleInput()拖动控制台窗口

Dragging the console window with ReadConsoleInput()

本文关键字:控制台 窗口 拖动 ReadConsoleInput 使用      更新时间:2023-10-16

正如您从下面的代码片段中看到的,我已经实现了一种按客户端区域拖动Windows命令提示符的方法。

此代码的问题在于,如果用户遵循以下步骤:

  1. 展开控制台窗口
  2. 单击并拖动焦点控制台窗口(不释放)
  3. 拖动窗口,使光标脱离窗口区域(这可能意味着光标移动过快,或超出设置的界限(第二个监视器),或越过任务栏/其他始终位于顶部的窗口)

控制台窗口将停止跟随光标,直到它再次移动到窗口内。

事实上,当控制台窗口在步骤1中已经处于焦点时,这种情况不会发生,这对我来说真的很奇怪。我已经试着调试了这么多小时,但我再也做不到了。我很感激在这方面的任何帮助。

// Continuously read input
while(ReadConsoleInput(hIn, &ir, 1, &nr))
{
    switch(ir.EventType)
    {
        // Left mouse button down that either focuses or unfocuses console window
        case FOCUS_EVENT:
            // Left mouse button down that focuses console window
            if(ir.Event.FocusEvent.bSetFocus)
            {
                GetCursorPos(&firstPos);
                ScreenToClient(hWnd, &firstPos);
            }
            break;
        case MOUSE_EVENT:
            // Mouse did something inside console window
            switch(ir.Event.MouseEvent.dwButtonState)
            {
                // Left mouse button down or up
                case FROM_LEFT_1ST_BUTTON_PRESSED:
                    // Left mouse down or up, no drag
                    if(!ir.Event.MouseEvent.dwEventFlags)
                    {
                        GetCursorPos(&firstPos);
                        ScreenToClient(hWnd, &firstPos);
                    }
                    // Left button down, and mouse move. -> drag
                    if(ir.Event.MouseEvent.dwEventFlags == MOUSE_MOVED)
                    {
                        GetCursorPos(&currentRelativeToScreen);
                        // Calculate window position while dragging
                        // |
                        // v
                        if(currentRelativeToScreen.x - firstPos.x > scrnSz.right - ca.right)
                            wndPos.X = scrnSz.right - ca.right;
                        else if(currentRelativeToScreen.x - firstPos.x < 0)
                            wndPos.X = 0;
                        else
                            wndPos.X = currentRelativeToScreen.x - firstPos.x;
                        if(currentRelativeToScreen.y - firstPos.y > scrnSz.bottom - ca.bottom)
                            wndPos.Y = scrnSz.bottom - ca.bottom;
                        else if(currentRelativeToScreen.y - firstPos.y < 0)
                            wndPos.Y = 0;
                        else
                            wndPos.Y = currentRelativeToScreen.y - firstPos.y;
                        // End window position calculations
                        SetWindowPos(hWnd, 0, wndPos.X, wndPos.Y, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
                    }
                    break;
                default:
                    break;
            }
            break;
        default:
            break;
    }

拖动窗口,使光标退出窗口区域[…]控制台窗口将停止跟随光标,直到光标再次移动到窗口内。

这是预期的行为,如https://msdn.microsoft.com/en-us/library/windows/desktop/ms684239.aspx(强调添加)。

每当用户移动鼠标、按下或释放其中一个鼠标按钮时,都会生成鼠标事件只有当控制台组具有键盘焦点并且光标位于控制台窗口的边界内时,鼠标事件才会被放置在控制台的输入缓冲区中。


[编辑]回答有关此问题的后续评论。

当控制台窗口在步骤1 中已经处于焦点时,这种情况不会发生

虽然从官方文档中看不出来,但控制台窗口在单击鼠标时似乎会捕捉鼠标(因此即使在移动到控制台窗口之外时也会跟踪鼠标),只有在单击时鼠标已经有焦点的情况下才。对于失焦的控制台窗口,第一次单击会使其获得焦点(不全局捕获鼠标,因此当光标在其客户端区域内时,它只接收mouse_MOVE通知),而第二次单击会进入独占捕获模式(无论光标位置如何,都会接收所有mouse_MMOVE通知)。

这可以通过设置为快速编辑模式的标准控制台窗口进行验证。如果窗口具有输入焦点,则单击并拖动可在控制台中选择文本,即使将鼠标移到窗口之外也是如此。但是,如果窗口没有焦点。第一次点击只会使其聚焦,但不会进入任何捕捉模式,拖动也不会选择任何文本。

鼠标移动do:

if(button==leftButton){
 ReleaseCapture();
 SenMessage(hWnd,WM_NCLBUTTON_DOWN,HTCAPTION,lParam);
}