WndProc in klasse?



  • hallo,
    ist es möglich, den WndProc innerhalb einer Klasse zu definieren?, was ich damit meine:

    class ASE
    {
    private:
    ......
    	WNDCLASSEX wcex;
    ......
    
    public:
    ......
    	LRESULT WINAPI WndProc(HWND, UINT, WPARAM, LPARAM);
    };
    

    und dann irgendwie sowas:

    bool ASE::CreateWClass(HINSTANCE hInst, TCHAR * AppName)
    {
    	......
    	wcex.lpfnWndProc	= WndProc;
    	......
    
    	if(!RegisterClassEx(&wcex)) return false;
    	return true;
    }
    

    hier fängt er an zu meckern, er meint, das wär ein funktionsaufruf und sagt, man soll &ASE::WndProc verwenden, das geht aber genauso wenig und (WNDPROC) &ASE::Wndroc geht auch nicht ^^"""
    hat jemand eine Idee, wie man das machen könnte? (bzw: weißt einer wie man das macht?)



  • Da die WndProc statisch sein muss, geht dies nicht so einfach... aber schau Dir doch einfach an wie es die diversen Wnd-Frameworks machen (MFC, wxWidgets, usw.)



  • ähm, jetzt mal für dumme - wie guck ich in die MFC? xD mfc.h? gibts net xDDDD



  • guck in den FAQ hier im forum 🙂



  • Du musst die Callback-Funktion einfach vor der Klasse definieren und dann kannst du die auch in der Klasse verwenden. So mache ich das auch.

    Mfg Ominion



  • So mache ich das auch.

    Soll das was heißen? Ist sicherlich ne Möglichkeit ...

    Naja ... kannst aber auch mit SetWindowLong und SetProp usw da was machen ... steht aber wirklich im F.A.Q.



  • Azrael, il Meraz schrieb:

    ähm, jetzt mal für dumme - wie guck ich in die MFC? xD mfc.h? gibts net xDDDD

    Der Quelltext der MFC wird AFAIK für gewöhnlich bei den entsprechenden Editionen von VC mitgeliefert...



  • Das habe ich mir schon von so langer zeit aus verschiedneen quellen zusammengecodet dass ich es selbst nichtmehr verstehe:

    Datei CWndUnknown.cpp:

    /*
    * Description: creates a CALLBACK-thunk.
    */
    
    #include <windows.h>
    #include "CWndUnknown.h"
    
    void CWndProcThunk::Init(WNDPROC WndProc, void* pThis)
    {
    	thunk.m_mov		= 0x042444C7;  //C7 44 24 04
    	thunk.m_this	= (DWORD)pThis;
    	thunk.m_jmp		= 0xe9;
    	thunk.m_relproc	= *((int*)&WndProc) - ((int)this + 
    						sizeof(_WndProcThunk));
    
    	FlushInstructionCache(GetCurrentProcess(), 
                &thunk, sizeof(thunk));
    }
    
    CWndUnknown::CWndUnknown()
    {
    	m_hwnd	= 0;
    }
    
    LRESULT CWndUnknown::ProcessWindowMessage(HWND hwnd, UINT message,
    									WPARAM wParam, LPARAM lParam)
    {
    	return false;
    }
    
    LRESULT CALLBACK CWndUnknown::StartWindowProc(HWND hwnd, UINT message,
    										WPARAM wParam, LPARAM lParam)
    {
    	if(message == WM_CREATE)
    	{
    		CWndUnknown *pThis = (CWndUnknown*)((LPCREATESTRUCT)lParam)->lpCreateParams;
    		pThis->m_hwnd = hwnd;
    		pThis->m_thunk.Init(WindowProc, pThis);
    		WNDPROC pProc = (WNDPROC)&(pThis->m_thunk.thunk);
    		SetWindowLong(hwnd, GWL_WNDPROC, (LONG)pProc);
    		return pProc(hwnd, message, wParam, lParam);
    	}
    
    	return DefWindowProc(hwnd, message, wParam, lParam);
    }
    
    LRESULT CALLBACK CWndUnknown::WindowProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
    {
    	CWndUnknown *pThis = (CWndUnknown*)hwnd;
    	return pThis->ProcessWindowMessage(pThis->m_hwnd, message, wParam, lParam);
    }
    
    void* CWndUnknown::GetUnknownClassPtr()
    {
    	return this;
    }
    
    WNDPROC CWndUnknown::GetStartWndProc()
    {
    	return (WNDPROC)&StartWindowProc;
    }
    

    Datei: CWndUnknown.h

    #ifndef _CWNDUNKNOWN_H_
    #define _CWNDUNKNOWN_H_
    
    #pragma pack(push,1)
    struct _WndProcThunk
    {
    	DWORD   m_mov;          // mov dword ptr [esp+0x4], pThis (esp+0x4 is hWnd)
    	DWORD   m_this;
    	BYTE    m_jmp;          // jmp WndProc
    	DWORD   m_relproc;      // relative jmp
    };
    #pragma pack(pop)
    
    class CWndProcThunk
    {
    public:
    	_WndProcThunk thunk;
    	void Init(WNDPROC WndProc, void* pThis);
    };
    
    class CWndUnknown
    {
    public:
    	CWndUnknown();
    	void* GetUnknownClassPtr();
    	static WNDPROC GetStartWndProc();
    
    protected:
    	HWND			m_hwnd;
    	CWndProcThunk	m_thunk;
    
    	virtual LRESULT ProcessWindowMessage(HWND, UINT, WPARAM, LPARAM);
    
    	static LRESULT CALLBACK StartWindowProc(HWND, UINT, WPARAM, LPARAM);
    	static LRESULT CALLBACK WindowProc(HWND, UINT, WPARAM, LPARAM);
    
    };
    
    #endif
    

    Das eigentliche Fenster, dessen CALLBACK-Funktion in die Klasse soll (CMainWnd.h):

    #ifndef _CMAINWND_H_
    #define _CMAINWND_H_
    
    #include "CWndUnknown.h"
    
    class CMainWnd : public CWndUnknown // !!!!!
    {
    public:
    	CMainWnd();
    	~CMainWnd();
    	HWND Create();
    	void Destroy();
    
    private:
    	HINSTANCE	m_hInstance;
    
    	void OnCreate(HWND, CREATESTRUCT*);
    	void OnDestroy();
    
    	virtual LRESULT ProcessWindowMessage(HWND, UINT, WPARAM, LPARAM); // !!!!
    };
    
    #endif
    

    CMainWnd.cpp (Auszüge):

    #include <windows.h>
    #include "CMainWnd.h"
    
    // !!!!!
    extern TCHAR *g_pszUnknownClass;
    
    CMainWnd::CMainWnd()
    {
    }
    
    CMainWnd::~CMainWnd()
    {
    	Destroy();
    }
    
    HWND CMainWnd::Create()
    {
    	m_hInstance = GetModuleHandle(0);
    // !!!!!!!!
    	CreateWindowEx(0, g_pszUnknownClass, "der titel",
    				WS_VISIBLE, 100, 100, 400, 600,
    				0, 0, m_hInstance, GetUnknownClassPtr());
    // !!!!!!!!
    	return m_hwnd;
    }
    
    LRESULT CMainWnd::ProcessWindowMessage(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
    {
    // hierdrin verhält es sich so wie bei jeder normalen wnd proc :)
    	case WM_DESTROY:
    		PostQuitMessage(0);
    		return 0;
    	}
    
    	return DefWindowProc(hwnd, message, wParam, lParam);
    }
    
    void CMainWnd::OnCreate(HWND hwnd, CREATESTRUCT *pCreateStruct)
    {
    }
    

    Noch zuletzt zu beachten ist beim registrieren der Fensterklasse:

    wc.cbSize		= sizeof(WNDCLASSEX);
    	wc.cbClsExtra	= 0;
    	wc.cbWndExtra	= 0;
    	wc.hbrBackground= (HBRUSH)GetStockObject(BLACK_BRUSH);
    	wc.hCursor		= LoadCursor(0, IDC_ARROW);
    	wc.hIcon		= 0;
    	wc.hIconSm		= 0;
    	wc.hInstance	= hInstance;
    	wc.lpfnWndProc	= CWndUnknown::GetStartWndProc(); // !!!!
    	wc.lpszClassName= g_pszUnknownClass; // !!!!
    	wc.lpszMenuName	= 0;
    	wc.style		= 0;
    	if(!RegisterClassEx(&wc))
    		return false;
    

    Ich mach das schon länger so, frag mich aber nicht warum das so funzt :D.
    Sehr vorteilig ist dass man beliebig viele Instanzen derselben Klasse machen kann, ohne dass sie sich in die Quere kommen :).



  • Das ist aber sehr Compiler-Sepzifisch und funktioniert nur bei bestimmten Compiler-Seetings!
    Rate ich dringend davon ab...



  • Jochen Kalmbach schrieb:

    Das ist aber sehr Compiler-Sepzifisch und funktioniert nur bei bestimmten Compiler-Seetings!
    Rate ich dringend davon ab...

    Hm kannst du das näher erläutern? Also bei meinem VC++ geht das wunderbar, genauso gehe ich davon aus dass das Visual Studio damit auch gut zurechtkommt. Wies bei anderen Marken aussieht kann ich nicht sagen.
    Und bei welchen Compiler-settings geht der Code nicht?

    MfG



  • ich selber hab die WndProc schon lange in einer klasse - zwar noch gefrickel da ich das noch aendern will aber es geht

    mein derzeitiges problem ist allerdings das DLGPROC

    void CSettings::ShowDlg(HINSTANCE hInst, unsigned int uiDlgID, HWND mainHandle){
        if(m_Handle == 0){
            m_Handle = ::CreateDialog(hInst, MAKEINTRESOURCE(uiDlgID), mainHandle, dlgProc);
            ::ShowWindow(m_Handle, SW_SHOW);
        }
    }
    

    das dlgProc moechte ich in die klasse packen damit ich auch methoden aufrufen kann
    die ganzen techniken fuer das main window hab ich angeschaut und probiert, klappt aber bisher nicht

    kann jemand helfen ?!



  • Ich hats so gemacht:

    void Window::Show()
    {
    	static bool initialized = false;
    	if(!initialized)
    	{
    		DialogBoxParam(GetModuleHandle(NULL), MAKEINTRESOURCEA(this->iId), NULL, WndProc, (LPARAM)this);
    	}
    	else
    	{
    		Widget::Show();
    	}
    }
    
    private:
    	static BOOL CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
    	{
    		if(message == WM_INITDIALOG)
    		{
    			SetWindowLongPtrA(hwnd, DWLP_USER, lParam);
    		}
    		Window* window = (Window*)GetWindowLongPtrA(hwnd, DWLP_USER);
    		return window->HandleMessage(hwnd, message, wParam, lParam);
    	}
    

    Dann natürlich noch die öffentliche HandleMessage Methode.



  • so aehnlich hab ich das bereits, also eine static message methode, nur ich moechte in dieser methoden aufrufen die nicht static sein koennen - das ist das problem



  • ? Les dir mal durch was der geschrieben hat ... HandleMessage ist sozusagen deine normale WindowProc in der Klasse ... die Static WindowProc leitet das sozusagen nur an die weiter ...

    aja ... reinterpret_cast wäre was schöner 😉



  • aaaaaaaaaaah - das is doch mal stylish - vielen vielen dank, der compiler schluckts, werds spaeter testen aber sieht gut aus {=



  • Mr Evil schrieb:

    ich selber hab die WndProc schon lange in einer klasse.

    Ich habs immer noch net hingekriegt.

    Wie, wie, wie?????

    Ich versteh garnichts

    memcpy(&Space,(const void*) &ASE::WndProc,(size_t) sizeof(LONG)) ;
    	SetWindowLong(hWnd, GWL_WNDPROC,(LONG) Space);
    

    geht auch net 😡 😡 😡

    Man, leute, hört auf, mich in die MFC zu schicken, o.ä. Wenn ich den Code nicht verstehe, dann verwende ich ihn nicht, weil ich im Falle eines Bugs nichts werde machen können.

    Kann mir den Niemand erklären, WARUM das nicht geht? Ich glaub, ich will das weniger hinkriegen, als es verstehen...

    Was ist ein Thunk?

    Kann mir denn keiner aus meiner Verzweiflung heraushelfen? 😞



  • Hmm du solltest dir mal das Problem von Funktionspointer usw angucken ... hmm und eines der Probleme kommt halt durch den this-Zeiger bei Methoden einer Klasse ... das hasse bei statischen Methoden net ^^



  • Azrael, il Meraz schrieb:

    Wenn ich den Code nicht verstehe, dann verwende ich ihn nicht, weil ich im Falle eines Bugs nichts werde machen können.

    Gesunde Einstellung. Respekt, Respekt. Hoffentlich kann ich mich verständlich ausdrücken.

    Um die "WinProc" in eine Klasse zu kriegen, braucht man immer zwei davon :

    -> eine die vom Betriebssystem aufgerufen wird (muss statisch sein) und
    -> eine die von der Instanz aufgerufen wird.

    Ausserdem braucht man eine Möglichkeit, um in einer statischen Funktion Zugriff auf den this-pointer der Instanz zu haben.

    Dazu gibt es viele Möglichkeiten.

    Eine davon geht mit Get/SetWindowLong() und einem "umdefinierten" Zeiger :

    #include <windows.h>
    //-----------------------------------------------------------------------------
    class ASE {
    //-----------------------------------------------------------------------------
     private   :
      static
      LRESULT WINAPI    WndProc_static  (HWND, UINT, WPARAM, LPARAM); // die statische WinProc, wird vom Betriebssystem aufgerufen
      LRESULT WINAPI    WndProc         (HWND, UINT, WPARAM, LPARAM); // die WinProc, die von der Instanz aufgerufen wird
     protected :
     public    :
      BOOL              CreateWClass    (HINSTANCE, TCHAR *);
    };
    //-----------------------------------------------------------------------------
    LRESULT WINAPI      ASE::WndProc_static  (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
    //-----------------------------------------------------------------------------
    // Statische WndProc, wird vom Betriebssystem aufgerufen.
    
     if ( msg == WM_CREATE ) {
    // Normalerweise zeigt hier lParam bei WM_CREATE auf eine Struktur vom Typ CREATESTRUCT.
    // Aber da der Zeiger bei CreateWindowEx () mit dem this-pointer der Instanz initialisiert wurde,
    // zeigt nun lParam auf den this-pointer der Instanz.
    // Damit wird nun GWL_USERDATA des Fensters initialisiert.
      SetWindowLong (hwnd,GWL_USERDATA,(LONG)((LPCREATESTRUCT)lParam) -> lpCreateParams);
     }
    
    // Hier wird der Wert von GWL_USERDATA als this-pointer der Instanz interpretiert.
    // Dadurch kann man sich einen Zeiger auf die Instanz schaffen.
     ASE *pThis = (ASE *) GetWindowLong (hwnd,GWL_USERDATA);
    
     if ( pThis == 0 ) {
    // Dann konnte GWL_USERDATA noch nicht via SetWindowLong () initialisiert werden.
    // Auf alle Botschaften die vor WM_CREATE gesendet werden kann die Instanz nicht reagieren, deshalb DefWindowProc().
      return DefWindowProc (hwnd,msg,wParam,lParam);
     } else {
    // Dann kann die WndProc der Instanz aufgerufen werden.
      return pThis -> WndProc (hwnd,msg,wParam,lParam);
     }
    
    }
    //-----------------------------------------------------------------------------
    LRESULT WINAPI      ASE::WndProc         (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
    //-----------------------------------------------------------------------------
    // WndProc der Instanz.
    // Hier gibt es Zugriff auf alle Eigenschaften und Methoden der Instanz.
    
     switch ( msg ) {
    
      case WM_CLOSE   : {
       PostQuitMessage (0);
       break;
      }
      case WM_COMMAND : {
       if ( HIWORD (wParam) == BN_CLICKED ) {
       if ( LOWORD (wParam) == 0x1234 ) {
        PostQuitMessage (0);
       }}
       break;
      }
      case WM_LBUTTONDOWN : {
       ReleaseCapture ();
       SendMessage    (hwnd,WM_SYSCOMMAND,SC_MOVE+HTCAPTION,0); 
       break;
      }
    
     }
    
     return DefWindowProc (hwnd,msg,wParam,lParam);
    }
    //-----------------------------------------------------------------------------
    BOOL                ASE::CreateWClass    (HINSTANCE hInst, TCHAR *AppName) {
    //-----------------------------------------------------------------------------
     WNDCLASSEX    wcex;
     FillMemory  (&wcex,sizeof(wcex),0); wcex.cbSize = sizeof (wcex);
    
     if ( !GetClassInfoEx (hInst,AppName,&wcex) ) {
      wcex.style         = 0x0000;
      wcex.hIcon         = LoadIcon       (0,IDI_HAND);
      wcex.hCursor       = LoadCursor     (0,IDC_ARROW);
      wcex.hbrBackground = GetStockObject (LTGRAY_BRUSH);
      wcex.lpszClassName = AppName;
      wcex.hInstance     = hInst;
    
    // lpfnWndProc muss die statische WndProc sein.
      wcex.lpfnWndProc   = WndProc_static;
    
      if ( !RegisterClassEx(&wcex) ) return FALSE;
     }
    
    // Normalerweise zeigt der letzte Parameter von CreateWindowsEx () auf eine initialisierte CREATESTRUCT,
    // damit man bei WM_CREATE auf sie Zugriff hat.
    // Im letzen Parameter von CreateWindowEx () wird jetzt einfach der this-pointer übergeben.
     HWND hWnd =
     CreateWindowEx (0x00000208,AppName, 0,0x90CC0880,4,4,300,200,0,   0,            hInst,this);
     CreateWindowEx (0x00000208,"BUTTON",0,0x50000000,4,4,100, 30,hWnd,(HMENU)0x1234,hInst,0);
    
     return TRUE;
    }
    //-----------------------------------------------------------------------------
    WINAPI              WinMain              (HINSTANCE hInst, HINSTANCE, LPSTR, int) {
    //-----------------------------------------------------------------------------
    // Das übliche halt.
    
     ASE app;
    
     if ( app.CreateWClass (hInst,"WindowWClass") ) {
    
      MSG  msg;
    
      while ( GetMessage(&msg,0,0,0) ) {
       TranslateMessage (&msg);
       DispatchMessage  (&msg);
      }
    
     }
    
     return 0;
    }
    


  • Azrael, il Meraz schrieb:

    Ich habs immer noch net hingekriegt.

    Wie, wie, wie?????

    Bei mir ist das so - mir ist die ganze zeit bekannt wie die klasse heisst und ich kann diese instanziieren

    MyApp.h

    class CMyAPP{
    private:
        HWND hWnd;
        void OnPaint();
        void OnDestroy() { PostQuitMessage(0); };
    .
    .
    public:
        LRESULT Messages(HWND hWnd, unsigned int message, WPARAM wParam, LPARAM lParam);
    }
    

    MyApp.cpp

    void CMyApp::OnPaint(){
        PAINTSTRUCT ps;
        HDC hDC;
        RECT rect;
        ::GetWindowRect(hWnd, &rect);
        hDC = BeginPaint(hWnd, &ps);
        EndPaint(hWnd, &ps);
    }
    LRESULT CMyApp::Messages(HWND hWnd, unsigned int message, WPARAM wParam, LPARAM lParam){
        this->hWnd = hWnd;
        switch(message){
            case WM_PAINT:    OnPaint();   break;
            case WM_DESTROY:  OnDestroy();  break;
    		default:  return DefWindowProc(hWnd, message, wParam, lParam);
        }
        return 0;
    }
    

    Main.cpp

    #include "MyApp.h"
    
    LRESULT CALLBACK WndProc(HWND, unsigned int, unsigned int, long);
    
    CMyApp myapp;
    
    .
    .
    .
    LRESULT CALLBACK WndProc(HWND hWnd, unsigned int message, unsigned int wParam, long lParam){
    	return myapp.Messages(hWnd, message, wParam, lParam);
    }
    

    beim generieren des fensters dann einfach das globale WndProc angeben



  • merker schrieb:

    [...] Um die "WinProc" in eine Klasse zu kriegen, braucht man immer zwei davon [...]

    Nop, eine reicht theoretisch aus, musst es halt via SetWindowLong(Ptr)/GetWindowLong(Ptr) machen.


Anmelden zum Antworten