Wie kann ich Callback-Funktion in einer Klasse bekannt machen?



  • Das Problem ist, dass als erster Parameter deiner Memberfunktionen in der CWindow Klasse immer implizit der this-Zeiger mitgegeben wird.
    Allerdings verlangt

    wc.lpfnWndProc
    

    eine Funktion mit der Signatur

    (HWND, UINT, WPARAM, LPARAM)
    

    erwartet, du mit deinem Code aber implizit solch eine Signatur bekommst:

    (CWindow*, HWND, UINT, WPARAM, LPARAM)
    

    Schau mal ins WINAPI-FAQ. Dort, ziemlich am Ende wird dein Problem mit Lösungsvorschlägen behandelt.

    EDIT: Habs zwar nicht getestet, aber saperos Vorschlag müsste AFAIK funktionieren.





  • Ich meinte hier in deinem Beitrag hat dir separo einen Lösungsvorschlag unterbreitet, worauf du geschrieben hattest, dass das nicht gehen würde, weil WndProc schon einen Rüchgabewert hätte, nämlich

    return DefWindowProc(hwnd, msg, wparam, lparam)
    

    Probier es mal so aus, wie separo es geschrieben hat. Müsste funktionieren. Dein Einwand mit dem Rüchgabewert ist falsch.



  • Nun kommt noch einen Fehler, nämlich 'xxx': nichtdeklarierter Bezeichner.

    Was habe ich nun falsch gemacht?



  • Ei ei ei, unter Umständen würde ich dir erstmal ein Tutorial und/oder ein Buch zum Programmieren mit C/C++ empfehlen.

    Ersetze

    return Window.WndProc(xxx);
    

    durch

    return Window.WndProc(hwnd, msg, wparam, lparam);
    

    EDIT: Ich seh grad' das könnt etwas unverständlich bzw. ungenau formuliert sein. Also:

    In deiner Klasse hast du eine Memberfunktion

    LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
    //dein Code
    }
    

    deklariert und definiert. Diese lässt du so stehen, wie du es in deinem ersten Post geschrieben hast.

    Du brauchst aber noch eine Funktion außerhalb deiner Klasse, nämlich:

    LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
        return Window.WndProc(hwnd, msg, wparam, lparam);
    }
    

    So, jetzt müsstest du wahrscheinlich

    CWindow Window;
    

    auch noch global deklarieren, dann sollte alles funktionieren.



  • Ich habe bereits vier Bücher über das Programmieren, und eines habe ich 2 mal komplett durchgelesen. Ich weiss, ich bin in der WinAPI noch nicht so drin.

    Ich habe nun diese (xxx) durch (hwnd, msg, wParam, lParam) ersetzt. Doch jetzt kommen drei weitere Fehler:

    ------ Erstellen gestartet: Projekt: MyEngine, Konfiguration: Debug Win32 ------
    Kompilieren...
    CreateWindow.cpp
    Verknüpfen...
    CreateWindow.obj : error LNK2019: Verweis auf nicht aufgelöstes externes Symbol ""long __stdcall WndProc(struct HWND__ *,unsigned int,unsigned int,long)" (?WndProc@@YGJPAUHWND__@@IIJ@Z)" in Funktion ""public: struct HWND__ * __thiscall CWindow::InitWindow(char const *,int,int,int,int)" (?InitWindow@CWindow@@QAEPAUHWND__@@PBDHHHH@Z)".
    MSVCRTD.lib(crtexew.obj) : error LNK2019: Verweis auf nicht aufgelöstes externes Symbol "_WinMain@16" in Funktion "___tmainCRTStartup".
    C:\Dokumente und Einstellungen\patrick\Eigene Dateien\Visual Studio 2008\Projects\MyEngine\Debug\MyEngine.exe : fatal error LNK1120: 2 nicht aufgelöste externe Verweise.
    Das Buildprotokoll wurde unter "file://c:\Dokumente und Einstellungen\patrick\Eigene Dateien\Visual Studio 2008\Projects\MyEngine\MyEngine\Debug\BuildLog.htm" gespeichert.
    MyEngine - 3 Fehler, 0 Warnung(en)
    ========== Erstellen: 0 erfolgreich, Fehler bei 1, 0 aktuell, 0 übersprungen ==========
    

    Den Code habe ich nochmals hier:

    // CreateWindow.h
    //
    // Die Funktionen und Variabeln definieren
    //
    #ifndef CREATEWINDOW_H
    #define CREATEWINDOW_H
    
    #include <windows.h>
    
    class CWindow
    
    {
    
    public:
    
    	HWND InitWindow (LPCTSTR WindowTitle, int XPos, int YPos, int WindowWidth, int WindowHeight);
    	LRESULT CALLBACK WndProc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
    	int g_WindowWidth;
    	int g_WindowHeight;
    
    private:
    
    };
    
    const char g_szClassName [] = "My Window";
    
    // Zeiger erzeugen
    //
    CWindow Window;
    CWindow * pWindow = &Window;
    
    #endif
    
    // CreateWindow.cpp
    //
    // Die Funktionen und Variablen deklarieren
    //
    #include "CreateWindow.h"
    
    LRESULT CALLBACK WndProc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
    
    HWND CWindow::InitWindow (LPCTSTR WindowTitle, int XPos, int YPos, int WindowWidth, int WindowHeight)
    
    {
    
    	WNDCLASSEX wc;
    	HWND hwnd;
    
    	ZeroMemory (&wc, sizeof (WNDCLASSEX));
    
    	wc.cbSize = sizeof (WNDCLASSEX);
    	wc.style = CS_HREDRAW | CS_VREDRAW;
    	wc.lpfnWndProc = ::WndProc;
    	wc.cbClsExtra = 0;
    	wc.cbWndExtra = 0;
    	wc.hInstance = GetModuleHandle (NULL);
    	wc.hIcon = LoadIcon (NULL, IDI_APPLICATION);
    	wc.hCursor = LoadCursor (NULL, IDC_ARROW);
    	wc.hbrBackground = (HBRUSH)GetStockObject (BLACK_BRUSH);
    	wc.lpszMenuName = NULL;
    	wc.lpszClassName = g_szClassName;
    	wc.hIconSm = LoadIcon (NULL, IDI_APPLICATION);
    
    	RegisterClassEx (&wc);
    
    	hwnd = CreateWindowEx (NULL,
    						   g_szClassName,
    						   WindowTitle,
    						   WS_VISIBLE | WS_EX_TOPMOST | WS_POPUP,
    						   XPos, YPos,
    						   WindowWidth, WindowHeight,
    						   NULL,
    						   NULL, 
    						   GetModuleHandle(NULL),
    						   NULL);
    
    	g_WindowWidth = WindowWidth;
    	g_WindowHeight = WindowHeight;
    
    	return 0;
    }
    
    LRESULT CALLBACK CWindow::WndProc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
    {
    	switch (msg)
    	{
    		case WM_DESTROY:
    		{
    			PostQuitMessage (0);
    			return (0);
    		} break;
    	}
    
    	return Window.WndProc (hwnd, msg, wParam, lParam); 
    }
    

    Weisst du was ich hier falsch gemacht habe?



  • okay okay, ich hack das mal schnell bei mir ein und guck mal weiter.



  • Okay, danke



  • So funktioniert es bei mir einwandfrei:

    #include <windows.h>
    
    class CWindow {
    	public:
    		HWND InitWindow (LPCTSTR, int, int, int, int);
    		LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM);
    		int g_WindowWidth;
    		int g_WindowHeight;
    };
    
    const char g_szClassName [] = "My Window";
    
    CWindow Window;
    
    LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
    	return Window.WndProc(hwnd, msg, wparam, lparam);
    }
    
    HWND CWindow::InitWindow (LPCTSTR WindowTitle, int XPos, int YPos, int WindowWidth, int WindowHeight) {
        WNDCLASSEX wc;
        HWND hwnd;
    
        ZeroMemory (&wc, sizeof (WNDCLASSEX));
    
        wc.cbSize = sizeof (WNDCLASSEX);
        wc.style = CS_HREDRAW | CS_VREDRAW;
        wc.lpfnWndProc = ::WndProc;
        wc.cbClsExtra = 0;
        wc.cbWndExtra = 0;
        wc.hInstance = GetModuleHandle (NULL);
        wc.hIcon = LoadIcon (NULL, IDI_APPLICATION);
        wc.hCursor = LoadCursor (NULL, IDC_ARROW);
        wc.hbrBackground = (HBRUSH)GetStockObject (BLACK_BRUSH);
        wc.lpszMenuName = NULL;
        wc.lpszClassName = g_szClassName;
        wc.hIconSm = LoadIcon (NULL, IDI_APPLICATION);
    
        RegisterClassEx (&wc);
    
        hwnd = CreateWindowEx (0,
                               g_szClassName,
                               WindowTitle,
                               WS_VISIBLE | WS_EX_TOPMOST | WS_POPUP,
                               XPos, YPos,
                               WindowWidth, WindowHeight,
                               NULL,
                               NULL,
                               GetModuleHandle(NULL),
                               NULL);
    
        g_WindowWidth = WindowWidth;
        g_WindowHeight = WindowHeight;
    
        return 0;
    }
    
    LRESULT CALLBACK CWindow::WndProc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
    {
        switch (msg)
        {
            case WM_DESTROY:
            {
                PostQuitMessage (0);
                return (0);
            } break;
        }
    
        return DefWindowProc(hwnd, msg, wParam, lParam);
    }
    
    int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
    	Window.InitWindow("Test", 150, 150, 275, 275);
    
    	MSG msg;
    	while(GetMessage(&msg, NULL, 0, 0)) {
    		TranslateMessage(&msg);
    		DispatchMessage(&msg);
    	}
    
    	return 0;
    }
    

    EDIT: Achja nochwas. Ich wollte nicht pampig sein oder so wegen dem "Du solltest mal ein Buch lesen bzgl. der C/C++ Programmierung", aber eigentlich sollte es klar sein, dass man das

    return Window.WndProc(xxx);
    

    nicht einfach so übernehemn kann. Hat ja nichts speziell mit der WINAPI zu tun.



  • Bei mir gibt es jetzt noch folgende zwei Fehler:

    Kompilieren...
    CreateWindow.cpp
    Verknüpfen...
    MSVCRTD.lib(crtexew.obj) : error LNK2019: Verweis auf nicht aufgelöstes externes Symbol "_WinMain@16" in Funktion "___tmainCRTStartup".
    C:\Dokumente und Einstellungen\patrick\Eigene Dateien\Visual Studio 2008\Projects\MyEngine\Debug\MyEngine.exe : fatal error LNK1120: 1 nicht aufgelöste externe Verweise.
    Das Buildprotokoll wurde unter "file://c:\Dokumente und Einstellungen\patrick\Eigene Dateien\Visual Studio 2008\Projects\MyEngine\MyEngine\Debug\BuildLog.htm" gespeichert.
    MyEngine - 2 Fehler, 0 Warnung(en)
    ========== Erstellen: 0 erfolgreich, Fehler bei 1, 0 aktuell, 0 übersprungen ==========
    

    Ich habe dein Beispiel komplett übernommen, ausser das ich die Klasse in eine Header-Detei geschrieben habe. Was habe ich nun falsch, Projekteinstellungen?



  • Also das Program an sich sollte korrekt sein. Wahrscheinlich ist wirklich bei den Projekteinstellungen etwas noch falsch und/oder unvollständig.

    Hast du eingestellt, dass eine GUI-Anwendung erstellt werden soll (was aber eigentlich auch keine Probleme geben sollte, wenn man es nicht angibt, dann erscheint halt noch zusätzlich ein Konsolenfenster) und alle benötigten Libs eingebunden (User32.lib, Kernel32.lib oder was weiß ich alles).

    Ich selbst programmiere mit Codeblocks, von daher weiß ich nicht inwiefern man was bei deiner Entwicklungsumgebung beachten muss.



  • Mit welcher Entwicklungsumgebung programmierst du?
    Hab auf MSDN mal deinen Fehlercode eingegeben und da irgendwas mit MFC-Anwendung und Unicode gelesen.

    Kenn mich mit der MFC nicht sonderlich aus, aber da wird AFAIK schon CWindow definiert.

    Ich kann dir da leider nicht weiterhelfen, aber könntest du mal deine Klasse umbenennen, von CWindow in MyWindow oder sowas. Das ist mein letzter Strohhalm 🙂



  • int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
    ➡
    int WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow )
    Sollte es bringen 🙂



  • Ich programmiere unter Visual C++ Express 2008, WinAPI.

    Ich habe nun die Klasse umbenannt, auf MyWindow. Ich erstelle gar keine WinMain-Funktion, da ich dieses Programm als Engine verwende. Dann kann ich per #include die Dateien einlesen. Und dann erstelle ich eine WinMain-Funktion, daher konnte ich keine abstände machen zwieschen Klammer und HINSTANCE.

    Doch es gibt auch mit MyWindow noch die selben Fehler.

    Was habe ich denn falsch gemacht?



  • Okay, dann hast du ja eigentlich alles.

    Also in deiner CreateWindow.h sollte nun stehen:

    #ifndef CREATEWINDOW_H
    #define CREATEWINDOW_H
    
    #include <windows.h>
    
    class CWindow {
    	public:
    		HWND InitWindow (LPCTSTR, int, int, int, int);
    		LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM);
    		int g_WindowWidth;
    		int g_WindowHeight;
    };
    
    const char g_szClassName [] = "My Window";
    
    CWindow Window;
    
    LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
    
    #endif
    

    Und in deiner CreateWindow.cpp sollte stehen:

    #include "CreateWindow.h"
    
    HWND CWindow::InitWindow (LPCTSTR WindowTitle, int XPos, int YPos, int WindowWidth, int WindowHeight) {
        WNDCLASSEX wc;
        HWND hwnd;
    
        ZeroMemory (&wc, sizeof (WNDCLASSEX));
    
        wc.cbSize = sizeof (WNDCLASSEX);
        wc.style = CS_HREDRAW | CS_VREDRAW;
        wc.lpfnWndProc = ::WndProc;
        wc.cbClsExtra = 0;
        wc.cbWndExtra = 0;
        wc.hInstance = GetModuleHandle (NULL);
        wc.hIcon = LoadIcon (NULL, IDI_APPLICATION);
        wc.hCursor = LoadCursor (NULL, IDC_ARROW);
        wc.hbrBackground = (HBRUSH)GetStockObject (BLACK_BRUSH);
        wc.lpszMenuName = NULL;
        wc.lpszClassName = g_szClassName;
        wc.hIconSm = LoadIcon (NULL, IDI_APPLICATION);
    
        RegisterClassEx (&wc);
    
        hwnd = CreateWindowEx (0,
                               g_szClassName,
                               WindowTitle,
                               WS_VISIBLE | WS_EX_TOPMOST | WS_POPUP,
                               XPos, YPos,
                               WindowWidth, WindowHeight,
                               NULL,
                               NULL,
                               GetModuleHandle(NULL),
                               NULL);
    
        g_WindowWidth = WindowWidth;
        g_WindowHeight = WindowHeight;
    
        return 0;
    }
    
    LRESULT CALLBACK CWindow::WndProc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
    {
        switch (msg)
        {
            case WM_DESTROY:
            {
                PostQuitMessage (0);
                return (0);
            } break;
        }
    
        return DefWindowProc(hwnd, msg, wParam, lParam);
    }
    
    LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
        return Window.WndProc(hwnd, msg, wparam, lparam);
    }
    

    Das Umbenennen der Klasse von CWindow in MyWindow war höchstwahrscheinlich unnötig, sorry, aber wusste mir da grad nicht weiterzuhelfen 🙂

    Nun hast du ja alles und so sollte alles passen. Natürlich wird aus diesen zwei Dateien keine EXE und auch keine LIB erzeugt (solange du nicht in den Projekteinstellungen angegeben hast, eine LIB zu erzeugen).

    Nun kannst du den Header CreateWindow.h in deine Programme includen, die Quelldatei CreateWindow.cpp deinen Projekten hinzufügen und somit deine Klasse CWindow in deinen Programmen verwenden.

    So, das müsste es gewesen sein.



  • Ich habe nun alles so übernommen, aber es gibt immer noch die selben Fehler.

    Wie kann man denn in den Projekteinstellungen auswählen, dass eine Lib erstellt werden soll?



  • Hast du die zwei Dateien in einem anderen Projekt verwendet, oder versuchst du Createwindow.cpp allein zu kompilieren (zu einer static library, was ich vorher flax LIB genannt habe)?

    Im ersten Fall kann ich dir nicht helfen. Wenn du in deiner main.cpp (dort wo die WinMain-Funktion deines Projekts steht) die Createwindow.h included hast und Createwindow.cpp dem Projekt hinzugefügt hast und noch eventuell benötigte Bibliotheken wie user32.lib und kernel32.lib dem Projekt mitgeteilt hast und du trotzdem noch Fehlermeldungen bekommst kann ich dir nicht helfen. Eigentlich müsste das funzen.

    Zweiter Fall: Du hast also ein Projekt erstellt, in dem nur die beiden Dateien Createwindow.h und Createwindow.cpp vorhanden sind. Daraus möchtest du wohl jetzt eine static library machen? Dafür musst du in deinen Projekteinstellungen nachsehen, "zu was dein Projekt kompiliert werden soll". In Codeblocks (der Entwicklungsumgebung, die ich verwende), sieht man beispielsweise ein Auswahlfeld, aus dem man wählen kann, ob man das Projekt zu einer

    -Konsolenanwendung
    -GUI-Anwendung
    -DLL
    -static library
    -nativ irgendwas
    -nur commands irgendwas

    kompilieren lassen möchte. Dort müsste man static library auswählen. Dann würde aus Createwindow.cpp (mit inkludierter Createwindow.h) nämlich die static library "libCreatewindow.a" kompiliert.
    Ich weiß nicht, wie das bei VC aussieht, aber da wird es wahrscheinlich eine ähnliche Einstellungsmöglichkeit geben, musst du halt mal gucken.

    Ich hoffe ich konnte dir ein bisschen weiterhelfen.



  • Nun habe ich eine static libary Anwendung gemacht. Jetzt gibt es keine Fehler mehr. Doch wenn ich nun diese Datei mit #include in einem anderen Projekt einlese, so kommen dort solche zwei Fehler:

    ------ Erstellen gestartet: Projekt: Engine Test, Konfiguration: Debug Win32 ------
    Verknüpfen...
    main.obj : error LNK2019: Verweis auf nicht aufgelöstes externes Symbol ""public: struct HWND__ * __thiscall CWindow::InitWindow(char const *,int,int,int,int)" (?InitWindow@CWindow@@QAEPAUHWND__@@PBDHHHH@Z)" in Funktion "_WinMain@16".
    C:\Dokumente und Einstellungen\patrick\Eigene Dateien\Visual Studio 2008\Projects\Engine Test\Debug\Engine Test.exe : fatal error LNK1120: 1 nicht aufgelöste externe Verweise.
    Das Buildprotokoll wurde unter "file://c:\Dokumente und Einstellungen\patrick\Eigene Dateien\Visual Studio 2008\Projects\Engine Test\Engine Test\Debug\BuildLog.htm" gespeichert.
    Engine Test - 2 Fehler, 0 Warnung(en)
    ========== Erstellen: 0 erfolgreich, Fehler bei 1, 0 aktuell, 0 übersprungen ==========
    

    Weiss jemand warum?



  • Du musst Createwindow.h einbinden und libCreatewindow.a in den Linkeroptionen angeben. Also:

    In deiner main.cpp des neuen Projekts:

    #include "Createwindow.h"
    

    und in deinen Projekteinstellungen muss du unter den Linkeroptionen die Datei "libCreatewindow.a" (die von dir erstellte static library heißt doch so oder?) angeben.



  • Also, ich bin jetzt gerade nicht sicher, denn ich finde keine Datei mit .a Ich habe eine Ordner unter meinem Projektordner namens Debug. In diesem Ordner ist eine Datei namens MyEngine.lib (Object File Libary Datei). Und sonst sehe ich leider keine weitere Datein die mit .a enden.


Anmelden zum Antworten