Herzlich Willkommen, lieber Gast!
  Sie befinden sich hier:

  Forum » FAQ C / C++ (WinAPI, Konsole) » Erstellen eines einfachen Fenster

Forum | Hilfe | Team | Links | Impressum | > Suche < | Mitglieder | Registrieren | Einloggen
  Quicklinks: MSDN-Online || STL || clib Reference Grundlagen || Literatur || E-Books || Zubehör || > F.A.Q. < || Downloads   

Autor Thread - Seiten: > 1 <
000
10.01.2003, 16:05 Uhr
void*
Generic Pointer
(Operator)


Erstellt von Uwe

Auf dieser Seite wollen wir uns etwas mit der Windows-Programmierung beschäftigen. Für alte "Hasen" in der Programmierung ist das folgende "lächerlich" aber denken wir an jene, welche erste Schritte mit der Programmierung von "Fenstern" machen. Deshalb und solange noch keine anderen Beispiele existieren, eine kleine Einführung.
Also, wir sind jetzt auf einem Punkt angelangt, wo die Konsoleprogramme keine richtige Herausforderung mehr sind. Achja..., wenn ich nur mit meinem einfachen Compiler eine Windows-Anwendung schreiben könnte. Hatte ich da nicht mal was über API gelesen? Machen wir uns doch erst mal schlau.

Für die Verständigung zwischen Windows und meinem Programm kann ich die Schnittstelle (Interface) Application Programming Interface kurz API genannt nutzen. Dieses API hat es in sich, man möge sich eine Dokumentation besorgen, denn in ihr gibt es jede Menge C-Funktionen für diese Programmierung.

Beginnen wir mit etwas Grundlegendem, der Notation Viele Programmierer, und wir ab sofort auch, haben sich angewöhnt vor die Variablen ein Präfix zu setzen. Das hat den Vorteil, dass wir Fehler bei der Verwendung von Variablen reduzieren können und unser Quellcode übersichtlicher wird. Die Verwendung von unten aufgelisteten Vorsätzen wird übrigens Ungarische Notation genannt. Sie ist aber keinesfalls vorgeschrieben.

Gebräuchliche Präfixe sind im folgenden aufgelistet

C++:
Präfix       Beschreibung
b            logische Variable vom Typ BOOL
by           ein Byte, unsigned char
c            Typ char
dw           Typ DWORD
fn           Funktion
h            Handle, Zugriffsnummer
i            Typ int
l            Typ long
lp           long pointer, 32 bit Windowszeiger
n            Typ int
p            Pointer, Zeiger
s            String, Zeichenkette
sz           zero-terminated string, also ein String mit Null endet



Ich hoffe ich habe die wichtigsten genannt.
--
Gruß
void*
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
001
10.01.2003, 16:05 Uhr
void*
Generic Pointer
(Operator)


Um nun unsere erste Anwendung zu schreiben müssen wir uns folgendes vor Augen halten: Windows, das Betriebssystem, ist der Chef- unser Programm ist untergeordnet und hat nur etwas auszuführen, wenn es das Betriebssystem zuläßt!

Wie ist nun ein typisches Win-Programm aufgebaut? Für eine simple Anwendung brauchen wir ein WinMain()- Funktion und eine Fensterprozedur . In unserem Fall nenne ich sie WfFu(), das war's.

Die WinMain() dient zur Initialisierung und zur Verwaltung von zwei Funktionen GetMessage() und DispatchMessage(), welche zum Datenaustausch mit dem Betriebsystem notwendig sind.

Achtung das folgende Beispiel wurde mit folgenden Compilern zwecks Ausführbarkeit getestet

VC++ 6.0
Lcc-32win
BCC32 (Borland 5.5)
Dev-C++ (4.0)
Beim Borland Compiler ist folgendes als Parameter zu übergeben: bcc32 -tW document

document steht für das Dokument in dem der Quellcode gespeichert ist also z.B.: test.cpp

So, jetzt genug der Worte. Erzeugen wir erst einmal ein Dokument mit dem Namen window.cpp und geben folgenden Funktionsrumpf ein:

C++:
#include <windows.h>
int APIENTRY WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int nCmdShow)
{
    return 0;
}



Unterschiede zur Konsolenanwendung werden deutlich, wir includieren den Windows-Header, welcher praktisch alle von Windows verwendeten Typen, Prototypen der API und Konstanten uns zugänglich macht. Auch die WinMain() unterscheidet sich zur main() Funktion. APIENTRY

ein von Windows definiertes Makro hInstance

eine Zugriffsnummer hPrevInstance

ein von 16 Bit Zeiten Überbleibsel, für uns nicht von Interesse, da immer NULL pCmdLine

Zeichenfolge enthält die Befehlzeile mit der unsere Anwendung gestartet worden ist mCmdShow

enthält den Wert wie unser Fenster (wenn es denn klappt) aussehen soll z.B. maximiert, minimiert etc. Nun kann das Grundgerüst noch nicht sehr viel, wir wollen ja ein Fenster erstellen. Als erstes legen wir die Art des Fensters fest. Windows legt in einem Struct-Code fest, wie die Definition eines Fensters erfolgen muß.

C++:
typedef struct _WNDCLASSEX
{
    UINT    cbSize;
    UINT    style;
    WNDPROC lpfnWndProc;
    int     cbClsExtra;
    int     cbWndExtra;
    HANDLE  hInstance;
    HICON   hIcon;
    HCURSOR hCursor;
    HBRUSH  hbrBackground;
    LPCTSTR lpszMenuName;
    LPCTSTR lpszClassName;
    HICON   hIconSm;
} WNDCLASSEX;


--
Gruß
void*
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
002
10.01.2003, 16:06 Uhr
void*
Generic Pointer
(Operator)


Genaueres zu der Definition bitte in einer Dokumentation nachlesen, ich werde nur die wichtigsten Sachen mit Kommentaren erläutern.

Aus diesem Wirrwarr soll ein Fenster werden? Nicht zu fassen! Und los geht es.

Als erstes müssen eine Variable (wc)erstellen und jedes Datenelement aus der Struktur mit einem Wert belegen. Wir schreiben in unsere WinMain()

C++:
#include <windows.h>

int APIENTRY WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int nCmdShow)
{
    // Überprüfung ob Windows ein Struktur des richtigen Typs empfängt
    WNDCLASSEX wc =   {0};

    // Festlegen auf sizeof(WNDCLASSEX)
    wc.cbSize = sizeof(WNDCLASSEX);

    // Name der Fensterklasse festlegen
    static TCHAR szAppName[] = _T("Fenster");

    // Wir müssen das Fenster neu zeichnen, wenn die Größe verändert wird
    // horizontal und vertikal
    wc.style = CS_HREDRAW | CS_VREDRAW;

    // Jetzt definieren wir die Funktion für die Nachrichtenbehandlung
    wc.lpfnWndProc = WfFu;

    // Keine Bytes hinter der Fensterklasse
    wc.cbClsExtra = 0;

    // Keine Bytes hinter der Fensterinstanz
    wc.cbWndExtra = 0;

    // Behandlung für Anwendungsinstanz hinzufügen
    wc.hInstance = hInstance;

    // Anwendungssymbol wird gesetzt
    wc.hIcon = LoadIcon(NULL,IDI_APPLICATION);

    // den Standart-Cursor von Windows setzen (Pfeil)
    wc.hCursor = LoadCursor(NULL,IDC_ARROW);

    // Wir setzen die Hintergrundfarbe und holen die aktive Windows Backgroundfarbe
    wc.hbrBackground = reinterpret_cast(COLOR_WINDOW+ 1);

    // Wir wollen kein Menü erzeugen also keine Menuressource
    wc.lpszMenuName =   NULL;

    // den Klassennamen setzen
    wc.lpszClassName = szAppName;

    // das Anwendungssymbol auf standard setzen
    wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION);

    return 0;
}



Wenn wir das Beispiel jetzt kompilieren lassen, werden wir kreidebleich, wir erhalten einige Fehlermeldungen. static TCHAR szAppName[] = _T("Fenster");

und wc.lpfnWndProc = WfFu;

verursachen Fehlermeldungen .
--
Gruß
void*
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
003
10.01.2003, 16:07 Uhr
void*
Generic Pointer
(Operator)


Das erste Problem lösen wir indem wir <tchar.h> includieren. Das andere Problem gehen wir gleich an.

C++:
#include <windows.h>

// Jetzt den Header mit einbinden!
#include <tchar.h>

int APIENTRY WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int nCmdShow)
{
    WNDCLASSEX wc = {0};
    wc.cbSize = sizeof(WNDCLASSEX);
    static TCHAR szAppName[] = _T("Fenster");
    wc.style = CS_HREDRAW | CS_VREDRAW;
    wc.lpfnWndProc = WfFu;
    wc.cbClsExtra = 0;
    wc.cbWndExtra = 0;
    wc.hInstance = hInstance;
    wc.hIcon = LoadIcon(NULL,IDI_APPLICATION);
    wc.hCursor = LoadCursor(NULL,IDC_ARROW);
    wc.hbrBackground = reinterpret_cast(COLOR_WINDOW+ 1);
    wc.lpszMenuName = NULL;
    wc.lpszClassName = szAppName;
    wc.hIconSm =  LoadIcon(NULL, IDI_APPLICATION);

    // Hier geht es weiter mit den Erläuterungen

    // Wir Registrieren die erzeugte Fensterklasse
    RegisterClassEx(&wc);

    // Wir erstellen unser Fenster und verwenden die
    // Funktion CreateWindowEx()
    HWND hWnd = CreateWindowEx(
    // Steht für erweiterte Stile (in Bezug auf Fenster)
        WS_EX_CLIENTEDGE,
    // Hier wird der Name des Fenster übergeben
        szAppName,
        _T("Unser erstes Fenster"),
        // Der Stil überlappend wird angegeben
        WS_OVERLAPPEDWINDOW,
        // Jetzt kommen vier Angaben zur Bildschirmposition
        // d.h. die Position und Größe des Fenster
        // welche wir in unserem Beispiel alle als Standart belassen
        CW_USEDEFAULT,
        CW_USEDEFAULT,
        CW_USEDEFAULT,
        CW_USEDEFAULT,
        // Dieses Argument gibt an das unser Fenster kein untergeordnetes
        // Fenster ist
        NULL,
        // Wir benötigen kein Menü für unsere Anwendung
        NULL,
        // Die Zugriffsnummer für unser Fenster wird erstellt, welche
        // Windows uns zugewiesen hat
        hInstance,
        // Das letze Argument bleibt NULL da wir kein Datum der
        // Fenstererstellung brauchen
        NULL);

    // Jetzt wird es Zeit unser Fenster anzuzeigen
    ShowWindow(hWnd, nCmdShow);
    // Sobald sich etwas am Fenster ändert wird neu gezeichnet
    // z.B. wenn wir die Größe des Fenster mit der Maus verändern
    UpdateWindow(hWnd);

    // Hier implementieren wir die letzte Aufgabe unserer WinMain()
    // Funktion, sendet Windows an uns eine Nachricht, brauchen wir
    // eine Schnittstelle, wir müssen also eine Nachrichtenschleife einfügen
    // Windows Nachrichtenstruktur
    MSG msg;

    // Empfangen der Nachrichten
    while (GetMessage(&msg,NULL, 0, 0) == TRUE)
    {
        // Übersetzen was ankommt
        TranslateMessage(&msg);
        // Jetzt tritt unser Dispatcher in Aktion und verteilt die Sache
        DispatchMessage(&msg);
    }

    // Schluß aus vorbei, besuchen wir mal wieder Windows
    return msg.wParam;
}




Der Spaß hat aber seinen Höhepunkt noch nicht erreicht! Wir erinnern uns, haben wir nicht zwei Funktionen, welche ein Programm im Grundgerüst braucht?

Also die Nachrichtenverarbeitungsfunktion muss noch erstellt werden.

Wir müssen zunächst einen Prototypen der WfFu() erstellen, welcher wie folgt aufgebaut ist.

C++:
LRESULT CALLBACK WfFu
// Ermitteln des Fensters, welches die Nachricht auslöste
(HWND hWnd,
// Nachrichten ID, gibt den Typ der Nachricht an
UINT message,
// Zusatzinfo, hängt von der Art der Nachricht ab
WPARAM wParam,
// Zusatzinfo, hängt von der Art der Nachricht ab
LPARAM lParam);



Diese Funktionsdefinition müssen wir in unser Programm aufnehmen und machen wie folgt weiter:

C++:
#include <windows.h>
#include <tchar.h>

// Den Prototyp von WfFu definieren!
LRESULT CALLBACK WfFu(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);

int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
    WNDCLASSEX wc = {0};
    wc.cbSize = sizeof(WNDCLASSEX);
    static TCHAR szAppName[] = _T("Fenster");
    wc.style = CS_HREDRAW | CS_VREDRAW;
    wc.lpfnWndProc = WfFu;
    wc.cbClsExtra = 0;
    wc.cbWndExtra = 0;
    wc.hInstance = hInstance;
    wc.hIcon = LoadIcon(NULL,IDI_APPLICATION);
    wc.hCursor = LoadCursor(NULL,IDC_ARROW);
    wc.hbrBackground = reinterpret_cast(COLOR_WINDOW + 1);
    wc.lpszMenuName = NULL;
    wc.lpszClassName = szAppName;
    wc.hIconSm =  LoadIcon(NULL, IDI_APPLICATION);

    RegisterClassEx(&wc);

    HWND hWnd = CreateWindowEx(
        WS_EX_CLIENTEDGE, szAppName,
        _T("Unser erstes Fenster"),
        WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT,
        CW_USEDEFAULT,
        CW_USEDEFAULT,
        CW_USEDEFAULT,
        NULL,
        NULL,
        hInstance,
        NULL);

    ShowWindow(hWnd, nCmdShow);
    UpdateWindow(hWnd);

    MSG msg;

    while (GetMessage(&msg,NULL, 0, 0) == TRUE)
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    return msg.wParam;
}

// Hier geht es weiter mit den Erläuterungen
// Unsere globale Klasse programmieren wir so:
LRESULT CALLBACK WfFu(HWND hWnd, UINT message,WPARAM wParam, LPARAM lParam)
{
    // Wir verarbeiten die ausgewählte Nachricht
    switch (message)
    {
        // Die Nachricht soll unser Fenster neu zeichnen
    case WM_PAINT:
        {
            // Struktur für den neu zu zeichnenden Bereich festlegen
            // Wir erzeugen den Grätekontex, ist verantwortlich für
            // die Verbindung der einzelnen Geräte, bei uns der Bildschirm
            PAINTSTRUCT paintst;

            // Wir bereiten das Fenster zum zeichnen vor
            HDC hDC = BeginPaint(hWnd, &paintst);
            // Innenbereich unseres Fensters ermitteln, rcClient ist ein Zeiger
            RECT rcClient;
            // Ermitteln der Koordinaten unseres Fenster ab sofort können wir über
            // den Innenbereich verfügen und damit arbeiten (Text ausgeben u.s.w.)
            GetClientRect(hWnd, &rcClient);
            // Hier speichern wir den Modus vom Texthintergrund
            int nOldBkMode = SetBkMode(hDC, TRANSPARENT);
            // Als nächstes wird die Textfarbe festgelegt bei uns die Systemfarbe
            COLORREF clrOldTextColor = SetTextColor(hDC, GetSysColor(COLOR_WINDOWTEXT));
            // Wir wollen nun etwas Text in das Fenster zaubern
            DrawText(
            // Unser Gerätekontex
            hDC,
            // Der Text welcher ausgegeben wird
            _T("Hier erfolgt die Textausgabe"),
            // Eine mit Null endende Zeichenfolge
             -1,
            // Einen rechteckigen Text erzeugen
            &rcClient,
            // Das ganze wird eine einzelne Zeile
            DT_SINGLELINE|
            // Ausgabe erfolgt horizontal zentriert
            DT_CENTER |
            // Ausgabe erfolgt vertikal zentriert
            DT_VCENTER );
            // Wir ersetzen die Textfarbe
            SetTextColor(hDC, clrOldTextColor);
            // und nun auch die des Texthintergrundes
            SetBkMode(hDC, nOldBkMode);
            // Genug gezeichnet, Ende des FensterNeuZeichnens

            EndPaint(hWnd, &paintst);

            return 0;
        }

    // Das Fenster wird gelöscht (zerstört)
    case WM_DESTROY:
        // Die Anwendung wird beendet
        PostQuitMessage(0);
        return 0;

    // wenn sonstige Nachrichten ankommen
    // rufe bitte standardmäßige Nachrichtenbehandlung auf und
    // lasse mich damit zufrieden
    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
    }
}




So. Glückwunsch die erste Windowsanwendung ist erstellt. Wer Lust hat kann mir einmal mitteilen, wie dieser Beitrag angekommen ist. Mich würde interessieren ob die Verständlichkeit gegeben ist, oder was verändert werden muß!
--
Gruß
void*
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
004
10.01.2003, 16:07 Uhr
void*
Generic Pointer
(Operator)


Im Anschluss noch einmal die gesamte Anwendung

C++:
#include <windows.h>
#include <tchar.h>

LRESULT CALLBACK WfFu(HWND hWnd, UINT message,WPARAM wParam, LPARAM lParam);

int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine,

int nCmdShow)
{
    WNDCLASSEX wc = {0};
    wc.cbSize =  sizeof(WNDCLASSEX);
    static TCHAR szAppName[] =
    _T("Fenster");
    wc.style = CS_HREDRAW | CS_VREDRAW;
    wc.lpfnWndProc = WfFu;
    wc.cbClsExtra = 0;
    wc.cbWndExtra = 0;
    wc.hInstance = hInstance;
    wc.hIcon = LoadIcon(NULL,IDI_APPLICATION);
    wc.hCursor = LoadCursor(NULL,IDC_ARROW);
    wc.hbrBackground = reinterpret_cast(COLOR_WINDOW + 1);
    wc.lpszMenuName = NULL;
    wc.lpszClassName = szAppName;
    wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION);

    RegisterClassEx(&wc);

    HWND hWnd = CreateWindowEx(
        WS_EX_CLIENTEDGE,
        szAppName,
        _T("Unser erstes Fenster"),
        WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT,
        CW_USEDEFAULT,
        CW_USEDEFAULT,
        CW_USEDEFAULT,
        NULL,
        NULL,
        hInstance,
        NULL);

    ShowWindow(hWnd, nCmdShow);
    UpdateWindow(hWnd);

    MSG msg;
    while (GetMessage(&msg,NULL, 0, 0) == TRUE)
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    return msg.wParam;
}

LRESULT CALLBACK WfFu(HWND hWnd, UINT message,WPARAM wParam, LPARAM lParam)
{
    switch (message)
    {
    case WM_PAINT:
        {
            PAINTSTRUCT paintst;
            HDC hDC = BeginPaint(hWnd, &paintst);
            RECT rcClient;
            GetClientRect(hWnd, &rcClient);
            int nOldBkMode = SetBkMode(hDC, TRANSPARENT);
            COLORREF clrOldTextColor = SetTextColor(hDC, GetSysColor(COLOR_WINDOWTEXT));
            DrawText(
            hDC,
            _T("Hier erfolgt die Textausgabe"),
            -1,
            &rcClient,
            DT_SINGLELINE|
            DT_CENTER|
            DT_VCENTER );
            SetTextColor(hDC, clrOldTextColor);
            SetBkMode(hDC, nOldBkMode);
            EndPaint(hWnd, &paintst);
            return 0;
        }

    case WM_DESTROY:
        PostQuitMessage(0);
        return 0;

    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
    }
}


--
Gruß
void*
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
Seiten: > 1 <     [ FAQ C / C++ (WinAPI, Konsole) ]  


ThWBoard 2.73 FloSoft-Edition
© by Paul Baecher & Felix Gonschorek (www.thwboard.de)

Anpassungen des Forums
© by Flo-Soft (www.flo-soft.de)

Sie sind Besucher: