Prosta konsola dla programów okienkowych w WinAPI

Patyk

<justify>Podczas tworzenia aplikacji okienkowych stosunkowo często zachodzi potrzeba wyświetlenia wartości którejś ze zmiennych w możliwie najprostszy, a zarazem szybki sposób. Wykorzystanie w tym celu środowiska debugującego zwykle okazuje się zbyt czasochłonnym przedsięwzięciem, co nadmiernie komplikuje zadanie.</justify>
<justify>Alternatywnym rozwiązaniem jest użycie konsoli dostarczanej przez Windows API w postaci szeregu funkcji umożliwiających korzystanie z terminala w programie okienkowym.</justify>
<justify>Klasa Console, będąca przedmiotem niniejszego artykułu, stanowi opakowanie wspomnianych funkcji w prosty i jednocześnie intuicyjny interfejs. Główną jej zaletą jest sprytne wykorzystanie potęgi strumieni (będących częścią biblioteki standardowej C++). W praktyce oznacza to, że korzystanie z klasy Console nie różni się niczym od pracy ze strumieniem wyjściowym cout – do dyspozycji mamy tą samą gamę manipulatorów i efektorów.</justify>

Kod źródłowy

<justify>Całość kodu klasy zawarta jest w pojedynczym pliku nagłówkowym console.h, który należy włączyć do wybranej jednostki translacyjnej za pomocą dyrektywy #include.</justify> ```cpp #ifndef CONSOLE_H #define CONSOLE_H

#include <windows.h>
#include <ostream>

class Console : private std::basic_streambuf<char>, public std::ostream {
typedef std::char_traits<char> traits_type;
HANDLE output;
protected:
int overflow(int character) {
if(character != traits_type::eof()) {
DWORD written;
WriteConsole(output, &character, 1, &written, NULL);
}
return traits_type::not_eof(character);
}
public:
Console() : std::ostream(this) {
AllocConsole();
output = GetStdHandle(STD_OUTPUT_HANDLE);
if(output == INVALID_HANDLE_VALUE || output == NULL)
setstate(badbit);
}
~Console() { FreeConsole(); }
};

#endif // CONSOLE_H

<h1>Przykład użycia</h1>
<justify>Obiekt klasy <b>Console</b> najwygodniej uczynić zmienną globalną, dzięki czemu konsola będzie dostępna przez cały okres życia programu.</justify>
<justify>Umieszczony poniżej przykład prezentuje sposób użycia konsoli w celu wypisywania współrzędnych kursora myszy w obrębie obszaru roboczego okna w reakcji na komunikat <b>WM_MOUSEMOVE</b> (kod obejmuje jedynie procedurę okna).</justify>
```cpp
#include "console.h"
#include <windows.h>

using namespace std;

Console con;

// ...

LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    switch (uMsg)
    {
        case WM_MOUSEMOVE:
            con << "X: " << LOWORD(lParam) << ", Y: " << HIWORD(lParam) << endl;
            break;

        case WM_DESTROY:
            PostQuitMessage(0);
            break;

        default:
            return DefWindowProc (hwnd, uMsg, wParam, lParam);
    }
    return 0;
}

<justify>W rezultacie powinniśmy otrzymać aplikację zachowującą się podobnie jak program na poniższym zrzucie ekranowym:</justify>
console.png

4 komentarzy

#include <cstdio>
#include <iostream>
#include <fstream>
#include <windows.h>

using namespace std;

int __stdcall WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
{
	AllocConsole();
	freopen("CONOUT$", "w", stdout);
	freopen("CONIN$", "r", stdin);
	ios::sync_with_stdio();
	int foo;
	cout << "test";
	cin >> foo;
	cout << foo;
	printf("test");
	scanf("%d", &foo);
	printf("%d", foo);
}

Dzięki za komentarze.
Właściwie trudno nie zgodzić się z tym co napisaliście - poprzedni kod miał niewiele wspólnego z użytecznością ;)

Żeby artykuł nie pozostał bezwartościowy, podążając za sugestią Manfreda, przeobraziłem Console w implementację klasy streambuf, dzięki czemu zamieszczony kod może być ciekawym przykładem tworzenia własnego strumienia (pomijając fakt, że klasę można łatwo zastąpić odpowiednim wywołaniem linkera, co słusznie zauważył crayze).

A tak poza tym, co napisał crayze, to mógłbyś zrobić z tego prawdziwy strumień, a nie tylko coś, co "z wierzchu" go przypomina? Dalej - #include <string> i #include <iomanip> raczej potrzebne nie są. Jeszcze jedno - jeden operator jest friend, drugi metodą. WTF? Nie można dać obydwu tak samo (albo metody, albo friendy)?

a nie prościej wybrać projekt konsolowy i dalej normalnie pisać pod jakimś okienkowym GUI? PRZECIEŻ WYBRANIE PROJEKTU KONSOLOWEGO NIE ZABRANIA KORZYSTANIA Z GUI, będzie i konsola i okno jeśli sobie takowe zrobimy...

to co napisałeś domyślnie zrobi za nas kompilator gdy wybierzemy projekt konsolowy, więc niepotrzebnie się namęczyłeś :>

poza tym do podglądania wartości zmiennych służy debugger, no chyba że nie możemy go użyć (program w fullscreen), ale wtedy konsola też dużo nie pomaga, co najwyżej po zamknięciu programu