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).
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
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:
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
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
10125200cookie-checkWie man WndProc als Klassenfunktion verwendet [duplicate]yes
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 usingSetProp
/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