WndProc in Klasse



  • Hallo,

    Ich versuche, die WndProc für ein Fenster in eine Klasse einzubauen..
    Viele mögen jetzt aufstöhnen und gleich mal google.de oder die FAQ posten, aber ich habe bereits lange (!) nach möglichen Lösungen gesucht, und immer war ein Haken dabei.. Ich bin aber durchaus offen für Links, wenn sie nicht gerade auf eine Suchmaschine verweisen 😉

    Ich habe also folgende Klasse geschrieben:

    class CWindow {
    	private:
    		static LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
    
    	protected:
    		HWND hWnd;
    
    		// Diese Funktionen returnen standardmäßig DefWindowProc
    		virtual LRESULT OnCreate(LPCREATESTRUCT lpCS);
    		virtual void OnDestroy();
    		virtual void OnTimer(UINT uID);
    		...
    
    	public:
    		CWindow() { hWnd = NULL; }
    		bool Create(LPCTSTR lpClassName, HINSTANCE hInstance, LPCTSTR lpWindowName, DWORD dwStyle, DWORD dwStyleEx = 0,
    			HWND hWndParent = NULL, HMENU hMenu = NULL, int x = CW_USEDEFAULT, int y = CW_USEDEFAULT,
    			int nWidth = CW_USEDEFAULT, int nHeight = CW_USEDEFAULT);
    };
    
    bool CWindow::Create(LPCTSTR lpClassName, HINSTANCE hInstance, LPCTSTR lpWindowName, DWORD dwStyle, DWORD dwStyleEx,
    					 HWND hWndParent, HMENU hMenu, int x, int y, int nWidth, int nHeight) {
    	hWnd = CreateWindowEx(dwStyleEx, lpClassName, lpWindowName, dwStyle, x, y, nWidth, nHeight,
    		hWndParent, hMenu, hInstance, this);
    
    	return (hWnd != NULL);
    }
    
    LRESULT CALLBACK CWindow::WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
    	static CPairList<HWND, CWindow*> plWindowList; // meine eigene Klasse - speichert hWnds mit Pointern
    	CWindow *pWndInstance = NULL;
    
    	if (message == WM_NCCREATE) {
    		pWndInstance = (CWindow*)((LPCREATESTRUCT)lParam)->lpCreateParams;
    		plWindowList.AddItem(hWnd, pWndInstance);
    
    		pWndInstance->hWnd = hWnd; // Set hWnd
    	} else {
    		plWindowList.GetItem(hWnd, pWndInstance);
    	}
    
    	if (!pWndInstance)
    		return DefWindowProc(hWnd, message, wParam, lParam);
    
    	switch (message) {
    		case WM_BLA:
    			pWndInstance->OnBla();
    			return 0;
    
    		case WM_NOCHWAS:
    			return pWndInstance->OnNochwas();
    
    		...
    
    	}
    
    	return DefWindowProc(hWnd, message, wParam, lParam);
    }
    

    Bei der WNDCLASSEX Struktur gebe ich die statische Funktion CWindow::WndProc an (natürlich innerhalb der Klasse: ist ja private..).

    Was funktioniert: Alles, man kann Fenster erstellen (auch mehrere auf einmal)

    Was nicht funktioniert: Wenn man ein neues Fenster innerhalb (!!) einer der virtuellen Funktionen (z.b. OnLMouseButtonDown) erstellt, stürzt das Prog ab.

    Die WM_NCCREATE wird korrekt erkannt, der Pointer auf die Klasse mit zugehörigem hWnd werden gespeichert.
    Sobald aber die nächste Message nach WM_CREATE ankommt, stürzt das Programm ab, sobald in der WndProc eine der virtuellen Funktionen der Klasse aufgerufen wird.
    Compilermeldung (MS VC++ 2005): Access violation reading location 0xcccccd24 (in debug mode immer 0xcccccd24; in release mode immer unterschiedlich).

    Zusammenfassung: Der Absturz tritt auf wenn das erfüllt ist:
    - Das neue Fenster wurde innerhalb einer der virtuellen Funktionen (wie z.b. OnLMouseButtonDown) erstellt
    - in der WndProc wird eine virtuelle Funktion der Klasse aufgerufen (normale Funktionen gehen - entferne ich das virtual vor den OnX() Funktionen, funktioniert zwar alles, aber das ist ja nicht Sinn der Sache..)

    MfG,
    Max



  • dazu gibt es schon den einen oder anderen thread hier 😉
    du brauchst sowohl eine statische als auch eine "normale" WinProc



  • Leute, ich bin ein großer Idiot.
    Ich habe jetzt nach stundenlangem Suchen endlich den Fehler gefunden:
    Die Testfunktion, mit der ich das 2. Fenster erstelle, wird danach verlassen, und somit wird der Pointer in der Windowliste ungültig. Mann, mann.. 😡 😡



  • Warum so schwer, wenn es auch einfach geht?

    class Window
    {
    	::HWND m_hWnd;
    public:
    	Window()
    		: m_hWnd(NULL)
    	{}
    	virtual ~Window() { if (m_hWnd != NULL) ::DestroyWindow(m_hWnd); }
    
    public:
    	operator ::HWND() const	{return m_hWnd; }
    #pragma warning(disable: 4312)
    	static Window* FromHandle(::HWND const & hWnd) { return reinterpret_cast<Window*>(GetWindowLongPtr(hWnd, GWL_USERDATA)); };
    #pragma warning(default: 4312)
    
    public:
        virtual LRESULT message_proc(::HWND const& hWnd, const UINT message, WPARAM wParam, LPARAM lParam)
        { return ::DefWindowProc(hWnd, message, wParam, lParam); }
    
    private:
        inline static LRESULT CALLBACK _message_proc(::HWND, UINT, WPARAM, LPARAM); 
    };
    
    LRESULT CALLBACK Window::_message_proc(::HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) 
    {
    	Window* ptr_window(Window::FromHandle(hWnd));
    #pragma warning(disable: 4311)
    	if (message == WM_NCCREATE)
    	{
    		ptr_window = reinterpret_cast<Window*>(reinterpret_cast<::LPCREATESTRUCT>(lParam)->lpCreateParams);
    		SetWindowLongPtr(hWnd, GWL_USERDATA, reinterpret_cast<LONG>(ptr_window));
    	}
    #pragma warning(default: 4311)
    	return (ptr_window != NULL ? ptr_window->message_proc(hWnd, message, wParam, lParam) : ::DefWindowProc(hWnd, message, wParam, lParam));
    }
    

    So macht man das 😉 Fehlt nur eine Methode create, in der du als Window_Prozedure dann &Window::_message_proc übergibst! Jetzt einfach nur eine Klasse von Window ableiten:

    class foo : public Window
    {
    public:
        LRESULT message_proc(::HWND const&, const UINT, WPARAM, LPARAM);
    };
    
    LRESULT foo::message_proc(::HWND const& hWnd, const UINT message, WPARAM wParam, LPARAM lParam)
    {
        // Beispiel !
        switch (message)
        {
        case WM_DESTROY:
        {
            ::PostQuitMessage(0);
        } break;
        default:
            return Window::message_proc(hWnd, message, wParam, lParam);
        }
        return 0;
    }
    


  • (D)Evil schrieb:

    Warum so schwer, wenn es auch einfach geht?
    So macht man das 😉

    LRESULT foo::message_proc(::HWND const& hWnd, const UINT message, WPARAM wParam, LPARAM lParam)
    {
        // Beispiel !
        switch (message)
        {
        case WM_DESTROY:
        {
            ::PostQuitMessage(0);
        } break;
        default:
            return Window::message_proc(hWnd, message, wParam, lParam);
        }
        return 0;
    }
    

    dann ist allerdings der ganze code für WM_DESTROY weg, wenn er den handler der abgeleiteten klasse aber doch noch gebrauchen will bzw. nur ergänzen will? dann ist ein

    void MyWindow::OnDestroy()
    {
        // ...
        // foo(); bar();
        //
        CWindow::OnDestroy();
    }
    

    wohl einfacher zu handhaben.



  • (D)Evil, kannst du deinen Code bitte ein wenig erläutern? Vorallem die ganzen :: bereiten mir Kopfzerbrechen. 😕 Ist das vllt. einfach ein Hinweis für den Compiler, dass er gleich im globalen Gültigkeitsbereich nach den Namen sucht?

    Und was hat es mit dem Operator ::HWND auf sich?



  • Ist das vllt. einfach ein Hinweis für den Compiler, dass er gleich im globalen Gültigkeitsbereich nach den Namen sucht?

    Ja.

    Und was hat es mit dem Operator ::HWND auf sich?

    Dadurch kann man schreiben

    Window window;
    // ...
    HWND hWnd = window;
    


  • ihr immer mit euren ekelhaftem c++ code, lernt mal c.



  • Dann wird dir das hier überhaupt nicht gefallen Rofler^^

    Aber vllt dem Threadersteller:

    http://furix.net/c-win32-api-wrapper



  • markusrw schrieb:

    Dann wird dir das hier überhaupt nicht gefallen Rofler^^

    Aber vllt dem Threadersteller:

    http://furix.net/c-win32-api-wrapper

    herzlichen dank, toller link! 👍


Log in to reply