You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何获取运行FarManager的控制台窗口显示内容?

实现思路与优化建议

核心需求

已完成通过CreateProcess()启动FarManager、客户端指令传输与服务器端模拟按键执行的功能,现需捕获FarManager所在控制台窗口的显示内容,回传给未安装FarManager的客户端。


实现思路

1. 控制台窗口内容捕获(优先方案)

FarManager是基于Windows控制台的程序,直接读取其控制台缓冲区是最高效的方式,无需屏幕截图:

  • 获取控制台句柄:通过子进程ID关联控制台,调用AttachConsole(pi.dwProcessId)将当前进程附加到FarManager的控制台,再通过GetStdHandle(STD_OUTPUT_HANDLE)获取输出缓冲区句柄;使用完成后调用FreeConsole()解除附加。
  • 读取缓冲区内容:调用GetConsoleScreenBufferInfo()获取缓冲区大小、光标位置等信息,再用ReadConsoleOutput()读取完整的CHAR_INFO数组,该数组包含每个字符的ASCII值、前景色/背景色属性。

2. 内容序列化与传输

  • 将CHAR_INFO数组转换为客户端可解析的格式:
    • 自定义二进制格式:按顺序存储字符编码、颜色属性,体积小、解析快;
    • JSON格式:可读性强,适合调试,比如每个位置用{"char":"x", "fg":0x07, "bg":0x00}表示。
  • 传输优化:
    • 记录上一次读取的缓冲区状态,仅传输差异区域,减少带宽消耗;
    • 定时轮询读取(比如100ms/次),避免高频读取占用资源。

3. 备选方案:屏幕截图(仅当控制台API无法使用时)

如果无法通过控制台API获取内容,可对FarManager窗口进行截图:

  • 使用GetWindowDC(hwnd)获取窗口设备上下文,调用BitBlt()将窗口内容复制到内存DC,再转换为位图格式(如PNG/JPEG)传输;
  • 缺点:效率低、窗口最小化时无法捕获、需要处理图像压缩,仅作为 fallback 方案。

现有代码优化建议

1. 修正CreateProcess参数

当前代码中prog为Far.exe路径,szCmdline为cmd.exe路径,会导致用cmd启动Far,可能出现控制台关联问题。建议调整为直接启动Far:

TCHAR szCmdline[] = TEXT(""); // 或传递Far的启动参数
TCHAR prog[] = TEXT("D:\\Program Files\\Far Manager x86\\Far.exe");

bSuccess = CreateProcess(
    prog,           
    szCmdline,      
    NULL,           
    NULL,           
    TRUE,           
    CREATE_NEW_CONSOLE,
    NULL,           
    NULL,           
    &si,            
    &pi);           

2. 优化窗口句柄获取逻辑

原GetLastWindowsFromProcessID()遍历所有窗口效率低,且可能获取错误窗口。改用控制台API获取准确句柄:

HWND ProcessInfo::GetFarConsoleWindow() {
    // 附加到子进程控制台
    if (!AttachConsole(pi.dwProcessId)) {
        return NULL;
    }
    // 获取控制台窗口句柄
    HWND consoleHwnd = GetConsoleWindow();
    // 解除附加
    FreeConsole();
    return consoleHwnd;
}

3. 增强按键模拟可靠性

在RunFar()中增加窗口恢复逻辑,避免窗口最小化时SendInput失效:

void ProcessInfo::RunFar(int nVirtKey){ 
    INPUT inputs[2] = {};
    ZeroMemory(inputs, sizeof(inputs));

    inputs[0].type = INPUT_KEYBOARD;
    inputs[0].ki.wVk = static_cast<WORD>(nVirtKey);

    inputs[1].type = INPUT_KEYBOARD;
    inputs[1].ki.wVk = static_cast<WORD>(nVirtKey);
    inputs[1].ki.dwFlags = KEYEVENTF_KEYUP;

    // 先恢复窗口(如果最小化)
    ShowWindow(hwnd, SW_RESTORE);
    SetForegroundWindow(hwnd);
    // 增加短暂延迟,确保窗口激活
    Sleep(50);

    UINT uSent = SendInput(ARRAYSIZE(inputs), inputs, sizeof(INPUT));   
}

原代码参考

CreateProcess函数代码

TCHAR szCmdline[] = TEXT("C:\\Windows\\System32\\cmd.exe");
TCHAR prog[] = TEXT("D:\\Program Files\\Far Manager x86\\Far.exe");

// Create the child process. 
bSuccess = CreateProcess(
    prog,           // far
    szCmdline,      // command line 
    NULL,           // process security attributes 
    NULL,           // primary thread security attributes 
    TRUE,           // handles are inherited
    CREATE_NEW_CONSOLE,   // creation flags
    NULL,           // use parent's environment 
    NULL,           // use parent's current directory 
    &si,            // STARTUPINFO pointer 
    &pi);           // receives PROCESS_INFORMATION

FarManager指令执行函数

//nVirtKey - code of virtual key to run in far
void ProcessInfo::RunFar(int nVirtKey){ 
    //Create structure with buttons and their state
    INPUT inputs[2] = {};
    ZeroMemory(inputs, sizeof(inputs));

    //Set key, that must be "pressed"
    inputs[0].type = INPUT_KEYBOARD;
    inputs[0].ki.wVk = static_cast<WORD>(nVirtKey);

    //Set state of key
    inputs[1].type = INPUT_KEYBOARD;
    inputs[1].ki.wVk = static_cast<WORD>(nVirtKey);
    inputs[1].ki.dwFlags = KEYEVENTF_KEYUP;

    //Set position of window with FarManger to the foreground (by the handle of window)
    SetForegroundWindow(hwnd);

    //Send key structure to window
    UINT uSent = SendInput(ARRAYSIZE(inputs), inputs, sizeof(INPUT));   
}

通过进程ID查找FarManager窗口句柄的函数

//Find handle of far windows
HWND ProcessInfo::GetLastWindowsFromProcessID(){
    HWND vhWnds = 0;
    // find all hWnds (vhWnds) associated with a process id (dwProcessID)
    HWND hCurWnd = NULL;
    do
    {
        hCurWnd = FindWindowEx(NULL, hCurWnd, NULL, NULL);
        DWORD dwProcID = 0;
        GetWindowThreadProcessId(hCurWnd, &dwProcID);
        if (dwProcID == pi.dwProcessId)
        {
            vhWnds = hCurWnd;  // add the found hCurWnd
        }
    } while (hCurWnd != NULL);

    return vhWnds;
}

内容的提问来源于stack exchange,提问作者Zhe87

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.24 14:06:19