Programowanie w języku C/C++ » Artykuły

Prosta konsola dla programów okienkowych w WinAPI

  • 2009-04-28 21:30
  • 4 komentarze
  • 2696 odsłon
  • Oceń ten tekst jako pierwszy
<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>
#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

Przykład użycia


<justify>Obiekt klasy Console 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 WM_MOUSEMOVE (kod obejmuje jedynie procedurę okna).</justify>
#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>

4 komentarze

manfredek 2009-04-29 15:02

#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);
}

Patyk 2009-04-28 21:33

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).

manfredek 2009-04-27 14:57

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)?

crayze 2009-04-27 12:51

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