WndProc in eine Klasse bekommen...



  • Hallo!
    Irgendwie ist mir keine passende Überschrift eingefallen.
    😃
    Naja!
    Also ich bin gerade dabei einen sehr simplen winAPI-Wrapper zu schreiben!
    Bei diesem möchte ich im Konstruktor:

    WNDCLASSEX wndclass;
    [...]
    wndclass.lpfnWndProc = MyClass::WndProc;
    
    [...]
    

    In jenem fall ist das im Konstruktor der Klasse.
    Ich möchte also die lpfnWndProc Eigenschaft mit eine r/m Funktion (szeiger) belegen, welcher in der Klasse ist.
    Ich habe auch schon einfach versucht
    Ich habe auch schon versucht einfah versuicht fo,lgendes zuzuordtnen:

    MyClass::WndProc
    WndProc
    this->WndProc
    usw.

    Aber er meldet immer:
    long(stdcall__ MyClass::*)(...) nicht in
    long(stdcall__ *)(...) konvertierbar.

    Ich könnte die Funktion natürlich aus der Klasse rausnehemen!
    Aber dann verliere ich doch die OOP, oder?
    Die möchte ich gerne behalten!

    Danke im Voraus!

    geeky: Hab die Überschrift für die FAQ geändert...

    [ Dieser Beitrag wurde am 24.06.2002 um 16:16 Uhr von geeky editiert. ]



  • Hi,

    also du weisst ja von dem this-Zeiger, das bei Memberfunktionen immer verborgenes 1. Element ist.
    Somit stimmt die Signatur deiner WinProc als Member nicht mit der WinProc die Windows bzw. WNDCLASS::lpfnWndProc erwartet überein, da sie ja noch ein 1. Element mehr hat.
    Eine Lösung wäre die WinProc static zu machen, da kannst du aber nicht mehr auf deine Klassenelemente zugreifen, ausser du machst sie alle static.

    Es gibt auch andere Möglichkeiten, allerdings sind die etwas komplizierter. Guck dir mal das hier an [url] http://www.codeconduct.com/ATL/thunkwnd.asp [/url] so kann man auch auf Attribute der Klasse zugreifen ohne sie static zu machen.

    [ Dieser Beitrag wurde am 13.06.2002 um 22:37 Uhr von Real_PsychoDAD editiert. ]



  • nochmal ich 🙂

    ich habe mir das in dem Beitrag noch nicht so genau angeguckt, aber ich seh grad da kommt auch Assembler drin vor, scheint also wirklich komliziert zu sein.
    Hier wäre vielleicht noch ein andererer Ansatz wie du es machen könntest, ist mir gerade so eingefallen also kann auch sein das es Mist ist :).
    Füge deine Klasse einen Referenzzähler und ein statische liste aller erzeugten Objekte hinzu, dann noch eine private static WndProc, wenn diese WndProc jetzt benachrichtigt wird, gehst du die Liste durch und schickst die Eingabedaten an jedes Element der Liste, dazu hast du dann zum Beispiel eine WndProc als Member, die du dann aufrufst und einfach die Daten wie WPARAM etc ihnen übergibst.

    Viel Glück



  • Ich habe mal folgenden Ansatz gemacht. Mein Compiler frisst das.

    typedef long(__stdcall* WNDPROC)(void*,unsigned int,unsigned int,long);
    
    class Window
    {
       private:
       WNDPROC WndProc;
    
       public:
       RegisterWndClass(LPCTSTR app_name, HINSTANCE hInst)
       {
          WNDCLASSEX  wndclass;
    
          wndclass.cbSize        = sizeof(wndclass);
          wndclass.style         = CS_HREDRAW | CS_VREDRAW;
          wndclass.lpfnWndProc   = WndProc;
          wndclass.cbClsExtra    = 0;
          wndclass.cbWndExtra    = 0;
          wndclass.hInstance     = hInst;
          wndclass.hIcon         = LoadIcon(0, IDI_APPLICATION);
          wndclass.hCursor       = LoadCursor (NULL, IDC_ARROW);
          wndclass.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1);
          wndclass.lpszMenuName  = NULL;
          wndclass.lpszClassName = app_name;
          wndclass.hIconSm       = NULL;
    
          return RegisterClassEx(&wndclass);
       }
    };
    

    Ob du jetzt die RegisterWindowClass-Funktion in der Klasse drin hast oder nicht... klappt beides.

    [ Dieser Beitrag wurde am 13.06.2002 um 23:22 Uhr von WebFritzi editiert. ]



  • Ich habe letztens in der Cpp der Datei der Klasse eine globale Variable auf die Klasse gesetzt und die WndProc als friend in der Klasse deklariert. Ist eine sehr unelegante Lösung und hat das Problem, dass man nur ein Objekt der Klasse haben kann (wegen der globalen Variable).
    Klappt allerdings auch...



  • Also ich denke, dass ich alles statisch mache.
    Maximal möchte ich für meine Zwecke sowieso nur eine Instanz, da ich mit DirectX arbeite und einfach keine Lust habe, den ganzen WinAPI-Code immer abzutippen.

    Ansonsten:
    Welche Lösung wäre denn jetzt mal 'elegant'?
    😕



  • Original erstellt von Mis2com:
    Welche Lösung wäre denn jetzt mal 'elegant'?
    😕

    Ich persoenlich halte folgende fuer sehr elegant:

    WndProc ist static, beim WM_CREATE wird allerdings mit SetWindowLong() der this Zeiger, der bei CreateWindow als letzter parameter uebergeben wird, gespeichert.

    WndProc fragt dann jedesmal diesen this Zeiger wieder ab.



  • Die Lösung gefällt mir, da brauche ich meine globale Variable gar nicht mehr 🙂



  • Hey was soll das? Werde ich hier einfach ignoriert, oder was? 😡



  • WebFritzi, deine Lösung ist völliger quatsch!



  • *LOL* Ok. Ich geb mich geschlagen. Aber dann würde ich doch noch gerne wissen, warum. Bzw. möchte ich gerne wissen, was ich an dem Problem falsch verstanden habe. Ich dachte nur, er will ein Objekt haben, dem man eine WindowProc zuordnen kann. Klärt mich bitte auf. Will doch was lernen. 🙄



  • OK, ich habe jetzt verstanden, um was es geht. Ich habe mir jetzt eine kleine Klasse namens CWindow gemacht, für die jede Instanz eine eigene WindowProc haben soll, die NICHT außerhalb der Klasse zu finden sein soll. Ich denke, das war genau Mis2coms Problem. Jetzt verstehe ich aber Shades Vorschlag nicht. Wie macht man das? Hier mal meine Header-Datei der Klasse:

    //---------------------------------------------------------------------------
    #ifndef CWindowH
    #define CWindowH
    //---------------------------------------------------------------------------
    #include <windows.h>
    //---------------------------------------------------------------------------
    typedef long(__stdcall* WNDPROC)(void*,unsigned int,unsigned int,long);
    
    class CWindow
    {
       private:
       LPCTSTR     fClassName;
       HINSTANCE   fInstance;
       HWND        fHandle;
       HWND        fParentHandle;
       DWORD       fStyle;
       DWORD       fExStyle;
       HICON       fIcon;
       HCURSOR     fCursor;
       HMENU       fMenu;
       WORD        fID;
       int         fLeft;
       int         fTop;
       int         fWidth;
       int         fHeight;
       LPCTSTR     fCaption;
       WNDPROC     fWndProc;
    
       BOOL        fRegistered;
       BOOL        fCreated;
    
       protected:
       VOID Initialize();
    
       public:
       CWindow(LPCTSTR classname, HINSTANCE);
       CWindow(LPCTSTR classname, HINSTANCE, HWND parent);
       ~CWindow(void);
    
       ATOM    Register(WNDPROC);
       BOOL    Create(void);
    
       BOOL    SetStyle(DWORD);
       BOOL    SetExStyle(DWORD);
       VOID    SetIcon(HICON);
       VOID    SetCursor(HCURSOR);
       VOID    SetMenu(HMENU);
       VOID    SetID(WORD);
       VOID    SetLeft(int);
       VOID    SetTop(int);
       VOID    SetWidth(int);
       VOID    SetHeight(int);
       VOID    SetBounds(int left, int top, int width, int height);
       VOID    SetRect(RECT);
       VOID    SetCaption(LPCTSTR);
       //VOID    SetWindowProc(WNDPROC);
    
       HWND    Handle();
       HWND    ParentHandle();
       /*DWORD   Style();
       DWORD   ExStyle();
       HICON   Icon();
       HCURSOR Cursor();
       HMENU   Menu();
       WORD    ID();
       int     Left();
       int     Top();
       int     Width();
       int     Height();
       RECT    Rect();
       LPCTSTR Caption();
       WNDPROC WndProc();*/
    
       LRESULT Perform(UINT, WPARAM, LPARAM);
       /*BOOL    AddStyle(DWORD);
       BOOL    DeleteStyle(DWORD);
       BOOL    AddExStyle(DWORD);
       BOOL    DeleteExStyle(DWORD);*/
    };
    //---------------------------------------------------------------------------
    
    LRESULT CALLBACK ThisWndProc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)
    {
      CWindow*  This;
    
      switch(iMsg)
      {
         case WM_CREATE:
            break;
    
         case WM_COMMAND:
            MessageBox(NULL, "HALLO", "HALLO", MB_OK);
            break;
    
         case WM_PAINT:
            break;
    
         case WM_DESTROY:
            PostQuitMessage(0);
            break;
      }
      return DefWindowProc(hwnd, iMsg, wParam, lParam);
    }
    //---------------------------------------------------------------------------
    #endif
    


  • Shade meint glaube ich :

    class Window
    {
        static LRESULT CALLBACK ThisWndProc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam);
    };
    
    LRESULT CALLBACK Window::ThisWndProc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)
    {
        CWindow*  This;
    
        switch(iMsg)
        {
        case WM_CREATE:
            This = (Window *) (((LPCREATESTRUCT) lParam)->lpCreateParams);
            break;
    
        }
    }
    

    Beim erstellen musst du jetzt den Window Pointer übergeben.



  • Jo, so ähnlich hatte ich mir das dann auch gedacht. Aber ich verstehe das nicht. Warum sollte in WM_CREATE in den CreateParams genau der Zeiger auf das CWindow-Objekt sein, das das Handle hwnd hat? Also, der "momentane" Zeiger? Und warum nimmt er WndProc ohne Angabe von static nicht, und so schon? Fragen über Fragen - aber ich hoffe, ihr könnt sie beantworten. Danke.



  • lol, den zeiger auf die fensterklasse übergibt man bei CreateWindow, also CreateWindow(..., ..., this); Deshalb kann man dann bei den CreateParams darauf zugreifen



  • Ich hab das so gemeint wie KPC erklärt hat...

    Nur hat KPC vergessen im WM_CREATE den This auch mit SetWindowLong bzw. SetWindowLongPtr zu speichern und die erste Zeile von ThisWndProc sollte This mit GetWindowLong/Ptr füllen...

    @Webfritzi:
    Die wndproc MUSS static sein, anders kann sie nicht in der klasse sein, was sie aber sein muss, sonst ist die kapselung im *****...

    deshalb machen wir sie static.

    den this gibt man bei CreateWindow als letzten Parameter an, der wird beim WM_CREATE dann mitgeliefert in der CREATESTRUCT. somit können wir ihn speichern...

    bisher habe ich noch nie eine andere vernüftige möglichkeit gesehen, das anders zu lösen. ich lasse mich aber gene eines besseren belehren.



  • Ich habe das jetzt so gemacht, dass ich den Pointer in der USERDATA den Windows speichere. Dann brauche ich diesen CREATESTRUCT auch nicht. Außerdem steht in meiner SDK-Hilfe:

    lpCreateParams

    Points to data to be used for creating the window.
    Windows NT: This member is the address of a SHORT (16_bit) value that specifies the size, in bytes, of the window creation data. The value is immediately followed by the creation data. For more information, see the following Remarks section.



  • Original erstellt von Shade@work:
    **@Webfritzi:
    Die wndproc MUSS static sein, anders kann sie nicht in der klasse sein, was sie aber sein muss, sonst ist die kapselung im *****...

    deshalb machen wir sie static.
    **

    Das habe ich schon gemerkt, dass sie static sein MUSS. Aber wieso? Ich meine, ich wüsste gerne die Bedeutung des Schlüsselwortes static in diesem Zusammenhang. Und warum geht's nicht ohne static, was wieder Mis2coms Frage vom Anfang wäre.



  • die frage wurde doch schon beantwortet!

    Jeder Methode wird der this zeiger uebergeben.
    A a;
    a.b() == b(&a)
    von der funktionsweise her!

    eine static methode hat aber keinen this zeiger, deshalb ist
    a.b() nicht moeglich wenn b static ist.
    A::b() ist dann aber sehr wohl moeglich!
    A::b() == b()
    und somit sind wir das problem losgeworden, dass der compiler der wndproc den this pointer mitgeben wollte...

    Denn die wndproc darf keinen this zeiger bekommen, denn sie ist callback, was soviel heisst wie: windows ruft die funktion auf! und windows hat keine ahnung welchen this zeiger es mitgeben muss.



  • Wenn du ne Wrapperklasse schreiben willst kannst du bei jedem aufruf der funktion einfach eine funktion deiner Wrapperklasse starten, du legst in der funktion ne statische map an und öffnest immer jeweils die funktion zum HWND das ist dann fast das selbe... du musst halt noch den zwischenschritt machen...

    erm... schau mal in meinem Tut unter Entwicklung eines Steuerelements!
    ciaoy 🙂

    > Endy <<


Anmelden zum Antworten