[C++] Übergabe eines Message-Handlers erzeugt Fehler

Sircoly

Mitglied
Halli Hallo,





ich habe zwei Funktion ...
  • Xavion::xFramework::MessageHandler(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam)
  • Xavion::xFrameWork::Show()
Die Funkion MessageHandler() hat die Aufgabe, alle Nachrichten für das Framework zu bearbeiten.
z.B. Wenn das Fenster geschlossen wird, dass alle Variablen zerstört werden und u.a. auch die DirectX-Schnittstelle released wird, oder bei einem Tastendruck ein Ereignis eintrift.

Die Funktion Show() registriert eine Fensterklasse (WNDCLASSEX) mit RegisterClassEx() und erzeugt mir das Fensterhandle mit CreateWindow(). Danach wird das erzeugte Fenster-Handle an eine intere Variable übergeben.

Mein Problem ist nun das Folgende:
Ich muss beim füllen von WNDCLASSEX den Message-Handler angeben. Dieser ist ja in der gleichen Klasse implementiert. Also ganz einfach (denke ich mir):
Code:
// Fensterinformationen ausfüllen
WNDCLASSEX wndClass = 
{
sizeof( WNDCLASSEX ),
CS_CLASSDC,
this->MessageHandler, 
0,
0,
GetModuleHandle( NULL ),
NULL,
NULL,
NULL,
NULL,
this->attWindowTitle,
NULL
};
 
// Fenster registrieren
RegisterClassEx( &wndClass );

Dann knallts ganz genau 1x.
Dabei drückt der Compiler sich wie folgt aus:
Code:
error C2440: 'Initialisierung': 'overloaded-function' kann nicht in 'WNDPROC' konvertiert werden

Ich habe versucht herauszufinden, was ein "overloaded-function" ist. Leider ohne erfolg.
Klar bin ich dem Englischen mächtig und habe direkt interpretiert: Eine überladene Funktion kann hier anscheinend nicht als Parameter übergeben werden.
Leider gibts da einen kleinen Hacken: "MessageHandler" ist keine überladene Funktion! - Zumindest habe ich in meinem Source keine weitere Funktion, die "MessageHandler" heißt, in der Klasse "xFramework".

Ich habe auch schon gedacht, dass es vielleicht eine Funktion "MessageHandler" von Haus aus gibt. (Also schon vordefiniert in der windows.h)
Auch das habe ich ausgeschlossen, da ich die Funktion bereits "MsgHandler" getauft habe. Allerdings führte dies zu dem gleichen Fehler.

Ich poste euch die entsprechenden Stellen im Quellcode einmal:
==> Methode "Show()"
Code:
void Xavion::xFramework::Show( )
{
// Rückgabevariable
HWND hWindBuffer;
// Fensterinformationen ausfüllen
WNDCLASSEX wndClass = {
sizeof( WNDCLASSEX ),
CS_CLASSDC,
NULL, // TODO Message-Handler ermitteln
0,
0,
GetModuleHandle( NULL ),
NULL,
NULL,
NULL,
NULL,
this->attWindowTitle,
NULL
};
// Fenster registrieren
RegisterClassEx( &wndClass );
 
 
// Fenster erzeugen
hWindBuffer = CreateWindow( 
this->attWindowTitle, 
this->attWindowTitle, 
WS_OVERLAPPEDWINDOW, 
this->attWindowPosition.x,
this->attWindowPosition.y,
this->attWindowSize.x,
this->attWindowSize.y,
GetDesktopWindow( ),
NULL,
wndClass.hInstance,
NULL
);
// Prüfen, ob das Fenster erzeugt werden konnte
// ==> Falls YES: Fenster anzeigen
if( &hWindBuffer != NULL )
{
// Fensterhandle übernehmen
this->attWindowHandle = &hWindBuffer;
// Fenster anzeigen
ShowWindow( *this->attWindowHandle, SW_SHOW );
}
}
==> Methode "MessageHandler()"
Code:
LRESULT WINAPI Xavion::xFramework::MessageHandler( HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam )
{
// Eingehende Nachricht analysieren
switch ( iMessage )
{
// Fenster wird geschlossen
case WM_DESTROY:
// Fenster schließen
this->Close( );
break;
// Fenster wird fokusiert
case WM_SETFOCUS:
// Fenster aktualisieren
this->ReNew( );
break;
// Taste wurde gedrückt
case WM_KEYDOWN:
switch( wParam )
{
// ESC-Taste
case VK_ESCAPE:
// Fenster schließen
this->Close( );
break;
}
break;
}
// Funktion terminieren
return DefWindowProc( hWnd, iMessage, wParam, lParam );
}

Ich bin allmäglich mit meinem Latein am Ende und frage nun euch:
Könnt ihr mir einen Tipp geben, wo der Fehler liegt? - Oder mir gar sagen, wie ich den Fehler beheben kann?

Ich hoffe auf eure Hilfe!
 
Hallo,

das Problem liegt darin, das eine Klassenmethode eine andere Signatur hat, als eine Funktion und daher nicht ohne weiteres als Callback-Funktion verwendet werden kann.

Es gibt zwei Möglichkeiten, das Problem zu lösen:
1. Den Message-Handler nicht als Klassenmethode implementieren.
2. Eine statische Klassenmethode verwenden.

Dann musst du aber eine globale Instanz deiner Klasse erzeugen oder bei Möglichkeit 2) alle in der Callback-Methode genutzten Klassenmember ebenfalls statisch machen.


Gruß
MCoder
 
Zuletzt bearbeitet:
Halli Hallo,

danke erstmal für deine tolle Hilfe! - Zumindest verstehe ich nun das Problem!
Ich habe mich für die 1. Möglichkeit entschieden: Die externe Methode.

Allerdings würde ich diese etwas abwandeln:
Ich erstelle einen Callback-Handler als externe Funktion. In dieser, "verlinke" ich auf den Message-Handler aus der Klasse "Xavion::xFramework".
Code:
XAVION_API LRESULT WINAPI ExtMsgHandler( HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam )
{
	// Funktion terminieren
	return G_FrameWork.MessageHandler( hWnd, iMessage, wParam, lParam );
}

Die Variable "G_FrameWork" ist die globale Instanz der Klasse "Xavion::xFramework".

Code:
// Abbreviatur für Framework-Klasse
typedef Xavion::xFramework FRAMEWORK;
// Verwendungszweck:	Framework der Engine
Xavion::FRAMEWORK	G_FrameWork;

Nun übergebe ich die externe Funktion, die wiederrum auf die intere Methode verweist, der WNDCLASSEX-Struktur. Dadurch sollte das Problem gelöst sein.
Code:
// Fensterinformationen ausfüllen
WNDCLASSEX wndClass =	{
			sizeof( WNDCLASSEX ),
			CS_CLASSDC,
			Xavion::ExtMsgHandler,
			0,
			0,
			GetModuleHandle( NULL ),
			NULL,
			NULL,
			NULL,
			NULL,
			this->attWindowTitle,
			NULL
			};

// Fenster registrieren
RegisterClassEx( &wndClass );

Bevor du jetzt denkst, dass ich es geschafft habe: Nein, habe ich nicht.
Die Hürde des Compillers habe ich nun mit deiner Hilfe genommen.
Allerdings meldet sich nun der Linker zu Worte, der Wohl auch noch was los werden möchte. Er sagt folgendes dazu:
Code:
error LNK2019: Verweis auf nicht aufgelöstes externes Symbol 
""long __stdcall Xavion::ExtMsgHandler(struct HWND__ *,unsigned int,unsigned int,long)" (?ExtMsgHandler@Xavion@@YGJPAUHWND__@@IIJ@Z)" 
in Funktion 
""public: void __thiscall Xavion::xFramework::Show(void)" (?Show@xFramework@Xavion@@QAEXXZ)".

Meine Datei-Architektur sieht wie folgt aus:
  • Jede Source-Datei includiert die Xavion.h
  • Die Xavion.h includiert alle restlichen Header-Dateien
  • In den restlichen Header-Dateien sind die jeweiligen strukturen/enum/Klassendefinitionen

Daraus folgt folgendes:
Der Prototyp des externen Callbacks ist in der Xavion.h
Die dazugehörige Funktionsdefinition ist in der Xavion.cpp
Der Protoyp der internen Klassen-Methode (MessageHandler()) ist in der xFramework.h
Die dazugehörige Funktionsdefinition ist in der xFramework.cpp

Ich frage mich nun, wieso er diesen Fehler ausgibt und wie ich den Fehler beheben kann.
Kann mir da auch jemand helfen?
 
Zuletzt bearbeitet:
Möp. Elegant kannst du es machen, in dem du CreateWindowEx nimmst. Da bekommst du dann die Möglichkeit, deinen this-Zeiger zu übergeben. Dann sieht das in etwa so aus:

C++:
class Window
{
    ::HWND m_hWnd;

public:
    operator ::HWND() const        {    return m_hWnd;    }
#pragma warning(disable: 4312)
    static Window* FromHandle(const ::HWND& hWnd) { return reinterpret_cast<Window*>(::GetWindowLongPtr(hWnd, GWL_USERDATA)); };
#pragma warning(default: 4312)

protected:
    virtual LRESULT messages(::HWND const& hWnd, const UINT message, WPARAM wParam, LPARAM lParam)
    { return S_OK; }

private:
    static LRESULT _message_proc(::HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
    {
        Window* ptr_window(Window::FromHandle(hWnd));
#pragma warning(disable: 4311)
        if (message == WM_NCCREATE)
        {
            ptr_window = reinterpret_cast<Window*>(reinterpret_cast<::LPCREATESTRUCT>(lParam)->lpCreateParams);
            ::SetWindowLongPtr(hWnd, GWL_USERDATA, reinterpret_cast<LONG>(pWindow));
        }
#pragma warning(default: 4311)
        return (ptr_window != NULL ? ptr_window->messages(hWnd, message, wParam, lParam) : ::DefWindowProc(hWnd, message, wParam, lParam));
    }
};
beim erstellen der Fensterklasse dann als MessageProc einfach angeben:
C++:
&Window::_message_proc
und das Fenster mit CreateWindowEx erzeugen ... als letzten Parameter dann einfach
C++:
static_cast<LPVOID>(this)
und gut ist ;)
Ist so ziemlich eleganteste, was man in richtung WinAPI => C++ machen kann ;)

Am einfachsten der Klasse WIndow jetzt ne Funktion create hinzufügen ;) DAnn kannste jedes Fenster von Window ableiten und einfach messages überschreiben ;) dann gehts wie du es brauchst.
 
Halli Hallo,

ich habe einen Fehler gemacht und diesen erkannt.
Ich weiß zwar nicht, wie ich diesen elementaren Programmierfehler überhaupt begehen konnte, aber ich habe ihn behoben.

Das ganze muss wie folgt aussehen:
  • Die WNDCLASSEX-Klasse füllen:
Code:
// Fensterinformationen ausfüllen
WNDCLASSEX wndClass =	{
				sizeof( WNDCLASSEX ),
				CS_CLASSDC,
				Xavion::ExtMsgHandler,
				0,								0,
				GetModuleHandle( NULL ),
				NULL,
				NULL,
				NULL,
				NULL,
				this->attWindowTitle,
				NULL
			};
  • Die Deklaration des externen Message-Handlers:
Code:
// Verwendungszweck:	Globaler Message-Handler für interne Nachrichten
XAVION_API LRESULT WINAPI ExtMsgHandler( HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam );
  • Die Definition des externen Message-Handlers:
Code:
XAVION_API LRESULT WINAPI Xavion::ExtMsgHandler( HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam )
{
	// Funktion terminieren
	return Xavion::G_FrameWork->MessageHandler( hWnd, iMessage, wParam, lParam );
}
  • Die Deklaration der internen Klassen-Methode:
Code:
// Verwendungszweck:	Message-Handler des Frameworks
LRESULT WINAPI MessageHandler( HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam );
  • Die Definition der internen Klassen-Methode:
Code:
LRESULT WINAPI Xavion::xFramework::MessageHandler( HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam )
{
	// ...
}
  • Und die globale Variable vom Typ "Xavion::xFramework":
Code:
// Verwendungszweck:	Framework der Engine
static Xavion::FRAMEWORK*	Xavion::G_FrameWork = NULL;
// Framework initialisieren
Xavion::G_FrameWork = new Xavion::FRAMEWORK();

Mein Fehler lag in der Definition dex externen Message-Handlers.
Dem geschulten Leser ist nicht entgangen, dass ich beim Header das "Xavion::" vor dem Funktionsnamen vergessen hatte.

Beschissene Problem erfordern manchmal heroisch einfache Lösungen. ;-)
 
Zurück