Wieso haben so viele Windows-Strukturen eine Variable, die ihre eigene Größe beinhaltet?



  • Am Beispiel der WNDCLASSEX-Struktur:

    typedef struct {
        UINT cbSize;
        UINT style;
        WNDPROC lpfnWndProc;
        int cbClsExtra;
        int cbWndExtra;
        HINSTANCE hInstance;
        HICON hIcon;
        HCURSOR hCursor;
        HBRUSH hbrBackground;
        LPCTSTR lpszMenuName;
        LPCTSTR lpszClassName;
        HICON hIconSm;
    } WNDCLASSEX, *PWNDCLASSEX;
    

    cbSize
    Specifies the size, in bytes, of this structure. Set this member to sizeof(WNDCLASSEX). Be sure to set this member before calling the GetClassInfoEx function.

    Und so ist das bei verdammt vielen Windows-Geschichten. Wieso muss in der Struktur die eigene Größe gespeichert sein?



  • Falls man in einer späteren Windows-Version die Funktionalität einer API-Funktion
    erweitern will, kann man das ganz einfach machen ohne das API zu ändern.

    Die API-Funktion brauch nur die Größe der Struktur prüfen und weis automatisch
    was extra noch in der Struktur steht.



  • Und noch ein kleiner Zusatz: Es ist auch eine Art Sicherheitsmechanismus, damit man nicht die falsche Struktur übergibt.



  • Verstehe ich nicht. Windows könnte die Größe der Struktur doch auch so überprüfen, ohne dass er in der Struktur gespeichert wird, oder?



  • Ne, wie denn? Es bekommt ja einfach nur einen Zeiger übergeben.

    Wenn du eine Windows-Funktion wärst, könntest du ja z.B. so aussehen:

    struct CIRCLE
    {
        uint size;
        ....
    };
    
    void DrawCircle( CIRCLE* circle )
    {
    }
    

    Innerhalb der Draw-Circle-Funktion hast du keine Möglichkeit zu prüfen, ob der Speicher hinter dem Parameter "circle" auch die Größe der Struktur "CIRCLE" hat. Der naive Ansatz if ( sizeof(*circle) == sizeof(CIRCLE) ) wird nicht funktionieren, da das der Compiler prüft; dieser weiß zum Zeitpunkt des Kompilierens nur, dass "circle" ein Zeiger auf die Struktur "CIRCLE" ist und gibt für sizeof(*circle) deshalb immer dasselbe an wie sizeof(CIRCLE) .

    Durch die Überprüfung mit

    void DrawCircle( CIRCLE* circle )
    {
        if ( circle->size == sizeof(CIRCLE) )
            ;// alles ok
    }
    

    kannst du aber auch nicht 100% wissen, ob die richtige Struktur übergeben wurde, du bekommst ja nur eine Adresse übergeben und prüfst, ob der erste "uint" hinter dieser Adresse den Wert sizeof(CIRCLE) hat.
    Du könntest ein wenig tricksen und das ganze so umgehen:

    // Von hier wird die Funktion DrawCircle aufgerufen
    struct MyOwnStruct
    {
        uint bla;
        ...
    };
    MyOwnStruct tricky;
    tricky.bla = sizeof(CIRCLE);
    DrawCircle( reinterpret_cast<CIRCLE*>( &tricky ) );
    

    Hier stimmt alles aus der Sicht von "DrawCircle", es bekommt einen Zeiger übergeben und der erste Wert hinter dem Zeiger stimmt mit sizeof(CIRCLE) überein. Man kann es sogar noch einfacher umgehen: uint a=sizeof(CIRCLE); DrawCircle( reinterpret_cast<CIRCLE*>( &a ) ); .

    Windows hat selbst mit diesem Mechanismus also keine Möglichkeit, sicherzugehen, dass ein Zeiger mit validem Hintergrund übergeben wurde. Aber es ist besser als nichts und zu guter Letzt ist die "size"-Information auch wichtig, wenn man die Struktur erweitert, size "AZ"s Post.


Anmelden zum Antworten