WinApi - OOP



  • Hey leute ich habe mal versucht mir eine Klasse zu schreiben um es zu vereinfachen ein WinApi Fenster zu erstellen. Aber bei der Registrierung der Window Class und/oder bei der Fenster Erstellung (hWnd = CreateWindow(...)) geht scheinbar irgendwas schief

    Hier die Dateien:
    WinAPI_TEST.cpp:

    // WinAPI_TEST.cpp : Definiert den Einstiegspunkt für die Anwendung.
    //
    
    #include "stdafx.h"
    #include "Application.h"
    
    bool done = false;
    
    int WINAPI WinMain(HINSTANCE hInstance,
                         HINSTANCE hPrevInstance,
                         LPTSTR    lpCmdLine,
                         int       nCmdShow)
    {
    
     	CApplication app("WinAPI", hInstance, nCmdShow);
    	app.SetSize(800, 600);
    	app.Init();
    	app.Show();
    
    	while(!app.isDone()) {
    		app.MessageHandling();
    	}
    
    	return 0;
    }
    

    Application.h:

    #pragma once
    
    class CApplication
    {
    public:
    	CApplication(void);
    	CApplication(LPCSTR n, HINSTANCE hInstance, int nCmdShow);
    	~CApplication(void);
    
    	bool Init();
    	void Show();
    
    	// Setters for the Window Properties
    	void SetTitle(char* title);
    	void SetSize(int width, int height);
    	void SetPosition(int x, int y);
    	void MessageHandling();
    	bool isDone() { return done; }
    
    private:
    	LPCSTR WndTitle;
    	int iWndWidth;
    	int iWndHeight;
    	int xPos;
    	int yPos;
    	bool done;
    
    	HWND hWnd;
    	HINSTANCE hInstance;
    	MSG msg;
    	WNDCLASSEX wndClassEX;
    	int nCmdShow;
    
    	HWND Create();
    	void RegisterWndClass();
    
    protected:
    	static LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
    
    };
    

    Application.cpp:

    #include "StdAfx.h"
    #include "Application.h"
    
    CApplication::CApplication(LPCSTR n, HINSTANCE hInstance, int cCmdShow) 
    {
    	this->WndTitle = n;
    	this->hInstance = hInstance;
    	this->nCmdShow = nCmdShow;
    }
    
    CApplication::~CApplication(void)
    {
    }
    
    bool CApplication::Init()
    {
    	RegisterWndClass();
    
    	hWnd = Create();
    	if(!IsWindow(hWnd)) {
    		return false;
    	}
    
    	return true;
    }
    
    HWND CApplication::Create()
    {
    
    	HWND temp = CreateWindow("API_TEST", "API_TEST", WS_OVERLAPPEDWINDOW, 100, 100, 800, 600, NULL, NULL, hInstance, NULL); 
    
    	return temp;
    
    }
    
    void CApplication::Show() 
    {
    	ShowWindow(hWnd, SW_NORMAL);
    	UpdateWindow(hWnd);
    }
    
    void CApplication::MessageHandling() 
    {
    	while(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
    			if(msg.message == WM_QUIT) 
    			{
    				done= true;
    				break;
    			}
    
    			TranslateMessage(&msg);
    			DispatchMessage(&msg);
    
    		}
    }
    
    void CApplication::RegisterWndClass()
    {
    
    	wndClassEX.cbSize 			= sizeof(WNDCLASSEX);
    	wndClassEX.style			= CS_HREDRAW | CS_VREDRAW;
    	wndClassEX.lpfnWndProc		= WndProc;
    	wndClassEX.cbClsExtra		= 0;
    	wndClassEX.cbWndExtra		= 0;
    	wndClassEX.hInstance		= hInstance;
    	wndClassEX.hIcon			= LoadIcon(NULL, IDI_APPLICATION);
    	wndClassEX.hCursor			= LoadCursor(NULL, IDC_ARROW);
    	wndClassEX.hbrBackground	= (HBRUSH)GetStockObject(BLACK_BRUSH);
    	wndClassEX.lpszMenuName		= NULL;
    	wndClassEX.lpszClassName	= "API_TEST";
    
    	RegisterClassEx(&wndClassEX);
    
    }
    
    void CApplication::SetSize(int width, int height) 
    {
    	iWndWidth = width;
    	iWndHeight = height;
    }
    
    LRESULT CALLBACK CApplication::WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) 
    {
    	switch (message)                  /* handle the messages */
        {
            case WM_DESTROY:
                PostQuitMessage (0);       /* send a WM_QUIT to the message queue */
                break;
            default:                      /* for messages that we don't deal with */
                return DefWindowProc (hWnd, message, wParam, lParam);
        }
    
        return 0;
    }
    

    und stdafx.h:

    // stdafx.h : Includedatei für Standardsystem-Includedateien
    // oder häufig verwendete projektspezifische Includedateien,
    // die nur in unregelmäßigen Abständen geändert werden.
    //
    
    #pragma once
    
    #define WIN32_LEAN_AND_MEAN             // Selten verwendete Teile der Windows-Header nicht einbinden.
    // Windows-Headerdateien:
    #include <windows.h>
    
    // C RunTime-Headerdateien
    #include <stdlib.h>
    #include <malloc.h>
    #include <memory.h>
    #include <tchar.h>
    
    #include <string>
    #include <sstream>
    
    // OpenGL-Headerdateien
    #include <gl/GL.h>
    #include <gl/GLU.h>
    

    wär cool wenn mir jemand helfen könnte.


  • Mod

    Es geht also "irgendwas schief". So, so. Lies dir bitte mal den ersten Link in meiner Signatur durch.

    Gibt es einen bestimmten Grund, warum du diesen Thread im C++-Forum und nicht im Winapi-Forum erstellt hast? Geht etwas mit der Sprache "schief"? Oder soll das doch lieber nach Winapi verschoben werden, weil irgendetwas beim Erstellen des Fensters schiefgeht?



  • hIconSm der WNDCLASSEX-Struktur ist nicht initialisiert. Das hättest du aber mit einem Debugger leicht selber herausfinden können.
    Zum restlichen Design sag' ich jetzt mal nichts.



  • das war ja das problem, der Debugger hat gar nichts gesagt.
    Das Programm hat sich einfach wieder beendet.
    Ich probier das dann aber mal aus mit hIconSm
    Danke für die Hilfe.



  • darman96 schrieb:

    das war ja das problem, der Debugger hat gar nichts gesagt.
    Das Programm hat sich einfach wieder beendet.

    ROFL 😉 Mein Debugger spricht auch niemals mit mir (schluchz)...

    Im Ernst: schon mal etwas von Breakpoints und Watch-Window gehört?

    P.S. Deinen Ansatz gibt es schon und nennt sich MFC.



  • Da "done" ebenfalls nicht initialisiert ist, wird das Fenster vermutlich nur kurz eingeblendet.
    Und durch PeekMessage hat dein Programm ständig maximale Auslastung.



  • ja das mit "done" hab ich selbst schon bemerkt und breakpoints hab ich benutzt so konnte ich den Fehler auf die Klassen registrierung und die CreateWindow funktion beschrenken. Aber von Watch-Window hab ich noch nie was gehört. was ist das denn?



  • @Th69 und ich weiß das es sowas schon gibt. ich möchte es aber selber machen.
    und ich will versuchen awesomium zu implementieren falls das hier jemand kennt.



  • kennt hier denn jemand awesomium?



  • darman96 schrieb:

    [...] ich habe mal versucht mir eine Klasse zu schreiben um es zu vereinfachen ein WinApi Fenster zu erstellen. [...]

    Th69 schrieb:

    Deinen Ansatz gibt es schon und nennt sich MFC.

    btw: Application developers should not write frameworks and toolkits

    darman96 schrieb:

    [...] ich will versuchen awesomium zu implementieren [...]

    Brauchst nicht - das machen schon die Typen auf http://awesomium.com

    darman96 schrieb:

    [...] breakpoints hab ich benutzt so konnte ich den Fehler auf [...] die CreateWindow funktion beschränken. [...]

    . o O ( Rückgabewerte zu prüfen scheint total out zu sein 😕 )

    darman96 schrieb:

    [...] Aber von Watch-Window hab ich noch nie was gehört. was ist das denn?

    1. Breakpoint setzten
    2. Programm mit [F5] debuggen
    3. Menü Debug -> Windows -> Watch -> ...
    4. ?
    5. Profit!


  • @Swordfish:

    Ich mach das mit der klasse auch nur zum spaß und um was zu lernen.
    Außerdem bin ich noch in der Schule und muss für niemanden irgendwelche anwendungen schreiben also hab ich genug zeit sowas mal auszuprobieren 😃

    zu Awesomium:
    ich will das SDK von Awesomium.com benutzen um die GUI für meine Programme zu machen.

    zu Watch-Windows:
    Ich werds mal ausprobieren danke.

    Gruß darman96



  • hat jemand Vorschläge wie ich die Klasse noch verbessern könnte?

    Gruß darman96



  • Ja, löschen und neu schreiben. Ernsthaft. Guck dir mal RAII an. Insbesondere Konstruktoren und Destruktoren. Eine Methode Init() weist so gut wie immer darauf hin, dass man das Prinzip nicht verstanden hat. Eine Methode die man vor Init aufrufen muss noch mehr. Nutze Initialisierungslisten. Höre auf auf Membervariablen mit this-> zuzugreifen wenn es nicht unbedingt nötig ist. Nimm Abstand von der ungarischen Notation. (Außer bei windowseigenen Typen vll.)
    WNDCLASS ist kein Member eines Fensterobjekts. msg, title, Größe etc. musst du auch nicht speichern, das macht die WinAPI doch schon. Der einzige Member den du brauchst ist das Handle. (HWND) Was zum Henker macht isDone()? auf const correctness achten. int ist nicht der richtige Typ für Bildschirmkoordinaten oder Größenangaben, oder hast du da schon mal negative Werte gesehen? (std::uint16_t wäre der richtige Typ.)
    Warum heißt deine Klasse überhaupt Application? Das ist so ziemlich der am wenigsten aussagekräftige Name den man sich vorstellen kann. Das ist eine Fensterklasse verdammt, nenn sie window oder so. Und so kannst du auch nicht mehrere Fenster erstellen. Guck dir mal den Beitrag von Shade Of Mine hier an.



  • darman96 schrieb:

    hat jemand Vorschläge wie ich die Klasse noch verbessern könnte?

    • Auf C-Präfix und generell auf UN verzichten (bei hWnd kann man noch darüber streiten, aber "i" und "n" vor Integers ist fragwürdig)
    • Leere Parameterlisten ohne void
    • Konstruktor-Initialisierungsliste verwenden
    • Const-Correctness bei Get-Methoden beachten
    • Keine leeren Destruktoren definieren
    • Init() privat machen und direkt im Konstruktor aufrufen
    • Konsistente Namenskonvention ( isDone vs. SetTitle )
    • Konsistente Namen ("Befehle" wie HandleMessages statt MessageHandling )
    • static_cast statt C-Casts verwenden
    • Sich überlegen, nach welchem Kriterium Funktionen inline sind (oder gleich alles in der .cpp-Datei definieren)


  • cooky451 schrieb:

    ... int ist nicht der richtige Typ für Bildschirmkoordinaten oder Größenangaben, oder hast du da schon mal negative Werte gesehen? (std::uint16_t wäre der richtige Typ.)

    Für Bildschirmangaben schon oder welchen Wert hat bei dir Top und Left, wenn du ein Fenster links bzw. oben teilweise außerhalb des sichtbaren Bereichs schiebst?



  • Th69 schrieb:

    cooky451 schrieb:

    ... int ist nicht der richtige Typ für Bildschirmkoordinaten oder Größenangaben, oder hast du da schon mal negative Werte gesehen? (std::uint16_t wäre der richtige Typ.)

    Für Bildschirmangaben schon oder welchen Wert hat bei dir Top und Left, wenn du ein Fenster links bzw. oben teilweise außerhalb des sichtbaren Bereichs schiebst?

    Dabei auch nicht zu vergessen: Multimonitorsysteme...



  • Könnte mir denn jemand mal ein Beispiel für so eine Klasse geben?



  • Zum Beispiel sf::Window von SFML, da ist WinAPI komplett weggekapselt. Kommt halt drauf an, was du genau machen willst... Wofür brauchst du das Fenster?



  • Ich hatte mir gedacht damit opengl Anwendungen zu machen und für die GUI awesowmium zu benutzen.

    Edit: Wo in den SFML dateien ist denn die WinMain funktion

    Edit 2: Habs gefunden.



  • So ein neuer Versuch:

    main.cpp:

    #include "stdafx.h"
    
    int WINAPI WinMain(HWND Handle, HINSTANCE Instance, HINSTANCE PrevInstance, LPCSTR CmdLine, int CmdShow) 
    {
    
    	Window window(Handle, Instance, "Test");
    
    }
    

    Window.h:

    #pragma once
    #pragma once
    
    #include "stdafx.h"
    
    class Window
    {
    public:
    	Window();
    	Window(HWND Handle, HINSTANCE hInstance, std::string title);
    	~Window();
    private:
    	HWND Handle;
    	HINSTANCE hInstance;
    
    	void RegisterWindowClass();
    	static LRESULT CALLBACK WndProc(HWND Handle, UINT Message, WPARAM wParam, LPARAM lParam);
    };
    

    Window.cpp:

    #include "Window.h"
    
    // static members
    const char* className = "MyClass";
    
    Window::Window(HWND Handle, HINSTANCE Instance, std::string title) : Handle(Handle), Instance(Instance)
    {
    
    	RegisterWindowClass();
    
    	Handle = CreateWindow(className, title.c_str(), WS_OVERLAPPEDWINDOW, 100, 100, 800, 600, NULL, NULL, Instance, NULL);
    
    	ShowWindow(Handle, SW_NORMAL);
    	UpdateWindow(Handle);
    
    }
    
    Window::~Window(void)
    {
    }
    
    void Window::RegisterWindowClass() 
    {
    	WNDCLASSEX wcex;
    
    	wcex.cbSize = sizeof(WNDCLASSEX);
    	wcex.style = CS_HREDRAW | CS_VREDRAW;
    	wcex.lpfnWndProc = WndProc;
    	wcex.cbClsExtra = 0;
    	wcex.cbWndExtra = 0;
    	wcex.hInstance = Instance;
    	wcex.hIcon = LoadIcon(NULL, IDI_APPLICATION);
    	wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
    	wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
    	wcex.lpszMenuName = NULL;
    	wcex.lpszClassName = className;
    	wcex.hIconSm = NULL;
    
    	RegisterClassEx(&wcex);
    }
    
    LRESULT CALLBACK Window::WndProc(HWND Handle, UINT Message, WPARAM wParam, LPARAM lParam)
    {
    
    	if(Message == WM_CREATE) 
    	{
    		long This = reinterpret_cast<long>(reinterpret_cast<CREATESTRUCT*>(lParam)->lpCreateParams);
    		SetWindowLongPtr(Handle, GWLP_USERDATA, This);
    	}
    
    	Window* window = reinterpret_cast<Window*>(GetWindowLongPtr(Handle, GWLP_USERDATA));
    
    	return DefWindowProc(Handle, Message, wParam, lParam);
    
    }
    

    Jetzt bekomm ich aber immer diese fehlermeldung:

    Fehler	1	error C2731: 'WinMain': Überladen der Funktion nicht möglich
    

    PS: ich weiß das da noch einiges fehlt vor allem was message handling angeht aber ich will das jetzt erst mal so ans laufen kriegen 😉



  • Das liegt daran, dass die Parameterliste deiner WinMain Mist ist...


Anmelden zum Antworten