【问题标题】:Moving console input text lower将控制台输入文本向下移动
【发布时间】:2021-04-13 16:22:52
【问题描述】:

问题:

我有一个控制台聊天。当我正在输入一条消息并且另一个人发送一条消息时,他们的消息会在我输入的最后一个字符之后立即打印,从而破坏它。

例子:

"HellAndrew: Hi!o!"

我尝试输入“Hello!”,但“Andrew”干扰了“Hi!”

我想如何处理:

我在想,在打印来自另一个客户端的消息之前,我会复制我当前在标准输入中的文本,打印其他用户的文本,然后将文本放回标准输入中,再低一行。

我尝试使用 ReadConsoleInput/WriteConsoleInput API,但它并没有达到我想要的效果并增加了显着的 (>10s) 延迟。

ReadConsoleInputW(GetStdHandle(STD_INPUT_HANDLE), Inputs, 100, &Count);
//printing
WriteConsoleInputW(GetStdHandle(STD_INPUT_HANDLE), Inputs, 100, &Count);

我在 MSDN 上找不到任何其他我可以使用的控制台 API。也许我以某种方式错误地使用了上面的其他方法。

在使用这些工具时,我将不胜感激。谢谢!

【问题讨论】:

  • 常规控制台输入/输出将无法处理此问题。我建议使用ncurses 之类的东西来更好地控制控制台,以便您可以在控制台屏幕的不同区域显示您的输入和客户端的输出,以免它们重叠。
  • 您需要“阻止”写入控制台,直到一个用户完成;或缓冲对方的文字,稍后再打印出来。
  • their message gets printed 它是如何“打印”的?如果您的应用程序处理所有 I/O 与与另一个进程共享控制台与谁知道其他内容,这很重要。
  • @ThomasMatthews 那不可行。如果用户不小心按了某个键,则不会有更多消息出现
  • @dxiv 只是一个基本的 printf。而且我猜控制台是绑定到我的进程的控制台进程,所以它在技术上不是我的

标签: c++ c windows winapi


【解决方案1】:

研究了你的问题,我做了如下测试,可以得到预期的效果。

读取另一个进程字符串后,使用WriteConsoleOutputCharacter在不同位置写入字符。

 WriteConsoleOutputCharacterA(hStdOut, lpszString, strlen(lpszString), { 0,0 }, &dwWritten);

光标的坐标可以根据实际情况进行修改,比如实现消息滚动。

注意:您可以先使用SetConsoleCursorPosition定位到另一个位置接受输入流。

这样,

coord.X = 0;
coord.Y = 10;
SetConsoleCursorPosition(hStdOut, coord);
ReadFile(hStdIn, strMessage, 256, &dwRead, NULL);

更新:

这是我测试的示例,我附上一个快速gif demo

服务器.cpp

#include <Windows.h>
#include <stdio.h>
#include <iostream>
#include <thread>

using namespace std;

DWORD dwWritten;
DWORD num;
DWORD dwRead;
char strMessage[256];
HWND h_client;
void td1();
COORD coord;
SHORT i = 0;
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) {
    HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
    HANDLE hStdIn = GetStdHandle(STD_INPUT_HANDLE);
    if (message == WM_DESTROY) {

        PostQuitMessage(0);
    }
    if (message == WM_COPYDATA)
    {        
        COPYDATASTRUCT* pcds = (COPYDATASTRUCT*)lParam;
        if (pcds->dwData == 1)
        {
            CHAR* lpszString = (CHAR*)(pcds->lpData);
            WriteConsoleOutputCharacterA(hStdOut, lpszString, strlen(lpszString)-2, { 0,i++ }, &dwWritten); 
        }
        if (i > 24)
        {
            system("cls");
            i = 0;
            coord.X = 0;
            coord.Y = 25;
            SetConsoleCursorPosition(hStdOut, coord);
            printf("----------------------------------------------------------------------------------------------------------\n");
        }
    }
    return DefWindowProc(hwnd, message, wParam, lParam);
};

HINSTANCE hinst;

int main() {
    HWND hwnd;

    hinst = GetModuleHandle(NULL);
    // create a window class:
    WNDCLASS wc = {};
    wc.lpfnWndProc = WndProc;
    wc.hInstance = hinst;
    wc.lpszClassName = L"win32";

    // register class with operating system:
    RegisterClass(&wc);

    // create and show window:
    hwnd = CreateWindow(L"win32", L"Server", 0, 0, 0, 0, 0, HWND_MESSAGE, NULL, NULL, NULL);
    std::thread t1(td1);
    if (hwnd == NULL) {
        return 0;
    }

    ShowWindow(hwnd, SW_SHOW);


    MSG msg = {};

    while (GetMessage(&msg, NULL, 0, 0)) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

}

void td1()
{
    HANDLE hStdIn = GetStdHandle(STD_INPUT_HANDLE);
    HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
    while (1)
    {
        h_client = FindWindow(L"win32", L"Client");
        if (h_client)
        {
            break;
        }
    }  
    coord.X = 0;
    coord.Y = 25;
    SetConsoleCursorPosition(hStdOut, coord);
    printf("----------------------------------------------------------------------------------------------------------");
    while (1)
    {
        coord.X = 0;
        coord.Y = 26;
        SetConsoleCursorPosition(hStdOut, coord);
        memset(strMessage, 0, 256);
        ReadFile(hStdIn, strMessage, 256, &dwRead, NULL); 
        SetConsoleCursorPosition(hStdOut, coord);
        for (int i = strlen(strMessage); i > 0; i--)
        {
            putchar(32);
        }  
        COPYDATASTRUCT cds;
        cds.dwData = 1; // can be anything
        cds.cbData = sizeof(CHAR) * strlen(strMessage);
        cds.lpData = strMessage;
        SendMessage(h_client, WM_COPYDATA, (WPARAM)h_client, (LPARAM)(LPVOID)&cds);        
    }
}

客户端.cpp

#include <Windows.h>
#include <stdio.h>
#include <iostream>
#include <thread>

using namespace std;

DWORD dwWritten;
DWORD dwRead;
char strMessage[256];
HWND h_server;
SHORT i = 0;
COORD coord;
void td1();

LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) {
    HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
    HANDLE hStdIn = GetStdHandle(STD_INPUT_HANDLE);
    if (message == WM_DESTROY) {

        PostQuitMessage(0);
    }
    if (message == WM_COPYDATA)
    {       
        COPYDATASTRUCT* pcds = (COPYDATASTRUCT*)lParam;
        if (pcds->dwData == 1)
        {
            CHAR* lpszString = (CHAR*)(pcds->lpData);
            int len = pcds->cbData;
            WriteConsoleOutputCharacterA(hStdOut, lpszString, len - 2, { 0,i++ }, &dwWritten);
        }
        if (i > 24)
        {
            system("cls");
            i = 0;        
            coord.X = 0;
            coord.Y = 25;
            SetConsoleCursorPosition(hStdOut, coord);
            printf("----------------------------------------------------------------------------------------------------------\n");
        }
    }
    return DefWindowProc(hwnd, message, wParam, lParam);
}

HINSTANCE hinst;

int main() {
    HWND hwnd;

    hinst = GetModuleHandle(NULL);
    // create a window class:
    WNDCLASS wc = {};
    wc.lpfnWndProc = WndProc;
    wc.hInstance = hinst;
    wc.lpszClassName = L"win32";

    // register class with operating system:
    RegisterClass(&wc);

    // create and show window:
    hwnd = CreateWindow(L"win32", L"Client", 0, 0, 0, 0, 0, HWND_MESSAGE, NULL, NULL, NULL);
    std::thread t1(td1);

    if (hwnd == NULL) {
        return 0;
    }

    ShowWindow(hwnd, SW_SHOW);


    MSG msg = {};

    while (GetMessage(&msg, NULL, 0, 0)) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

}

void td1()
{
    HANDLE hStdIn = GetStdHandle(STD_INPUT_HANDLE);
    HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);  
    while (1)
    {
        h_server = FindWindow(L"win32", L"Server");
        if (h_server)
        {
            break;
        }
    }
    coord.X = 0;
    coord.Y = 25;
    SetConsoleCursorPosition(hStdOut, coord);
    printf("----------------------------------------------------------------------------------------------------------");
    while (1)
    {              
        coord.X = 0;
        coord.Y = 26;
        SetConsoleCursorPosition(hStdOut, coord);
        memset(strMessage, 0, 256);
        ReadFile(hStdIn, strMessage, 256, &dwRead, NULL);  
        SetConsoleCursorPosition(hStdOut, coord);
        for (int i = strlen(strMessage); i > 0; i--)
        {
            putchar(32);
        }
        COPYDATASTRUCT cds;
        cds.dwData = 1; // can be anything
        cds.cbData = sizeof(CHAR) * (strlen(strMessage) + 1);
        cds.lpData = strMessage;
        SendMessage(h_server, WM_COPYDATA, (WPARAM)h_server, (LPARAM)(LPVOID)&cds);
    }
}

其实客户端和服务器端的代码几乎是一样的。我通过发送WM_COPYDATA 消息做到了。

这仅适用于同一台计算机上的两个不同进程。两台机器的控制台聊天,需要依赖TCP协议。

【讨论】:

  • @Carol Victor 我在答案中添加了一个 gif 演示和代码示例。希望能帮到你。
猜你喜欢
  • 2019-06-04
  • 2013-07-25
  • 2011-02-22
  • 2012-02-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-09-30
  • 2018-11-05
相关资源
最近更新 更多