Wie man WndProc als Klassenfunktion verwendet [duplicate]

Lesezeit: 5 Minuten

Ich versuche, eine Klasse zu erstellen, die WndProc enthält, aber ich erhalte eine Fehlermeldung:

Error 2 error C2440: '=' : cannot convert from 'LRESULT (__stdcall Client::* )(HWND,UINT,WPARAM,LPARAM)' to 'WNDPROC'

Ich habe im Internet danach gesucht und gesehen, dass Sie WndProc statisch machen müssen, aber dann kompiliert es und alles ist großartig, aber wenn ich etwas ändern möchte, lässt es mich nicht:

Error 3 error C2352: 'Client::CreateMen' : illegal call of non-static member function

(CreateMen ist eine Funktion in der Klasse, die das Menü mit HMENU und so erstellt).

das ist mein Funktionstitel:

LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);

Was kann ich tun? Ich bin wirklich verwirrt…

Vielen Dank!

  • Hinweis: Der erste Parameter in Klassenfunktionen ist der ‘this’-Zeiger.

    – Xearinox

    26. Januar 2014 um 21:14 Uhr

  • Sie müssen die machen WndProc static und speichern Sie einen Zeiger auf Ihre Klasse an einem Ort, an dem Sie darauf zugreifen können (z. B. über eine Fenstereigenschaft using SetProp/GetProp).

    – Jonathan Potter

    26. Januar 2014 um 21:31 Uhr

  • Remys Antwort unten sieht richtig aus. Möglicherweise erfinden Sie jedoch etwas, das bereits existiert, neu. Schauen Sie sich die Vorlagenklassen CWindow und CWindowImpl an. Ich habe vor einiger Zeit hier eine Antwort geschrieben: stackoverflow.com/a/3122743/104458

    – selbe

    26. Januar 2014 um 21:46 Uhr


Benutzer-Avatar
Rémy Lebeau

Eine nicht-statische Klassenmethode hat eine versteckte this Parameter. Das verhindert, dass die Methode als WndProc (oder irgendein anderer API-Callback) verwendet wird. Sie müssen die Klassenmethode als deklarieren static um das zu entfernen this Parameter. Aber wie Sie bereits bemerkt haben, können Sie von einer statischen Methode nicht auf nicht statische Member zugreifen. Sie benötigen einen Zeiger auf das Objekt, um darauf zugreifen zu können.

Im speziellen Fall eines WndProc-Rückrufs können Sie den Objektzeiger im HWND selbst speichern (unter Verwendung von entweder SetWindowLong/Ptr(GWL_USERDATA) oder SetProp()), dann kann Ihre statische Methode diesen Objektzeiger aus der abrufen hWnd Parameter (mit GetWindowLong/Ptr(GWL_USERDATA) oder GetProp()) und greifen Sie bei Bedarf mit diesem Objektzeiger auf nicht statische Member zu. Zum Beispiel:

private:
    HWND m_Wnd;
    static LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);

LRESULT CALLBACK Client::WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    Client *pThis;

    if (msg == WM_NCCREATE)
    {
        pThis = static_cast<Client*>(reinterpret_cast<CREATESTRUCT*>(lParam)->lpCreateParams);

        SetLastError(0);
        if (!SetWindowLongPtr(hwnd, GWL_USERDATA, reinterpret_cast<LONG_PTR>(pThis)))
        {
            if (GetLastError() != 0)
                return FALSE;
        }
    }
    else
    {
        pThis = reinterpret_cast<Client*>(GetWindowLongPtr(hwnd, GWL_USERDATA));
    }

    if (pThis)
    {
        // use pThis->member as needed...
    }

    return DefWindowProc(hwnd, msg, wParam, lParam);
}

m_Wnd = CreateWindowEx(..., this);

  • Vielen Dank! es hat sehr gut funktioniert!

    – Amit

    27. Januar 2014 um 13:48 Uhr

  • HINWEIS: setzen WndProc im öffentlichen Bereich nicht privat, da Sie private Mitglieder nicht als Callback-Eingabe in Funktionen verwenden können 🙂

    Benutzer13119880

    23. September 2020 um 7:39 Uhr

  • @PinnedObject private funktioniert in meinem Beispiel einwandfrei, es besteht keine Notwendigkeit dafür public.

    – Rémy Lebeau

    23. September 2020 um 7:40 Uhr


  • @PinnedObject Ja, die Fensterklasse, an die übergeben wird CreateWindowEx() muss innerhalb von registriert werden Client um für ein privates WndProc arbeiten. Passieren macht wenig Sinn this zu CreateWindowEx() und registrieren Sie die Fensterklasse außerhalb von Client.

    – Rémy Lebeau

    23. September 2020 um 8:11 Uhr

  • @PinnedObject wenn Sie ein per-class WndProc, ist es sinnvoll, es in derselben zu registrieren class das nutzt es. Dann private funktioniert gut. Wenn Sie sich außerhalb der registrieren class dann muss natürlich die WndProc für externen Code zugänglich gemacht werden, via public oder friendetc.

    – Rémy Lebeau

    23. September 2020 um 8:28 Uhr

Benutzer-Avatar
Radu Chivu

Leider können Sie eine Klassenfunktion nicht als wndproc verwenden, da der Compiler versucht, Ihnen mitzuteilen, dass sich die Aufrufkonvention unterscheidet, obwohl die beiden Funktionen dieselbe Signatur haben, eine Klassenfunktion erwartet, dass der this-Zeiger an sie übergeben wird. Bei 64-Bit-Builds wird erwartet, dass es sich in der RCX/ECX-Registrierung befindet, während bei 32-Bit-Builds erwartet wird, dass der this-Zeiger das letzte Argument ist, das auf den Stapel verschoben wird. Der Fenstercode wird dies nicht tun, wenn er Ihre WndProc aufruft und dies im Wesentlichen in einen Funktionsaufruf für einen Müllzeiger umwandelt.

Was du kann machen Sie eine statische Methode, die etwa Folgendes tut:

LRESULT Client::CreateMen(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
{
    // The OS makes sure GWLP_USERDATA is always 0 before being initialized by the application
    Client* client = (Client*)GetWindowLongPtr(hwnd, GWLP_USERDATA);

    if(msg == WM_INIT)
    {
        client = new Client();
        SetWindowLongPtr(hwnd, GWLP_USERDATA, client);
    }

    if(msg == WM_DESTROY)
    {
        client = (Client*)GetWindowLongPtr(hwnd, GWLP_USERDATA, client);
        SetWindowLongPtr(hwnd, GWLP_USERDATA, NULL);
        delete client;
        client = NULL;
    }

    if(client)
    {
        // Do stuff with the client instance
    }

    return DefWindowProc(hwnd, msg, wparam, lparam);
}

Ich habe dies nicht getestet, daher könnte es einige Fehler enthalten, aber lassen Sie es mich wissen, wenn Sie irgendwelche Probleme damit haben, und ich werde es bei Bedarf verfeinern.

  • Warum ein Client Objekt (auf dem CreateMen()` aufgerufen wird) ein zweites erstellen Client Objekt? Sie können das Original weitergeben Client widersprechen CreateWindow/Ex() direkt und muss dann die WndProc aufrufen SetWindowLong() während das Fenster erstellt wird. Dieser Weg, GWL_USERDATA ist bereit für nachfolgende Nachrichten, und Sie müssen keine benutzerdefinierte INIT-Nachricht verwenden.

    – Rémy Lebeau

    26. Januar 2014 um 21:48 Uhr

  • Der this-Zeiger kommt nicht durch dieses Register für eine stdcall-Funktion

    – David Heffernan

    26. Januar 2014 um 22:31 Uhr

  • @RemyLebeau CreateMen wird nicht für ein Client-Objekt aufgerufen, es ist die Implementierung für die statische Fensterprozedur. Ich dachte, es wäre besser, das Objekt innerhalb der Fensterprozedur zu halten, damit externer Code es nicht berührt.

    – Radu Chivu

    26. Januar 2014 um 23:22 Uhr

  • @DavidHeffernan Danke für die Korrektur, ich habe mehr Zeit damit verbracht, 64-Bit-Code als 32-Bit-Code zu debuggen, also hatte ich den Eindruck, dass die gleiche Konvention zum Übergeben von Argumenten in Registern wann immer möglich galt, ich werde das in meinem Beitrag korrigieren, Danke.

    – Radu Chivu

    26. Januar 2014 um 23:36 Uhr

1012520cookie-checkWie man WndProc als Klassenfunktion verwendet [duplicate]

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy