Probleme beim Kapseln einer nicht statischen Callback-Funtion



  • Hallo,

    ich hab mich vor ein paar Tagen bei CodeGuru durchgeschlagen auf der Suche nach einem Beipiel wie man sich Controlls mit OnMouseOver-Effekt erstellt.
    Nach ein bischen Recherche wurde ich dann auch fündig.

    mein Beispiel klappte ganz gut:

    Ich passte hier mal der reihe nach meinen Source:

    Hier meine template.cpp

    #include "template.h"
    
    #include "skin/skin.h"
    
    HINSTANCE hMainInstance=NULL;
    
    BOOL CALLBACK AboutDlgProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
    	if(uMsg==WM_INITDIALOG) {
    		SetWindowText(hwnd, APPLICATION_ABOUT_NAME);
    		return TRUE;
    	}//WM_INITDIALOG
    
    	else if(uMsg==WM_COMMAND) {
    	}//WM_COMMAND
    
    	else if(uMsg==WM_CLOSE) {
    		EndDialog(hwnd,1);
            return 0;
        }// WM_CLOSE
    
    	else {
    		return FALSE;
    	}//ELSE
    
    	return TRUE;
    }
    
    LRESULT CALLBACK NewStaticProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
    	if(uMsg == WM_MOUSEMOVE) {
    		if (!bMouseInWindow) {		
    			bMouseInWindow = true;
    
    			SendMessage(hWnd, STM_SETIMAGE, (WPARAM)IMAGE_BITMAP, (LPARAM)hBitmapOkOver );
    
    			TRACKMOUSEEVENT tme;
    			tme.cbSize = sizeof(tme);
    			tme.dwFlags = TME_LEAVE | TME_HOVER;
    			tme.hwndTrack = hWnd;
    			tme.dwHoverTime = HOVER_DEFAULT;
    			TrackMouseEvent(&tme);
    		}else
    			return CallWindowProc(wpOrigButtonOkProc, hWnd, uMsg, wParam, lParam);
    
    	}else if(uMsg == WM_MOUSELEAVE) {
    		bMouseInWindow = false;			
    
    		SendMessage(hWnd, STM_SETIMAGE, (WPARAM)IMAGE_BITMAP, (LPARAM)hBitmapOk);
    
    	}else
    		return CallWindowProc(wpOrigButtonOkProc, hWnd, uMsg, wParam, lParam);
    
    	return true;
    }
    
    BOOL CALLBACK MainDlgProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam) {	
    	if(Message==WM_INITDIALOG) {		
    		SetWindowText(hwnd, APPLICATION_NAME);			
    
    			hBitmapOk	  = LoadBitmap(hMainInstance, MAKEINTRESOURCE(IDB_OK));
    			hBitmapOkOver = LoadBitmap(hMainInstance, MAKEINTRESOURCE(IDB_OK_OVER));		
    
    			hWndStaticButtonOk = GetDlgItem(hwnd, IDC_STATICOK);
    
    			SendMessage(hWndStaticButtonOk, STM_SETIMAGE, (WPARAM)IMAGE_BITMAP, (LPARAM)hBitmapOk );			
    
    			wpOrigButtonOkProc = (WNDPROC) SetWindowLong(hWndStaticButtonOk, GWL_WNDPROC, (LONG) NewStaticProc); 		
    	}
    
    	else if(Message==WM_COMMAND) {
    		if(wParam==IDC_STATIC1){}			
    	}
    
    	else if(Message==WM_CLOSE) {
    		DestroyWindow( hwnd );
    		return 0;
    	}
    
    	else if(Message==WM_DESTROY) {
    		PostQuitMessage (0);
    		return 0;
    	}
    
    	else{
    		return FALSE;
    	}
    
    	return TRUE;
    }
    
    BOOL CALLBACK SplashDlgProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam) {
    	if(Message==WM_INITDIALOG) {	
    
    		// sets the icon of the application displayed 
    		// in the upper left corner
    		SetClassLong(hwnd, GCL_HICON, (LONG)LoadIcon(hMainInstance, (LPCTSTR)IDI_TEMPLATE));
    
    		SetWindowText(hwnd, APPLICATION_NAME);
    		SetTimer(hwnd,1,2000,NULL);
    
    		// Skin-Init-Beginn
    		CSkin skSkin(IDR_SPLASH, IDB_SPLASH);		
    		skSkin.Hook(hwnd);		
    		skSkin.Enable(true);		
    
    		int iWidth = skSkin.Width();
    		int iHeight = skSkin.Height();		
    		int iSw = (WORD)GetSystemMetrics(SM_CXSCREEN); 
    		int iSh = (WORD)GetSystemMetrics(SM_CYSCREEN);
    		RECT rc = { (iSw - iWidth)/2, (iSh - iHeight)/2, iWidth, iHeight };		
    
    		ShowWindow(hwnd, SW_SHOW);
    
    		MSG mMsg;
    		while( GetMessage(&mMsg, NULL, 0, 0) ) {			
    			if( PeekMessage(&mMsg, 0, 0, 0, PM_REMOVE) )
    		        if( mMsg.message == WM_QUIT ) 
    					break;
    		    TranslateMessage( &mMsg );
    			DispatchMessage( &mMsg );
    		}		
    
    		DestroyWindow(hwnd);
    		// Skin-Init-Ende
    
            return TRUE;				
    	}//WM_INITDIALOG
    
    	else if (Message == WM_TIMER) {
    		KillTimer(hwnd,1);
    
    		EndDialog(hwnd,IDD_SPLASH);		
    		DialogBox(hMainInstance, MAKEINTRESOURCE(IDD_MAIN), NULL, MainDlgProc);	
    	}//WM_TIMER
    
    	else if(Message==WM_COMMAND) {
    	}//WM_COMMAND
    
    	else if(Message==WM_CLOSE) {
    		EndDialog(hwnd,1);
            return 0;
        }// WM_CLOSE
    
    	else {
    		return FALSE;
    	}//ELSE
    
    	return TRUE;
    }
    
    int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
    	LPSTR lpCmdLine, int nCmdShow) { 
    	hMainInstance = hInstance;
    
    	// show splashscreen on startup ?
    	DEBUG_LOAD_SPLASH = false;
    
    	if(DEBUG_LOAD_SPLASH) {
    		return DialogBox(hInstance, MAKEINTRESOURCE(IDD_SPLASH), NULL, SplashDlgProc);
    	}else {
    		return DialogBox(hInstance, MAKEINTRESOURCE(IDD_MAIN), NULL, MainDlgProc);
    	}	
    }
    

    Hier die dazugehörige header file

    #pragma once
    
    #define _WIN32_WINNT 0x0400
    #define WINVER 0x0500
    #define WIN32_LEAN_AND_MEAN
    #define STRICT
    #include <windows.h>
    
    #include "resource.h"
    
    // debug flags
    BOOL DEBUG_LOAD_SPLASH;
    
    // contants strings
    static char APPLICATION_NAME[]       = "Template";
    static char APPLICATION_ABOUT_NAME[] = "About Template";
    
    HWND hMain					= NULL;
    HWND hWndStaticButtonOk		= NULL;
    BOOL bMouseInWindow			= FALSE;
    HINSTANCE hInst				= NULL;
    WNDPROC	wpOrigButtonOkProc	= NULL;
    HBITMAP hBitmapOk			= NULL;
    HBITMAP hBitmapOkOver		= NULL;
    

    Diese Beispiel läuft problemlos. Der Nachteil dieser Methode is jedoch ersichtlich. Ich muss für jedes controll ne extra WndProc-Variable anlegen oder sogar eine eigene CallBackFunktion.
    Es gibt da verschiedenste Möglichkeiten aber die gefallen mir alle nicht.

    Also hab ich mir überlegt das ganze in eine Klasse zu packen um anschließen für jedes Controll das ich erstellen will einfach ein neues Objekt meiner Klasse zu erstellen. Zumindest WILL ich das jetzt so machen 😃

    Gut gesagt getan:

    Hier meine Entprechende Klasse:

    Button.cpp

    #include "Button.h"
    
    // Default-Konstruktor
    Button::Button(void) {
    }
    
    // Custom-Konstruktor
    Button::Button(HINSTANCE hMainInstance,
    			   HWND hwnd,
    			   int idButtonNorm, 
    			   int idButtonOver,
    			   int idStatic) {
    
    	HWND hMain					= NULL;
    	HWND hWndStaticButtonOk		= NULL;
    	BOOL bMouseInWindow			= FALSE;
    	HINSTANCE hInst				= NULL;
    	WNDPROC	wpOrigButtonOkProc	= NULL;
    	HBITMAP hBitmapOk			= NULL;
    	HBITMAP hBitmapOkOver		= NULL;
    
    	hBitmapOk	  = LoadBitmap(hMainInstance, MAKEINTRESOURCE(IDB_OK));
    	hBitmapOkOver = LoadBitmap(hMainInstance, MAKEINTRESOURCE(IDB_OK_OVER));		
    
    	hWndStaticButtonOk = GetDlgItem(hwnd, IDC_STATICOK);
    
    	SendMessage(hWndStaticButtonOk, STM_SETIMAGE, (WPARAM)IMAGE_BITMAP, (LPARAM)hBitmapOk );			
    
    	wpOrigButtonOkProc = (WNDPROC) SetWindowLong(hWndStaticButtonOk, GWL_WNDPROC, (LONG)NewStaticProc);
    }
    
    LRESULT CALLBACK Button::NewStaticProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
    	if(uMsg == WM_MOUSEMOVE) {
    		if (!bMouseInWindow) {		
    			bMouseInWindow = true;
    
    			SendMessage(hWnd, STM_SETIMAGE, (WPARAM)IMAGE_BITMAP, (LPARAM)hBitmapOkOver );
    
    			TRACKMOUSEEVENT tme;
    			tme.cbSize = sizeof(tme);
    			tme.dwFlags = TME_LEAVE | TME_HOVER;
    			tme.hwndTrack = hWnd;
    			tme.dwHoverTime = HOVER_DEFAULT;
    			TrackMouseEvent(&tme);
    		}else
    			return CallWindowProc(wpOrigButtonOkProc, hWnd, uMsg, wParam, lParam);
    
    	}else if(uMsg == WM_MOUSELEAVE) {
    		bMouseInWindow = false;			
    
    		SendMessage(hWnd, STM_SETIMAGE, (WPARAM)IMAGE_BITMAP, (LPARAM)hBitmapOk);
    
    	}else
    		return CallWindowProc(wpOrigButtonOkProc, hWnd, uMsg, wParam, lParam);
    
    	return true;
    }
    

    und noch die Header file:

    #pragma once
    
    #define _WIN32_WINNT 0x0400
    #define WINVER 0x0500
    #define WIN32_LEAN_AND_MEAN
    #define STRICT
    #include <windows.h>
    
    #include "resource.h"
    
    class Button;
    
    class Button {
        private:
    		// Instanzvariablen
    		HWND hMain;
    		HWND hWndStaticButtonOk;
    		BOOL bMouseInWindow;
    		HINSTANCE hInst;
    		WNDPROC	wpOrigButtonOkProc;
    		HBITMAP hBitmapOk;
    		HBITMAP hBitmapOkOver;		
    
        public:
            // Prototypen        
            // Konstruktoren
            Button(void);
    		Button(HINSTANCE hMainInstance,
    			   HWND hwnd,
    			   int idButtonNorm, 
    			   int idButtonOver,
    			   int idStatic);
    
            // Destruktoren
            ~Button(void);
    
            // Funktionen        
            LRESULT CALLBACK		NewStaticProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);               
    };
    

    So, ja ich weiß im Konstruktor meiner Klasse finden noch einige schwachsinnige Sachen statt, aber das ist nicht das Problem.
    Was sich im Beispiel 1 noch problemlos kompilieren lie´ße geht nun nicht mehr
    Anscheind kann man nicht statische Callback-Funtionen nicht so ohne Weiteres in Classen verwenden. Ich hab schon ein bischen nachgeforscht und herausgefunden das ich ein "Aufrufende" Funktion schreiben müsste.
    Wenn ich aber ehrlich bin so reicht mein Wissen an der Stelle einfach nicht aus 😞

    Wenn mir hier also jemand helfen könnte mit einer Ausführlichen Erklärung oder einem Beispiel das in meine Richtung geht wäre ich sehr dankbar.

    Ich warte gespannt auf eure Antworten,

    mfg

    GBen



  • ich zeig dir mal, wie ich das mache

    .h:

    class GUIHandler
    {
    	protected:
    		virtual long OnInitDialog(WPARAM wparam, LPARAM lparam) = 0;
    		virtual long OnCommand(WPARAM wparam, LPARAM lparam) = 0;
    		virtual long OnNotify(WPARAM wparam, LPARAM lparam) = 0;
    		virtual long OnClose(WPARAM wparam, LPARAM lparam) = 0;
    		virtual long OnDestroy(WPARAM wparam, LPARAM lparam) = 0;
    		virtual long OnSize(WPARAM wparam, LPARAM lparam) = 0;		
    		virtual long OnOwnerDraw(WPARAM wparam, LPARAM lparam) = 0;
    };
    
    class GUIWindow : public GUIElement, public GUIHandler
    {
    	public:
    		GUIWindow();		
    		GUIWindow(HINSTANCE hinst, long resid);
    		virtual ~GUIWindow();
    
    		virtual bool New(long resid, const GUIElement *parent = NULL);
    		long Close();
    
    		void CenterOnScreen(long xp = 0, long yp = 0) const;
    
    		long IsWindow() const;
    		long IsDialogMessage(LPMSG lpmsg) const;		
    		long EndDialog(long code) const;
    
    		long SetIcon(HICON hicon, int type);
    		HICON GetIcon(int type) const;
    
    		virtual long MainLoop() const;
    
    	protected:
    		virtual long OnInitDialog(WPARAM wparam, LPARAM lparam);
    		virtual long OnCommand(WPARAM wparam, LPARAM lparam);
    		virtual long OnNotify(WPARAM wparam, LPARAM lparam);
    		virtual long OnClose(WPARAM wparam, LPARAM lparam);
    		virtual long OnDestroy(WPARAM wparam, LPARAM lparam);
    		virtual long OnSize(WPARAM wparam, LPARAM lparam);
    		virtual long OnOwnerDraw(WPARAM wparam, LPARAM lparam);
    
    	protected:
    		static long WINAPI WindowProc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam);
    
    };
    

    .cpp

    GUIWindow::GUIWindow() : GUIElement()
    {
    	m_resid = 0;
    }
    
    GUIWindow::GUIWindow(HINSTANCE hinst, long resid) : GUIElement(hinst)
    {
    	m_resid = resid;
    }
    
    GUIWindow::~GUIWindow()
    {
    }
    
    bool GUIWindow::New(long resid, const GUIElement *parent)
    {
    	Destroy();
    	m_hwnd = CreateDialogParam(GetInstance(),
    							   MAKEINTRESOURCE(resid),
    							   parent ? parent->GetHandle() : NULL,
    							   reinterpret_cast<DLGPROC>(WindowProc),
    							   reinterpret_cast<LPARAM>(this));
    	Attach(m_hwnd);
    	return m_hwnd != NULL;
    }
    
    void GUIWindow::CenterOnScreen(long xp, long yp) const
    {
    	RECT rect;
        long xx, yy;
        GetWindowRect(m_hwnd, &rect);
    
    	xx = GetSystemMetrics(SM_CXSCREEN);
    	yy = GetSystemMetrics(SM_CYSCREEN);
    
        xx = (xx - (rect.right-rect.left)) >> 1;
        yy = (yy - (rect.bottom-rect.top)) >> 1;
        SetWindowPos(GUIObject(), xx+xp, yy+yp, 0, 0, SWP_NOZORDER | SWP_NOSIZE);
    }
    
    long GUIWindow::EndDialog(long code) const
    {
    	return ::EndDialog(GetHandle(), code);
    }
    
    static std::map<HWND, LPARAM> Windows;
    
    long WINAPI GUIWindow::WindowProc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam)
    {	
    	GUIWindow *gwnd = NULL;
    	long retval = 0;
    
    	switch(message)
    	{
    		case WM_INITDIALOG:			
    			Windows.insert(std::pair<HWND, LPARAM>(hwnd, lparam));
    
    			gwnd = reinterpret_cast<GUIWindow *>(Windows[hwnd]);
    			gwnd->Attach(hwnd);
    			retval = gwnd->OnInitDialog(wparam, lparam);			
    			break;
    
    		case WM_COMMAND:
    			gwnd = reinterpret_cast<GUIWindow *>(Windows[hwnd]);
    			retval = gwnd->OnCommand(wparam, lparam);			
    			break;
    
    		case WM_NOTIFY:
    			gwnd = reinterpret_cast<GUIWindow *>(Windows[hwnd]);
    			retval = gwnd->OnNotify(wparam, lparam);
    			break;
    
    		case WM_CLOSE:
    			gwnd = reinterpret_cast<GUIWindow *>(Windows[hwnd]);
    			retval = gwnd->OnClose(wparam, lparam);
    			break;
    
    		case WM_DESTROY:
    			gwnd = reinterpret_cast<GUIWindow *>(Windows[hwnd]);
    			retval = gwnd->OnDestroy(wparam, lparam);
    			break;
    
    		case WM_SIZE:
    			gwnd = reinterpret_cast<GUIWindow *>(Windows[hwnd]);
    			retval = gwnd->OnSize(wparam, lparam);
    			break;
    
    		case WM_DRAWITEM:
    			gwnd = reinterpret_cast<GUIWindow *>(Windows[hwnd]);
    			retval = gwnd->OnOwnerDraw(wparam, lparam);
    			break;		
    	}
    
    	return retval;
    }
    
    long GUIWindow::OnInitDialog(WPARAM wparam, LPARAM lparam)
    {
    	return 0;
    }
    
    long GUIWindow::OnCommand(WPARAM wparam, LPARAM lparam)
    {
    	return 0;
    }
    
    long GUIWindow::OnNotify(WPARAM wparam, LPARAM lparam)
    {
    	return 0;
    }
    
    long GUIWindow::OnClose(WPARAM wparam, LPARAM lparam)
    {
    	return 0;
    }
    
    long GUIWindow::OnDestroy(WPARAM wparam, LPARAM lparam)
    {
    	return 0;
    }
    
    long GUIWindow::OnSize(WPARAM wparam, LPARAM lparam)
    {
    	return 0;
    }
    
    long GUIWindow::OnOwnerDraw(WPARAM wparam, LPARAM lparam)
    {
    	return 0;
    }
    
    long GUIWindow::IsWindow() const
    {
    	if(m_hwnd) return ::IsWindow(m_hwnd);
    	return 0;
    }
    
    long GUIWindow::IsDialogMessage(LPMSG lpmsg) const
    {
    	if(m_hwnd) return ::IsDialogMessage(m_hwnd, lpmsg);
    	return 0;
    }
    
    long GUIWindow::MainLoop() const
    {
    	MSG msg;
    
    	memset(&msg, 0, sizeof(msg));
    
    	while(GetMessage(&msg, NULL, 0, 0))
    	{
    		if(IsWindow() && IsDialogMessage(&msg)) continue;		
    
    		TranslateMessage(&msg);
    		DispatchMessage(&msg);
    	}
    
    	return msg.wParam;
    }
    
    long GUIWindow::Close()
    {
    	return ::CloseWindow(GetHandle());
    }
    
    long GUIWindow::SetIcon(HICON hicon, int type)
    {
    	return SendMessage(WM_SETICON, (WPARAM)type, (LPARAM)hicon);
    }
    
    HICON GUIWindow::GetIcon(int type) const
    {
    	return (HICON)SendMessage(WM_GETICON, (WPARAM)type, (LPARAM)0);
    }
    

    Jetzt hab ich für jede Klasse, die ich von GUIWindow abgeleitet wird, nur eine einzige Callback Funktion, die dann die jeweiligen Objecte läd;
    für Controls wir Buttons und so geht es ähnlich.

    viel spass



  • Danke esskar,

    wobei WOWWOWWOW, ich fang gerade erst an mit simples Klassen geschichten und komme eigentlich aus der Java-Welt wo ableiten wirklich sehr simple ist.
    Da wollte ich mich eigentlich nich gleich reinstürzen müssen, später bestimmt mal.

    Mir wurde von jemand anderem geraten ein reinterpret_cast zu machen.

    das einzige problem hab ich ja in der Zeile:

    wpOrigButtonOkProc = (WNDPROC) SetWindowLong(hWndStaticButtonOk, GWL_WNDPROC, (LONG)NewStaticProc);
    

    Weil ich LRESULT hier nicht mehr auf LONG casten konnte. Auch nicht mit einem
    reinterpret_cast. Das muss mit der Kapselung zu tun haben da es ja im ersten Beispiel auch ging.

    Danke trotzdem esskar

    PS: Du hast recht wenn du dir nun denkst das ich noch nicht so fit bin was dieses Thema angeht. Ich weiß jedoch das es auch so gehen kann wie ich mir das vorstelle, daher würde mich eine Lösung in diesem Stil sehr freun



  • LRESULT ist ein typedef auf long
    zumindest irgendwann; deswegen funzt das casten nicht; static_cast hätt wohl geklappt

    aber callbacks in klassen müssen immer static sein; ist eben so
    deklarieren

    static LRESULT CALLBACK        NewStaticProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
    

    dann musst du dir dann aber dein object in einer globalen variable speichern (oder in einer map, wie ich das gemacht habe), damit du es in der callback benutzen kannst


Anmelden zum Antworten