CALLBACK Funktionen



  • So, da die Suche weder hier im Forum, noch bei Google, etc, etwas für mich Verständliches herausgebracht hat, hoffe ich, dass mich hier jemand aufklären kann. Und zwar verstehe ich die Arbeitsweise einer Callback-Funktion nicht.
    Von der Theorie her würde ich sagen, dass es so ne Art Ereignis-Behandlungs-Funktion ist. Von der Praxis her kann ich es nicht nachvollziehen. Bei meinem nun endlich gelösten Problem habe ich den Artikel von www.codeproject.com gelesen und damit gearbeitet.
    Dort wird zum Auffinden einer Instanz eine Callback-Funktion deklariert und definiert:

    static BOOL CALLBACK searcher(HWND hWnd, LPARAM lParam);
    
    BOOL CALLBACK CMyApp::searcher(HWND hWnd, LPARAM lParam)
    {
        DWORD result;
        LRESULT ok = ::SendMessageTimeout(hWnd,
                                          UWM_ARE_YOU_ME,
                                          0, 0, 
                                          SMTO_BLOCK |
                                          SMTO_ABORT_IF_HUNG,
                                          200,
                                          &result);
        if(ok == 0)
           return TRUE; // ignore this and continue
        if(result == UWM_ARE_YOU_ME)
        { /* found it */
            HWND * target = (HWND *)lParam;
            *target = hWnd;
            return FALSE; // stop search
        } /* found it */
        return TRUE; // continue search
    } // CMyApp::searcher
    

    Diese Funktion wird in der InitInstance() der EnumWindows-Funktion übergeben:

    if ( AlreadyRunning )
    { /* kill this */
        HWND hOther = NULL;
        EnumWindows(searcher, (LPARAM)&hOther);
    
        if ( hOther != NULL )
        { /* pop up */
            ::SetForegroundWindow( hOther );
    
            if ( IsIconic( hOther ) )
            { /* restore */
                ::ShowWindow( hOther, SW_RESTORE );
            } /* restore */
        } /* pop up */
    
        return FALSE; // terminates the creation
    } /* kill this */
    

    Nun meine Frage: Was passiert beim Aufrufen von EnumWindows() genau? Und warum muss ich unbedingt CALLBACK in der Deklaration schreiben?
    Die Definition von __stdcall bringt mich da nicht weiter.
    Und was ist, wenn ich eine eigene CALLBACK - Funktion schreiben möchte, aber nicht EnumWindows nutzen möchte (ich weiß, dass es prinzipiell nichts mit der Funktionsweise von CALLBACKS zu tun hat). Was muss ich da beachten? Warum kann man nicht ganz normale Funktionen nutzen?

    Hoffe mich kann endlich mal einer Aufklären. Hatte schon lange die Frage im Kopf. Da ich aber wegen der Lösung meines Problems nun aber tatsächlich mal eine CALLBACK-Funktion genutzt habe, möchte ich es auch gerne selber zu 100 Prozent verstehen.

    MfG,
    Paul.
    😉



  • Da gibts auch eine Erklärung: http://www.c-plusplus.net/tuts/cpp/cpp_functionpointers_eng.pdf

    Aber eine deutsche würde mich auch interessieren, so wirklich habe ich das auch nicht begriffen. 🙄



  • Super, lese ich mal durch. Wenn ich es verstehe, dann versuche ich es mal zu erklären!! Danke dir...
    ... mal wieder. 🤡



  • CALLBACK, besser gesagt der Zusatz _stdcall ist eine aufrufkonvention. Sprich damit wird beschrieben, wie ein funktion aufgerufen wird (ja, da gibt es unterschiedliche methoden;)) Die Funktion EnumWindows setzt einfach voraus, das es sich um eine _stdcall function handelt und ruft diese wie eine auf, deswegen musst du deine funktion auch als _stdcall definieren. Er bekommt ja nur den Zeiger auf die Funktion und daran kann er nicht erkennen wie die Funktion aufgerufen werden muss, und da es nunmal verschiedene möglichkeiten gibt wird das duch _stdcall auf eine feste konvention definiert. Dieses Problem hat der Compiler ja nicht, wenn man eine Funktion aufruft. Da nimmt er einfach die Aufrufkonvention die für die Funktion definiert ist, aber bei einem Zeiger weis er das nicht mehr.



  • Als Ergänzung:

    Es gibt 4 Aufrufkonventionen. In C/C++ benutzt wird standardmäßig cdecl. Aus der Pascalwelt stammt stdcall.
    Der Unterschied liegt im Ablegen der Funktionsparameter, man kann die vom links nach recht auf den Stack hauen, aber auch von rechts nach links(das dürfte cdecl sein, wenn ich mich recht erinner).

    So das sind 2 Möglichkeiten, ich habe aber von 4 geredet. Die kommen so zu Stande:
    Man kann noch unterscheiden, wer den Stack wieder beräumt. Soll ja von Vorteil sein bei Verlassen der Funktion die Variablen wieder vom Stack zu hauen 😉
    Auch hier gibt es 2 Möglichkeiten: der Aufrufer kann den Stack bereinigen oder aber auch der aufgerufene, also noch in der Funktion am Ende kann das geschehen.

    Macht insgesamt 4 Möglichkeiten.

    Wie gesagt: aus der C-Welt kommt cdecl und das wird, insofern nicht beim Kompiler anders angegeben, als Standard genommen. Und wenn man halt nicht die Standardkonevntion seine Kompilers benötigt, muss man dem das bei der Funktionsdeklaration sagen.

    Wenn man aber mal rumschaut, ist stdcall weiter verbreitet: siehe WinAPI, in PASCAL, VB(manchmal kommt ma nicht drum rum 😞 ) nutzt nur stdcall.

    Wobei der Trend momentan zu fastcall hingeht bei neueren Sachen.
    Ach ja das ganze ist man höre und staune sehr anschaulich in der Delphi-Hilfe beschrieben 🤡



  • Jo, danke euch. Muss mich aber erst noch durcharbeiten.

    Aber warum gerade in "nur" in der Delphi-Hilfe?



  • Paul_C. schrieb:

    Aber warum gerade in "nur" in der Delphi-Hilfe?

    Eigentlich nur deshalb weil man zu Faul ist zum Suchen...

    Calling Conventions
    http://msdn2.microsoft.com/en-us/library/k2b2ssfy.aspx

    Overview of x64 Calling Conventions
    http://msdn2.microsoft.com/en-us/library/ms235286.aspx

    Obsolete Calling Conventions
    http://msdn2.microsoft.com/library/wda6h6df.aspx



  • Paul_C. schrieb:

    Jo, danke euch. Muss mich aber erst noch durcharbeiten.

    Aber warum gerade in "nur" in der Delphi-Hilfe?

    Habe nicht gesagt, dass es nur in der Delphi-Hilfe steht. Habe nur geschrieben, dass es dort anschaulich drinnen steht.



  • @Pelleaon: Ok, hatte irgendwie noch ein "nur" im Kopf...

    @Jochen: Zu faul nicht, aber tipp mal im MSDN mal CALLBACK ein und du wirst keine Verknüpfung zu den Calling Conventions bekommen.
    Mir war ja vorher nicht klar, dass ich nach Funktionszeigern und den Konventionen suchen müsste. 😉



  • Paul_C. schrieb:

    @Jochen: Zu faul nicht, aber tipp mal im MSDN mal CALLBACK ein und du wirst keine Verknüpfung zu den Calling Conventions bekommen.
    Mir war ja vorher nicht klar, dass ich nach Funktionszeigern und den Konventionen suchen müsste. 😉

    Das ist oft das, warum man nichts findet.
    Die MSDN ist so komplex, da braucht man eigentlich einen Fremdenführer. 😉



  • @Paul_C, @estartu: Die MSDN darf man nie zum suchen verwenden :p ist das reinste chaos!

    Ich verwende immer google mit dem zusatz:

    Google-Siche:

    "keyword(s) site:msdn.microsoft.com"

    oder bei Suche nach KB-Artikeln:

    "keyword(s) site:support.microsoft.com"

    oder bei Suche nach VC8/.NET2 Dingen

    "keyword(s) site:msdn2.microsoft.com"



  • Ahja, DAS ist der Trick. 😮 👍
    Okay, ich nehme eigentlich immer meine hier offline - das sollte ich dann wohl mal testweise bleiben lassen. 😃

    Kann hier irgendwer Search-Plugins für Firefox machen? Ich hätte gerne das Googledings um das von Jochen eben erweitert. 🤡



  • @Jochen: Super! Gut zu wissen!!! 👍
    🙂



  • So habe mich nun durch den Text gearbeitet, welchen estartu am Anfang des Threads gepostet hat. (Link:http://www.c-plusplus.net/tuts/cpp/cpp_functionpointers_eng.pdf)
    In dem Text wird gut erklärt, wie man solche Funktionszeiger erstellt.
    Mir ist aber immer noch schleierhaft warum man diese aber generell verwendet.
    Als eine Möglichkeit wird da genannt, dass man unter Umständen switch-case-Anweisungen vermeiden kann. Die anderen Beispiele Beispiele beziehen sich auf Callback-Funktionen.
    Liegt denn das "Geheimnis" solcher Callback-Funktionen nun darin, dass man die Auswahl hat, welche Funktion man in einer anderen Funktion aufruft?
    Wie steht das denn dann im Zusammenhang mit Ereignisbehandlung?
    Wenn Ereignis A eintritt, dann rufe Funktion FuncA() auf, wenn Ereignis B eintritt, dann nimm die Funktion FuncB()? Dann müsste man doch eine Auswahl-Anweisung vor dem Funktionsaufruf zum Entscheiden implementieren und dann wäre es doch eigentlich egal, wo ich das mache?

    Wäre schön, wenn mir jemand sagen würde, ob ich einigermaßen richtig liege?!?

    Mit freundlichen Grüßen,
    Paul.
    😉


Anmelden zum Antworten