Carol Victor
Carol Victor

Reputation: 331

Moving console input text lower

Problem:

I have a console chat. When I am typing a message and another person sends a message, their message gets printed right after the last character i typed, breaking it.

Example:

"HellAndrew: Hi!o!"

I try to type "Hello!", but "Andrew" interferes with a "Hi!"

How I'd like to handle this:

I was thinking that before printing a message from another client I copy the text I currently have in my standard input, print the other user's text then put the text back in the standard input, a row lower.

I tried using ReadConsoleInput/WriteConsoleInput APIs, but it does not do what I want and adds a significant (>10s) delay.

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

I cannot spot any other console APIs on MSDN that I can use. Perhaps I am using the others above wrong somehow.

I would appreciate some help in using these. Thanks!

Upvotes: 1

Views: 347

Answers (1)

Strive Sun
Strive Sun

Reputation: 6289

After researching your question, I made the following test and can get the expected effect.

After reading another process string, use WriteConsoleOutputCharacter to write characters in different positions.

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

The coordinates of the cursor can be modified according to the actual situation, such as to achieve message scrolling.

Note: You can use SetConsoleCursorPosition to locate to another position to accept the input stream firstly.

Like this,

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

enter image description here

Updated:

This is the sample I tested and I attach a quick gif demo.

Server.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);        
    }
}

Client.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);
    }
}

In fact, the code on the client and server is almost the same. I did it by sending WM_COPYDATA message.

This only applies to two different processes on the same computer. For the console chat of two machines, it is necessary to rely on the TCP protocol.

Upvotes: 2

Related Questions