如何获取运行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
相关产品推荐
相关产品推荐

