WinAPI mit C++



  • Hi
    ich bin gerade dabei den Petzold zu lesen. Da das Buch aber schon etwas in die Jahre gekommen ist und die Quellcodes alle in C geschrieben sind, frag ich mich, wie man das ganze mit C++ sprich Templates, Polymorphie etc. benutzen kann.
    Kennt ihr ein paar gute Tutorials oder Bücher, in denen dies beschrieben wird sprich Kapselung der WinAPI etc.?
    Danke im voraus...
    [Edit] Was haltet ihr davon: http://www.relisoft.com/book/win/2control.html? [/Edit]



  • Es gibt dafür Bibliotheken die die C-API von Windows in C++ kapseln. Z.B. SmartWin++ ist schon das was du suchst.
    http://smartwin.sf.net

    Die MFC hat es schon Jahre gemacht, ist aber auch in die Jahre gekommen und nicht mehr wirklich schön. Wenn du Win32-Programmierung in modernem C++ machen willst, finde ich pers. SmartWin++ ein gutes Beispiel.



  • Wollte mir schon länger mal winstl von stlsoft nächer ansehen, bin bisher aber nicht dazu gekommen:

    http://synesis.com.au/software/winstl/



  • mhhh ok das sieht schon ma gut aus.
    Allerdings ist es schwer nur anhand von Sources das Prinzip hinter diesen Bibliotheken zu verstehen.
    Kennt ihr vielleicht Tutorials oder Bücher, die beschreiben, wie man die WinAPI (so wie bei diesen Bibliotheken) kapselt? Mich interessieren Dinge wie die WinMain als Hauptfunktion oder in einer Klasse, die WndProc in eine Klasse gekapselt etc. Eben das was diese Bibliotheken machen nur beschrieben wie man daran geht.
    Ok, man könnte die WinMain und die WndProc auch einfach so als Funktionen im Programm lassen und nur Informationen in Klassen speichern und so aber dann ist das ganze wieder kein wirkliches C++ und auch kein wirkliches C sondern eher so "C+". Deshalb suche ich Buchempfehlungen und Tutorials, die eben WinAPI mit korrektem C++ verbinden.



  • Ich würde es so machen zunächst muss man zwischen Echtzeit- und Ereignissgesteuerten Anwendungen unterscheiden. Echtzeit ist alles was z. B. 70 Mal in der Sekunde auf den Bildschirm ein komplexes 3D Model renden will - EventDriven sind diese Schlaftabletten Anwendungen 😉 - es passiert erst was wenn eine Eingabe vom Benutzer kommt -> Benutzer drückt Schaltfläche -> es wird das neue Gehalt berechnet -> Benutzer Tippt Textein und drück Return -> Datensatz wird in Datenbank übernommen
    Realzeitanwendungen erzeugen 100% CPU Auslastung, Ereignisgesteuerte Anwendungen verbrauchen nur Energie wenn man sie tritt...

    Event-driven programming:

    The programmer writes small "event handlers" that respond to "events." When an event occurs, the operating system calls the program's relevant event handler, it responds to the event, and control is returned to the operating system (which deals with the next event).

    class Application
    		{
    			public:
    				virtual bool initialize();
    				bool isActive();
    				void setActive(bool isActive);
    				virtual int run() = 0;
    			private:
    				bool m_isActive;
    		};
    
    class RunAlways : public Application
    {
    	public:
    		RunAlways();
    
    		int run();
    		virtual bool loop();
    	private:
    
    };
    
    RunAlways::RunAlways()
    {
    	setActive(true);
    }
    
    bool RunAlways::loop()
    {
    	Sleep(10);
    	return true;
    }
    
    int RunAlways::run()
    {
    	MSG        msg; 
    
    	if(!initialize())
    		return -1;
    
    	while(isActive())
    	{
    	if (PeekMessage(&msg,NULL,0,0,PM_REMOVE))
    	{
    	  if (msg.message == WM_QUIT)
    		setActive(false);
    	  else
    	  {
    		TranslateMessage(&msg);
    		DispatchMessage(&msg);
    	  }
    	}
    	else
    	 if(!loop())
    	   break;
    	}
    	return int(msg.wParam);
    }
    

    Beispielprogramm:

    int  WINAPI WinMain(HINSTANCE   hInstance,
                        HINSTANCE   hPrevInstance,
                        LPSTR       lpCmdLine,
                        int         nCmdShow)
    {
    	RunAlways App;
    	return App.run();
    }
    

    Die Methode run Ruft zuerst die Methode initialze() auf. Liefert diese den Wert false so wird die Anwendung beendet, ansonsten wird in einer Endlosschleife geprüft, ob die Anwendung noch aktiv ist (also ob sie beendet werden soll oder nicht). In der Endlosschleife werden zunächst eventuell anfallende Windows-Nachrichten behandelt. Falls keine Windows-Nachrichten vorliegen wird die Methode loop Aufgerufen die in der Defaultimplementierung Sleep(50); aufruft, also den Thread für 50 Millisekunden schlafen legt, wenn keine Windowsnachrichten anliegen

    Die Idee ist folgende: Schreib dir selber ne Klasse MyApplication die von RunAlways abgeleitet ist - überschreibe die Initialisierungsmethode mit eigenen Initialisierungen und überschreib wahlweiße die loop Methode mit irgendetwas das ständig z. B. ein neues Bild auf den Bildschirm malt

    so ohne Fenster ist das alles noch recht langweilig.

    /// Der Fenstermanager verwaltet alle Fenster und schickt nachrichten
    /// an die ActionListener weiter
    class WindowsManager
    {
    public:
    	// Fuegt ein Fenster zum Window Manager hinzu.
    	static bool  addWindow    (Window *windowPTR);
    
    	// Entfernt ein Fenster aus dem WindowManager.
    	static bool  removeWindow (Window *windowPTR);
    
    	// Event Hanlder fuer alle Fenster.
    	static LRESULT CALLBACK windowEventsProcessor(HWND   hWnd  , UINT   wMsg,
    												  WPARAM wParam, LPARAM lParam);
    private:
    	WindowsManager(const WindowsManager& src) {};
    	WindowsManager& operator=(const WindowsManager& src) {return *this;}
    
    	static vector<Window*>  windowsList;  // Liste aller Fenster
    	static ActionEvent Event;			  // Event
    	static MouseEvent  _MouseEvent;		  // MausEvent
    };
    

    Wie du siehst hat die Klasse nur statische Elemente und es sollte nur einen WindowManager geben (kann man einen Singleton drauß machen)
    Die Idee ist folgende: Der WindowManager merkt sich jedes Fenster. Wenn ein Fenster mit CreateWindow... erzeugt wird, dann muss das Fenster ja einen MessageHandler angeben. Als MessageHandler wird aber immer der gleiche verwendet - nämlich der aus der Klasse WindowManager::windowEventsProcessor:

    Window::Window()
    {
    ...
    windowsClass.style            = CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
    windowsClass.lpfnWndProc      = (WNDPROC)WindowsManager::windowEventsProcessor;
    ...
    WindowsManager::addWindow(this);
    ..
    }
    

    Außerdem meldest du das Fenster mit addWindow gleich beim Fenstermanager an (addWindow). Wenn jetzt ein Nachricht an eines der Fenster geschickt wird landet die in der Methode windowEventsProcessor. Diese erhält ja einen Handle des Fenster an die die Nachricht geht. Jetzt kannst du anhand der windowList per Window.getHandle raussuchen an welches Fenster die Nachricht eigentlich geht (hier wäre eigentlich eine Hashmap noch besser, da ja die Liste nur linear durchsucht werden kann, oder man macht ne SortedList die binär durchsucht werden kann oder Hashing... naja das ist dann dir überlassen - zum Anfang tut es eine Liste die du einfach durchiterierst und nach dem richten WindowHandle suchst)

    zum Besseren Verständnis ein Überblick über die Window Klasse:

    class Window 
    {
    public:
    	/// Creates a new, initially invisible Window with the specified title.
    	Window(const char* title);
    
    	/// Adds a component (for example a button) to the window.
    	void add(Component *component);
    
    	/// Legt einen Nachrichtenempfaenger fuer das Fenster fest.
    	void setActionListener(ActionListener *listener);
    
    	/// Liefert den Handle des Fensters.
    	HWND getHandle() const;
    
    private:
    
    	/// Der actionListner verweißt auf ein ActionListner Objekt, das die actionPerfomed implementiert
    	ActionListener		*actionListener;
    
    	// A window is a countainer for components like labels, textboxes or buttons.
    	vector<Component*> ComponentContainer;
    
    	friend class WindowsManager;
    };
    

    Nachdem du weißt an welches Fenster die Nachricht geht:

    Window             *winPTR       = NULL;
    
    // es muss gelten:
    // hWnd == winPTR->getHandle()
    // einfach uber vector windowList iterieren und gucken welches Fenster
    // den handle hWnd hat an dieses dan einfach per InputEventListener Events schicken
    for(unsigned int i = 0; i < windowsList.size(); i++)
    {
    	if(hWnd == windowsList[i]->getHandle())
    	{
    		winPTR = windowsList[i];
    		break;
    	}
    }
    

    Kannst du die Nachricht an das Fenster schicken. Mit z. B.:

    winPTR->EtwasIstPassiert();
    

    Im Prinzip das Observer Pattern

    da man im Regelfall mit etwsIstPassiert nicht zufrieden ist kann man sich neue Objekt überlegen, die Informationen über das konkrette Event speichern z. B.

    class ActionEvent
    		{
    		public:
    			// Aufzaehulung der verschiedenen Events
    			enum Type {BUTTON_CLICKED, LEFT_MOUSE_BUTTON_DOWN};
    
    		private:
    			Type		m_Type;		// von welchem Typ ist das Event
    			Component	*m_Source;	// Quelle des Events - Komponente, die Event ausgeloest hat
    
    		public:
    			/// liefert den Typ des Events
    			Type getType();
    
    			/// liefert die Quelle (Komponente), welche das Event verursacht hat
    			Component* getEventSource();
    
    			friend class WindowsManager;
    		};
    

    usw... keine zeit mehr 😉



  • http://turing.fh-landshut.de/~jamann/IMB/IMB.html
    http://turing.fh-landshut.de/~jamann/DrawPixel.pdf
    http://turing.fh-landshut.de/~jamann/IMB/winapivsoop.pdf

    wollte eigentlich zu genau deiner Fragen schon mal ein Tutorial schreiben, aber mir fehlen da einfach dir Resourcen 😉





  • hmmm das sieht doch gut aus...
    ich werds mir mal genau anschauen und durchlesen.
    wäre auf jedenfall was für die faq.
    und es wäre super wenn du nen richtiges tutorial dafür schreiben könntest. so wie dus schon gepostet hast nur noch weiter fortgeführt.


Anmelden zum Antworten