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



  • Hallo zusammen

    Kann mir jemand sagen, wie ich die Callback-Funktion in einer Klasse bekannt machen kann? Ich habe es versucht mit:

    wc.lpfnWndProc = WndProc;
    

    Doch das gibt bei mir immer folgende zwei Fehler:

    ------ Erstellen gestartet: Projekt: MyEngine, Konfiguration: Debug Win32 ------
    Kompilieren...
    CreateWindow.cpp
    c:\dokumente und einstellungen\patrick\eigene dateien\visual studio 2008\projects\myengine\myengine\createwindow.cpp(15) : error C3867: "CWindow::WndProc": Dem Funktionsaufruf fehlt die Argumentliste. Verwenden Sie "&CWindow::WndProc", um einen Zeiger auf den Member zu erstellen.
    c:\dokumente und einstellungen\patrick\eigene dateien\visual studio 2008\projects\myengine\myengine\createwindow.cpp(15) : error C2440: '=': 'LRESULT (__stdcall CWindow::* )(HWND,UINT,WPARAM,LPARAM)' kann nicht in 'WNDPROC' konvertiert werden
            Es gibt keinen Kontext, in dem diese Konvertierung möglich ist
    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 ==========
    

    Hier ist der vollständige Code:

    // CreateWindow.h
    //
    // Die Funktionen und Variabeln definieren
    //
    #include <windows.h>
    
    // Klasse erzeugen
    //
    class CWindow
    {
    public:
    
    	HWND CreateWindowME (HINSTANCE hInstance, LPCSTR WindowTitle);
    	LRESULT WINAPI WndProc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
    
    };
    
    // Zeiger erzeugen
    //
    CWindow Window;
    CWindow * pWindow = &Window;
    
    // CreateWindow.cpp
    //
    // Die Funktionen und Variabeln deklarieren
    //
    #include "CreateWindow.h"
    
    HWND CWindow::CreateWindowME (HINSTANCE hInstance, LPCSTR WindowTitle)
    {
    	WNDCLASSEX wc;
    
    	wc.cbSize = sizeof (WNDCLASSEX);
    	wc.style = CS_HREDRAW | CS_VREDRAW;
    	wc.hInstance = hInstance;
    	wc.lpfnWndProc = WndProc;
    	wc.cbWndExtra = 0;
    	wc.cbClsExtra = 0;
    	wc.lpszMenuName = NULL;
    	wc.lpszClassName = "My Engine";
    	wc.hbrBackground = (HBRUSH) GetStockObject (BLACK_BRUSH);
    	wc.hIcon = LoadIcon (NULL, IDI_APPLICATION);
    	wc.hCursor = LoadCursor (NULL, IDC_ARROW);
    	wc.hIconSm = LoadIcon (NULL, IDI_APPLICATION);
    
    	if (!RegisterClassEx (&wc))
    	{
    		MessageBox (NULL, "Fehler beim registrieren des Fensters!", "Fehler aufgetreten",
    					MB_OK | MB_ICONEXCLAMATION);
    
    		return (0);
    	}
    
    	return (CreateWindowEx (NULL,
    							"My Engine",
    							WindowTitle,
    							WS_OVERLAPPEDWINDOW | WS_VISIBLE,
    							100, 100,
    							500, 500,
    							NULL,
    							NULL,
    							hInstance,
    							NULL));
    }
    
    LRESULT WINAPI CWindow::WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
    {
    	switch (msg)
    	{
    		case WM_CREATE:
    		{
    			return (0);
    		} break;
    
    		case WM_DESTROY:
    		{
    	             PostQuitMessage(0);
    				 return (0);
    		} break;
    
    		default:
    		{
    		}break;
    	}
    
    	return DefWindowProc (hwnd, msg, wParam, lParam);
    }
    

    Weiss jemand, was ich falsch gemacht habe?

    Ich freue mich auf eure Antworten!

    Gruss Patrick



  • wc.lpfnWndProc = ::WndProc;
    
    LRESULT WINAPI WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) 
    {
    	return Window.WndProc(xxx);
    }
    


  • Das geht ja gar nicht, denn ich habe bei WndProc schon ein Rückgabewert, nämlich:

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


  • schonmal was vom this aka noob-zeiger gehört?



  • Mit dem this-Zeiger kommen immer noch Fehler!



  • orz



  • Kann mir jemand eine richtige Antwort geben, evtl. mit Codebeispiel.



  • // TestWin.cpp : Defines the entry point for the application.
    //
    
    #include "stdafx.h"
    #include "resource.h"
    
    #define MAX_LOADSTRING 100
    
    // Global Variables:
    HINSTANCE hInst;								// current instance
    TCHAR szTitle[MAX_LOADSTRING];								// The title bar text
    TCHAR szWindowClass[MAX_LOADSTRING];								// The title bar text
    
    // Foward declarations of functions included in this code module:
    ATOM				MyRegisterClass(HINSTANCE hInstance);
    BOOL				InitInstance(HINSTANCE, int);
    LRESULT CALLBACK	WndProc(HWND, UINT, WPARAM, LPARAM);
    LRESULT CALLBACK	About(HWND, UINT, WPARAM, LPARAM);
    
    int APIENTRY WinMain(HINSTANCE hInstance,
                         HINSTANCE hPrevInstance,
                         LPSTR     lpCmdLine,
                         int       nCmdShow)
    {
     	// TODO: Place code here.
    	MSG msg;
    	HACCEL hAccelTable;
    
    	// Initialize global strings
    	LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
    	LoadString(hInstance, IDC_TESTWIN, szWindowClass, MAX_LOADSTRING);
    	MyRegisterClass(hInstance);
    
    	// Perform application initialization:
    	if (!InitInstance (hInstance, nCmdShow)) 
    	{
    		return FALSE;
    	}
    
    	hAccelTable = LoadAccelerators(hInstance, (LPCTSTR)IDC_TESTWIN);
    
    	// Main message loop:
    	while (GetMessage(&msg, NULL, 0, 0)) 
    	{
    		if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg)) 
    		{
    			TranslateMessage(&msg);
    			DispatchMessage(&msg);
    		}
    	}
    
    	return msg.wParam;
    }
    
    //
    //  FUNCTION: MyRegisterClass()
    //
    //  PURPOSE: Registers the window class.
    //
    //  COMMENTS:
    //
    //    This function and its usage is only necessary if you want this code
    //    to be compatible with Win32 systems prior to the 'RegisterClassEx'
    //    function that was added to Windows 95. It is important to call this function
    //    so that the application will get 'well formed' small icons associated
    //    with it.
    //
    ATOM MyRegisterClass(HINSTANCE hInstance)
    {
    	WNDCLASSEX wcex;
    
    	wcex.cbSize = sizeof(WNDCLASSEX); 
    
    	wcex.style			= CS_HREDRAW | CS_VREDRAW;
    	wcex.lpfnWndProc	= (WNDPROC)WndProc;
    	wcex.cbClsExtra		= 0;
    	wcex.cbWndExtra		= 0;
    	wcex.hInstance		= hInstance;
    	wcex.hIcon			= LoadIcon(hInstance, (LPCTSTR)IDI_TESTWIN);
    	wcex.hCursor		= LoadCursor(NULL, IDC_ARROW);
    	wcex.hbrBackground	= (HBRUSH)(COLOR_WINDOW+1);
    	wcex.lpszMenuName	= (LPCSTR)IDC_TESTWIN;
    	wcex.lpszClassName	= szWindowClass;
    	wcex.hIconSm		= LoadIcon(wcex.hInstance, (LPCTSTR)IDI_SMALL);
    
    	return RegisterClassEx(&wcex);
    }
    
    //
    //   FUNCTION: InitInstance(HANDLE, int)
    //
    //   PURPOSE: Saves instance handle and creates main window
    //
    //   COMMENTS:
    //
    //        In this function, we save the instance handle in a global variable and
    //        create and display the main program window.
    //
    BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
    {
       HWND hWnd;
    
       hInst = hInstance; // Store instance handle in our global variable
    
       hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
          CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);
    
       if (!hWnd)
       {
          return FALSE;
       }
    
       ShowWindow(hWnd, nCmdShow);
       UpdateWindow(hWnd);
    
       return TRUE;
    }
    
    //
    //  FUNCTION: WndProc(HWND, unsigned, WORD, LONG)
    //
    //  PURPOSE:  Processes messages for the main window.
    //
    //  WM_COMMAND	- process the application menu
    //  WM_PAINT	- Paint the main window
    //  WM_DESTROY	- post a quit message and return
    //
    //
    LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
    {
    	int wmId, wmEvent;
    	PAINTSTRUCT ps;
    	HDC hdc;
    	TCHAR szHello[MAX_LOADSTRING];
    	LoadString(hInst, IDS_HELLO, szHello, MAX_LOADSTRING);
    
    	switch (message) 
    	{
    		case WM_COMMAND:
    			wmId    = LOWORD(wParam); 
    			wmEvent = HIWORD(wParam); 
    			// Parse the menu selections:
    			switch (wmId)
    			{
    				case IDM_\1:
    				   DialogBox(hInst, (LPCTSTR)IDD_ABOUTBOX, hWnd, (DLGPROC)About);
    				   break;
    				case IDM_EXIT:
    				   DestroyWindow(hWnd);
    				   break;
    				default:
    				   return DefWindowProc(hWnd, message, wParam, lParam);
    			}
    			break;
    		case WM_PAINT:
    			hdc = BeginPaint(hWnd, &ps);
    			// TODO: Add any drawing code here...
    			RECT rt;
    			GetClientRect(hWnd, &rt);
    			DrawText(hdc, szHello, strlen(szHello), &rt, DT_CENTER);
    			EndPaint(hWnd, &ps);
    			break;
    		case WM_DESTROY:
    			PostQuitMessage(0);
    			break;
    		default:
    			return DefWindowProc(hWnd, message, wParam, lParam);
       }
       return 0;
    }
    
    // Mesage handler for about box.
    LRESULT CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
    {
    	switch (message)
    	{
    		case WM_INITDIALOG:
    				return TRUE;
    
    		case WM_COMMAND:
    			if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL) 
    			{
    				EndDialog(hDlg, LOWORD(wParam));
    				return TRUE;
    			}
    			break;
    	}
        return FALSE;
    }
    


  • Du arbeitest in diesem Codebeispiel gar nicht mit Klassen, und was ich wissen will, ist, wie man die Callback-Funktion in einer Klasse bekannt machen kann.



  • 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.


Anmelden zum Antworten